#include "minecraft/GameHostOptions.h" #include "app/common/Game.h" #include "platform/PlatformTypes.h" #include "platform/InputActions.h" #include "platform/sdl2/Profile.h" #include "platform/sdl2/Render.h" #include "platform/sdl2/Storage.h" #include "app/common/App_Defines.h" #include "app/common/App_enums.h" #include "app/common/App_structs.h" #include "app/common/Console_Debug_enum.h" #include "app/common/DLC/DLCManager.h" #include "app/common/DLC/DLCSkinFile.h" #include "app/common/GameRules/GameRuleManager.h" #include "app/common/Network/GameNetworkManager.h" #include "app/common/Network/NetworkPlayerInterface.h" #include "app/common/Tutorial/Tutorial.h" #include "app/common/UI/All Platforms/UIEnums.h" #include "app/common/UI/All Platforms/UIStructs.h" #include "app/common/UI/Scenes/UIScene_FullscreenProgress.h" #include "app/linux/LinuxGame.h" #include "app/linux/Linux_UIController.h" #include "app/linux/Stubs/winapi_stubs.h" #include "platform/NetTypes.h" #include "minecraft/client/SkinBox.h" #include "platform/XboxStubs.h" #include "platform/PlatformServices.h" #include "java/Class.h" #include "java/File.h" #include "java/Random.h" #include "minecraft/client/Minecraft.h" #include "minecraft/client/Options.h" #include "minecraft/client/ProgressRenderer.h" #include "minecraft/client/model/geom/Model.h" #include "minecraft/client/multiplayer/ClientConnection.h" #include "minecraft/client/multiplayer/MultiPlayerGameMode.h" #include "minecraft/client/multiplayer/MultiPlayerLevel.h" #include "minecraft/client/multiplayer/MultiPlayerLocalPlayer.h" #include "minecraft/client/renderer/GameRenderer.h" #include "minecraft/client/renderer/Textures.h" #include "minecraft/client/renderer/entity/EntityRenderer.h" #include "minecraft/client/skins/TexturePack.h" #include "minecraft/network/packet/DisconnectPacket.h" #include "minecraft/server/MinecraftServer.h" #include "minecraft/stats/StatsCounter.h" #include "minecraft/world/Container.h" #include "minecraft/world/entity/item/MinecartHopper.h" #include "minecraft/world/entity/player/Player.h" #include "minecraft/world/item/crafting/Recipy.h" #include "minecraft/world/level/tile/Tile.h" #include "minecraft/world/level/tile/entity/HopperTileEntity.h" #include "strings.h" #if defined(_WINDOWS64) #include "app/windows/XML/ATGXmlParser.h" #include "app/windows/XML/xmlFilesCallback.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "platform/sdl2/Input.h" #include "app/common/Audio/SoundEngine.h" #include "app/common/Colours/ColourTable.h" #include "app/common/DLC/DLCPack.h" #include "app/common/Localisation/StringTable.h" #include "app/common/UI/All Platforms/ArchiveFile.h" #include "app/common/UI/Scenes/In-Game Menu Screens/UIScene_PauseMenu.h" #include "Minecraft_Macros.h" #include "util/Timer.h" #include "util/StringHelpers.h" #include "minecraft/world/level/storage/ConsoleSaveFileIO/compression.h" #include "minecraft/client/User.h" #include "minecraft/client/gui/Gui.h" #include "minecraft/client/renderer/entity/EntityRenderDispatcher.h" #include "minecraft/client/skins/DLCTexturePack.h" #include "minecraft/client/skins/TexturePackRepository.h" #include "minecraft/server/PlayerList.h" #include "minecraft/server/level/ServerPlayer.h" class BeaconTileEntity; class BrewingStandTileEntity; class DispenserTileEntity; class EntityHorse; class FurnaceTileEntity; class INVITE_INFO; class Inventory; class Level; class LevelChunk; class LevelGenerationOptions; class LocalPlayer; class Merchant; class ModelPart; class SignTileEntity; // Game app; unsigned int Game::m_uiLastSignInData = 0; const float Game::fSafeZoneX = 64.0f; // 5% of 1280 const float Game::fSafeZoneY = 36.0f; // 5% of 720 Game::Game() { GameHostOptions::init(&m_uiGameHostSettings); if (GAME_SETTINGS_PROFILE_DATA_BYTES != sizeof(GAME_SETTINGS)) { // 4J Stu - See comment for GAME_SETTINGS_PROFILE_DATA_BYTES in // Xbox_App.h DebugPrintf( "WARNING: The size of the profile GAME_SETTINGS struct has " "changed, so all stat data is likely incorrect. Is: %d, Should be: " "%d\n", sizeof(GAME_SETTINGS), GAME_SETTINGS_PROFILE_DATA_BYTES); #if !defined(_CONTENT_PACKAGE) __debugbreak(); #endif } for (int i = 0; i < XUSER_MAX_COUNT; i++) { m_eTMSAction[i] = eTMSAction_Idle; m_eXuiAction[i] = eAppAction_Idle; m_eXuiActionParam[i] = nullptr; // m_dwAdditionalModelParts[i] = 0; if (FAILED(XUserGetSigninInfo(i, XUSER_GET_SIGNIN_INFO_OFFLINE_XUID_ONLY, &m_currentSigninInfo[i]))) { m_currentSigninInfo[i].xuid = INVALID_XUID; m_currentSigninInfo[i].dwGuestNumber = 0; } DebugPrintf("Player at index %d has guest number %d\n", i, m_currentSigninInfo[i].dwGuestNumber); m_uiOpacityCountDown[i] = 0; } m_eGlobalXuiAction = eAppAction_Idle; m_eGlobalXuiServerAction = eXuiServerAction_Idle; m_bResourcesLoaded = false; m_bGameStarted = false; m_bIsAppPaused = false; // m_bSplitScreenEnabled = false; m_bIntroRunning = false; m_eGameMode = eMode_Singleplayer; // m_bInterfaceRenderingOff = false; // m_bHandRenderingOff = false; m_bTutorialMode = false; m_disconnectReason = DisconnectPacket::eDisconnect_None; m_bLiveLinkRequired = false; m_bChangingSessionType = false; m_bReallyChangingSessionType = false; // memset(m_PreviewBuffer, 0, sizeof(XSOCIAL_PREVIEWIMAGE)*XUSER_MAX_COUNT); // m_xuidNotch moved to SkinManager memset(&m_InviteData, 0, sizeof(JoinFromInviteData)); // m_bRead_TMS_XUIDS_XML=false; // m_bRead_TMS_DLCINFO_XML=false; m_pDLCFileBuffer = nullptr; m_dwDLCFileSize = 0; m_bDefaultCapeInstallAttempted = false; m_bDLCInstallProcessCompleted = false; m_bDLCInstallPending = false; m_iTotalDLC = 0; m_iTotalDLCInstalled = 0; mfTrialPausedTime = 0.0f; m_bNewDLCAvailable = false; m_bSeenNewDLCTip = false; m_uiGameHostSettings = 0; #if defined(_LARGE_WORLDS) m_GameNewWorldSize = 0; m_bGameNewWorldSizeUseMoat = false; m_GameNewHellScale = 0; #endif memset(m_playerColours, 0, MINECRAFT_NET_MAX_PLAYERS); m_iDLCOfferC = 0; m_bAllDLCContentRetrieved = true; m_bAllTMSContentRetrieved = true; m_bTickTMSDLCFiles = true; m_bResetNether = false; LocaleAndLanguageInit(); } void Game::DebugPrintf(const char* szFormat, ...) { #if !defined(_FINAL_BUILD) char buf[1024]; va_list ap; va_start(ap, szFormat); vsnprintf(buf, sizeof(buf), szFormat, ap); va_end(ap); OutputDebugStringA(buf); #endif } void Game::DebugPrintf(int user, const char* szFormat, ...) { #if !defined(_FINAL_BUILD) if (user == USER_NONE) return; char buf[1024]; va_list ap; va_start(ap, szFormat); vsnprintf(buf, sizeof(buf), szFormat, ap); va_end(ap); OutputDebugStringA(buf); if (user == USER_UI) { ui.logDebugString(buf); } #endif } const wchar_t* Game::GetString(int iID) { // return L"Değişiklikler ve Yenilikler"; // return L"ÕÕÕÕÖÖÖÖ"; return app.m_localizationManager.getString(iID); } void Game::SetAction(int iPad, eXuiAction action, void* param) { if ((m_eXuiAction[iPad] == eAppAction_ReloadTexturePack) && (action == eAppAction_EthernetDisconnected)) { app.DebugPrintf( "Invalid change of App action for pad %d from %d to %d, ignoring\n", iPad, m_eXuiAction[iPad], action); } else if ((m_eXuiAction[iPad] == eAppAction_ReloadTexturePack) && (action == eAppAction_ExitWorld)) { app.DebugPrintf( "Invalid change of App action for pad %d from %d to %d, ignoring\n", iPad, m_eXuiAction[iPad], action); } else if (m_eXuiAction[iPad] == eAppAction_ExitWorldCapturedThumbnail && action != eAppAction_Idle) { app.DebugPrintf( "Invalid change of App action for pad %d from %d to %d, ignoring\n", iPad, m_eXuiAction[iPad], action); } else { app.DebugPrintf("Changing App action for pad %d from %d to %d\n", iPad, m_eXuiAction[iPad], action); m_eXuiAction[iPad] = action; m_eXuiActionParam[iPad] = param; } } bool Game::IsAppPaused() { return m_bIsAppPaused; } void Game::SetAppPaused(bool val) { m_bIsAppPaused = val; } void Game::HandleButtonPresses() { for (int i = 0; i < 4; i++) { HandleButtonPresses(i); } } void Game::HandleButtonPresses(int iPad) { // // test an update of the profile data // void *pData=ProfileManager.GetGameDefinedProfileData(iPad); // // unsigned char *pchData= (unsigned char *)pData; // int iCount=0; // for(int i=0;i player, bool bNavigateBack) { bool success = true; InventoryScreenInput* initData = new InventoryScreenInput(); initData->player = player; initData->bNavigateBack = bNavigateBack; initData->iPad = iPad; if (app.GetLocalPlayerCount() > 1) { initData->bSplitscreen = true; success = ui.NavigateToScene(iPad, eUIScene_InventoryMenu, initData); } else { initData->bSplitscreen = false; success = ui.NavigateToScene(iPad, eUIScene_InventoryMenu, initData); } return success; } bool Game::LoadCreativeMenu(int iPad, std::shared_ptr player, bool bNavigateBack) { bool success = true; InventoryScreenInput* initData = new InventoryScreenInput(); initData->player = player; initData->bNavigateBack = bNavigateBack; initData->iPad = iPad; if (app.GetLocalPlayerCount() > 1) { initData->bSplitscreen = true; success = ui.NavigateToScene(iPad, eUIScene_CreativeMenu, initData); } else { initData->bSplitscreen = false; success = ui.NavigateToScene(iPad, eUIScene_CreativeMenu, initData); } return success; } bool Game::LoadCrafting2x2Menu(int iPad, std::shared_ptr player) { bool success = true; CraftingPanelScreenInput* initData = new CraftingPanelScreenInput(); initData->player = player; initData->iContainerType = RECIPE_TYPE_2x2; initData->iPad = iPad; initData->x = 0; initData->y = 0; initData->z = 0; if (app.GetLocalPlayerCount() > 1) { initData->bSplitscreen = true; success = ui.NavigateToScene(iPad, eUIScene_Crafting2x2Menu, initData); } else { initData->bSplitscreen = false; success = ui.NavigateToScene(iPad, eUIScene_Crafting2x2Menu, initData); } return success; } bool Game::LoadCrafting3x3Menu(int iPad, std::shared_ptr player, int x, int y, int z) { bool success = true; CraftingPanelScreenInput* initData = new CraftingPanelScreenInput(); initData->player = player; initData->iContainerType = RECIPE_TYPE_3x3; initData->iPad = iPad; initData->x = x; initData->y = y; initData->z = z; if (app.GetLocalPlayerCount() > 1) { initData->bSplitscreen = true; success = ui.NavigateToScene(iPad, eUIScene_Crafting3x3Menu, initData); } else { initData->bSplitscreen = false; success = ui.NavigateToScene(iPad, eUIScene_Crafting3x3Menu, initData); } return success; } bool Game::LoadFireworksMenu(int iPad, std::shared_ptr player, int x, int y, int z) { bool success = true; FireworksScreenInput* initData = new FireworksScreenInput(); initData->player = player; initData->iPad = iPad; initData->x = x; initData->y = y; initData->z = z; if (app.GetLocalPlayerCount() > 1) { initData->bSplitscreen = true; success = ui.NavigateToScene(iPad, eUIScene_FireworksMenu, initData); } else { initData->bSplitscreen = false; success = ui.NavigateToScene(iPad, eUIScene_FireworksMenu, initData); } return success; } bool Game::LoadEnchantingMenu(int iPad, std::shared_ptr inventory, int x, int y, int z, Level* level, const std::wstring& name) { bool success = true; EnchantingScreenInput* initData = new EnchantingScreenInput(); initData->inventory = inventory; initData->level = level; initData->x = x; initData->y = y; initData->z = z; initData->iPad = iPad; initData->name = name; if (app.GetLocalPlayerCount() > 1) { initData->bSplitscreen = true; success = ui.NavigateToScene(iPad, eUIScene_EnchantingMenu, initData); } else { initData->bSplitscreen = false; success = ui.NavigateToScene(iPad, eUIScene_EnchantingMenu, initData); } return success; } bool Game::LoadFurnaceMenu( int iPad, std::shared_ptr inventory, std::shared_ptr furnace) { bool success = true; FurnaceScreenInput* initData = new FurnaceScreenInput(); initData->furnace = furnace; initData->inventory = inventory; initData->iPad = iPad; // Load the scene. if (app.GetLocalPlayerCount() > 1) { initData->bSplitscreen = true; success = ui.NavigateToScene(iPad, eUIScene_FurnaceMenu, initData); } else { initData->bSplitscreen = false; success = ui.NavigateToScene(iPad, eUIScene_FurnaceMenu, initData); } return success; } bool Game::LoadBrewingStandMenu( int iPad, std::shared_ptr inventory, std::shared_ptr brewingStand) { bool success = true; BrewingScreenInput* initData = new BrewingScreenInput(); initData->brewingStand = brewingStand; initData->inventory = inventory; initData->iPad = iPad; // Load the scene. if (app.GetLocalPlayerCount() > 1) { initData->bSplitscreen = true; success = ui.NavigateToScene(iPad, eUIScene_BrewingStandMenu, initData); } else { initData->bSplitscreen = false; success = ui.NavigateToScene(iPad, eUIScene_BrewingStandMenu, initData); } return success; } bool Game::LoadContainerMenu(int iPad, std::shared_ptr inventory, std::shared_ptr container) { bool success = true; ContainerScreenInput* initData = new ContainerScreenInput(); initData->inventory = inventory; initData->container = container; initData->iPad = iPad; // Load the scene. if (app.GetLocalPlayerCount() > 1) { initData->bSplitscreen = true; bool bLargeChest = (initData->container->getContainerSize() > 3 * 9) ? true : false; if (bLargeChest) { success = ui.NavigateToScene(iPad, eUIScene_LargeContainerMenu, initData); } else { success = ui.NavigateToScene(iPad, eUIScene_ContainerMenu, initData); } } else { initData->bSplitscreen = false; success = ui.NavigateToScene(iPad, eUIScene_ContainerMenu, initData); } return success; } bool Game::LoadTrapMenu(int iPad, std::shared_ptr inventory, std::shared_ptr trap) { bool success = true; TrapScreenInput* initData = new TrapScreenInput(); initData->inventory = inventory; initData->trap = trap; initData->iPad = iPad; // Load the scene. if (app.GetLocalPlayerCount() > 1) { initData->bSplitscreen = true; success = ui.NavigateToScene(iPad, eUIScene_DispenserMenu, initData); } else { initData->bSplitscreen = false; success = ui.NavigateToScene(iPad, eUIScene_DispenserMenu, initData); } return success; } bool Game::LoadSignEntryMenu(int iPad, std::shared_ptr sign) { bool success = true; SignEntryScreenInput* initData = new SignEntryScreenInput(); initData->sign = sign; initData->iPad = iPad; success = ui.NavigateToScene(iPad, eUIScene_SignEntryMenu, initData); delete initData; return success; } bool Game::LoadRepairingMenu(int iPad, std::shared_ptr inventory, Level* level, int x, int y, int z) { bool success = true; AnvilScreenInput* initData = new AnvilScreenInput(); initData->inventory = inventory; initData->level = level; initData->x = x; initData->y = y; initData->z = z; initData->iPad = iPad; if (app.GetLocalPlayerCount() > 1) initData->bSplitscreen = true; else initData->bSplitscreen = false; success = ui.NavigateToScene(iPad, eUIScene_AnvilMenu, initData); return success; } bool Game::LoadTradingMenu(int iPad, std::shared_ptr inventory, std::shared_ptr trader, Level* level, const std::wstring& name) { bool success = true; TradingScreenInput* initData = new TradingScreenInput(); initData->inventory = inventory; initData->trader = trader; initData->level = level; initData->iPad = iPad; if (app.GetLocalPlayerCount() > 1) initData->bSplitscreen = true; else initData->bSplitscreen = false; success = ui.NavigateToScene(iPad, eUIScene_TradingMenu, initData); return success; } bool Game::LoadHopperMenu(int iPad, std::shared_ptr inventory, std::shared_ptr hopper) { bool success = true; HopperScreenInput* initData = new HopperScreenInput(); initData->inventory = inventory; initData->hopper = hopper; initData->iPad = iPad; if (app.GetLocalPlayerCount() > 1) initData->bSplitscreen = true; else initData->bSplitscreen = false; success = ui.NavigateToScene(iPad, eUIScene_HopperMenu, initData); return success; } bool Game::LoadHopperMenu(int iPad, std::shared_ptr inventory, std::shared_ptr hopper) { bool success = true; HopperScreenInput* initData = new HopperScreenInput(); initData->inventory = inventory; initData->hopper = std::dynamic_pointer_cast(hopper); initData->iPad = iPad; if (app.GetLocalPlayerCount() > 1) initData->bSplitscreen = true; else initData->bSplitscreen = false; success = ui.NavigateToScene(iPad, eUIScene_HopperMenu, initData); return success; } bool Game::LoadHorseMenu(int iPad, std::shared_ptr inventory, std::shared_ptr container, std::shared_ptr horse) { bool success = true; HorseScreenInput* initData = new HorseScreenInput(); initData->inventory = inventory; initData->container = container; initData->horse = horse; initData->iPad = iPad; if (app.GetLocalPlayerCount() > 1) initData->bSplitscreen = true; else initData->bSplitscreen = false; success = ui.NavigateToScene(iPad, eUIScene_HorseMenu, initData); return success; } bool Game::LoadBeaconMenu(int iPad, std::shared_ptr inventory, std::shared_ptr beacon) { bool success = true; BeaconScreenInput* initData = new BeaconScreenInput(); initData->inventory = inventory; initData->beacon = beacon; initData->iPad = iPad; if (app.GetLocalPlayerCount() > 1) initData->bSplitscreen = true; else initData->bSplitscreen = false; success = ui.NavigateToScene(iPad, eUIScene_BeaconMenu, initData); return success; } ////////////////////////////////////////////// // GAME SETTINGS ////////////////////////////////////////////// void Game::InitGameSettings() { for (int i = 0; i < XUSER_MAX_COUNT; i++) { GameSettingsA[i] = (GAME_SETTINGS*)ProfileManager.GetGameDefinedProfileData(i); // clear the flag to say the settings have changed GameSettingsA[i]->bSettingsChanged = false; // SetDefaultGameSettings(i); - done on a callback from the profile // manager // 4J-PB - adding in for Windows & PS3 to set the defaults for the // joypad #if defined(_WINDOWS64) C_4JProfile::PROFILESETTINGS* pProfileSettings = ProfileManager.GetDashboardProfileSettings(i); // clear this for now - it will come from reading the system values memset(pProfileSettings, 0, sizeof(C_4JProfile::PROFILESETTINGS)); SetDefaultOptions(pProfileSettings, i); #else // 4jcrqaft: Linux (and any other platform): profile data is // zero-initialised, so explicitly apply defaults C_4JProfile::PROFILESETTINGS* pProfileSettings = ProfileManager.GetDashboardProfileSettings(i); memset(pProfileSettings, 0, sizeof(C_4JProfile::PROFILESETTINGS)); SetDefaultOptions(pProfileSettings, i); #endif } } int Game::SetDefaultOptions(C_4JProfile::PROFILESETTINGS* pSettings, const int iPad) { SetGameSettings(iPad, eGameSetting_MusicVolume, DEFAULT_VOLUME_LEVEL); SetGameSettings(iPad, eGameSetting_SoundFXVolume, DEFAULT_VOLUME_LEVEL); SetGameSettings(iPad, eGameSetting_Gamma, 50); // 4J-PB - Don't reset the difficult level if we're in-game if (Minecraft::GetInstance()->level == nullptr) { app.DebugPrintf("SetDefaultOptions - Difficulty = 1\n"); SetGameSettings(iPad, eGameSetting_Difficulty, 1); } SetGameSettings(iPad, eGameSetting_Sensitivity_InGame, 100); SetGameSettings(iPad, eGameSetting_ViewBob, 1); SetGameSettings(iPad, eGameSetting_ControlScheme, 0); SetGameSettings(iPad, eGameSetting_ControlInvertLook, (pSettings->iYAxisInversion != 0) ? 1 : 0); SetGameSettings(iPad, eGameSetting_ControlSouthPaw, pSettings->bSwapSticks ? 1 : 0); SetGameSettings(iPad, eGameSetting_SplitScreenVertical, 0); SetGameSettings(iPad, eGameSetting_GamertagsVisible, 1); // Interim TU 1.6.6 SetGameSettings(iPad, eGameSetting_Sensitivity_InMenu, 100); SetGameSettings(iPad, eGameSetting_DisplaySplitscreenGamertags, 1); SetGameSettings(iPad, eGameSetting_Hints, 1); SetGameSettings(iPad, eGameSetting_Autosave, 2); SetGameSettings(iPad, eGameSetting_Tooltips, 1); SetGameSettings(iPad, eGameSetting_InterfaceOpacity, 80); // TU 5 SetGameSettings(iPad, eGameSetting_Clouds, 1); SetGameSettings(iPad, eGameSetting_Online, 1); SetGameSettings(iPad, eGameSetting_InviteOnly, 0); SetGameSettings(iPad, eGameSetting_FriendsOfFriends, 1); // default the update changes message to zero // 4J-PB - We'll only display the message if the profile is pre-TU5 // SetGameSettings(iPad,eGameSetting_DisplayUpdateMessage,0); // TU 6 SetGameSettings(iPad, eGameSetting_BedrockFog, 0); SetGameSettings(iPad, eGameSetting_DisplayHUD, 1); SetGameSettings(iPad, eGameSetting_DisplayHand, 1); // TU 7 SetGameSettings(iPad, eGameSetting_CustomSkinAnim, 1); // TU 9 SetGameSettings(iPad, eGameSetting_DeathMessages, 1); SetGameSettings(iPad, eGameSetting_UISize, 1); SetGameSettings(iPad, eGameSetting_UISizeSplitscreen, 2); SetGameSettings(iPad, eGameSetting_AnimatedCharacter, 1); // TU 12 GameSettingsA[iPad]->ucCurrentFavoriteSkinPos = 0; for (int i = 0; i < MAX_FAVORITE_SKINS; i++) { GameSettingsA[iPad]->uiFavoriteSkinA[i] = 0xFFFFFFFF; } // TU 13 GameSettingsA[iPad]->uiMashUpPackWorldsDisplay = 0xFFFFFFFF; // 1.6.4 app.SetGameHostOption(eGameHostOption_MobGriefing, 1); app.SetGameHostOption(eGameHostOption_KeepInventory, 0); app.SetGameHostOption(eGameHostOption_DoMobSpawning, 1); app.SetGameHostOption(eGameHostOption_DoMobLoot, 1); app.SetGameHostOption(eGameHostOption_DoTileDrops, 1); app.SetGameHostOption(eGameHostOption_NaturalRegeneration, 1); app.SetGameHostOption(eGameHostOption_DoDaylightCycle, 1); // 4J-PB - leave these in, or remove from everywhere they are referenced! // Although probably best to leave in unless we split the profile settings // into platform specific classes - having different meaning per platform // for the same bitmask could get confusing // #ifdef 0 // PS3DEC13 SetGameSettings(iPad, eGameSetting_PS3_EULA_Read, 0); // EULA not read // PS3 1.05 - added Greek // 4J-JEV: We cannot change these in-game, as they could affect localised // strings and font. XB1: Fix for #172947 - Content: Gameplay: While playing // in language different form system default one and resetting options to // their defaults in active gameplay causes in-game language to change and // HUD to disappear if (!app.GetGameStarted()) { GameSettingsA[iPad]->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; // use the system language GameSettingsA[iPad]->ucLocale = MINECRAFT_LANGUAGE_DEFAULT; // use the system locale } // #endif return 0; } int Game::DefaultOptionsCallback( void* pParam, C_4JProfile::PROFILESETTINGS* pSettings, const int iPad) { Game* pApp = (Game*)pParam; // flag the default options to be set pApp->DebugPrintf("Setting default options for player %d", iPad); pApp->SetAction(iPad, eAppAction_SetDefaultOptions, (void*)pSettings); // pApp->SetDefaultOptions(pSettings,iPad); // if the profile data has been changed, then force a profile write // It seems we're allowed to break the 5 minute rule if it's the result of a // user action // pApp->CheckGameSettingsChanged(); return 0; } int Game::OldProfileVersionCallback(void* pParam, unsigned char* pucData, const unsigned short usVersion, const int iPad) { // check what needs to be done with this version to update to the current // one switch (usVersion) { case PROFILE_VERSION_8: { GAME_SETTINGS* pGameSettings = (GAME_SETTINGS*)pucData; // reset the display new message counter pGameSettings->uiBitmaskValues |= GAMESETTING_DISPLAYUPDATEMSG; // eGameSetting_DisplayUpdateMessage // (counter) // Added a bitmask in TU13 to enable/disable display of the Mash-up // pack worlds in the saves list pGameSettings->uiMashUpPackWorldsDisplay = 0xFFFFFFFF; // PS3DEC13 pGameSettings->uiBitmaskValues &= ~GAMESETTING_PS3EULAREAD; // eGameSetting_PS3_EULA_Read - off // PS3 1.05 - added Greek pGameSettings->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; // use the system language } break; case PROFILE_VERSION_9: // PS3DEC13 { GAME_SETTINGS* pGameSettings = (GAME_SETTINGS*)pucData; pGameSettings->uiBitmaskValues |= GAMESETTING_DISPLAYUPDATEMSG; // eGameSetting_DisplayUpdateMessage // (counter) pGameSettings->uiBitmaskValues &= ~GAMESETTING_PS3EULAREAD; // eGameSetting_PS3_EULA_Read - // off // PS3 1.05 - added Greek pGameSettings->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; // use the system language } break; case PROFILE_VERSION_10: { GAME_SETTINGS* pGameSettings = (GAME_SETTINGS*)pucData; pGameSettings->uiBitmaskValues |= GAMESETTING_DISPLAYUPDATEMSG; // eGameSetting_DisplayUpdateMessage // (counter) pGameSettings->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; // use the system language } break; case PROFILE_VERSION_11: { GAME_SETTINGS* pGameSettings = (GAME_SETTINGS*)pucData; pGameSettings->uiBitmaskValues |= GAMESETTING_DISPLAYUPDATEMSG; // eGameSetting_DisplayUpdateMessage // (counter) } break; case PROFILE_VERSION_12: { GAME_SETTINGS* pGameSettings = (GAME_SETTINGS*)pucData; pGameSettings->uiBitmaskValues |= GAMESETTING_DISPLAYUPDATEMSG; // eGameSetting_DisplayUpdateMessage // (counter) } break; default: { // This might be from a version during testing of new profile // updates app.DebugPrintf( "Don't know what to do with this profile version!\n"); GAME_SETTINGS* pGameSettings = (GAME_SETTINGS*)pucData; pGameSettings->ucMenuSensitivity = 100; // eGameSetting_Sensitivity_InMenu pGameSettings->ucInterfaceOpacity = 80; // eGameSetting_Sensitivity_InMenu pGameSettings->usBitmaskValues |= 0x0200; // eGameSetting_DisplaySplitscreenGamertags - on pGameSettings->usBitmaskValues |= 0x0400; // eGameSetting_Hints - // on pGameSettings->usBitmaskValues |= 0x1000; // eGameSetting_Autosave - 2 pGameSettings->usBitmaskValues |= 0x8000; // eGameSetting_Tooltips - on pGameSettings->uiBitmaskValues = 0L; // reset pGameSettings->uiBitmaskValues |= GAMESETTING_CLOUDS; // eGameSetting_Clouds - on pGameSettings->uiBitmaskValues |= GAMESETTING_ONLINE; // eGameSetting_GameSetting_Online - on // eGameSetting_GameSetting_Invite - off pGameSettings->uiBitmaskValues |= GAMESETTING_FRIENDSOFFRIENDS; // eGameSetting_GameSetting_FriendsOfFriends // - on pGameSettings->uiBitmaskValues |= GAMESETTING_DISPLAYUPDATEMSG; // eGameSetting_DisplayUpdateMessage // (counter) pGameSettings->uiBitmaskValues &= ~GAMESETTING_BEDROCKFOG; // eGameSetting_BedrockFog - off pGameSettings->uiBitmaskValues |= GAMESETTING_DISPLAYHUD; // eGameSetting_DisplayHUD - on pGameSettings->uiBitmaskValues |= GAMESETTING_DISPLAYHAND; // eGameSetting_DisplayHand - on pGameSettings->uiBitmaskValues |= GAMESETTING_CUSTOMSKINANIM; // eGameSetting_CustomSkinAnim - on pGameSettings->uiBitmaskValues |= GAMESETTING_DEATHMESSAGES; // eGameSetting_DeathMessages - on pGameSettings->uiBitmaskValues |= (GAMESETTING_UISIZE & 0x00000800); // uisize 2 pGameSettings->uiBitmaskValues |= (GAMESETTING_UISIZE_SPLITSCREEN & 0x00004000); // splitscreen ui size 3 pGameSettings->uiBitmaskValues |= GAMESETTING_ANIMATEDCHARACTER; // eGameSetting_AnimatedCharacter // - on // TU12 // favorite skins added, but only set in TU12 - set to FFs for (int i = 0; i < MAX_FAVORITE_SKINS; i++) { pGameSettings->uiFavoriteSkinA[i] = 0xFFFFFFFF; } pGameSettings->ucCurrentFavoriteSkinPos = 0; // Added a bitmask in TU13 to enable/disable display of the Mash-up // pack worlds in the saves list pGameSettings->uiMashUpPackWorldsDisplay = 0xFFFFFFFF; // PS3DEC13 pGameSettings->uiBitmaskValues &= ~GAMESETTING_PS3EULAREAD; // eGameSetting_PS3_EULA_Read - off // PS3 1.05 - added Greek pGameSettings->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; // use the system language } break; } return 0; } void Game::ApplyGameSettingsChanged(int iPad) { ActionGameSettings(iPad, eGameSetting_MusicVolume); ActionGameSettings(iPad, eGameSetting_SoundFXVolume); ActionGameSettings(iPad, eGameSetting_Gamma); ActionGameSettings(iPad, eGameSetting_Difficulty); ActionGameSettings(iPad, eGameSetting_Sensitivity_InGame); ActionGameSettings(iPad, eGameSetting_ViewBob); ActionGameSettings(iPad, eGameSetting_ControlScheme); ActionGameSettings(iPad, eGameSetting_ControlInvertLook); ActionGameSettings(iPad, eGameSetting_ControlSouthPaw); ActionGameSettings(iPad, eGameSetting_SplitScreenVertical); ActionGameSettings(iPad, eGameSetting_GamertagsVisible); // Interim TU 1.6.6 ActionGameSettings(iPad, eGameSetting_Sensitivity_InMenu); ActionGameSettings(iPad, eGameSetting_DisplaySplitscreenGamertags); ActionGameSettings(iPad, eGameSetting_Hints); ActionGameSettings(iPad, eGameSetting_InterfaceOpacity); ActionGameSettings(iPad, eGameSetting_Tooltips); ActionGameSettings(iPad, eGameSetting_Clouds); ActionGameSettings(iPad, eGameSetting_BedrockFog); ActionGameSettings(iPad, eGameSetting_DisplayHUD); ActionGameSettings(iPad, eGameSetting_DisplayHand); ActionGameSettings(iPad, eGameSetting_CustomSkinAnim); ActionGameSettings(iPad, eGameSetting_DeathMessages); ActionGameSettings(iPad, eGameSetting_UISize); ActionGameSettings(iPad, eGameSetting_UISizeSplitscreen); ActionGameSettings(iPad, eGameSetting_AnimatedCharacter); ActionGameSettings(iPad, eGameSetting_PS3_EULA_Read); } void Game::ActionGameSettings(int iPad, eGameSetting eVal) { Minecraft* pMinecraft = Minecraft::GetInstance(); switch (eVal) { case eGameSetting_MusicVolume: if (iPad == ProfileManager.GetPrimaryPad()) { pMinecraft->options->set( Options::Option::MUSIC, ((float)GameSettingsA[iPad]->ucMusicVolume) / 100.0f); } break; case eGameSetting_SoundFXVolume: if (iPad == ProfileManager.GetPrimaryPad()) { pMinecraft->options->set( Options::Option::SOUND, ((float)GameSettingsA[iPad]->ucSoundFXVolume) / 100.0f); } break; case eGameSetting_Gamma: if (iPad == ProfileManager.GetPrimaryPad()) { // ucGamma range is 0-100, UpdateGamma is 0 - 32768 float fVal = ((float)GameSettingsA[iPad]->ucGamma) * 327.68f; RenderManager.UpdateGamma((unsigned short)fVal); } break; case eGameSetting_Difficulty: if (iPad == ProfileManager.GetPrimaryPad()) { pMinecraft->options->toggle( Options::Option::DIFFICULTY, GameSettingsA[iPad]->usBitmaskValues & 0x03); app.DebugPrintf("Difficulty toggle to %d\n", GameSettingsA[iPad]->usBitmaskValues & 0x03); // Update the Game Host setting app.SetGameHostOption(eGameHostOption_Difficulty, pMinecraft->options->difficulty); // send this to the other players if we are in-game bool bInGame = pMinecraft->level != nullptr; // Game Host only (and for now we can't change the diff while in // game, so this shouldn't happen) if (bInGame && g_NetworkManager.IsHost() && (iPad == ProfileManager.GetPrimaryPad())) { app.SetXuiServerAction( iPad, eXuiServerAction_ServerSettingChanged_Difficulty); } } else { app.DebugPrintf( "NOT ACTIONING DIFFICULTY - Primary pad is %d, This pad is " "%d\n", ProfileManager.GetPrimaryPad(), iPad); } break; case eGameSetting_Sensitivity_InGame: // 4J-PB - we don't use the options value // tell the input that we've changed the sensitivity - range of the // slider is 0 to 200, default is 100 pMinecraft->options->set( Options::Option::SENSITIVITY, ((float)GameSettingsA[iPad]->ucSensitivity) / 100.0f); // InputManager.SetJoypadSensitivity(iPad,((float)GameSettingsA[iPad]->ucSensitivity)/100.0f); break; case eGameSetting_ViewBob: // 4J-PB - not handled here any more - it's read from the // gamesettings per player // pMinecraft->options->toggle(Options::Option::VIEW_BOBBING,GameSettingsA[iPad]->usBitmaskValues&0x04); break; case eGameSetting_ControlScheme: InputManager.SetJoypadMapVal( iPad, (GameSettingsA[iPad]->usBitmaskValues & 0x30) >> 4); break; case eGameSetting_ControlInvertLook: // Nothing specific to do for this setting. break; case eGameSetting_ControlSouthPaw: // What is the setting? if (GameSettingsA[iPad]->usBitmaskValues & 0x80) { // Southpaw. InputManager.SetJoypadStickAxisMap(iPad, AXIS_MAP_LX, AXIS_MAP_RX); InputManager.SetJoypadStickAxisMap(iPad, AXIS_MAP_LY, AXIS_MAP_RY); InputManager.SetJoypadStickAxisMap(iPad, AXIS_MAP_RX, AXIS_MAP_LX); InputManager.SetJoypadStickAxisMap(iPad, AXIS_MAP_RY, AXIS_MAP_LY); InputManager.SetJoypadStickTriggerMap(iPad, TRIGGER_MAP_0, TRIGGER_MAP_1); InputManager.SetJoypadStickTriggerMap(iPad, TRIGGER_MAP_1, TRIGGER_MAP_0); } else { // Right handed. InputManager.SetJoypadStickAxisMap(iPad, AXIS_MAP_LX, AXIS_MAP_LX); InputManager.SetJoypadStickAxisMap(iPad, AXIS_MAP_LY, AXIS_MAP_LY); InputManager.SetJoypadStickAxisMap(iPad, AXIS_MAP_RX, AXIS_MAP_RX); InputManager.SetJoypadStickAxisMap(iPad, AXIS_MAP_RY, AXIS_MAP_RY); InputManager.SetJoypadStickTriggerMap(iPad, TRIGGER_MAP_0, TRIGGER_MAP_0); InputManager.SetJoypadStickTriggerMap(iPad, TRIGGER_MAP_1, TRIGGER_MAP_1); } break; case eGameSetting_SplitScreenVertical: if (iPad == ProfileManager.GetPrimaryPad()) { pMinecraft->updatePlayerViewportAssignments(); } break; case eGameSetting_GamertagsVisible: { bool bInGame = pMinecraft->level != nullptr; // Game Host only if (bInGame && g_NetworkManager.IsHost() && (iPad == ProfileManager.GetPrimaryPad())) { // Update the Game Host setting if you are the host and you are // in-game app.SetGameHostOption( eGameHostOption_Gamertags, ((GameSettingsA[iPad]->usBitmaskValues & 0x0008) != 0) ? 1 : 0); app.SetXuiServerAction( iPad, eXuiServerAction_ServerSettingChanged_Gamertags); PlayerList* players = MinecraftServer::getInstance()->getPlayerList(); for (auto it3 = players->players.begin(); it3 != players->players.end(); ++it3) { std::shared_ptr decorationPlayer = *it3; decorationPlayer->setShowOnMaps( (app.GetGameHostOption(eGameHostOption_Gamertags) != 0) ? true : false); } } } break; // Interim TU 1.6.6 case eGameSetting_Sensitivity_InMenu: // 4J-PB - we don't use the options value // tell the input that we've changed the sensitivity - range of the // slider is 0 to 200, default is 100 // pMinecraft->options->set(Options::Option::SENSITIVITY,((float)GameSettingsA[iPad]->ucSensitivity)/100.0f); // InputManager.SetJoypadSensitivity(iPad,((float)GameSettingsA[iPad]->ucSensitivity)/100.0f); break; case eGameSetting_DisplaySplitscreenGamertags: for (std::uint8_t idx = 0; idx < XUSER_MAX_COUNT; ++idx) { if (pMinecraft->localplayers[idx] != nullptr) { if (pMinecraft->localplayers[idx]->m_iScreenSection == C4JRender::VIEWPORT_TYPE_FULLSCREEN) { ui.DisplayGamertag(idx, false); } else { ui.DisplayGamertag(idx, true); } } } break; case eGameSetting_InterfaceOpacity: // update the tooltips display ui.RefreshTooltips(iPad); break; case eGameSetting_Hints: // nothing to do here break; case eGameSetting_Tooltips: if ((GameSettingsA[iPad]->usBitmaskValues & 0x8000) != 0) { ui.SetEnableTooltips(iPad, true); } else { ui.SetEnableTooltips(iPad, false); } break; case eGameSetting_Clouds: // nothing to do here break; case eGameSetting_Online: // nothing to do here break; case eGameSetting_InviteOnly: // nothing to do here break; case eGameSetting_FriendsOfFriends: // nothing to do here break; case eGameSetting_BedrockFog: { bool bInGame = pMinecraft->level != nullptr; // Game Host only if (bInGame && g_NetworkManager.IsHost() && (iPad == ProfileManager.GetPrimaryPad())) { // Update the Game Host setting if you are the host and you are // in-game app.SetGameHostOption( eGameHostOption_BedrockFog, GetGameSettings(iPad, eGameSetting_BedrockFog) ? 1 : 0); app.SetXuiServerAction( iPad, eXuiServerAction_ServerSettingChanged_BedrockFog); } } break; case eGameSetting_DisplayHUD: // nothing to do here break; case eGameSetting_DisplayHand: // nothing to do here break; case eGameSetting_CustomSkinAnim: // nothing to do here break; case eGameSetting_DeathMessages: // nothing to do here break; case eGameSetting_UISize: // nothing to do here break; case eGameSetting_UISizeSplitscreen: // nothing to do here break; case eGameSetting_AnimatedCharacter: // nothing to do here break; case eGameSetting_PS3_EULA_Read: // nothing to do here break; case eGameSetting_PSVita_NetworkModeAdhoc: // nothing to do here break; default: break; } } // Skin/Cape/FavoriteSkin methods moved to SkinManager // Mash-up pack worlds void Game::HideMashupPackWorld(int iPad, unsigned int iMashupPackID) { unsigned int uiPackID = iMashupPackID - 1024; // mash-up ids start at 1024 GameSettingsA[iPad]->uiMashUpPackWorldsDisplay &= ~(1 << uiPackID); GameSettingsA[iPad]->bSettingsChanged = true; } void Game::EnableMashupPackWorlds(int iPad) { GameSettingsA[iPad]->uiMashUpPackWorldsDisplay = 0xFFFFFFFF; GameSettingsA[iPad]->bSettingsChanged = true; } unsigned int Game::GetMashupPackWorlds(int iPad) { return GameSettingsA[iPad]->uiMashUpPackWorldsDisplay; } void Game::SetMinecraftLanguage(int iPad, unsigned char ucLanguage) { GameSettingsA[iPad]->ucLanguage = ucLanguage; GameSettingsA[iPad]->bSettingsChanged = true; } unsigned char Game::GetMinecraftLanguage(int iPad) { // if there are no game settings read yet, return the default language if (GameSettingsA[iPad] == nullptr) { return 0; } else { return GameSettingsA[iPad]->ucLanguage; } } void Game::SetMinecraftLocale(int iPad, unsigned char ucLocale) { GameSettingsA[iPad]->ucLocale = ucLocale; GameSettingsA[iPad]->bSettingsChanged = true; } unsigned char Game::GetMinecraftLocale(int iPad) { // if there are no game settings read yet, return the default language if (GameSettingsA[iPad] == nullptr) { return 0; } else { return GameSettingsA[iPad]->ucLocale; } } void Game::SetGameSettings(int iPad, eGameSetting eVal, unsigned char ucVal) { // Minecraft *pMinecraft=Minecraft::GetInstance(); switch (eVal) { case eGameSetting_MusicVolume: if (GameSettingsA[iPad]->ucMusicVolume != ucVal) { GameSettingsA[iPad]->ucMusicVolume = ucVal; if (iPad == ProfileManager.GetPrimaryPad()) { ActionGameSettings(iPad, eVal); } GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_SoundFXVolume: if (GameSettingsA[iPad]->ucSoundFXVolume != ucVal) { GameSettingsA[iPad]->ucSoundFXVolume = ucVal; if (iPad == ProfileManager.GetPrimaryPad()) { ActionGameSettings(iPad, eVal); } GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_Gamma: if (GameSettingsA[iPad]->ucGamma != ucVal) { GameSettingsA[iPad]->ucGamma = ucVal; if (iPad == ProfileManager.GetPrimaryPad()) { ActionGameSettings(iPad, eVal); } GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_Difficulty: if ((GameSettingsA[iPad]->usBitmaskValues & 0x03) != (ucVal & 0x03)) { GameSettingsA[iPad]->usBitmaskValues &= ~0x03; GameSettingsA[iPad]->usBitmaskValues |= ucVal & 0x03; if (iPad == ProfileManager.GetPrimaryPad()) { ActionGameSettings(iPad, eVal); } GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_Sensitivity_InGame: if (GameSettingsA[iPad]->ucSensitivity != ucVal) { GameSettingsA[iPad]->ucSensitivity = ucVal; ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_ViewBob: if ((GameSettingsA[iPad]->usBitmaskValues & 0x0004) != ((ucVal & 0x01) << 2)) { if (ucVal != 0) { GameSettingsA[iPad]->usBitmaskValues |= 0x0004; } else { GameSettingsA[iPad]->usBitmaskValues &= ~0x0004; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_ControlScheme: // bits 5 and 6 if ((GameSettingsA[iPad]->usBitmaskValues & 0x30) != ((ucVal & 0x03) << 4)) { GameSettingsA[iPad]->usBitmaskValues &= ~0x0030; if (ucVal != 0) { GameSettingsA[iPad]->usBitmaskValues |= (ucVal & 0x03) << 4; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_ControlInvertLook: if ((GameSettingsA[iPad]->usBitmaskValues & 0x0040) != ((ucVal & 0x01) << 6)) { if (ucVal != 0) { GameSettingsA[iPad]->usBitmaskValues |= 0x0040; } else { GameSettingsA[iPad]->usBitmaskValues &= ~0x0040; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_ControlSouthPaw: if ((GameSettingsA[iPad]->usBitmaskValues & 0x0080) != ((ucVal & 0x01) << 7)) { if (ucVal != 0) { GameSettingsA[iPad]->usBitmaskValues |= 0x0080; } else { GameSettingsA[iPad]->usBitmaskValues &= ~0x0080; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_SplitScreenVertical: if ((GameSettingsA[iPad]->usBitmaskValues & 0x0100) != ((ucVal & 0x01) << 8)) { if (ucVal != 0) { GameSettingsA[iPad]->usBitmaskValues |= 0x0100; } else { GameSettingsA[iPad]->usBitmaskValues &= ~0x0100; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_GamertagsVisible: if ((GameSettingsA[iPad]->usBitmaskValues & 0x0008) != ((ucVal & 0x01) << 3)) { if (ucVal != 0) { GameSettingsA[iPad]->usBitmaskValues |= 0x0008; } else { GameSettingsA[iPad]->usBitmaskValues &= ~0x0008; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; // 4J-PB - Added for Interim TU for 1.6.6 case eGameSetting_Sensitivity_InMenu: if (GameSettingsA[iPad]->ucMenuSensitivity != ucVal) { GameSettingsA[iPad]->ucMenuSensitivity = ucVal; ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_DisplaySplitscreenGamertags: if ((GameSettingsA[iPad]->usBitmaskValues & 0x0200) != ((ucVal & 0x01) << 9)) { if (ucVal != 0) { GameSettingsA[iPad]->usBitmaskValues |= 0x0200; } else { GameSettingsA[iPad]->usBitmaskValues &= ~0x0200; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_Hints: if ((GameSettingsA[iPad]->usBitmaskValues & 0x0400) != ((ucVal & 0x01) << 10)) { if (ucVal != 0) { GameSettingsA[iPad]->usBitmaskValues |= 0x0400; } else { GameSettingsA[iPad]->usBitmaskValues &= ~0x0400; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_Autosave: if ((GameSettingsA[iPad]->usBitmaskValues & 0x7800) != ((ucVal & 0x0F) << 11)) { GameSettingsA[iPad]->usBitmaskValues &= ~0x7800; if (ucVal != 0) { GameSettingsA[iPad]->usBitmaskValues |= (ucVal & 0x0F) << 11; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_Tooltips: if ((GameSettingsA[iPad]->usBitmaskValues & 0x8000) != ((ucVal & 0x01) << 15)) { if (ucVal != 0) { GameSettingsA[iPad]->usBitmaskValues |= 0x8000; } else { GameSettingsA[iPad]->usBitmaskValues &= ~0x8000; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_InterfaceOpacity: if (GameSettingsA[iPad]->ucInterfaceOpacity != ucVal) { GameSettingsA[iPad]->ucInterfaceOpacity = ucVal; ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_Clouds: if ((GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_CLOUDS) != (ucVal & 0x01)) { if (ucVal == 1) { GameSettingsA[iPad]->uiBitmaskValues |= GAMESETTING_CLOUDS; } else { GameSettingsA[iPad]->uiBitmaskValues &= ~GAMESETTING_CLOUDS; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_Online: if ((GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_ONLINE) != (ucVal & 0x01) << 1) { if (ucVal == 1) { GameSettingsA[iPad]->uiBitmaskValues |= GAMESETTING_ONLINE; } else { GameSettingsA[iPad]->uiBitmaskValues &= ~GAMESETTING_ONLINE; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_InviteOnly: if ((GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_INVITEONLY) != (ucVal & 0x01) << 2) { if (ucVal == 1) { GameSettingsA[iPad]->uiBitmaskValues |= GAMESETTING_INVITEONLY; } else { GameSettingsA[iPad]->uiBitmaskValues &= ~GAMESETTING_INVITEONLY; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_FriendsOfFriends: if ((GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_FRIENDSOFFRIENDS) != (ucVal & 0x01) << 3) { if (ucVal == 1) { GameSettingsA[iPad]->uiBitmaskValues |= GAMESETTING_FRIENDSOFFRIENDS; } else { GameSettingsA[iPad]->uiBitmaskValues &= ~GAMESETTING_FRIENDSOFFRIENDS; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_DisplayUpdateMessage: if ((GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_DISPLAYUPDATEMSG) != (ucVal & 0x03) << 4) { GameSettingsA[iPad]->uiBitmaskValues &= ~GAMESETTING_DISPLAYUPDATEMSG; if (ucVal > 0) { GameSettingsA[iPad]->uiBitmaskValues |= (ucVal & 0x03) << 4; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_BedrockFog: if ((GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_BEDROCKFOG) != (ucVal & 0x01) << 6) { if (ucVal == 1) { GameSettingsA[iPad]->uiBitmaskValues |= GAMESETTING_BEDROCKFOG; } else { GameSettingsA[iPad]->uiBitmaskValues &= ~GAMESETTING_BEDROCKFOG; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_DisplayHUD: if ((GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_DISPLAYHUD) != (ucVal & 0x01) << 7) { if (ucVal == 1) { GameSettingsA[iPad]->uiBitmaskValues |= GAMESETTING_DISPLAYHUD; } else { GameSettingsA[iPad]->uiBitmaskValues &= ~GAMESETTING_DISPLAYHUD; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_DisplayHand: if ((GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_DISPLAYHAND) != (ucVal & 0x01) << 8) { if (ucVal == 1) { GameSettingsA[iPad]->uiBitmaskValues |= GAMESETTING_DISPLAYHAND; } else { GameSettingsA[iPad]->uiBitmaskValues &= ~GAMESETTING_DISPLAYHAND; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_CustomSkinAnim: if ((GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_CUSTOMSKINANIM) != (ucVal & 0x01) << 9) { if (ucVal == 1) { GameSettingsA[iPad]->uiBitmaskValues |= GAMESETTING_CUSTOMSKINANIM; } else { GameSettingsA[iPad]->uiBitmaskValues &= ~GAMESETTING_CUSTOMSKINANIM; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; // TU9 case eGameSetting_DeathMessages: if ((GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_DEATHMESSAGES) != (ucVal & 0x01) << 10) { if (ucVal == 1) { GameSettingsA[iPad]->uiBitmaskValues |= GAMESETTING_DEATHMESSAGES; } else { GameSettingsA[iPad]->uiBitmaskValues &= ~GAMESETTING_DEATHMESSAGES; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_UISize: if ((GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_UISIZE) != ((ucVal & 0x03) << 11)) { GameSettingsA[iPad]->uiBitmaskValues &= ~GAMESETTING_UISIZE; if (ucVal != 0) { GameSettingsA[iPad]->uiBitmaskValues |= (ucVal & 0x03) << 11; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_UISizeSplitscreen: if ((GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_UISIZE_SPLITSCREEN) != ((ucVal & 0x03) << 13)) { GameSettingsA[iPad]->uiBitmaskValues &= ~GAMESETTING_UISIZE_SPLITSCREEN; if (ucVal != 0) { GameSettingsA[iPad]->uiBitmaskValues |= (ucVal & 0x03) << 13; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_AnimatedCharacter: if ((GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_ANIMATEDCHARACTER) != (ucVal & 0x01) << 15) { if (ucVal == 1) { GameSettingsA[iPad]->uiBitmaskValues |= GAMESETTING_ANIMATEDCHARACTER; } else { GameSettingsA[iPad]->uiBitmaskValues &= ~GAMESETTING_ANIMATEDCHARACTER; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_PS3_EULA_Read: if ((GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_PS3EULAREAD) != (ucVal & 0x01) << 16) { if (ucVal == 1) { GameSettingsA[iPad]->uiBitmaskValues |= GAMESETTING_PS3EULAREAD; } else { GameSettingsA[iPad]->uiBitmaskValues &= ~GAMESETTING_PS3EULAREAD; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; case eGameSetting_PSVita_NetworkModeAdhoc: if ((GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_PSVITANETWORKMODEADHOC) != (ucVal & 0x01) << 17) { if (ucVal == 1) { GameSettingsA[iPad]->uiBitmaskValues |= GAMESETTING_PSVITANETWORKMODEADHOC; } else { GameSettingsA[iPad]->uiBitmaskValues &= ~GAMESETTING_PSVITANETWORKMODEADHOC; } ActionGameSettings(iPad, eVal); GameSettingsA[iPad]->bSettingsChanged = true; } break; } } unsigned char Game::GetGameSettings(eGameSetting eVal) { int iPad = ProfileManager.GetPrimaryPad(); return GetGameSettings(iPad, eVal); } unsigned char Game::GetGameSettings(int iPad, eGameSetting eVal) { switch (eVal) { case eGameSetting_MusicVolume: return GameSettingsA[iPad]->ucMusicVolume; break; case eGameSetting_SoundFXVolume: return GameSettingsA[iPad]->ucSoundFXVolume; break; case eGameSetting_Gamma: return GameSettingsA[iPad]->ucGamma; break; case eGameSetting_Difficulty: return GameSettingsA[iPad]->usBitmaskValues & 0x0003; break; case eGameSetting_Sensitivity_InGame: return GameSettingsA[iPad]->ucSensitivity; break; case eGameSetting_ViewBob: return ((GameSettingsA[iPad]->usBitmaskValues & 0x0004) >> 2); break; case eGameSetting_GamertagsVisible: return ((GameSettingsA[iPad]->usBitmaskValues & 0x0008) >> 3); break; case eGameSetting_ControlScheme: return ((GameSettingsA[iPad]->usBitmaskValues & 0x0030) >> 4); // 2 bits break; case eGameSetting_ControlInvertLook: return ((GameSettingsA[iPad]->usBitmaskValues & 0x0040) >> 6); break; case eGameSetting_ControlSouthPaw: return ((GameSettingsA[iPad]->usBitmaskValues & 0x0080) >> 7); break; case eGameSetting_SplitScreenVertical: return ((GameSettingsA[iPad]->usBitmaskValues & 0x0100) >> 8); break; // 4J-PB - Added for Interim TU for 1.6.6 case eGameSetting_Sensitivity_InMenu: return GameSettingsA[iPad]->ucMenuSensitivity; break; case eGameSetting_DisplaySplitscreenGamertags: return ((GameSettingsA[iPad]->usBitmaskValues & 0x0200) >> 9); break; case eGameSetting_Hints: return ((GameSettingsA[iPad]->usBitmaskValues & 0x0400) >> 10); break; case eGameSetting_Autosave: { unsigned char ucVal = (GameSettingsA[iPad]->usBitmaskValues & 0x7800) >> 11; return ucVal; } break; case eGameSetting_Tooltips: return ((GameSettingsA[iPad]->usBitmaskValues & 0x8000) >> 15); break; case eGameSetting_InterfaceOpacity: return GameSettingsA[iPad]->ucInterfaceOpacity; break; case eGameSetting_Clouds: return (GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_CLOUDS); break; case eGameSetting_Online: return (GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_ONLINE) >> 1; break; case eGameSetting_InviteOnly: return (GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_INVITEONLY) >> 2; break; case eGameSetting_FriendsOfFriends: return (GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_FRIENDSOFFRIENDS) >> 3; break; case eGameSetting_DisplayUpdateMessage: return (GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_DISPLAYUPDATEMSG) >> 4; break; case eGameSetting_BedrockFog: return (GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_BEDROCKFOG) >> 6; break; case eGameSetting_DisplayHUD: return (GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_DISPLAYHUD) >> 7; break; case eGameSetting_DisplayHand: return (GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_DISPLAYHAND) >> 8; break; case eGameSetting_CustomSkinAnim: return (GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_CUSTOMSKINANIM) >> 9; break; // TU9 case eGameSetting_DeathMessages: return (GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_DEATHMESSAGES) >> 10; break; case eGameSetting_UISize: { unsigned char ucVal = (GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_UISIZE) >> 11; return ucVal; } break; case eGameSetting_UISizeSplitscreen: { unsigned char ucVal = (GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_UISIZE_SPLITSCREEN) >> 13; return ucVal; } break; case eGameSetting_AnimatedCharacter: return (GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_ANIMATEDCHARACTER) >> 15; case eGameSetting_PS3_EULA_Read: return (GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_PS3EULAREAD) >> 16; case eGameSetting_PSVita_NetworkModeAdhoc: return (GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_PSVITANETWORKMODEADHOC) >> 17; } return 0; } void Game::CheckGameSettingsChanged(bool bOverride5MinuteTimer, int iPad) { // If the settings have changed, write them to the profile if (iPad == XUSER_INDEX_ANY) { for (int i = 0; i < XUSER_MAX_COUNT; i++) { if (GameSettingsA[i]->bSettingsChanged) { ProfileManager.WriteToProfile(i, true, bOverride5MinuteTimer); GameSettingsA[i]->bSettingsChanged = false; } } } else { if (GameSettingsA[iPad]->bSettingsChanged) { ProfileManager.WriteToProfile(iPad, true, bOverride5MinuteTimer); GameSettingsA[iPad]->bSettingsChanged = false; } } } void Game::ClearGameSettingsChangedFlag(int iPad) { GameSettingsA[iPad]->bSettingsChanged = false; } /////////////////////////// // // Remove the debug settings in the content package build // //////////////////////////// #if !defined(_DEBUG_MENUS_ENABLED) unsigned int Game::GetGameSettingsDebugMask( int iPad, bool bOverridePlayer) // bOverridePlayer is to force the send for // the server to get the read options { return 0; } void Game::SetGameSettingsDebugMask(int iPad, unsigned int uiVal) {} void Game::ActionDebugMask(int iPad, bool bSetAllClear) {} #else unsigned int Game::GetGameSettingsDebugMask( int iPad, bool bOverridePlayer) // bOverridePlayer is to force the send for // the server to get the read options { if (iPad == -1) { iPad = ProfileManager.GetPrimaryPad(); } if (iPad < 0) iPad = 0; std::shared_ptr player = Minecraft::GetInstance()->localplayers[iPad]; if (bOverridePlayer || player == nullptr) { return GameSettingsA[iPad]->uiDebugBitmask; } else { return player->GetDebugOptions(); } } void Game::SetGameSettingsDebugMask(int iPad, unsigned int uiVal) { #if !defined(_CONTENT_PACKAGE) GameSettingsA[iPad]->bSettingsChanged = true; GameSettingsA[iPad]->uiDebugBitmask = uiVal; // update the value so the network server can use it std::shared_ptr player = Minecraft::GetInstance()->localplayers[iPad]; if (player) { Minecraft::GetInstance()->localgameModes[iPad]->handleDebugOptions( uiVal, player); } #endif } void Game::ActionDebugMask(int iPad, bool bSetAllClear) { unsigned int ulBitmask = app.GetGameSettingsDebugMask(iPad); if (bSetAllClear) ulBitmask = 0L; // these settings should only be actioned for the primary player if (ProfileManager.GetPrimaryPad() != iPad) return; for (int i = 0; i < eDebugSetting_Max; i++) { switch (i) { case eDebugSetting_LoadSavesFromDisk: if (ulBitmask & (1 << i)) { app.SetLoadSavesFromFolderEnabled(true); } else { app.SetLoadSavesFromFolderEnabled(false); } break; case eDebugSetting_WriteSavesToDisk: if (ulBitmask & (1 << i)) { app.SetWriteSavesToFolderEnabled(true); } else { app.SetWriteSavesToFolderEnabled(false); } break; case eDebugSetting_FreezePlayers: // eDebugSetting_InterfaceOff: if (ulBitmask & (1 << i)) { app.SetFreezePlayers(true); // Turn off interface rendering. // app.SetInterfaceRenderingOff( true ); } else { app.SetFreezePlayers(false); // Turn on interface rendering. // app.SetInterfaceRenderingOff( false ); } break; case eDebugSetting_Safearea: if (ulBitmask & (1 << i)) { app.ShowSafeArea(true); } else { app.ShowSafeArea(false); } break; // case eDebugSetting_HandRenderingOff: // if(ulBitmask&(1<iPad, actionInfo->action); } void Game::HandleXuiActions(void) { eXuiAction eAction; eTMSAction eTMS; void* param; Minecraft* pMinecraft = Minecraft::GetInstance(); std::shared_ptr player; // are there any global actions to deal with? eAction = app.GetGlobalXuiAction(); if (eAction != eAppAction_Idle) { switch (eAction) { case eAppAction_DisplayLavaMessage: // Display a warning about placing lava in the spawn area { unsigned int uiIDA[1]; uiIDA[0] = IDS_CONFIRM_OK; C4JStorage::EMessageResult result = ui.RequestErrorMessage(IDS_CANT_PLACE_NEAR_SPAWN_TITLE, IDS_CANT_PLACE_NEAR_SPAWN_TEXT, uiIDA, 1, XUSER_INDEX_ANY); if (result != C4JStorage::EMessage_Busy) SetGlobalXuiAction(eAppAction_Idle); } break; default: break; } } // are there any app actions to deal with? for (int i = 0; i < XUSER_MAX_COUNT; i++) { eAction = app.GetXuiAction(i); param = m_eXuiActionParam[i]; if (eAction != eAppAction_Idle) { switch (eAction) { // // the renderer will capture a screenshot // case eAppAction_SocialPost: // if (ProfileManager.IsFullVersion()) { // // Facebook Share // if (CSocialManager::Instance() // ->IsTitleAllowedToPostImages() && // CSocialManager::Instance() // ->AreAllUsersAllowedToPostImages()) { // // disable character name tags for the shot // // m_bwasHidingGui = // pMinecraft->options->hideGui; // // // 4J Stu - Removed 1.8.2 bug fix (TU6) as // don't // // need this // pMinecraft->options->hideGui = true; // SetAction(i, eAppAction_SocialPostScreenshot); // } else { // SetAction(i, eAppAction_Idle); // } // } else { // SetAction(i, eAppAction_Idle); // } // break; // case eAppAction_SocialPostScreenshot: { // SetAction(i, eAppAction_Idle); // bool bKeepHiding = false; // for (int j = 0; j < XUSER_MAX_COUNT; ++j) { // if (app.GetXuiAction(j) == // eAppAction_SocialPostScreenshot) { // bKeepHiding = true; // break; // } // } // pMinecraft->options->hideGui = bKeepHiding; // // Facebook Share // if (app.GetLocalPlayerCount() > 1) { // ui.NavigateToScene(i, eUIScene_SocialPost); // } else { // ui.NavigateToScene(i, eUIScene_SocialPost); // } // } break; case eAppAction_SaveGame: SetAction(i, eAppAction_Idle); if (!GetChangingSessionType()) { // flag the render to capture the screenshot for the // save SetAction(i, eAppAction_SaveGameCapturedThumbnail); } break; case eAppAction_AutosaveSaveGame: { // Need to run a check to see if the save exists in order to // stop the dialog asking if we want to overwrite it coming // up on an autosave bool bSaveExists; StorageManager.DoesSaveExist(&bSaveExists); SetAction(i, eAppAction_Idle); if (!GetChangingSessionType()) { // flag the render to capture the screenshot for the // save SetAction(i, eAppAction_AutosaveSaveGameCapturedThumbnail); } } break; case eAppAction_SaveGameCapturedThumbnail: // reset the autosave timer app.SetAutosaveTimerTime(); SetAction(i, eAppAction_Idle); // Check that there is a name for the save - if we're saving // from the tutorial and this is the first save from the // tutorial, we'll not have a name /*if(StorageManager.GetSaveName()==nullptr) { app.NavigateToScene(i,eUIScene_SaveWorld); } else*/ { // turn off the gamertags in splitscreen for the primary // player, since they are about to be made fullscreen ui.HideAllGameUIElements(); // Hide the other players scenes ui.ShowOtherPlayersBaseScene( ProfileManager.GetPrimaryPad(), false); // int saveOrCheckpointId = 0; // bool validSave = // StorageManager.GetSaveUniqueNumber(&saveOrCheckpointId); // SentientManager.RecordLevelSaveOrCheckpoint(ProfileManager.GetPrimaryPad(), // saveOrCheckpointId); LoadingInputParams* loadingParams = new LoadingInputParams(); loadingParams->func = &UIScene_PauseMenu::SaveWorldThreadProc; loadingParams->lpParam = (void*)false; // 4J-JEV - PS4: Fix for #5708 - [ONLINE] - If the user // pulls their network cable out while saving the title // will hang. loadingParams->waitForThreadToDelete = true; UIFullscreenProgressCompletionData* completionData = new UIFullscreenProgressCompletionData(); completionData->bShowBackground = true; completionData->bShowLogo = true; completionData->type = e_ProgressCompletion_NavigateBackToScene; completionData->iPad = ProfileManager.GetPrimaryPad(); if (ui.IsSceneInStack(ProfileManager.GetPrimaryPad(), eUIScene_EndPoem)) { completionData->scene = eUIScene_EndPoem; } else { completionData->scene = eUIScene_PauseMenu; } loadingParams->completionData = completionData; // 4J Stu - Xbox only ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_FullscreenProgress, loadingParams, eUILayer_Fullscreen, eUIGroup_Fullscreen); } break; case eAppAction_AutosaveSaveGameCapturedThumbnail: { app.SetAutosaveTimerTime(); SetAction(i, eAppAction_Idle); // turn off the gamertags in splitscreen for the primary // player, since they are about to be made fullscreen ui.HideAllGameUIElements(); // app.CloseAllPlayersXuiScenes(); // Hide the other players scenes ui.ShowOtherPlayersBaseScene(ProfileManager.GetPrimaryPad(), false); // This just allows it to be shown if (pMinecraft ->localgameModes[ProfileManager.GetPrimaryPad()] != nullptr) pMinecraft ->localgameModes[ProfileManager.GetPrimaryPad()] ->getTutorial() ->showTutorialPopup(false); // int saveOrCheckpointId = 0; // bool validSave = // StorageManager.GetSaveUniqueNumber(&saveOrCheckpointId); // SentientManager.RecordLevelSaveOrCheckpoint(ProfileManager.GetPrimaryPad(), // saveOrCheckpointId); LoadingInputParams* loadingParams = new LoadingInputParams(); loadingParams->func = &UIScene_PauseMenu::SaveWorldThreadProc; loadingParams->lpParam = (void*)true; UIFullscreenProgressCompletionData* completionData = new UIFullscreenProgressCompletionData(); completionData->bShowBackground = true; completionData->bShowLogo = true; completionData->type = e_ProgressCompletion_AutosaveNavigateBack; completionData->iPad = ProfileManager.GetPrimaryPad(); // completionData->bAutosaveWasMenuDisplayed=ui.GetMenuDisplayed(ProfileManager.GetPrimaryPad()); loadingParams->completionData = completionData; // 4J Stu - Xbox only ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_FullscreenProgress, loadingParams, eUILayer_Fullscreen, eUIGroup_Fullscreen); } break; case eAppAction_ExitPlayer: // a secondary player has chosen to quit { int iPlayerC = g_NetworkManager.GetPlayerCount(); // Since the player is exiting, let's flush any profile // writes for them, and hope we're not breaking TCR // 136... ProfileManager.ForceQueuedProfileWrites(i); // not required - it's done within the // removeLocalPlayerIdx // if(pMinecraft->level->isClientSide) // { // // we need to // remove the qnetplayer, or this player won't be able // to get back into the game until qnet times out and // removes them // g_NetworkManager.NotifyPlayerLeaving(g_NetworkManager.GetLocalPlayerByUserIndex(i)); // } // if there are any tips showing, we need to close them pMinecraft->gui->clearMessages(i); // Make sure we've not got this player selected as // current - this shouldn't be the case anyway pMinecraft->setLocalPlayerIdx( ProfileManager.GetPrimaryPad()); pMinecraft->removeLocalPlayerIdx(i); // Wipe out the tooltips ui.SetTooltips(i, -1); // Change the presence info // Are we offline or online, and how many players are // there if (iPlayerC > 2) // one player is about to leave here // - they'll be set to idle in the // qnet manager player leave { for (int iPlayer = 0; iPlayer < XUSER_MAX_COUNT; iPlayer++) { if ((iPlayer != i) && pMinecraft->localplayers[iPlayer]) { if (g_NetworkManager.IsLocalGame()) { ProfileManager.SetCurrentGameActivity( iPlayer, CONTEXT_PRESENCE_MULTIPLAYEROFFLINE, false); } else { ProfileManager.SetCurrentGameActivity( iPlayer, CONTEXT_PRESENCE_MULTIPLAYER, false); } } } } else { for (int iPlayer = 0; iPlayer < XUSER_MAX_COUNT; iPlayer++) { if ((iPlayer != i) && pMinecraft->localplayers[iPlayer]) { if (g_NetworkManager.IsLocalGame()) { ProfileManager.SetCurrentGameActivity( iPlayer, CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE, false); } else { ProfileManager.SetCurrentGameActivity( iPlayer, CONTEXT_PRESENCE_MULTIPLAYER_1P, false); } } } } SetAction(i, eAppAction_Idle); } break; case eAppAction_ExitPlayerPreLogin: { int iPlayerC = g_NetworkManager.GetPlayerCount(); // Since the player is exiting, let's flush any profile // writes for them, and hope we're not breaking TCR 136... ProfileManager.ForceQueuedProfileWrites(i); // if there are any tips showing, we need to close them pMinecraft->gui->clearMessages(i); // Make sure we've not got this player selected as current - // this shouldn't be the case anyway pMinecraft->setLocalPlayerIdx( ProfileManager.GetPrimaryPad()); pMinecraft->removeLocalPlayerIdx(i); // Wipe out the tooltips ui.SetTooltips(i, -1); // Change the presence info // Are we offline or online, and how many players are there if (iPlayerC > 2) // one player is about to leave here - they'll be // set to idle in the qnet manager player leave { for (int iPlayer = 0; iPlayer < XUSER_MAX_COUNT; iPlayer++) { if ((iPlayer != i) && pMinecraft->localplayers[iPlayer]) { if (g_NetworkManager.IsLocalGame()) { ProfileManager.SetCurrentGameActivity( iPlayer, CONTEXT_PRESENCE_MULTIPLAYEROFFLINE, false); } else { ProfileManager.SetCurrentGameActivity( iPlayer, CONTEXT_PRESENCE_MULTIPLAYER, false); } } } } else { for (int iPlayer = 0; iPlayer < XUSER_MAX_COUNT; iPlayer++) { if ((iPlayer != i) && pMinecraft->localplayers[iPlayer]) { if (g_NetworkManager.IsLocalGame()) { ProfileManager.SetCurrentGameActivity( iPlayer, CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE, false); } else { ProfileManager.SetCurrentGameActivity( iPlayer, CONTEXT_PRESENCE_MULTIPLAYER_1P, false); } } } } SetAction(i, eAppAction_Idle); } break; case eAppAction_ExitWorld: pMinecraft->exitingWorldRightNow = true; SetAction(i, eAppAction_Idle); // If we're already leaving don't exit if (g_NetworkManager.IsLeavingGame()) { break; } pMinecraft->gui->clearMessages(); // turn off the gamertags in splitscreen for the primary // player, since they are about to be made fullscreen ui.HideAllGameUIElements(); // reset the flag stopping new dlc message being shown if // you've seen the message before DisplayNewDLCTipAgain(); // clear the autosave timer that might be on screen ui.ShowAutosaveCountdownTimer(false); // Hide the selected item text ui.HideAllGameUIElements(); // Since the player forced the exit, let's flush any profile // writes, and hope we're not breaking TCR 136... // 4J-PB - cancel any possible std::string verifications // queued with LIVE // InputManager.CancelAllVerifyInProgress(); // In a split screen, only the primary player actually // quits the game, others just remove their players if (i != ProfileManager.GetPrimaryPad()) { // Make sure we've not got this player selected as // current - this shouldn't be the case anyway pMinecraft->setLocalPlayerIdx( ProfileManager.GetPrimaryPad()); pMinecraft->removeLocalPlayerIdx(i); SetAction(i, eAppAction_Idle); return; } // flag to capture the save thumbnail SetAction(i, eAppAction_ExitWorldCapturedThumbnail, param); // Change the presence info // Are we offline or online, and how many players are there if (g_NetworkManager.GetPlayerCount() > 1) { for (int j = 0; j < XUSER_MAX_COUNT; j++) { if (pMinecraft->localplayers[j]) { if (g_NetworkManager.IsLocalGame()) { app.SetRichPresenceContext( j, CONTEXT_GAME_STATE_BLANK); ProfileManager.SetCurrentGameActivity( j, CONTEXT_PRESENCE_MULTIPLAYEROFFLINE, false); } else { app.SetRichPresenceContext( j, CONTEXT_GAME_STATE_BLANK); ProfileManager.SetCurrentGameActivity( j, CONTEXT_PRESENCE_MULTIPLAYER, false); } } } } else { app.SetRichPresenceContext(i, CONTEXT_GAME_STATE_BLANK); if (g_NetworkManager.IsLocalGame()) { ProfileManager.SetCurrentGameActivity( i, CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE, false); } else { ProfileManager.SetCurrentGameActivity( i, CONTEXT_PRESENCE_MULTIPLAYER_1P, false); } } break; case eAppAction_ExitWorldCapturedThumbnail: { SetAction(i, eAppAction_Idle); // Stop app running SetGameStarted(false); SetChangingSessionType( true); // Added to stop handling ethernet disconnects ui.CloseAllPlayersScenes(); // turn off the gamertags in splitscreen for the primary // player, since they are about to be made fullscreen ui.HideAllGameUIElements(); // 4J Stu - Fix for #12368 - Crash: Game crashes when saving // then exiting and selecting to save for (unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx) { // 4J Stu - Fix for #13257 - CRASH: Gameplay: Title // crashed after exiting the tutorial It doesn't matter // if they were in the tutorial already pMinecraft->playerLeftTutorial(idx); } LoadingInputParams* loadingParams = new LoadingInputParams(); loadingParams->func = &UIScene_PauseMenu::ExitWorldThreadProc; loadingParams->lpParam = param; UIFullscreenProgressCompletionData* completionData = new UIFullscreenProgressCompletionData(); // If param is non-null then this is a forced exit by the // server, so make sure the player knows why 4J Stu - // Changed - Don't use the FullScreenProgressScreen for // action, use a dialog instead completionData->bRequiresUserAction = false; //(param != nullptr) ? true : false; completionData->bShowTips = (param != nullptr) ? false : true; completionData->bShowBackground = true; completionData->bShowLogo = true; completionData->type = e_ProgressCompletion_NavigateToHomeMenu; completionData->iPad = DEFAULT_XUI_MENU_USER; loadingParams->completionData = completionData; ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_FullscreenProgress, loadingParams); } break; case eAppAction_ExitWorldTrial: { SetAction(i, eAppAction_Idle); pMinecraft->gui->clearMessages(); // turn off the gamertags in splitscreen for the primary // player, since they are about to be made fullscreen ui.HideAllGameUIElements(); // Stop app running SetGameStarted(false); ui.CloseAllPlayersScenes(); // 4J Stu - Fix for #12368 - Crash: Game crashes when saving // then exiting and selecting to save for (unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx) { // 4J Stu - Fix for #13257 - CRASH: Gameplay: Title // crashed after exiting the tutorial It doesn't matter // if they were in the tutorial already pMinecraft->playerLeftTutorial(idx); } LoadingInputParams* loadingParams = new LoadingInputParams(); loadingParams->func = &UIScene_PauseMenu::ExitWorldThreadProc; loadingParams->lpParam = param; UIFullscreenProgressCompletionData* completionData = new UIFullscreenProgressCompletionData(); completionData->bShowBackground = true; completionData->bShowLogo = true; completionData->type = e_ProgressCompletion_NavigateToHomeMenu; completionData->iPad = DEFAULT_XUI_MENU_USER; loadingParams->completionData = completionData; ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_FullscreenProgress, loadingParams); } break; case eAppAction_ExitTrial: // XLaunchNewImage(XLAUNCH_KEYWORD_DASH_ARCADE, 0); ExitGame(); break; case eAppAction_Respawn: { ConnectionProgressParams* param = new ConnectionProgressParams(); param->iPad = i; param->stringId = IDS_PROGRESS_RESPAWNING; param->showTooltips = false; param->setFailTimer = false; ui.NavigateToScene(i, eUIScene_ConnectingProgress, param); // Need to reset this incase the player has already died and // respawned pMinecraft->localplayers[i]->SetPlayerRespawned(false); SetAction(i, eAppAction_WaitForRespawnComplete); if (app.GetLocalPlayerCount() > 1) { // In split screen mode, we don't want to do any async // loading or flushing of the cache, just a simple // respawn pMinecraft->localplayers[i]->respawn(); // If the respawn requires a dimension change then the // action will have changed // if(app.GetXuiAction(i) == eAppAction_Respawn) //{ // SetAction(i,eAppAction_Idle); // CloseXuiScenes(i); //} } else { // SetAction(i,eAppAction_WaitForRespawnComplete); // LoadingInputParams *loadingParams = new // LoadingInputParams(); loadingParams->func = // &CScene_Death::RespawnThreadProc; // loadingParams->lpParam = (void*)i; // Disable game & update thread whilst we do any of this // app.SetGameStarted(false); pMinecraft->gameRenderer->DisableUpdateThread(); // 4J Stu - We don't need this on a thread in // multiplayer as respawning is asynchronous. pMinecraft->localplayers[i]->respawn(); // app.SetGameStarted(true); pMinecraft->gameRenderer->EnableUpdateThread(); // UIFullscreenProgressCompletionData *completionData = // new UIFullscreenProgressCompletionData(); // completionData->bShowBackground=true; // completionData->bShowLogo=true; // completionData->type = // e_ProgressCompletion_CloseUIScenes; // completionData->iPad = i; // loadingParams->completionData = completionData; // app.NavigateToScene(i,eUIScene_FullscreenProgress, // loadingParams, true); } } break; case eAppAction_WaitForRespawnComplete: player = pMinecraft->localplayers[i]; if (player != nullptr && player->GetPlayerRespawned()) { SetAction(i, eAppAction_Idle); if (ui.IsSceneInStack(i, eUIScene_EndPoem)) { ui.NavigateBack(i, false, eUIScene_EndPoem); } else { ui.CloseUIScenes(i); } // clear the progress messages // pMinecraft->progressRenderer->progressStart(-1); // pMinecraft->progressRenderer->progressStage(-1); } else if (!g_NetworkManager.IsInGameplay()) { SetAction(i, eAppAction_Idle); } break; case eAppAction_WaitForDimensionChangeComplete: player = pMinecraft->localplayers[i]; if (player != nullptr && player->connection && player->connection->isStarted()) { SetAction(i, eAppAction_Idle); ui.CloseUIScenes(i); } else if (!g_NetworkManager.IsInGameplay()) { SetAction(i, eAppAction_Idle); } break; case eAppAction_PrimaryPlayerSignedOut: { // SetAction(i,eAppAction_Idle); // clear the autosavetimer that might be displayed ui.ShowAutosaveCountdownTimer(false); // If the player signs out before the game started the // server can be killed a bit earlier to stop the loading or // saving of a new game continuing running while the // UI/Guide is up if (!app.GetGameStarted()) MinecraftServer::HaltServer(true); // inform the player they are being returned to the menus // because they signed out StorageManager.SetSaveDeviceSelected(i, false); // need to clear the player stats - can't assume it'll be // done in setlevel - we may not be in the game StatsCounter* pStats = Minecraft::GetInstance()->stats[i]; pStats->clear(); // 4J-PB - the libs will display the Returned to Title // screen unsigned int // uiIDA[1]; uiIDA[0]=IDS_CONFIRM_OK; // // ui.RequestMessageBox(IDS_RETURNEDTOMENU_TITLE, // IDS_RETURNEDTOTITLESCREEN_TEXT, uiIDA, 1, // i,&Game::PrimaryPlayerSignedOutReturned,this,app.GetStringTable()); if (g_NetworkManager.IsInSession()) { app.SetAction( i, eAppAction_PrimaryPlayerSignedOutReturned); } else { app.SetAction( i, eAppAction_PrimaryPlayerSignedOutReturned_Menus); MinecraftServer::resetFlags(); } } break; case eAppAction_EthernetDisconnected: { app.DebugPrintf( "Handling eAppAction_EthernetDisconnected\n"); SetAction(i, eAppAction_Idle); // 4J Stu - Fix for #12530 -TCR 001 BAS Game Stability: // Title will crash if the player disconnects while starting // a new world and then opts to play the tutorial once they // have been returned to the Main Menu. if (!g_NetworkManager.IsLeavingGame()) { app.DebugPrintf( "Handling eAppAction_EthernetDisconnected - Not " "leaving game\n"); // 4J-PB - not the same as a signout. We should only // leave the game if this machine is not the host. We // shouldn't get rid of the save device either. if (g_NetworkManager.IsHost()) { app.DebugPrintf( "Handling eAppAction_EthernetDisconnected - Is " "Host\n"); // If it's already a local game, then an ethernet // disconnect should have no effect if (!g_NetworkManager.IsLocalGame() && g_NetworkManager.IsInGameplay()) { // Change the session to an offline session SetAction(i, eAppAction_ChangeSessionType); } else if (!g_NetworkManager.IsLocalGame() && !g_NetworkManager.IsInGameplay()) { // There are two cases here, either: // 1. We're early enough in the // create/load game that we can do a really // minimal shutdown or // 2. We're far enough in (game has started // but the actual game started flag hasn't // been set) that we should just wait until // we're in the game and switch to offline // mode // If there's a non-null level then, for our // purposes, the game has started bool gameStarted = false; for (int j = 0; j < pMinecraft->levels.size(); j++) { if (pMinecraft->levels.data()[j] != nullptr) { gameStarted = true; break; } } if (!gameStarted) { // 1. Exit MinecraftServer::HaltServer(); // Fix for #12530 - TCR 001 BAS Game // Stability: Title will crash if the player // disconnects while starting a new world // and then opts to play the tutorial once // they have been returned to the Main Menu. // 4J Stu - Leave the session g_NetworkManager.LeaveGame(false); // need to clear the player stats - can't // assume it'll be done in setlevel - we may // not be in the game StatsCounter* pStats = Minecraft::GetInstance()->stats[i]; pStats->clear(); unsigned int uiIDA[1]; uiIDA[0] = IDS_CONFIRM_OK; ui.RequestErrorMessage( g_NetworkManager.CorrectErrorIDS( IDS_CONNECTION_LOST), g_NetworkManager.CorrectErrorIDS( IDS_CONNECTION_LOST_LIVE), uiIDA, 1, i, &Game:: EthernetDisconnectReturned, this); } else { // 2. Switch to offline SetAction(i, eAppAction_ChangeSessionType); } } } else { { app.DebugPrintf( "Handling eAppAction_EthernetDisconnected " "- Not host\n"); // need to clear the player stats - can't assume // it'll be done in setlevel - we may not be in // the game StatsCounter* pStats = Minecraft::GetInstance()->stats[i]; pStats->clear(); unsigned int uiIDA[1]; uiIDA[0] = IDS_CONFIRM_OK; ui.RequestErrorMessage( g_NetworkManager.CorrectErrorIDS( IDS_CONNECTION_LOST), g_NetworkManager.CorrectErrorIDS( IDS_CONNECTION_LOST_LIVE), uiIDA, 1, i, &Game::EthernetDisconnectReturned, this); } } } } break; // We currently handle both these returns the same way. case eAppAction_EthernetDisconnectedReturned: case eAppAction_PrimaryPlayerSignedOutReturned: { SetAction(i, eAppAction_Idle); pMinecraft->gui->clearMessages(); // turn off the gamertags in splitscreen for the primary // player, since they are about to be made fullscreen ui.HideAllGameUIElements(); // set the state back to pre-game ProfileManager.ResetProfileProcessState(); if (g_NetworkManager.IsLeavingGame()) { // 4J Stu - If we are already leaving the game, then we // just need to signal that the player signed out to // stop saves pMinecraft->progressRenderer->progressStartNoAbort( IDS_EXITING_GAME); pMinecraft->progressRenderer->progressStage(-1); // This has no effect on client machines MinecraftServer::HaltServer(true); } else { // Stop app running SetGameStarted(false); // turn off the gamertags in splitscreen for the primary // player, since they are about to be made fullscreen ui.HideAllGameUIElements(); ui.CloseAllPlayersScenes(); // 4J Stu - Fix for #12368 - Crash: Game crashes when // saving then exiting and selecting to save for (unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx) { // 4J Stu - Fix for #13257 - CRASH: Gameplay: Title // crashed after exiting the tutorial It doesn't // matter if they were in the tutorial already pMinecraft->playerLeftTutorial(idx); } LoadingInputParams* loadingParams = new LoadingInputParams(); loadingParams->func = &Game::SignoutExitWorldThreadProc; UIFullscreenProgressCompletionData* completionData = new UIFullscreenProgressCompletionData(); completionData->bShowBackground = true; completionData->bShowLogo = true; completionData->iPad = DEFAULT_XUI_MENU_USER; completionData->type = e_ProgressCompletion_NavigateToHomeMenu; loadingParams->completionData = completionData; ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_FullscreenProgress, loadingParams); } } break; case eAppAction_PrimaryPlayerSignedOutReturned_Menus: SetAction(i, eAppAction_Idle); // set the state back to pre-game ProfileManager.ResetProfileProcessState(); // clear the save device StorageManager.SetSaveDeviceSelected(i, false); ui.UpdatePlayerBasePositions(); // there are multiple layers in the help menu, so a navigate // back isn't enough ui.NavigateToHomeMenu(); break; case eAppAction_EthernetDisconnectedReturned_Menus: SetAction(i, eAppAction_Idle); // set the state back to pre-game ProfileManager.ResetProfileProcessState(); ui.UpdatePlayerBasePositions(); // there are multiple layers in the help menu, so a navigate // back isn't enough ui.NavigateToHomeMenu(); break; case eAppAction_TrialOver: { SetAction(i, eAppAction_Idle); unsigned int uiIDA[2]; uiIDA[0] = IDS_UNLOCK_TITLE; uiIDA[1] = IDS_EXIT_GAME; ui.RequestErrorMessage( IDS_TRIALOVER_TITLE, IDS_TRIALOVER_TEXT, uiIDA, 2, i, &Game::TrialOverReturned, this); } break; // INVITES case eAppAction_DashboardTrialJoinFromInvite: { SetAction(i, eAppAction_Idle); unsigned int uiIDA[2]; uiIDA[0] = IDS_CONFIRM_OK; uiIDA[1] = IDS_CONFIRM_CANCEL; ui.RequestErrorMessage( IDS_UNLOCK_TITLE, IDS_UNLOCK_ACCEPT_INVITE, uiIDA, 2, i, &Game::UnlockFullInviteReturned, this); } break; case eAppAction_ExitAndJoinFromInvite: { unsigned int uiIDA[3]; SetAction(i, eAppAction_Idle); // Check the player really wants to do this if (!StorageManager.GetSaveDisabled() && i == ProfileManager.GetPrimaryPad() && g_NetworkManager.IsHost() && GetGameStarted()) { uiIDA[0] = IDS_CONFIRM_CANCEL; uiIDA[1] = IDS_EXIT_GAME_SAVE; uiIDA[2] = IDS_EXIT_GAME_NO_SAVE; ui.RequestAlertMessage( IDS_EXIT_GAME, IDS_CONFIRM_LEAVE_VIA_INVITE, uiIDA, 3, i, &Game:: ExitAndJoinFromInviteSaveDialogReturned, this); } else { uiIDA[0] = IDS_CONFIRM_CANCEL; uiIDA[1] = IDS_CONFIRM_OK; ui.RequestAlertMessage( IDS_EXIT_GAME, IDS_CONFIRM_LEAVE_VIA_INVITE, uiIDA, 2, i, &Game::ExitAndJoinFromInvite, this); } } break; case eAppAction_ExitAndJoinFromInviteConfirmed: { SetAction(i, eAppAction_Idle); pMinecraft->gui->clearMessages(); // turn off the gamertags in splitscreen for the primary // player, since they are about to be made fullscreen ui.HideAllGameUIElements(); // Stop app running SetGameStarted(false); ui.CloseAllPlayersScenes(); // 4J Stu - Fix for #12368 - Crash: Game crashes when saving // then exiting and selecting to save for (unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx) { // 4J Stu - Fix for #13257 - CRASH: Gameplay: Title // crashed after exiting the tutorial It doesn't matter // if they were in the tutorial already pMinecraft->playerLeftTutorial(idx); } // 4J-PB - may have been using a texture pack with audio , // so clean up anything texture pack related here // unload any texture pack audio // if there is audio in use, clear out the audio, and // unmount the pack TexturePack* pTexPack = Minecraft::GetInstance()->skins->getSelected(); DLCTexturePack* pDLCTexPack = nullptr; if (pTexPack->hasAudio()) { // get the dlc texture pack, and store it pDLCTexPack = (DLCTexturePack*)pTexPack; } // change to the default texture pack pMinecraft->skins->selectTexturePackById( TexturePackRepository::DEFAULT_TEXTURE_PACK_ID); if (pTexPack->hasAudio()) { // need to stop the streaming audio - by playing // streaming audio from the default texture pack now // reset the streaming sounds back to the normal ones pMinecraft->soundEngine->SetStreamingSounds( eStream_Overworld_Calm1, eStream_Overworld_piano3, eStream_Nether1, eStream_Nether4, eStream_end_dragon, eStream_end_end, eStream_CD_1); pMinecraft->soundEngine->playStreaming(L"", 0, 0, 0, 1, 1); const unsigned int result = StorageManager.UnmountInstalledDLC("TPACK"); app.DebugPrintf("Unmount result is %d\n", result); } LoadingInputParams* loadingParams = new LoadingInputParams(); loadingParams->func = &CGameNetworkManager::ExitAndJoinFromInviteThreadProc; loadingParams->lpParam = (void*)&m_InviteData; UIFullscreenProgressCompletionData* completionData = new UIFullscreenProgressCompletionData(); completionData->bShowBackground = true; completionData->bShowLogo = true; completionData->iPad = DEFAULT_XUI_MENU_USER; completionData->type = e_ProgressCompletion_NoAction; loadingParams->completionData = completionData; ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_FullscreenProgress, loadingParams); } break; case eAppAction_JoinFromInvite: { SetAction(i, eAppAction_Idle); // 4J Stu - Move this state block from // IPlatformNetwork::ExitAndJoinFromInviteThreadProc, // as g_NetworkManager.JoinGameFromInviteInfo ultimately can // call NavigateToScene, /// and we should only be calling that from the main thread app.SetTutorialMode(false); g_NetworkManager.SetLocalGame(false); JoinFromInviteData* inviteData = (JoinFromInviteData*)param; // 4J-PB - clear any previous connection errors Minecraft::GetInstance()->clearConnectionFailed(); app.DebugPrintf( "Changing Primary Pad on an invite accept - pad was " "%d, and is now %d\n", ProfileManager.GetPrimaryPad(), inviteData->dwUserIndex); ProfileManager.SetLockedProfile(inviteData->dwUserIndex); ProfileManager.SetPrimaryPad(inviteData->dwUserIndex); // change the minecraft player name Minecraft::GetInstance()->user->name = convStringToWstring(ProfileManager.GetGamertag( ProfileManager.GetPrimaryPad())); bool success = g_NetworkManager.JoinGameFromInviteInfo( inviteData->dwUserIndex, // dwUserIndex inviteData->dwLocalUsersMask, // dwUserMask inviteData->pInviteInfo); // pInviteInfo if (!success) { app.DebugPrintf("Failed joining game from invite\n"); // return hr; // 4J Stu - Copied this from XUI_FullScreenProgress to // properly handle the fail case, as the thread will no // longer be failing unsigned int uiIDA[1]; uiIDA[0] = IDS_CONFIRM_OK; ui.RequestErrorMessage( IDS_CONNECTION_FAILED, IDS_CONNECTION_LOST_SERVER, uiIDA, 1, ProfileManager.GetPrimaryPad()); ui.NavigateToHomeMenu(); ui.UpdatePlayerBasePositions(); } } break; case eAppAction_ChangeSessionType: { // If we are not in gameplay yet, then wait until the server // is setup before changing the session type if (g_NetworkManager.IsInGameplay()) { // This kicks off a thread that waits for the server to // end, then closes the current session, starts a new // one and joins the local players into it SetAction(i, eAppAction_Idle); if (!GetChangingSessionType() && !g_NetworkManager.IsLocalGame()) { SetGameStarted(false); SetChangingSessionType(true); SetReallyChangingSessionType(true); // turn off the gamertags in splitscreen for the // primary player, since they are about to be made // fullscreen ui.HideAllGameUIElements(); if (!ui.IsSceneInStack( ProfileManager.GetPrimaryPad(), eUIScene_EndPoem)) { ui.CloseAllPlayersScenes(); } ui.ShowOtherPlayersBaseScene( ProfileManager.GetPrimaryPad(), true); // Remove this line to fix: // #49084 - TU5: Code: Gameplay: The title crashes // every time client navigates to 'Play game' menu // and loads/creates new game after a "Connection to // Xbox LIVE was lost" message has appeared. // app.NavigateToScene(0,eUIScene_Main); LoadingInputParams* loadingParams = new LoadingInputParams(); loadingParams->func = &CGameNetworkManager:: ChangeSessionTypeThreadProc; loadingParams->lpParam = nullptr; UIFullscreenProgressCompletionData* completionData = new UIFullscreenProgressCompletionData(); completionData->bRequiresUserAction = true; completionData->bShowBackground = true; completionData->bShowLogo = true; completionData->iPad = DEFAULT_XUI_MENU_USER; if (ui.IsSceneInStack( ProfileManager.GetPrimaryPad(), eUIScene_EndPoem)) { completionData->type = e_ProgressCompletion_NavigateBackToScene; completionData->scene = eUIScene_EndPoem; } else { completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; } loadingParams->completionData = completionData; ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_FullscreenProgress, loadingParams); } } else if (g_NetworkManager.IsLeavingGame()) { // If we are leaving the game, then ignore the state // change SetAction(i, eAppAction_Idle); } } break; case eAppAction_SetDefaultOptions: SetAction(i, eAppAction_Idle); SetDefaultOptions((C_4JProfile::PROFILESETTINGS*)param, i); // if the profile data has been changed, then force a // profile write It seems we're allowed to break the 5 // minute rule if it's the result of a user action CheckGameSettingsChanged(true, i); break; case eAppAction_RemoteServerSave: { // If the remote server save has already finished, don't // complete the action if (GetGameStarted()) { SetAction(ProfileManager.GetPrimaryPad(), eAppAction_Idle); break; } SetAction(i, eAppAction_WaitRemoteServerSaveComplete); for (unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) { ui.CloseUIScenes(i, true); } // turn off the gamertags in splitscreen for the primary // player, since they are about to be made fullscreen ui.HideAllGameUIElements(); LoadingInputParams* loadingParams = new LoadingInputParams(); loadingParams->func = &Game::RemoteSaveThreadProc; loadingParams->lpParam = nullptr; UIFullscreenProgressCompletionData* completionData = new UIFullscreenProgressCompletionData(); completionData->bRequiresUserAction = false; completionData->bShowBackground = true; completionData->bShowLogo = true; completionData->iPad = DEFAULT_XUI_MENU_USER; if (ui.IsSceneInStack(ProfileManager.GetPrimaryPad(), eUIScene_EndPoem)) { completionData->type = e_ProgressCompletion_NavigateBackToScene; completionData->scene = eUIScene_EndPoem; } else { completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; } loadingParams->completionData = completionData; loadingParams->cancelFunc = &Game::ExitGameFromRemoteSave; loadingParams->cancelText = IDS_TOOLTIPS_EXIT; ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_FullscreenProgress, loadingParams); } break; case eAppAction_WaitRemoteServerSaveComplete: // Do nothing break; case eAppAction_FailedToJoinNoPrivileges: { unsigned int uiIDA[1]; uiIDA[0] = IDS_CONFIRM_OK; C4JStorage::EMessageResult result = ui.RequestErrorMessage( IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad()); if (result != C4JStorage::EMessage_Busy) SetAction(i, eAppAction_Idle); } break; case eAppAction_ProfileReadError: // Return player to the main menu - code largely copied from // that for handling eAppAction_PrimaryPlayerSignedOut, // although I don't think we should have got as far as // needing to halt the server, or running the game, before // returning to the menu if (!app.GetGameStarted()) MinecraftServer::HaltServer(true); if (g_NetworkManager.IsInSession()) { app.SetAction( i, eAppAction_PrimaryPlayerSignedOutReturned); } else { app.SetAction( i, eAppAction_PrimaryPlayerSignedOutReturned_Menus); MinecraftServer::resetFlags(); } break; case eAppAction_BanLevel: { // It's possible that this state can get set after the game // has been exited (e.g. by network disconnection) so we // can't ban the level at that point if (g_NetworkManager.IsInGameplay() && !g_NetworkManager.IsLeavingGame()) { // primary player would exit the world, secondary would // exit the player if (ProfileManager.GetPrimaryPad() == i) { SetAction(i, eAppAction_ExitWorld); } else { SetAction(i, eAppAction_ExitPlayer); } } } break; case eAppAction_LevelInBanLevelList: { unsigned int uiIDA[2]; uiIDA[0] = IDS_BUTTON_REMOVE_FROM_BAN_LIST; uiIDA[1] = IDS_EXIT_GAME; // pass in the gamertag format std::string wchar_t wchFormat[40]; INetworkPlayer* player = g_NetworkManager.GetLocalPlayerByUserIndex(i); // If not the primary player, but the primary player has // banned this level and decided not to unban then we may // have left the game by now if (player) { swprintf(wchFormat, 40, L"%ls\n\n%%ls", player->GetOnlineName()); C4JStorage::EMessageResult result = ui.RequestErrorMessage( IDS_BANNED_LEVEL_TITLE, IDS_PLAYER_BANNED_LEVEL, uiIDA, 2, i, &Game::BannedLevelDialogReturned, this, wchFormat); if (result != C4JStorage::EMessage_Busy) SetAction(i, eAppAction_Idle); } else { SetAction(i, eAppAction_Idle); } } break; case eAppAction_DebugText: // launch the xui for text entry { SetAction(i, eAppAction_Idle); } break; case eAppAction_ReloadTexturePack: { SetAction(i, eAppAction_Idle); Minecraft* pMinecraft = Minecraft::GetInstance(); pMinecraft->textures->reloadAll(); pMinecraft->skins->updateUI(); if (!pMinecraft->skins->isUsingDefaultSkin()) { TexturePack* pTexturePack = pMinecraft->skins->getSelected(); DLCPack* pDLCPack = pTexturePack->getDLCPack(); bool purchased = false; // do we have a license? if (pDLCPack && pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"")) { purchased = true; } } // 4J-PB - If the texture pack has audio, we need to switch // to this if (pMinecraft->skins->getSelected()->hasAudio()) { Minecraft::GetInstance()->soundEngine->playStreaming( L"", 0, 0, 0, 1, 1); } } break; case eAppAction_ReloadFont: { app.DebugPrintf( "[Consoles_App] eAppAction_ReloadFont, ingame='%s'.\n", app.GetGameStarted() ? "Yes" : "No"); SetAction(i, eAppAction_Idle); ui.SetTooltips(i, -1); ui.ReloadSkin(); ui.StartReloadSkinThread(); ui.setCleanupOnReload(); } break; case eAppAction_TexturePackRequired: { unsigned int uiIDA[2]; uiIDA[0] = IDS_TEXTUREPACK_FULLVERSION; uiIDA[1] = IDS_TEXTURE_PACK_TRIALVERSION; // Give the player a warning about the texture pack missing ui.RequestErrorMessage( IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 2, ProfileManager.GetPrimaryPad(), &Game::TexturePackDialogReturned, this); SetAction(i, eAppAction_Idle); } break; default: break; } } // Any TMS actions? eTMS = app.GetTMSAction(i); if (eTMS != eTMSAction_Idle) { switch (eTMS) { // TMS++ actions case eTMSAction_TMSPP_RetrieveFiles_CreateLoad_SignInReturned: case eTMSAction_TMSPP_RetrieveFiles_RunPlayGame: SetTMSAction(i, eTMSAction_TMSPP_UserFileList); break; case eTMSAction_TMSPP_UserFileList: // retrieve the file list first SetTMSAction(i, eTMSAction_TMSPP_XUIDSFile); break; case eTMSAction_TMSPP_XUIDSFile: SetTMSAction(i, eTMSAction_TMSPP_DLCFile); break; case eTMSAction_TMSPP_DLCFile: SetTMSAction(i, eTMSAction_TMSPP_BannedListFile); break; case eTMSAction_TMSPP_BannedListFile: // If we have one in TMSPP, then we can assume we can ignore // TMS SetTMSAction(i, eTMSAction_TMS_RetrieveFiles_Complete); break; // SPECIAL CASE - where the user goes directly in to Help & // Options from the main menu case eTMSAction_TMSPP_RetrieveFiles_HelpAndOptions: case eTMSAction_TMSPP_RetrieveFiles_DLCMain: // retrieve the file list first SetTMSAction(i, eTMSAction_TMSPP_DLCFileOnly); break; case eTMSAction_TMSPP_RetrieveUserFilelist_DLCFileOnly: SetTMSAction(i, eTMSAction_TMSPP_DLCFileOnly); break; case eTMSAction_TMSPP_DLCFileOnly: SetTMSAction(i, eTMSAction_TMSPP_RetrieveFiles_Complete); break; case eTMSAction_TMSPP_RetrieveFiles_Complete: SetTMSAction(i, eTMSAction_Idle); break; // TMS files /* case eTMSAction_TMS_RetrieveFiles_CreateLoad_SignInReturned: case eTMSAction_TMS_RetrieveFiles_RunPlayGame: #ifdef 0 SetTMSAction(i,eTMSAction_TMS_XUIDSFile_Waiting); // pass in the next app action on the call or callback completing app.ReadXuidsFileFromTMS(i,eTMSAction_TMS_DLCFile,true); #else SetTMSAction(i,eTMSAction_TMS_DLCFile); #endif break; case eTMSAction_TMS_DLCFile: SetTMSAction(i,eTMSAction_TMS_BannedListFile); break; case eTMSAction_TMS_RetrieveFiles_HelpAndOptions: case eTMSAction_TMS_RetrieveFiles_DLCMain: SetTMSAction(i,eTMSAction_Idle); break; case eTMSAction_TMS_BannedListFile: break; */ case eTMSAction_TMS_RetrieveFiles_Complete: SetTMSAction(i, eTMSAction_Idle); // if(StorageManager.SetSaveDevice(&CScene_Main::DeviceSelectReturned,pClass)) // { // // save device already // selected // // ensure we've applied // this player's settings // app.ApplyGameSettingsChanged(ProfileManager.GetPrimaryPad()); // app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_MultiGameJoinLoad); // } break; default: break; } } } } int Game::BannedLevelDialogReturned( void* pParam, int iPad, const C4JStorage::EMessageResult result) { Game* pApp = (Game*)pParam; // Minecraft *pMinecraft=Minecraft::GetInstance(); if (result == C4JStorage::EMessage_ResultAccept) { } else { if (iPad == ProfileManager.GetPrimaryPad()) { pApp->SetAction(iPad, eAppAction_ExitWorld); } else { pApp->SetAction(iPad, eAppAction_ExitPlayer); } } return 0; } // loadMediaArchive and loadStringTable moved to ArchiveManager/LocalizationManager int Game::PrimaryPlayerSignedOutReturned( void* pParam, int iPad, const C4JStorage::EMessageResult) { // Game* pApp = (Game*)pParam; // Minecraft *pMinecraft=Minecraft::GetInstance(); // if the player is null, we're in the menus // if(Minecraft::GetInstance()->player!=nullptr) // We always create a session before kicking of any of the game code, so // even though we may still be joining/creating a game at this point we want // to handle it differently from just being in a menu if (g_NetworkManager.IsInSession()) { app.SetAction(iPad, eAppAction_PrimaryPlayerSignedOutReturned); } else { app.SetAction(iPad, eAppAction_PrimaryPlayerSignedOutReturned_Menus); } return 0; } int Game::EthernetDisconnectReturned( void* pParam, int iPad, const C4JStorage::EMessageResult) { // Game* pApp = (Game*)pParam; Minecraft* pMinecraft = Minecraft::GetInstance(); // if the player is null, we're in the menus if (Minecraft::GetInstance()->player != nullptr) { app.SetAction(pMinecraft->player->GetXboxPad(), eAppAction_EthernetDisconnectedReturned); } else { // 4J-PB - turn off the PSN store icon just in case this happened when // we were in one of the DLC menus app.SetAction(iPad, eAppAction_EthernetDisconnectedReturned_Menus); } return 0; } int Game::SignoutExitWorldThreadProc(void* lpParameter) { // Share AABB & Vec3 pools with default (main thread) - should be ok as long // as we don't tick the main thread whilst this thread is running Compression::UseDefaultThreadStorage(); // app.SetGameStarted(false); Minecraft* pMinecraft = Minecraft::GetInstance(); int exitReasonStringId = -1; bool saveStats = false; if (pMinecraft->isClientSide() || g_NetworkManager.IsInSession()) { if (lpParameter != nullptr) { switch (app.GetDisconnectReason()) { case DisconnectPacket::eDisconnect_Kicked: exitReasonStringId = IDS_DISCONNECTED_KICKED; break; case DisconnectPacket::eDisconnect_NoUGC_AllLocal: exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL; break; case DisconnectPacket::eDisconnect_NoUGC_Single_Local: exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL; break; case DisconnectPacket::eDisconnect_NoFlying: exitReasonStringId = IDS_DISCONNECTED_FLYING; break; case DisconnectPacket::eDisconnect_OutdatedServer: exitReasonStringId = IDS_DISCONNECTED_SERVER_OLD; break; case DisconnectPacket::eDisconnect_OutdatedClient: exitReasonStringId = IDS_DISCONNECTED_CLIENT_OLD; break; default: exitReasonStringId = IDS_DISCONNECTED; } pMinecraft->progressRenderer->progressStartNoAbort( exitReasonStringId); // 4J - Force a disconnection, this handles the situation that the // server has already disconnected if (pMinecraft->levels[0] != nullptr) pMinecraft->levels[0]->disconnect(false); if (pMinecraft->levels[1] != nullptr) pMinecraft->levels[1]->disconnect(false); } else { exitReasonStringId = IDS_EXITING_GAME; pMinecraft->progressRenderer->progressStartNoAbort( IDS_EXITING_GAME); if (pMinecraft->levels[0] != nullptr) pMinecraft->levels[0]->disconnect(); if (pMinecraft->levels[1] != nullptr) pMinecraft->levels[1]->disconnect(); } // 4J Stu - This only does something if we actually have a server, so // don't need to do any other checks MinecraftServer::HaltServer(true); // We need to call the stats & leaderboards save before we exit the // session // pMinecraft->forceStatsSave(); saveStats = false; // 4J Stu - Leave the session once the disconnect packet has been sent g_NetworkManager.LeaveGame(false); } else { if (lpParameter != nullptr) { switch (app.GetDisconnectReason()) { case DisconnectPacket::eDisconnect_Kicked: exitReasonStringId = IDS_DISCONNECTED_KICKED; break; case DisconnectPacket::eDisconnect_NoUGC_AllLocal: exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL; break; case DisconnectPacket::eDisconnect_NoUGC_Single_Local: exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL; break; case DisconnectPacket::eDisconnect_OutdatedServer: exitReasonStringId = IDS_DISCONNECTED_SERVER_OLD; break; case DisconnectPacket::eDisconnect_OutdatedClient: exitReasonStringId = IDS_DISCONNECTED_CLIENT_OLD; default: exitReasonStringId = IDS_DISCONNECTED; } pMinecraft->progressRenderer->progressStartNoAbort( exitReasonStringId); } } pMinecraft->setLevel(nullptr, exitReasonStringId, nullptr, saveStats, true); // 4J-JEV: Fix for #106402 - TCR #014 BAS Debug Output: // TU12: Mass Effect Mash-UP: Save file "Default_DisplayName" is created on // all storage devices after signing out from a re-launched pre-generated // world app.m_gameRules.unloadCurrentGameRules(); // MinecraftServer::resetFlags(); // We can't start/join a new game until the session is destroyed, so wait // for it to be idle again while (g_NetworkManager.IsInSession()) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } return 0; } int Game::UnlockFullInviteReturned(void* pParam, int iPad, C4JStorage::EMessageResult result) { // Game* pApp = (Game*)pParam; Minecraft* pMinecraft = Minecraft::GetInstance(); bool bNoPlayer; // bug 11285 - TCR 001: BAS Game Stability: CRASH - When trying to join a // full version game with a trial version, the trial crashes 4J-PB - we may // be in the main menus here, and we don't have a pMinecraft->player if (pMinecraft->player == nullptr) { bNoPlayer = true; } return 0; } int Game::UnlockFullSaveReturned(void* pParam, int iPad, C4JStorage::EMessageResult result) { return 0; } int Game::UnlockFullExitReturned(void* pParam, int iPad, C4JStorage::EMessageResult result) { Game* pApp = (Game*)pParam; Minecraft* pMinecraft = Minecraft::GetInstance(); if (result != C4JStorage::EMessage_ResultAccept) { pApp->SetAction(pMinecraft->player->GetXboxPad(), eAppAction_ExitWorldTrial); } return 0; } int Game::TrialOverReturned(void* pParam, int iPad, C4JStorage::EMessageResult result) { Game* pApp = (Game*)pParam; Minecraft* pMinecraft = Minecraft::GetInstance(); if (result != C4JStorage::EMessage_ResultAccept) { pApp->SetAction(pMinecraft->player->GetXboxPad(), eAppAction_ExitTrial); } return 0; } void Game::ProfileReadErrorCallback(void* pParam) { Game* pApp = (Game*)pParam; int iPrimaryPlayer = ProfileManager.GetPrimaryPad(); pApp->SetAction(iPrimaryPlayer, eAppAction_ProfileReadError); } void Game::ClearSignInChangeUsersMask() { // 4J-PB - When in the main menu, the user is on pad 0, and any change they // make to their profile will be to pad 0 data If they then go in as a // secondary player to a splitscreen game, their profile will not be read // again on pad 1 if they were previously in a splitscreen game This is // because m_uiLastSignInData remembers they were in previously, and doesn't // read the profile data for them again Fix this by resetting the // m_uiLastSignInData on pressing play game for secondary users. The Primary // user does a read profile on play game anyway int iPrimaryPlayer = ProfileManager.GetPrimaryPad(); if (m_uiLastSignInData != 0) { if (iPrimaryPlayer >= 0) { m_uiLastSignInData = 1 << iPrimaryPlayer; } else { m_uiLastSignInData = 0; } } } void Game::SignInChangeCallback(void* pParam, bool bPrimaryPlayerChanged, unsigned int uiSignInData) { Game* pApp = (Game*)pParam; // check if the primary player signed out int iPrimaryPlayer = ProfileManager.GetPrimaryPad(); if ((ProfileManager.GetLockedProfile() != -1) && iPrimaryPlayer != -1) { if (((uiSignInData & (1 << iPrimaryPlayer)) == 0) || bPrimaryPlayerChanged) { // Primary Player gone or there's been a sign out and sign in of the // primary player, so kick them out pApp->SetAction(iPrimaryPlayer, eAppAction_PrimaryPlayerSignedOut); // 4J-PB - invalidate their banned level list pApp->InvalidateBannedList(iPrimaryPlayer); // need to ditch any DLCOffers info StorageManager.ClearDLCOffers(); pApp->ClearAndResetDLCDownloadQueue(); pApp->ClearDLCInstalled(); } else { unsigned int uiChangedPlayers = uiSignInData ^ m_uiLastSignInData; if (g_NetworkManager.IsInSession()) { bool hasGuestIdChanged = false; for (unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) { unsigned int guestNumber = 0; if (ProfileManager.IsSignedIn(i)) { XUSER_SIGNIN_INFO info; XUserGetSigninInfo( i, XUSER_GET_SIGNIN_INFO_OFFLINE_XUID_ONLY, &info); pApp->DebugPrintf( "Player at index %d has guest number %d\n", i, info.dwGuestNumber); guestNumber = info.dwGuestNumber; } if (pApp->m_currentSigninInfo[i].dwGuestNumber != 0 && guestNumber != 0 && pApp->m_currentSigninInfo[i].dwGuestNumber != guestNumber) { hasGuestIdChanged = true; } } if (hasGuestIdChanged) { unsigned int uiIDA[1]; uiIDA[0] = IDS_CONFIRM_OK; ui.RequestErrorMessage(IDS_GUEST_ORDER_CHANGED_TITLE, IDS_GUEST_ORDER_CHANGED_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad()); } // 4J Stu - On PS4 we can also cause to exit players if they are // signed out here, but we shouldn't do that if we are going to // switch to an offline game as it will likely crash due to // incompatible parallel processes bool switchToOffline = false; // If it's an online game, and the primary profile is no longer // signed into LIVE then we act as if disconnected if (!ProfileManager.IsSignedInLive( ProfileManager.GetLockedProfile()) && !g_NetworkManager.IsLocalGame()) { switchToOffline = true; } // printf("Old: %x, New: %x, Changed: %x\n", m_ulLastSignInData, // ulSignInData, changedPlayers); for (unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) { // Primary player shouldn't be subjected to these checks, // and shouldn't call ExitPlayer if (i == iPrimaryPlayer) continue; // A guest a signed in or out, out of order which // invalidates all the guest players we have in the game if (hasGuestIdChanged && pApp->m_currentSigninInfo[i].dwGuestNumber != 0 && g_NetworkManager.GetLocalPlayerByUserIndex(i) != nullptr) { pApp->DebugPrintf( "Recommending removal of player at index %d " "because their guest id changed\n", i); pApp->SetAction(i, eAppAction_ExitPlayer); } else { XUSER_SIGNIN_INFO info; XUserGetSigninInfo( i, XUSER_GET_SIGNIN_INFO_OFFLINE_XUID_ONLY, &info); // 4J Stu - Also need to detect the case where the sign // in mask is the same, but the player has swapped users // (eg still signed in but xuid different) Fix for // #48451 - TU5: Code: UI: Splitscreen: Title crashes // when switching to a profile previously signed out via // splitscreen profile selection // 4J-PB - compiler complained about if below ('&&' // within '||') - making it easier to read bool bPlayerChanged = (uiChangedPlayers & (1 << i)) == (1 << i); bool bPlayerSignedIn = ((uiSignInData & (1 << i)) != 0); if (bPlayerChanged && (!bPlayerSignedIn || (bPlayerSignedIn && !ProfileManager.AreXUIDSEqual( pApp->m_currentSigninInfo[i].xuid, info.xuid)))) { // 4J-PB - invalidate their banned level list pApp->DebugPrintf( "Player at index %d Left - invalidating their " "banned list\n", i); pApp->InvalidateBannedList(i); // 4J-HG: If either the player is in the network // manager or in the game, need to exit player // TODO: Do we need to check the network manager? if (g_NetworkManager.GetLocalPlayerByUserIndex(i) != nullptr || Minecraft::GetInstance()->localplayers[i] != nullptr) { pApp->DebugPrintf("Player %d signed out\n", i); pApp->SetAction(i, eAppAction_ExitPlayer); } } } } // If it's an online game, and the primary profile is no longer // signed into LIVE then we act as if disconnected if (switchToOffline) { pApp->SetAction(iPrimaryPlayer, eAppAction_EthernetDisconnected); } g_NetworkManager.HandleSignInChange(); } // Some menus require the player to be signed in to live, so if this // callback happens and the primary player is no longer signed in // then nav back else if (pApp->GetLiveLinkRequired() && !ProfileManager.IsSignedInLive( ProfileManager.GetLockedProfile())) { { pApp->SetAction(iPrimaryPlayer, eAppAction_EthernetDisconnected); } } } m_uiLastSignInData = uiSignInData; } else if (iPrimaryPlayer != -1) { // make sure the TMS banned list data is ditched - the player may have // gone in to help & options, backed out, and signed out pApp->InvalidateBannedList(iPrimaryPlayer); // need to ditch any DLCOffers info StorageManager.ClearDLCOffers(); pApp->ClearAndResetDLCDownloadQueue(); pApp->ClearDLCInstalled(); } // Update the guest numbers to the current state for (unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) { if (FAILED(XUserGetSigninInfo(i, XUSER_GET_SIGNIN_INFO_OFFLINE_XUID_ONLY, &pApp->m_currentSigninInfo[i]))) { pApp->m_currentSigninInfo[i].xuid = INVALID_XUID; pApp->m_currentSigninInfo[i].dwGuestNumber = 0; } app.DebugPrintf("Player at index %d has guest number %d\n", i, pApp->m_currentSigninInfo[i].dwGuestNumber); } } void Game::NotificationsCallback(void* pParam, std::uint32_t dwNotification, unsigned int uiParam) { Game* pClass = (Game*)pParam; // push these on to the notifications to be handled in qnet's dowork PNOTIFICATION pNotification = new NOTIFICATION; pNotification->dwNotification = dwNotification; pNotification->uiParam = uiParam; switch (dwNotification) { case XN_SYS_SIGNINCHANGED: { pClass->DebugPrintf("Signing changed - %d\n", uiParam); } break; case XN_SYS_INPUTDEVICESCHANGED: if (app.GetGameStarted() && g_NetworkManager.IsInSession()) { for (unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) { if (!InputManager.IsPadConnected(i) && Minecraft::GetInstance()->localplayers[i] != nullptr && !ui.IsPauseMenuDisplayed(i) && !ui.IsSceneInStack(i, eUIScene_EndPoem)) { ui.CloseUIScenes(i); ui.NavigateToScene(i, eUIScene_PauseMenu); } } } break; case XN_LIVE_CONTENT_INSTALLED: // Need to inform xuis that we've possibly had DLC installed { // app.m_dlcManager.SetNeedsUpdated(true); // Clear the DLC installed flag to cause a GetDLC to run if // it's called app.ClearDLCInstalled(); ui.HandleDLCInstalled(ProfileManager.GetPrimaryPad()); } break; case XN_SYS_STORAGEDEVICESCHANGED: { } break; } pClass->m_vNotifications.push_back(pNotification); } #if defined(_DEBUG_MENUS_ENABLED) bool Game::DebugArtToolsOn() { return m_debugOptions.debugArtToolsOn( GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())); } #endif void Game::SetDebugSequence(const char* pchSeq) { InputManager.SetDebugSequence(pchSeq, [this]() -> int { // printf("sequence matched\n"); m_debugOptions.setDebugOptions(!m_debugOptions.settingsOn()); for (int i = 0; i < XUSER_MAX_COUNT; i++) { if (app.DebugSettingsOn()) { app.ActionDebugMask(i); } else { // force debug mask off app.ActionDebugMask(i, true); } } return 0; }); } int Game::GetLocalPlayerCount(void) { int iPlayerC = 0; Minecraft* pMinecraft = Minecraft::GetInstance(); for (int i = 0; i < XUSER_MAX_COUNT; i++) { if (pMinecraft != nullptr && pMinecraft->localplayers[i] != nullptr) { iPlayerC++; } } return iPlayerC; } int Game::MarketplaceCountsCallback( void* pParam, C4JStorage::DLC_TMS_DETAILS* pTMSDetails, int iPad) { app.DebugPrintf("Marketplace Counts= New - %d Total - %d\n", pTMSDetails->dwNewOffers, pTMSDetails->dwTotalOffers); if (pTMSDetails->dwNewOffers > 0) { app.m_bNewDLCAvailable = true; app.m_bSeenNewDLCTip = false; } else { app.m_bNewDLCAvailable = false; app.m_bSeenNewDLCTip = true; } return 0; } bool Game::StartInstallDLCProcess(int iPad) { app.DebugPrintf("--- Game::StartInstallDLCProcess: pad=%i.\n", iPad); // If there is already a call to this in progress, then do nothing // If the app says dlc is installed, then there has been no new system // message to tell us there's new DLC since the last call to // StartInstallDLCProcess if ((app.DLCInstallProcessCompleted() == false) && (m_bDLCInstallPending == false)) { app.m_dlcManager.resetUnnamedCorruptCount(); m_bDLCInstallPending = true; m_iTotalDLC = 0; m_iTotalDLCInstalled = 0; app.DebugPrintf( "--- Game::StartInstallDLCProcess - " "StorageManager.GetInstalledDLC\n"); StorageManager.GetInstalledDLC( iPad, [this](int iInstalledC, int pad) { return dlcInstalledCallback(iInstalledC, pad); }); return true; } else { app.DebugPrintf( "--- Game::StartInstallDLCProcess - nothing to do\n"); return false; } } // Installed DLC callback int Game::dlcInstalledCallback(int iInstalledC, int iPad) { DebugPrintf( "--- Game::dlcInstalledCallback: totalDLC=%i, pad=%i.\n", iInstalledC, iPad); m_iTotalDLC = iInstalledC; MountNextDLC(iPad); return 0; } void Game::MountNextDLC(int iPad) { app.DebugPrintf("--- Game::MountNextDLC: pad=%i.\n", iPad); if (m_iTotalDLCInstalled < m_iTotalDLC) { // Mount it // We also need to match the ones the user wants to mount with the // installed DLC We're supposed to use a generic save game as a cache of // these to do this, with XUSER_ANY if (StorageManager.MountInstalledDLC( iPad, m_iTotalDLCInstalled, [this](int pad, std::uint32_t dwErr, std::uint32_t dwLicenceMask) { return dlcMountedCallback(pad, dwErr, dwLicenceMask); }) != ERROR_IO_PENDING) { // corrupt DLC app.DebugPrintf("Failed to mount DLC %d for pad %d\n", m_iTotalDLCInstalled, iPad); ++m_iTotalDLCInstalled; app.MountNextDLC(iPad); } else { app.DebugPrintf("StorageManager.MountInstalledDLC ok\n"); } } else { /* Removed - now loading these on demand instead of as each pack is mounted if(m_iTotalDLCInstalled > 0) { Minecraft *pMinecraft=Minecraft::GetInstance(); pMinecraft->levelRenderer->AddDLCSkinsToMemTextures(); } */ m_bDLCInstallPending = false; m_bDLCInstallProcessCompleted = true; ui.HandleDLCMountingComplete(); } } // 4J-JEV: For the sake of clarity in DLCMountedCallback. #if defined(_WINDOWS64) #define CONTENT_DATA_DISPLAY_NAME(a) (a.szDisplayName) #else #define CONTENT_DATA_DISPLAY_NAME(a) (a.wszDisplayName) #endif int Game::dlcMountedCallback(int iPad, std::uint32_t dwErr, std::uint32_t dwLicenceMask) { #if defined(_WINDOWS64) DebugPrintf("--- Game::dlcMountedCallback\n"); if (dwErr != ERROR_SUCCESS) { // corrupt DLC app.DebugPrintf("Failed to mount DLC for pad %d: %u\n", iPad, dwErr); app.m_dlcManager.incrementUnnamedCorruptCount(); } else { XCONTENT_DATA ContentData = StorageManager.GetDLC(app.m_iTotalDLCInstalled); DLCPack* pack = app.m_dlcManager.getPack(CONTENT_DATA_DISPLAY_NAME(ContentData)); if (pack != nullptr && pack->IsCorrupt()) { app.DebugPrintf( "Pack '%ls' is corrupt, removing it from the DLC Manager.\n", CONTENT_DATA_DISPLAY_NAME(ContentData)); app.m_dlcManager.removePack(pack); pack = nullptr; } if (pack == nullptr) { app.DebugPrintf("Pack \"%ls\" is not installed, so adding it\n", CONTENT_DATA_DISPLAY_NAME(ContentData)); #if defined(_WINDOWS64) pack = new DLCPack(ContentData.szDisplayName, dwLicenceMask); #else pack = new DLCPack(ContentData.wszDisplayName, dwLicenceMask); #endif pack->SetDLCMountIndex(app.m_iTotalDLCInstalled); pack->SetDLCDeviceID(ContentData.DeviceID); app.m_dlcManager.addPack(pack); app.HandleDLC(pack); if (pack->getDLCItemsCount(DLCManager::e_DLCType_Texture) > 0) { Minecraft::GetInstance()->skins->addTexturePackFromDLC( pack, pack->GetPackId()); } } else { app.DebugPrintf( "Pack \"%ls\" is already installed. Updating license to %u\n", CONTENT_DATA_DISPLAY_NAME(ContentData), dwLicenceMask); pack->SetDLCMountIndex(app.m_iTotalDLCInstalled); pack->SetDLCDeviceID(ContentData.DeviceID); pack->updateLicenseMask(dwLicenceMask); } StorageManager.UnmountInstalledDLC(); } ++app.m_iTotalDLCInstalled; app.MountNextDLC(iPad); #endif return 0; } #undef CONTENT_DATA_DISPLAY_NAME // void Game::InstallDefaultCape() // { // if(!m_bDefaultCapeInstallAttempted) // { // // we only attempt to install the cape once per launch of the // game m_bDefaultCapeInstallAttempted=true; // // std::wstring wTemp=L"Default_Cape.png"; // bool bRes=app.IsFileInMemoryTextures(wTemp); // // if the file is not already in the memory textures, then read // it from TMS if(!bRes) // { // std::uint8_t *pBuffer=nullptr; // std::uint32_t dwSize=0; // // 4J-PB - out for now for DaveK so he doesn't get the // birthday cape #ifdef _CONTENT_PACKAGE // C4JStorage::ETMSStatus eTMSStatus; // eTMSStatus=StorageManager.ReadTMSFile(ProfileManager.GetPrimaryPad(),C4JStorage::eGlobalStorage_Title,C4JStorage::eTMS_FileType_Graphic, // L"Default_Cape.png",&pBuffer, &dwSize); // if(eTMSStatus==C4JStorage::ETMSStatus_Idle) // { // app.AddMemoryTextureFile(wTemp,pBuffer,dwSize); // } // #endif // } // } // } void Game::HandleDLC(DLCPack* pack) { unsigned int dwFilesProcessed = 0; #if defined(_WINDOWS64) || defined(__linux__) std::vector dlcFilenames; #endif StorageManager.GetMountedDLCFileList("DLCDrive", dlcFilenames); for (int i = 0; i < dlcFilenames.size(); i++) { m_dlcManager.readDLCDataFile(dwFilesProcessed, dlcFilenames[i], pack); } if (dwFilesProcessed == 0) m_dlcManager.removePack(pack); } // int Game::DLCReadCallback(void* // pParam,C4JStorage::DLC_FILE_DETAILS *pDLCData) // { // // // return 0; // } //------------------------------------------------------------------------------------- // Name: InitTime() // Desc: Initializes the timer variables //------------------------------------------------------------------------------------- void Game::InitTime() { // Save the start time m_Time.qwTime = time_util::clock::now(); // Zero out the elapsed and total time m_Time.qwAppTime = {}; m_Time.fAppTime = 0.0f; m_Time.fElapsedTime = 0.0f; } //------------------------------------------------------------------------------------- // Name: UpdateTime() // Desc: Updates the elapsed time since our last frame. //------------------------------------------------------------------------------------- void Game::UpdateTime() { auto qwNewTime = time_util::clock::now(); auto qwDeltaTime = qwNewTime - m_Time.qwTime; m_Time.qwAppTime += qwDeltaTime; m_Time.qwTime = qwNewTime; m_Time.fElapsedTime = std::chrono::duration(qwDeltaTime).count(); m_Time.fAppTime = std::chrono::duration(m_Time.qwAppTime).count(); } bool Game::isXuidDeadmau5(PlayerUID xuid) { auto it = MojangData.find(xuid); // 4J Stu - The .at and [] accessors // insert elements if they don't exist if (it != MojangData.end()) { MOJANG_DATA* pMojangData = MojangData[xuid]; if (pMojangData && pMojangData->eXuid == eXUID_Deadmau5) { return true; } } return false; } void Game::StoreLaunchData() {} void Game::ExitGame() {} // Invites void Game::ProcessInvite(std::uint32_t dwUserIndex, std::uint32_t dwLocalUsersMask, const INVITE_INFO* pInviteInfo) { m_InviteData.dwUserIndex = dwUserIndex; m_InviteData.dwLocalUsersMask = dwLocalUsersMask; m_InviteData.pInviteInfo = pInviteInfo; // memcpy(&m_InviteData,pJoinData,sizeof(JoinFromInviteData)); SetAction(dwUserIndex, eAppAction_ExitAndJoinFromInvite); } int Game::ExitAndJoinFromInvite(void* pParam, int iPad, C4JStorage::EMessageResult result) { Game* pApp = (Game*)pParam; // Minecraft *pMinecraft=Minecraft::GetInstance(); // buttons are swapped on this menu if (result == C4JStorage::EMessage_ResultDecline) { pApp->SetAction(iPad, eAppAction_ExitAndJoinFromInviteConfirmed); } return 0; } int Game::ExitAndJoinFromInviteSaveDialogReturned( void* pParam, int iPad, C4JStorage::EMessageResult result) { Game* pClass = (Game*)pParam; // Exit with or without saving // Decline means save in this dialog if (result == C4JStorage::EMessage_ResultDecline || result == C4JStorage::EMessage_ResultThirdOption) { if (result == C4JStorage::EMessage_ResultDecline) // Save { // Check they have the full texture pack if they are using one // 4J-PB - Is the player trying to save but they are using a trial // texturepack ? if (!Minecraft::GetInstance()->skins->isUsingDefaultSkin()) { TexturePack* tPack = Minecraft::GetInstance()->skins->getSelected(); DLCPack* pDLCPack = tPack->getDLCPack(); if (!pDLCPack->hasPurchasedFile(DLCManager::e_DLCType_Texture, L"")) { // upsell // get the dlc texture pack unsigned int uiIDA[2]; uiIDA[0] = IDS_CONFIRM_OK; uiIDA[1] = IDS_CONFIRM_CANCEL; // Give the player a warning about the trial version of the // texture pack ui.RequestErrorMessage( IDS_WARNING_DLC_TRIALTEXTUREPACK_TITLE, IDS_WARNING_DLC_TRIALTEXTUREPACK_TEXT, uiIDA, 2, iPad, &Game::WarningTrialTexturePackReturned, pClass); return 0; } } // does the save exist? bool bSaveExists; StorageManager.DoesSaveExist(&bSaveExists); // 4J-PB - we check if the save exists inside the libs // we need to ask if they are sure they want to overwrite the // existing game if (bSaveExists) { unsigned int uiIDA[2]; uiIDA[0] = IDS_CONFIRM_CANCEL; uiIDA[1] = IDS_CONFIRM_OK; ui.RequestErrorMessage( IDS_TITLE_SAVE_GAME, IDS_CONFIRM_SAVE_GAME, uiIDA, 2, ProfileManager.GetPrimaryPad(), &Game::ExitAndJoinFromInviteAndSaveReturned, pClass); return 0; } else { MinecraftServer::getInstance()->setSaveOnExit(true); } } else { // been a few requests for a confirm on exit without saving unsigned int uiIDA[2]; uiIDA[0] = IDS_CONFIRM_CANCEL; uiIDA[1] = IDS_CONFIRM_OK; ui.RequestErrorMessage( IDS_TITLE_DECLINE_SAVE_GAME, IDS_CONFIRM_DECLINE_SAVE_GAME, uiIDA, 2, ProfileManager.GetPrimaryPad(), &Game::ExitAndJoinFromInviteDeclineSaveReturned, pClass); return 0; } app.SetAction(ProfileManager.GetPrimaryPad(), eAppAction_ExitAndJoinFromInviteConfirmed); } return 0; } int Game::WarningTrialTexturePackReturned( void* pParam, int iPad, C4JStorage::EMessageResult result) { // 4J Stu - I added this in when fixing an X1 bug. We should probably add // this as well but I don't have time to test all platforms atm return 0; } int Game::ExitAndJoinFromInviteAndSaveReturned( void* pParam, int iPad, C4JStorage::EMessageResult result) { // Game* pClass = (Game*)pParam; // results switched for this dialog if (result == C4JStorage::EMessage_ResultDecline) { int saveOrCheckpointId = 0; // Check they have the full texture pack if they are using one // 4J-PB - Is the player trying to save but they are using a trial // texturepack ? if (!Minecraft::GetInstance()->skins->isUsingDefaultSkin()) { TexturePack* tPack = Minecraft::GetInstance()->skins->getSelected(); DLCPack* pDLCPack = tPack->getDLCPack(); if (!pDLCPack->hasPurchasedFile(DLCManager::e_DLCType_Texture, L"")) { // upsell // get the dlc texture pack unsigned int uiIDA[2]; uiIDA[0] = IDS_CONFIRM_OK; uiIDA[1] = IDS_CONFIRM_CANCEL; // Give the player a warning about the trial version of the // texture pack ui.RequestErrorMessage( IDS_WARNING_DLC_TRIALTEXTUREPACK_TITLE, IDS_WARNING_DLC_TRIALTEXTUREPACK_TEXT, uiIDA, 2, iPad, &Game::WarningTrialTexturePackReturned, nullptr); return 0; } } // bool validSave = // StorageManager.GetSaveUniqueNumber(&saveOrCheckpointId); // SentientManager.RecordLevelSaveOrCheckpoint(ProfileManager.GetPrimaryPad(), // saveOrCheckpointId); MinecraftServer::getInstance()->setSaveOnExit(true); // flag a app action of exit and join game from invite app.SetAction(iPad, eAppAction_ExitAndJoinFromInviteConfirmed); } return 0; } int Game::ExitAndJoinFromInviteDeclineSaveReturned( void* pParam, int iPad, C4JStorage::EMessageResult result) { // results switched for this dialog if (result == C4JStorage::EMessage_ResultDecline) { MinecraftServer::getInstance()->setSaveOnExit(false); // flag a app action of exit and join game from invite app.SetAction(iPad, eAppAction_ExitAndJoinFromInviteConfirmed); } return 0; } ////////////////////////////////////////////////////////////////////////// // // FatalLoadError // // This is called when we can't load one of the required files at startup // It tends to mean the files have been corrupted. // We have to assume that we've not been able to load the text for the game. // ////////////////////////////////////////////////////////////////////////// void Game::FatalLoadError() {} void Game::SetSpecialTutorialCompletionFlag(int iPad, int index) { if (index >= 0 && index < 32 && GameSettingsA[iPad] != nullptr) { GameSettingsA[iPad]->uiSpecialTutorialBitmask |= (1 << index); } } void Game::AddCreditText(const wchar_t* lpStr) { DebugPrintf("ADDING CREDIT - %ls\n", lpStr); // add a std::string from the DLC to a credits std::vector SCreditTextItemDef* pCreditStruct = new SCreditTextItemDef; pCreditStruct->m_eType = eSmallText; pCreditStruct->m_iStringID[0] = NO_TRANSLATED_STRING; pCreditStruct->m_iStringID[1] = NO_TRANSLATED_STRING; pCreditStruct->m_Text = new wchar_t[wcslen(lpStr) + 1]; wcscpy((wchar_t*)pCreditStruct->m_Text, lpStr); vDLCCredits.push_back(pCreditStruct); } bool Game::AlreadySeenCreditText(const std::wstring& wstemp) { for (unsigned int i = 0; i < m_vCreditText.size(); i++) { std::wstring temp = m_vCreditText.at(i); // if they are the same, break out of the case if (temp.compare(wstemp) == 0) { return true; } } // add this text m_vCreditText.push_back((wchar_t*)wstemp.c_str()); return false; } unsigned int Game::GetDLCCreditsCount() { return (unsigned int)vDLCCredits.size(); } SCreditTextItemDef* Game::GetDLCCredits(int iIndex) { return vDLCCredits.at(iIndex); } // Game Host options void Game::SetGameHostOption(eGameHostOption eVal, unsigned int uiVal) { GameHostOptions::set(eVal, uiVal); } void Game::SetGameHostOption(unsigned int& uiHostSettings, eGameHostOption eVal, unsigned int uiVal) { switch (eVal) { case eGameHostOption_FriendsOfFriends: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_FRIENDSOFFRIENDS; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_FRIENDSOFFRIENDS; } break; case eGameHostOption_Difficulty: // clear the difficulty first uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_DIFFICULTY; uiHostSettings |= (GAME_HOST_OPTION_BITMASK_DIFFICULTY & uiVal); break; case eGameHostOption_Gamertags: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_GAMERTAGS; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_GAMERTAGS; } break; case eGameHostOption_GameType: // clear the game type first uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_GAMETYPE; uiHostSettings |= (GAME_HOST_OPTION_BITMASK_GAMETYPE & (uiVal << 4)); break; case eGameHostOption_LevelType: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_LEVELTYPE; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_LEVELTYPE; } break; case eGameHostOption_Structures: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_STRUCTURES; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_STRUCTURES; } break; case eGameHostOption_BonusChest: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_BONUSCHEST; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_BONUSCHEST; } break; case eGameHostOption_HasBeenInCreative: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_BEENINCREATIVE; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_BEENINCREATIVE; } break; case eGameHostOption_PvP: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_PVP; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_PVP; } break; case eGameHostOption_TrustPlayers: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_TRUSTPLAYERS; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_TRUSTPLAYERS; } break; case eGameHostOption_TNT: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_TNT; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_TNT; } break; case eGameHostOption_FireSpreads: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_FIRESPREADS; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_FIRESPREADS; } break; case eGameHostOption_CheatsEnabled: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_HOSTFLY; uiHostSettings |= GAME_HOST_OPTION_BITMASK_HOSTHUNGER; uiHostSettings |= GAME_HOST_OPTION_BITMASK_HOSTINVISIBLE; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_HOSTFLY; uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_HOSTHUNGER; uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_HOSTINVISIBLE; } break; case eGameHostOption_HostCanFly: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_HOSTFLY; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_HOSTFLY; } break; case eGameHostOption_HostCanChangeHunger: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_HOSTHUNGER; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_HOSTHUNGER; } break; case eGameHostOption_HostCanBeInvisible: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_HOSTINVISIBLE; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_HOSTINVISIBLE; } break; case eGameHostOption_BedrockFog: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_BEDROCKFOG; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_BEDROCKFOG; } break; case eGameHostOption_DisableSaving: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_DISABLESAVE; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_DISABLESAVE; } break; case eGameHostOption_WasntSaveOwner: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_NOTOWNER; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_NOTOWNER; } break; case eGameHostOption_MobGriefing: if (uiVal != 1) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_MOBGRIEFING; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_MOBGRIEFING; } break; case eGameHostOption_KeepInventory: if (uiVal != 0) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_KEEPINVENTORY; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_KEEPINVENTORY; } break; case eGameHostOption_DoMobSpawning: if (uiVal != 1) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_DOMOBSPAWNING; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_DOMOBSPAWNING; } break; case eGameHostOption_DoMobLoot: if (uiVal != 1) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_DOMOBLOOT; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_DOMOBLOOT; } break; case eGameHostOption_DoTileDrops: if (uiVal != 1) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_DOTILEDROPS; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_DOTILEDROPS; } break; case eGameHostOption_NaturalRegeneration: if (uiVal != 1) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_NATURALREGEN; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_NATURALREGEN; } break; case eGameHostOption_DoDaylightCycle: if (uiVal != 1) { uiHostSettings |= GAME_HOST_OPTION_BITMASK_DODAYLIGHTCYCLE; } else { // off uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_DODAYLIGHTCYCLE; } break; case eGameHostOption_WorldSize: // clear the difficulty first uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_WORLDSIZE; uiHostSettings |= (GAME_HOST_OPTION_BITMASK_WORLDSIZE & (uiVal << GAME_HOST_OPTION_BITMASK_WORLDSIZE_BITSHIFT)); break; case eGameHostOption_All: uiHostSettings = uiVal; break; default: break; } } unsigned int Game::GetGameHostOption(eGameHostOption eVal) { return GameHostOptions::get(eVal); } unsigned int Game::GetGameHostOption(unsigned int uiHostSettings, eGameHostOption eVal) { // unsigned int uiVal=0; switch (eVal) { case eGameHostOption_FriendsOfFriends: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_FRIENDSOFFRIENDS); break; case eGameHostOption_Difficulty: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_DIFFICULTY); break; case eGameHostOption_Gamertags: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_GAMERTAGS); break; case eGameHostOption_GameType: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_GAMETYPE) >> 4; break; case eGameHostOption_All: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_ALL); break; case eGameHostOption_Tutorial: // special case - tutorial is offline, but we want the gamertag // option, and set Easy mode, structures on, fire on, tnt on, pvp // on, trust players on return ((uiHostSettings & GAME_HOST_OPTION_BITMASK_GAMERTAGS) | GAME_HOST_OPTION_BITMASK_TRUSTPLAYERS | GAME_HOST_OPTION_BITMASK_FIRESPREADS | GAME_HOST_OPTION_BITMASK_TNT | GAME_HOST_OPTION_BITMASK_PVP | GAME_HOST_OPTION_BITMASK_STRUCTURES | 1); break; case eGameHostOption_LevelType: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_LEVELTYPE); break; case eGameHostOption_Structures: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_STRUCTURES); break; case eGameHostOption_BonusChest: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_BONUSCHEST); break; case eGameHostOption_HasBeenInCreative: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_BEENINCREATIVE); break; case eGameHostOption_PvP: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_PVP); break; case eGameHostOption_TrustPlayers: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_TRUSTPLAYERS); break; case eGameHostOption_TNT: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_TNT); break; case eGameHostOption_FireSpreads: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_FIRESPREADS); break; case eGameHostOption_CheatsEnabled: return (uiHostSettings & (GAME_HOST_OPTION_BITMASK_HOSTFLY | GAME_HOST_OPTION_BITMASK_HOSTHUNGER | GAME_HOST_OPTION_BITMASK_HOSTINVISIBLE)); break; case eGameHostOption_HostCanFly: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_HOSTFLY); break; case eGameHostOption_HostCanChangeHunger: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_HOSTHUNGER); break; case eGameHostOption_HostCanBeInvisible: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_HOSTINVISIBLE); break; case eGameHostOption_BedrockFog: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_BEDROCKFOG); break; case eGameHostOption_DisableSaving: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_DISABLESAVE); break; case eGameHostOption_WasntSaveOwner: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_NOTOWNER); case eGameHostOption_WorldSize: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_WORLDSIZE) >> GAME_HOST_OPTION_BITMASK_WORLDSIZE_BITSHIFT; case eGameHostOption_MobGriefing: return !(uiHostSettings & GAME_HOST_OPTION_BITMASK_MOBGRIEFING); case eGameHostOption_KeepInventory: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_KEEPINVENTORY); case eGameHostOption_DoMobSpawning: return !(uiHostSettings & GAME_HOST_OPTION_BITMASK_DOMOBSPAWNING); case eGameHostOption_DoMobLoot: return !(uiHostSettings & GAME_HOST_OPTION_BITMASK_DOMOBLOOT); case eGameHostOption_DoTileDrops: return !(uiHostSettings & GAME_HOST_OPTION_BITMASK_DOTILEDROPS); case eGameHostOption_NaturalRegeneration: return !(uiHostSettings & GAME_HOST_OPTION_BITMASK_NATURALREGEN); case eGameHostOption_DoDaylightCycle: return !(uiHostSettings & GAME_HOST_OPTION_BITMASK_DODAYLIGHTCYCLE); break; default: return 0; } return false; } bool Game::CanRecordStatsAndAchievements() { bool isTutorial = Minecraft::GetInstance() != nullptr && Minecraft::GetInstance()->isTutorial(); // 4J Stu - All of these options give the host player some advantage, so // should not allow achievements return !(app.GetGameHostOption(eGameHostOption_HasBeenInCreative) || app.GetGameHostOption(eGameHostOption_HostCanBeInvisible) || app.GetGameHostOption(eGameHostOption_HostCanChangeHunger) || app.GetGameHostOption(eGameHostOption_HostCanFly) || app.GetGameHostOption(eGameHostOption_WasntSaveOwner) || !app.GetGameHostOption(eGameHostOption_MobGriefing) || app.GetGameHostOption(eGameHostOption_KeepInventory) || !app.GetGameHostOption(eGameHostOption_DoMobSpawning) || (!app.GetGameHostOption(eGameHostOption_DoDaylightCycle) && !isTutorial)); } void Game::processSchematics(LevelChunk* levelChunk) { m_gameRules.processSchematics(levelChunk); } void Game::processSchematicsLighting(LevelChunk* levelChunk) { m_gameRules.processSchematicsLighting(levelChunk); } void Game::loadDefaultGameRules() { m_gameRules.loadDefaultGameRules(); } void Game::setLevelGenerationOptions( LevelGenerationOptions* levelGen) { m_gameRules.setLevelGenerationOptions(levelGen); } const wchar_t* Game::GetGameRulesString(const std::wstring& key) { return m_gameRules.GetGameRulesString(key); } unsigned char Game::m_szPNG[8] = {137, 80, 78, 71, 13, 10, 26, 10}; #define PNG_TAG_tEXt 0x74455874 unsigned int Game::FromBigEndian(unsigned int uiValue) { unsigned int uiReturn = ((uiValue >> 24) & 0x000000ff) | ((uiValue >> 8) & 0x0000ff00) | ((uiValue << 8) & 0x00ff0000) | ((uiValue << 24) & 0xff000000); return uiReturn; } void Game::GetImageTextData(std::uint8_t* imageData, unsigned int imageBytes, unsigned char* seedText, unsigned int& uiHostOptions, bool& bHostOptionsRead, std::uint32_t& uiTexturePack) { auto readPngUInt32 = [](const std::uint8_t* data) -> unsigned int { unsigned int value = 0; std::memcpy(&value, data, sizeof(value)); return value; }; std::uint8_t* ucPtr = imageData; unsigned int uiCount = 0; unsigned int uiChunkLen; unsigned int uiChunkType; unsigned int uiCRC; char szKeyword[80]; // check it's a png for (int i = 0; i < 8; i++) { if (m_szPNG[i] != ucPtr[i]) return; } uiCount += 8; while (uiCount < imageBytes) { uiChunkLen = FromBigEndian(readPngUInt32(&ucPtr[uiCount])); uiCount += sizeof(int); uiChunkType = FromBigEndian(readPngUInt32(&ucPtr[uiCount])); uiCount += sizeof(int); if (uiChunkType == PNG_TAG_tEXt) // tEXt { // check that it's the 4J text unsigned char* pszKeyword = &ucPtr[uiCount]; while (pszKeyword < ucPtr + uiCount + uiChunkLen) { memset(szKeyword, 0, 80); unsigned int uiKeywordC = 0; while (*pszKeyword != 0) { szKeyword[uiKeywordC++] = *pszKeyword; pszKeyword++; } pszKeyword++; if (strcmp(szKeyword, "4J_SEED") == 0) { // read the seed value unsigned int uiValueC = 0; while (*pszKeyword != 0 && (pszKeyword < ucPtr + uiCount + uiChunkLen)) { seedText[uiValueC++] = *pszKeyword; pszKeyword++; } // memcpy(seedText,pszKeyword,uiChunkLen-8); } else if (strcmp(szKeyword, "4J_HOSTOPTIONS") == 0) { bHostOptionsRead = true; // read the host options value unsigned int uiValueC = 0; unsigned char pszHostOptions[9]; // Hex representation of // unsigned int memset(&pszHostOptions, 0, 9); while (*pszKeyword != 0 && (pszKeyword < ucPtr + uiCount + uiChunkLen) && uiValueC < 8) { pszHostOptions[uiValueC++] = *pszKeyword; pszKeyword++; } uiHostOptions = 0; std::stringstream ss; ss << pszHostOptions; ss >> std::hex >> uiHostOptions; } else if (strcmp(szKeyword, "4J_TEXTUREPACK") == 0) { // read the texture pack value unsigned int uiValueC = 0; unsigned char pszTexturePack[9]; // Hex representation of // unsigned int memset(&pszTexturePack, 0, 9); while (*pszKeyword != 0 && (pszKeyword < ucPtr + uiCount + uiChunkLen) && uiValueC < 8) { pszTexturePack[uiValueC++] = *pszKeyword; pszKeyword++; } std::stringstream ss; ss << pszTexturePack; ss >> std::hex >> uiTexturePack; } } } uiCount += uiChunkLen; uiCRC = FromBigEndian(readPngUInt32(&ucPtr[uiCount])); uiCount += sizeof(int); } return; } unsigned int Game::CreateImageTextData(std::uint8_t* textMetadata, int64_t seed, bool hasSeed, unsigned int uiHostOptions, unsigned int uiTexturePackId) { int iTextMetadataBytes = 0; if (hasSeed) { strcpy((char*)textMetadata, "4J_SEED"); snprintf((char*)&textMetadata[8], 42, "%lld", (long long)seed); // get the length iTextMetadataBytes += 8; while (textMetadata[iTextMetadataBytes] != 0) iTextMetadataBytes++; ++iTextMetadataBytes; // Add a null terminator at the end of the seed // value } // Save the host options that this world was last played with strcpy((char*)&textMetadata[iTextMetadataBytes], "4J_HOSTOPTIONS"); snprintf((char*)&textMetadata[iTextMetadataBytes + 15], 9, "%X", uiHostOptions); iTextMetadataBytes += 15; while (textMetadata[iTextMetadataBytes] != 0) iTextMetadataBytes++; ++iTextMetadataBytes; // Add a null terminator at the end of the host // options value // Save the texture pack id strcpy((char*)&textMetadata[iTextMetadataBytes], "4J_TEXTUREPACK"); snprintf((char*)&textMetadata[iTextMetadataBytes + 15], 9, "%X", uiHostOptions); iTextMetadataBytes += 15; while (textMetadata[iTextMetadataBytes] != 0) iTextMetadataBytes++; return iTextMetadataBytes; } void Game::UpdatePlayerInfo(std::uint8_t networkSmallId, int16_t playerColourIndex, unsigned int playerGamePrivileges) { for (unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) { if (m_playerColours[i] == networkSmallId) { m_playerColours[i] = 0; m_playerGamePrivileges[i] = 0; } } if (playerColourIndex >= 0 && playerColourIndex < MINECRAFT_NET_MAX_PLAYERS) { m_playerColours[playerColourIndex] = networkSmallId; m_playerGamePrivileges[playerColourIndex] = playerGamePrivileges; } } short Game::GetPlayerColour(std::uint8_t networkSmallId) { short index = -1; for (unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) { if (m_playerColours[i] == networkSmallId) { index = i; break; } } return index; } unsigned int Game::GetPlayerPrivileges(std::uint8_t networkSmallId) { unsigned int privileges = 0; for (unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) { if (m_playerColours[i] == networkSmallId) { privileges = m_playerGamePrivileges[i]; break; } } return privileges; } std::wstring Game::getEntityName(eINSTANCEOF type) { switch (type) { case eTYPE_WOLF: return app.GetString(IDS_WOLF); case eTYPE_CREEPER: return app.GetString(IDS_CREEPER); case eTYPE_SKELETON: return app.GetString(IDS_SKELETON); case eTYPE_SPIDER: return app.GetString(IDS_SPIDER); case eTYPE_ZOMBIE: return app.GetString(IDS_ZOMBIE); case eTYPE_PIGZOMBIE: return app.GetString(IDS_PIGZOMBIE); case eTYPE_ENDERMAN: return app.GetString(IDS_ENDERMAN); case eTYPE_SILVERFISH: return app.GetString(IDS_SILVERFISH); case eTYPE_CAVESPIDER: return app.GetString(IDS_CAVE_SPIDER); case eTYPE_GHAST: return app.GetString(IDS_GHAST); case eTYPE_SLIME: return app.GetString(IDS_SLIME); case eTYPE_ARROW: return app.GetString(IDS_ITEM_ARROW); case eTYPE_ENDERDRAGON: return app.GetString(IDS_ENDERDRAGON); case eTYPE_BLAZE: return app.GetString(IDS_BLAZE); case eTYPE_LAVASLIME: return app.GetString(IDS_LAVA_SLIME); // 4J-PB - fix for #107167 - Customer Encountered: TU12: Content: // UI: There is no information what killed Player after being slain // by Iron Golem. case eTYPE_VILLAGERGOLEM: return app.GetString(IDS_IRONGOLEM); case eTYPE_HORSE: return app.GetString(IDS_HORSE); case eTYPE_WITCH: return app.GetString(IDS_WITCH); case eTYPE_WITHERBOSS: return app.GetString(IDS_WITHER); case eTYPE_BAT: return app.GetString(IDS_BAT); default: break; }; return L""; } std::uint32_t Game::m_dwContentTypeA[e_Marketplace_MAX] = { XMARKETPLACE_OFFERING_TYPE_CONTENT, // e_DLC_SkinPack, e_DLC_TexturePacks, // e_DLC_MashupPacks XMARKETPLACE_OFFERING_TYPE_THEME, // e_DLC_Themes XMARKETPLACE_OFFERING_TYPE_AVATARITEM, // e_DLC_AvatarItems XMARKETPLACE_OFFERING_TYPE_TILE, // e_DLC_Gamerpics }; int Game::TexturePackDialogReturned( void* pParam, int iPad, C4JStorage::EMessageResult result) { return 0; } std::unordered_map Game::MojangData; std::unordered_map Game::DLCTextures_PackID; std::unordered_map Game::DLCInfo_Trial; std::unordered_map Game::DLCInfo_Full; std::unordered_map Game::DLCInfo_SkinName; int32_t Game::RegisterMojangData(wchar_t* pXuidName, PlayerUID xuid, wchar_t* pSkin, wchar_t* pCape) { int32_t hr = 0; eXUID eTempXuid = eXUID_Undefined; MOJANG_DATA* pMojangData = nullptr; // ignore the names if we don't recognize them if (pXuidName != nullptr) { if (wcscmp(pXuidName, L"XUID_NOTCH") == 0) { eTempXuid = eXUID_Notch; // might be needed for the apple at some point } else if (wcscmp(pXuidName, L"XUID_DEADMAU5") == 0) { eTempXuid = eXUID_Deadmau5; // Needed for the deadmau5 ears } else { eTempXuid = eXUID_NoName; } } if (eTempXuid != eXUID_Undefined) { pMojangData = new MOJANG_DATA; memset(pMojangData, 0, sizeof(MOJANG_DATA)); pMojangData->eXuid = eTempXuid; wcsncpy(pMojangData->wchSkin, pSkin, MAX_CAPENAME_SIZE); wcsncpy(pMojangData->wchCape, pCape, MAX_CAPENAME_SIZE); MojangData[xuid] = pMojangData; } return hr; } MOJANG_DATA* Game::GetMojangDataForXuid(PlayerUID xuid) { return MojangData[xuid]; } int32_t Game::RegisterConfigValues(wchar_t* pType, int iValue) { int32_t hr = 0; // #ifdef 0 // if(pType!=nullptr) // { // if(wcscmp(pType,L"XboxOneTransfer")==0) // { // if(iValue>0) // { // app.m_bTransferSavesToXboxOne=true; // } // else // { // app.m_bTransferSavesToXboxOne=false; // } // } // else if(wcscmp(pType,L"TransferSlotCount")==0) // { // app.m_uiTransferSlotC=iValue; // } // // } // #endif return hr; } #if defined(_WINDOWS64) int32_t Game::RegisterDLCData(wchar_t* pType, wchar_t* pBannerName, int iGender, uint64_t ullOfferID_Full, uint64_t ullOfferID_Trial, wchar_t* pFirstSkin, unsigned int uiSortIndex, int iConfig, wchar_t* pDataFile) { int32_t hr = 0; DLC_INFO* pDLCData = new DLC_INFO; memset(pDLCData, 0, sizeof(DLC_INFO)); pDLCData->ullOfferID_Full = ullOfferID_Full; pDLCData->ullOfferID_Trial = ullOfferID_Trial; pDLCData->eDLCType = e_DLC_NotDefined; pDLCData->iGender = iGender; pDLCData->uiSortIndex = uiSortIndex; pDLCData->iConfig = iConfig; // ignore the names if we don't recognize them if (pBannerName != L"") { wcsncpy_s(pDLCData->wchBanner, pBannerName, MAX_BANNERNAME_SIZE); } if (pDataFile[0] != 0) { wcsncpy_s(pDLCData->wchDataFile, pDataFile, MAX_BANNERNAME_SIZE); } if (pType != nullptr) { if (wcscmp(pType, L"Skin") == 0) { pDLCData->eDLCType = e_DLC_SkinPack; } else if (wcscmp(pType, L"Gamerpic") == 0) { pDLCData->eDLCType = e_DLC_Gamerpics; } else if (wcscmp(pType, L"Theme") == 0) { pDLCData->eDLCType = e_DLC_Themes; } else if (wcscmp(pType, L"Avatar") == 0) { pDLCData->eDLCType = e_DLC_AvatarItems; } else if (wcscmp(pType, L"MashUpPack") == 0) { pDLCData->eDLCType = e_DLC_MashupPacks; DLCTextures_PackID[pDLCData->iConfig] = ullOfferID_Full; } else if (wcscmp(pType, L"TexturePack") == 0) { pDLCData->eDLCType = e_DLC_TexturePacks; DLCTextures_PackID[pDLCData->iConfig] = ullOfferID_Full; } } if (ullOfferID_Trial != 0ll) DLCInfo_Trial[ullOfferID_Trial] = pDLCData; if (ullOfferID_Full != 0ll) DLCInfo_Full[ullOfferID_Full] = pDLCData; if (pFirstSkin[0] != 0) DLCInfo_SkinName[pFirstSkin] = ullOfferID_Full; return hr; } #elif defined(__linux__) int32_t Game::RegisterDLCData(wchar_t* pType, wchar_t* pBannerName, int iGender, uint64_t ullOfferID_Full, uint64_t ullOfferID_Trial, wchar_t* pFirstSkin, unsigned int uiSortIndex, int iConfig, wchar_t* pDataFile) { fprintf(stderr, "warning: Game::RegisterDLCData unimplemented for " "platform `__linux__`\n"); return 0; } #else int32_t Game::RegisterDLCData(char* pchDLCName, unsigned int uiSortIndex, char* pchImageURL) { // on PS3 we get all the required info from the name char chDLCType[3]; int32_t hr = 0; DLC_INFO* pDLCData = new DLC_INFO; memset(pDLCData, 0, sizeof(DLC_INFO)); chDLCType[0] = pchDLCName[0]; chDLCType[1] = pchDLCName[1]; chDLCType[2] = 0; pDLCData->iConfig = app.GetiConfigFromName(pchDLCName); pDLCData->uiSortIndex = uiSortIndex; pDLCData->eDLCType = app.GetDLCTypeFromName(pchDLCName); strcpy(pDLCData->chImageURL, pchImageURL); // bool bIsTrialDLC = app.GetTrialFromName(pchDLCName); switch (pDLCData->eDLCType) { case e_DLC_TexturePacks: { char* pchName = (char*)malloc(strlen(pchDLCName) + 1); strcpy(pchName, pchDLCName); DLCTextures_PackID[pDLCData->iConfig] = pchName; } break; case e_DLC_MashupPacks: { char* pchName = (char*)malloc(strlen(pchDLCName) + 1); strcpy(pchName, pchDLCName); DLCTextures_PackID[pDLCData->iConfig] = pchName; } break; default: break; } app.DebugPrintf(5, "Adding DLC - %s\n", pchDLCName); DLCInfo[pchDLCName] = pDLCData; // if(ullOfferID_Trial!=0ll) DLCInfo_Trial[ullOfferID_Trial]=pDLCData; // if(ullOfferID_Full!=0ll) DLCInfo_Full[ullOfferID_Full]=pDLCData; // if(pFirstSkin[0]!=0) DLCInfo_SkinName[pFirstSkin]=ullOfferID_Full; // DLCInfo[ullOfferID_Trial]=pDLCData; return hr; } #endif bool Game::GetDLCFullOfferIDForSkinID(const std::wstring& FirstSkin, uint64_t* pullVal) { auto it = DLCInfo_SkinName.find(FirstSkin); if (it == DLCInfo_SkinName.end()) { return false; } else { *pullVal = (uint64_t)it->second; return true; } } bool Game::GetDLCFullOfferIDForPackID(const int iPackID, uint64_t* pullVal) { auto it = DLCTextures_PackID.find(iPackID); if (it == DLCTextures_PackID.end()) { *pullVal = (uint64_t)0; return false; } else { *pullVal = (uint64_t)it->second; return true; } } DLC_INFO* Game::GetDLCInfoForTrialOfferID(uint64_t ullOfferID_Trial) { // DLC_INFO *pDLCInfo=NULL; if (DLCInfo_Trial.size() > 0) { auto it = DLCInfo_Trial.find(ullOfferID_Trial); if (it == DLCInfo_Trial.end()) { // nothing for this return nullptr; } else { return it->second; } } else return nullptr; } DLC_INFO* Game::GetDLCInfoTrialOffer(int iIndex) { std::unordered_map::iterator it = DLCInfo_Trial.begin(); for (int i = 0; i < iIndex; i++) { ++it; } return it->second; } DLC_INFO* Game::GetDLCInfoFullOffer(int iIndex) { std::unordered_map::iterator it = DLCInfo_Full.begin(); for (int i = 0; i < iIndex; i++) { ++it; } return it->second; } uint64_t Game::GetDLCInfoTexturesFullOffer(int iIndex) { std::unordered_map::iterator it = DLCTextures_PackID.begin(); for (int i = 0; i < iIndex; i++) { ++it; } return it->second; } DLC_INFO* Game::GetDLCInfoForFullOfferID(uint64_t ullOfferID_Full) { if (DLCInfo_Full.size() > 0) { auto it = DLCInfo_Full.find(ullOfferID_Full); if (it == DLCInfo_Full.end()) { // nothing for this return nullptr; } else { return it->second; } } else return nullptr; } int Game::RemoteSaveThreadProc(void* lpParameter) { // The game should be stopped while we are doing this, but the connections // ticks may try to create some AABB's or Vec3's Compression::UseDefaultThreadStorage(); // 4J-PB - Xbox 360 - 163153 - [CRASH] TU17: Code: Multiplayer: During the // Autosave in an online Multiplayer session, the game occasionally crashes // for one or more Clients callstack - > if(tls->tileId != this->id) // updateDefaultShape(); callstack - > // default.exe!WaterlilyTile::getAABB(Level * level, int x, int y, int z) // line 38 + 8 bytes C++ // ... // default.exe!Game::RemoteSaveThreadProc(void * // lpParameter) line 6694 C++ // host autosave, and the clients can crash on receiving handleMoveEntity // when it's a tile within this thread, so need to do the tls for tiles Tile::CreateNewThreadStorage(); Minecraft* pMinecraft = Minecraft::GetInstance(); pMinecraft->progressRenderer->progressStartNoAbort( IDS_PROGRESS_HOST_SAVING); pMinecraft->progressRenderer->progressStage(-1); pMinecraft->progressRenderer->progressStagePercentage(0); while (!app.GetGameStarted() && app.GetXuiAction(ProfileManager.GetPrimaryPad()) == eAppAction_WaitRemoteServerSaveComplete) { // Tick all the games connections pMinecraft->tickAllConnections(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } if (app.GetXuiAction(ProfileManager.GetPrimaryPad()) != eAppAction_WaitRemoteServerSaveComplete) { // Something cancelled us? return ERROR_CANCELLED; } app.SetAction(ProfileManager.GetPrimaryPad(), eAppAction_Idle); ui.UpdatePlayerBasePositions(); Tile::ReleaseThreadStorage(); return 0; } void Game::ExitGameFromRemoteSave(void* lpParameter) { int primaryPad = ProfileManager.GetPrimaryPad(); unsigned int uiIDA[3]; uiIDA[0] = IDS_CONFIRM_CANCEL; uiIDA[1] = IDS_CONFIRM_OK; ui.RequestAlertMessage( IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 2, primaryPad, &Game::ExitGameFromRemoteSaveDialogReturned, nullptr); } int Game::ExitGameFromRemoteSaveDialogReturned( void* pParam, int iPad, C4JStorage::EMessageResult result) { // CScene_Pause* pClass = (CScene_Pause*)pParam; // results switched for this dialog if (result == C4JStorage::EMessage_ResultDecline) { app.SetAction(iPad, eAppAction_ExitWorld); } else { // Inform fullscreen progress scene that it's not being cancelled after // all UIScene_FullscreenProgress* pScene = (UIScene_FullscreenProgress*)ui.FindScene( eUIScene_FullscreenProgress); if (pScene != nullptr) { pScene->SetWasCancelled(false); } } return 0; } unsigned int Game::AddDLCRequest(eDLCMarketplaceType eType, bool bPromote) { // lock access { std::lock_guard lock(csDLCDownloadQueue); // If it's already in there, promote it to the top of the list int iPosition = 0; for (auto it = m_DLCDownloadQueue.begin(); it != m_DLCDownloadQueue.end(); ++it) { DLCRequest* pCurrent = *it; if (pCurrent->dwType == m_dwContentTypeA[eType]) { // already got this in the list if (pCurrent->eState == e_DLC_ContentState_Retrieving || pCurrent->eState == e_DLC_ContentState_Retrieved) { // already retrieved this return 0; } else { // promote if (bPromote) { m_DLCDownloadQueue.erase(m_DLCDownloadQueue.begin() + iPosition); m_DLCDownloadQueue.insert(m_DLCDownloadQueue.begin(), pCurrent); } return 0; } } iPosition++; } DLCRequest* pDLCreq = new DLCRequest; pDLCreq->dwType = m_dwContentTypeA[eType]; pDLCreq->eState = e_DLC_ContentState_Idle; m_DLCDownloadQueue.push_back(pDLCreq); m_bAllDLCContentRetrieved = false; } app.DebugPrintf("[Consoles_App] Added DLC request.\n"); return 1; } unsigned int Game::AddTMSPPFileTypeRequest(eDLCContentType eType, bool bPromote) { // lock access std::lock_guard lock(csTMSPPDownloadQueue); // If it's already in there, promote it to the top of the list int iPosition = 0; // ignore promoting for now /* bool bPromoted=false; for(auto it = m_TMSPPDownloadQueue.begin(); it != m_TMSPPDownloadQueue.end(); ++it) { TMSPPRequest *pCurrent = *it; if(pCurrent->eType==eType) { if(!(pCurrent->eState == e_TMS_ContentState_Retrieving || pCurrent->eState == e_TMS_ContentState_Retrieved)) { // promote if(bPromote) { m_TMSPPDownloadQueue.erase(m_TMSPPDownloadQueue.begin()+iPosition); m_TMSPPDownloadQueue.insert(m_TMSPPDownloadQueue.begin(),pCurrent); bPromoted=true; } } } iPosition++; } if(bPromoted) { // re-ordered the list, so leave now return 0; } */ // special case for data files (not image files) if (eType == e_DLC_TexturePackData) { int iCount = GetDLCInfoFullOffersCount(); for (int i = 0; i < iCount; i++) { DLC_INFO* pDLC = GetDLCInfoFullOffer(i); if ((pDLC->eDLCType == e_DLC_TexturePacks) || (pDLC->eDLCType == e_DLC_MashupPacks)) { // first check if the image is already in the memory textures, // since we might be loading some from the Title Update // partition if (pDLC->wchDataFile[0] != 0) { // wchar_t *cString = pDLC->wchDataFile; // 4J-PB - shouldn't check this here - let the TMS files // override it, so if they are on TMS, we'll take them // first // int iIndex = // app.GetLocalTMSFileIndex(pDLC->wchDataFile,true); // if(iIndex!=-1) { bool bPresent = app.IsFileInTPD(pDLC->iConfig); if (!bPresent) { // this may already be present in the vector because // of a previous trial/full offer bool bAlreadyInQueue = false; for (auto it = m_TMSPPDownloadQueue.begin(); it != m_TMSPPDownloadQueue.end(); ++it) { TMSPPRequest* pCurrent = *it; if (wcscmp(pDLC->wchDataFile, pCurrent->wchFilename) == 0) { bAlreadyInQueue = true; break; } } if (!bAlreadyInQueue) { TMSPPRequest* pTMSPPreq = new TMSPPRequest; pTMSPPreq->CallbackFunc = &Game::TMSPPFileReturned; pTMSPPreq->lpCallbackParam = this; pTMSPPreq->eStorageFacility = C4JStorage::eGlobalStorage_Title; pTMSPPreq->eFileTypeVal = C4JStorage::TMS_FILETYPE_BINARY; memcpy(pTMSPPreq->wchFilename, pDLC->wchDataFile, sizeof(wchar_t) * MAX_BANNERNAME_SIZE); pTMSPPreq->eType = e_DLC_TexturePackData; pTMSPPreq->eState = e_TMS_ContentState_Queued; m_bAllTMSContentRetrieved = false; m_TMSPPDownloadQueue.push_back(pTMSPPreq); } } else { app.DebugPrintf( "Texture data already present in the TPD\n"); } } } } } } else { // for all the files of type eType, add them to the download list // run through the trial offers first, then the full offers. Any // duplicates won't be added to the download queue int iCount; // and the full offers iCount = GetDLCInfoFullOffersCount(); for (int i = 0; i < iCount; i++) { DLC_INFO* pDLC = GetDLCInfoFullOffer(i); // if(wcscmp(pDLC->wchType,wchDLCTypeNames[eType])==0) if (pDLC->eDLCType == eType) { // first check if the image is already in the memory textures, // since we might be loading some from the Title Update // partition wchar_t* cString = pDLC->wchBanner; // 4J-PB - shouldn't check this here - let the TMS files // override it, so if they are on TMS, we'll take them first // int iIndex = app.GetLocalTMSFileIndex(cString,true); // if(iIndex!=-1) { bool bPresent = app.IsFileInMemoryTextures(cString); if (!bPresent) { // this may already be present in the vector because of // a previous trial/full offer bool bAlreadyInQueue = false; for (auto it = m_TMSPPDownloadQueue.begin(); it != m_TMSPPDownloadQueue.end(); ++it) { TMSPPRequest* pCurrent = *it; if (wcscmp(pDLC->wchBanner, pCurrent->wchFilename) == 0) { bAlreadyInQueue = true; break; } } if (!bAlreadyInQueue) { // app.DebugPrintf("Adding a request to the TMSPP // download queue - %ls\n",pDLC->wchBanner); TMSPPRequest* pTMSPPreq = new TMSPPRequest; memset(pTMSPPreq, 0, sizeof(TMSPPRequest)); pTMSPPreq->CallbackFunc = &Game::TMSPPFileReturned; pTMSPPreq->lpCallbackParam = this; // 4J-PB - testing for now // pTMSPPreq->eStorageFacility=C4JStorage::eGlobalStorage_TitleUser; pTMSPPreq->eStorageFacility = C4JStorage::eGlobalStorage_Title; pTMSPPreq->eFileTypeVal = C4JStorage::TMS_FILETYPE_BINARY; // wcstombs(pTMSPPreq->szFilename,pDLC->wchBanner,MAX_TMSFILENAME_SIZE); memcpy(pTMSPPreq->wchFilename, pDLC->wchBanner, sizeof(wchar_t) * MAX_BANNERNAME_SIZE); pTMSPPreq->eType = eType; pTMSPPreq->eState = e_TMS_ContentState_Queued; m_bAllTMSContentRetrieved = false; m_TMSPPDownloadQueue.push_back(pTMSPPreq); app.DebugPrintf( "===m_TMSPPDownloadQueue Adding %ls, q size is " "%d\n", pTMSPPreq->wchFilename, m_TMSPPDownloadQueue.size()); } } } } } } return 1; } bool Game::CheckTMSDLCCanStop() { std::lock_guard lock(csTMSPPDownloadQueue); for (auto it = m_TMSPPDownloadQueue.begin(); it != m_TMSPPDownloadQueue.end(); ++it) { TMSPPRequest* pCurrent = *it; if (pCurrent->eState == e_TMS_ContentState_Retrieving) { return false; } } return true; } bool Game::RetrieveNextDLCContent() { // If there's already a retrieve in progress, quit // we may have re-ordered the list, so need to check every item // is there a primary player and a network connection? int primPad = ProfileManager.GetPrimaryPad(); if (primPad == -1 || !ProfileManager.IsSignedInLive(primPad)) { return true; // 4J-JEV: We need to wait until the primary player is // online. } { std::lock_guard lock(csDLCDownloadQueue); for (auto it = m_DLCDownloadQueue.begin(); it != m_DLCDownloadQueue.end(); ++it) { DLCRequest* pCurrent = *it; if (pCurrent->eState == e_DLC_ContentState_Retrieving) { return true; } } // Now look for the next retrieval for (auto it = m_DLCDownloadQueue.begin(); it != m_DLCDownloadQueue.end(); ++it) { DLCRequest* pCurrent = *it; if (pCurrent->eState == e_DLC_ContentState_Idle) { #if defined(_DEBUG) app.DebugPrintf("RetrieveNextDLCContent - type = %d\n", pCurrent->dwType); #endif C4JStorage::EDLCStatus status = StorageManager.GetDLCOffers( ProfileManager.GetPrimaryPad(), [this](int iOfferC, std::uint32_t dwType, int pad) { return dlcOffersReturned(iOfferC, dwType, pad); }, pCurrent->dwType); if (status == C4JStorage::EDLC_Pending) { pCurrent->eState = e_DLC_ContentState_Retrieving; } else { // no content of this type, or some other problem app.DebugPrintf("RetrieveNextDLCContent - PROBLEM\n"); pCurrent->eState = e_DLC_ContentState_Retrieved; } return true; } } } app.DebugPrintf("[Consoles_App] Finished downloading dlc content.\n"); return false; } int Game::TMSPPFileReturned(void* pParam, int iPad, int iUserData, C4JStorage::PTMSPP_FILEDATA pFileData, const char* szFilename) { Game* pClass = (Game*)pParam; // find the right one in the vector { std::lock_guard lock(pClass->csTMSPPDownloadQueue); for (auto it = pClass->m_TMSPPDownloadQueue.begin(); it != pClass->m_TMSPPDownloadQueue.end(); ++it) { TMSPPRequest* pCurrent = *it; #if defined(_WINDOWS64) char szFile[MAX_TMSFILENAME_SIZE]; wcstombs(szFile, pCurrent->wchFilename, MAX_TMSFILENAME_SIZE); if (strcmp(szFilename, szFile) == 0) #endif { // set this to retrieved whether it found it or not pCurrent->eState = e_TMS_ContentState_Retrieved; if (pFileData != nullptr) { switch (pCurrent->eType) { case e_DLC_TexturePackData: { app.DebugPrintf("--- Got texturepack data %ls\n", pCurrent->wchFilename); // get the config value for the texture pack int iConfig = app.GetTPConfigVal(pCurrent->wchFilename); app.AddMemoryTPDFile(iConfig, pFileData->pbData, pFileData->size); } break; default: app.DebugPrintf("--- Got image data - %ls\n", pCurrent->wchFilename); app.AddMemoryTextureFile(pCurrent->wchFilename, pFileData->pbData, pFileData->size); break; } } else { app.DebugPrintf("TMSImageReturned failed (%s)...\n", szFilename); } break; } } } return 0; } bool Game::RetrieveNextTMSPPContent() { return false; } void Game::TickDLCOffersRetrieved() { if (!m_bAllDLCContentRetrieved) { if (!app.RetrieveNextDLCContent()) { app.DebugPrintf("[Consoles_App] All content retrieved.\n"); m_bAllDLCContentRetrieved = true; } } } void Game::ClearAndResetDLCDownloadQueue() { app.DebugPrintf("[Consoles_App] Clear and reset download queue.\n"); int iPosition = 0; { std::lock_guard lock(csTMSPPDownloadQueue); for (auto it = m_DLCDownloadQueue.begin(); it != m_DLCDownloadQueue.end(); ++it) { DLCRequest* pCurrent = *it; delete pCurrent; iPosition++; } m_DLCDownloadQueue.clear(); m_bAllDLCContentRetrieved = true; } } void Game::TickTMSPPFilesRetrieved() { if (m_bTickTMSDLCFiles && !m_bAllTMSContentRetrieved) { if (app.RetrieveNextTMSPPContent() == false) { m_bAllTMSContentRetrieved = true; } } } void Game::ClearTMSPPFilesRetrieved() { int iPosition = 0; { std::lock_guard lock(csTMSPPDownloadQueue); for (auto it = m_TMSPPDownloadQueue.begin(); it != m_TMSPPDownloadQueue.end(); ++it) { TMSPPRequest* pCurrent = *it; delete pCurrent; iPosition++; } m_TMSPPDownloadQueue.clear(); m_bAllTMSContentRetrieved = true; } } int Game::dlcOffersReturned(int iOfferC, std::uint32_t dwType, int iPad) { // find the right one in the vector { std::lock_guard lock(csTMSPPDownloadQueue); for (auto it = m_DLCDownloadQueue.begin(); it != m_DLCDownloadQueue.end(); ++it) { DLCRequest* pCurrent = *it; // avatar items are coming back as type Content, so we can't trust // the type setting if (pCurrent->dwType == static_cast(dwType)) { m_iDLCOfferC = iOfferC; DebugPrintf( "DLCOffersReturned - type %u, count %d - setting to " "retrieved\n", dwType, iOfferC); pCurrent->eState = e_DLC_ContentState_Retrieved; break; } } } return 0; } eDLCContentType Game::Find_eDLCContentType(std::uint32_t dwType) { for (int i = 0; i < e_DLC_MAX; i++) { if (m_dwContentTypeA[i] == dwType) { return (eDLCContentType)i; } } return (eDLCContentType)0; } bool Game::DLCContentRetrieved(eDLCMarketplaceType eType) { // If there's already a retrieve in progress, quit // we may have re-ordered the list, so need to check every item std::lock_guard lock(csDLCDownloadQueue); for (auto it = m_DLCDownloadQueue.begin(); it != m_DLCDownloadQueue.end(); ++it) { DLCRequest* pCurrent = *it; if ((pCurrent->dwType == m_dwContentTypeA[eType]) && (pCurrent->eState == e_DLC_ContentState_Retrieved)) { return true; } } return false; } // DLC int Game::GetDLCInfoTrialOffersCount() { return (int)DLCInfo_Trial.size(); } int Game::GetDLCInfoFullOffersCount() { return (int)DLCInfo_Full.size(); } int Game::GetDLCInfoTexturesOffersCount() { return (int)DLCTextures_PackID.size(); } // AUTOSAVE void Game::SetAutosaveTimerTime(void) { int settingValue = GetGameSettings(ProfileManager.GetPrimaryPad(), eGameSetting_Autosave); m_saveManager.setAutosaveTimerTime(settingValue); } void Game::SetTrialTimerStart(void) { m_fTrialTimerStart = m_Time.fAppTime; mfTrialPausedTime = 0.0f; } float Game::getTrialTimer(void) { return m_Time.fAppTime - m_fTrialTimerStart - mfTrialPausedTime; } bool Game::IsLocalMultiplayerAvailable() { unsigned int connectedControllers = 0; for (unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) { if (InputManager.IsPadConnected(i) || ProfileManager.IsSignedIn(i)) ++connectedControllers; } bool available = RenderManager.IsHiDef() && connectedControllers > 1; return available; // Found this in GameNetworkManager? // #ifdef 0 // iOtherConnectedControllers = // InputManager.GetConnectedGamepadCount(); // if((InputManager.IsPadConnected(userIndex) || // ProfileManager.IsSignedIn(userIndex))) // { // --iOtherConnectedControllers; // } // #else // for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) // { // if( (i!=userIndex) && (InputManager.IsPadConnected(i) || // ProfileManager.IsSignedIn(i)) ) // { // iOtherConnectedControllers++; // } // } // #endif } // 4J-PB - language and locale function // (moved to manager class) void Game::SetTickTMSDLCFiles(bool bVal) { // 4J-PB - we need to stop the retrieval of minecraft store images from TMS // when we aren't in the DLC, since going in to Play Game will change the // title id group m_bTickTMSDLCFiles = bVal; } std::wstring Game::getFilePath(std::uint32_t packId, std::wstring filename, bool bAddDataFolder, std::wstring mountPoint) { std::wstring path = getRootPath(packId, true, bAddDataFolder, mountPoint) + filename; File f(path); if (f.exists()) { return path; } return getRootPath(packId, false, true, mountPoint) + filename; } enum ETitleUpdateTexturePacks { // eTUTP_MassEffect = 0x400, // eTUTP_Skyrim = 0x401, // eTUTP_Halo = 0x402, // eTUTP_Festive = 0x405, // eTUTP_Plastic = 0x801, // eTUTP_Candy = 0x802, // eTUTP_Fantasy = 0x803, eTUTP_Halloween = 0x804, // eTUTP_Natural = 0x805, // eTUTP_City = 0x01000806, // 4J Stu - The released City pack had a // sub-pack ID eTUTP_Cartoon = 0x807, eTUTP_Steampunk = 0x01000808, // 4J // Stu - The released Steampunk pack had a sub-pack ID }; #if defined(_WINDOWS64) std::wstring titleUpdateTexturePackRoot = L"Windows64\\DLC\\"; #else std::wstring titleUpdateTexturePackRoot = L"CU\\DLC\\"; #endif std::wstring Game::getRootPath(std::uint32_t packId, bool allowOverride, bool bAddDataFolder, std::wstring mountPoint) { std::wstring path = mountPoint; if (allowOverride) { switch (packId) { case eTUTP_Halloween: path = titleUpdateTexturePackRoot + L"Halloween Texture Pack"; break; }; File folder(path); if (!folder.exists()) { path = mountPoint; } } if (bAddDataFolder) { return path + L"\\Data\\"; } else { return path + L"\\"; } }