#include "stdafx.h" #include "../Common/Consoles_App.h" #include "../User.h" #include "../../Minecraft.Client/Minecraft.h" #include "../../Minecraft.Client/MinecraftServer.h" #include "../../Minecraft.Client/PlayerList.h" #include "../../Minecraft.Client/ServerPlayer.h" #include "../../Minecraft.World/Level.h" #include "../../Minecraft.World/LevelSettings.h" #include "../../Minecraft.World/BiomeSource.h" #include "../../Minecraft.World/LevelType.h" CConsoleMinecraftApp app; CConsoleMinecraftApp::CConsoleMinecraftApp() : CMinecraftApp() { m_bShutdown = false; } void CConsoleMinecraftApp::SetRichPresenceContext(int iPad, int contextId) { ProfileManager.SetRichPresenceContextValue(iPad,CONTEXT_GAME_STATE,contextId); } void CConsoleMinecraftApp::StoreLaunchData() { } void CConsoleMinecraftApp::ExitGame() { m_bShutdown = true; } void CConsoleMinecraftApp::FatalLoadError() { } void CConsoleMinecraftApp::CaptureSaveThumbnail() { RenderManager.CaptureThumbnail(&m_ThumbnailBuffer); // CaptureThumbnail in the render lib doesn't produce data on Win64, // so grab the back buffer via D3D11 and encode a PNG manually if (!m_ThumbnailBuffer.Allocated()) { extern ID3D11Device* g_pd3dDevice; extern ID3D11DeviceContext* g_pImmediateContext; extern IDXGISwapChain* g_pSwapChain; if (!g_pd3dDevice || !g_pImmediateContext || !g_pSwapChain) return; ID3D11Texture2D* pBackBuffer = nullptr; HRESULT hr = g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer); if (FAILED(hr) || !pBackBuffer) return; D3D11_TEXTURE2D_DESC bbDesc; pBackBuffer->GetDesc(&bbDesc); // 64x64 to match other platforms const int THUMB_W = 64; const int THUMB_H = 64; // staging texture for CPU readback D3D11_TEXTURE2D_DESC stagingDesc = {}; stagingDesc.Width = bbDesc.Width; stagingDesc.Height = bbDesc.Height; stagingDesc.MipLevels = 1; stagingDesc.ArraySize = 1; stagingDesc.Format = bbDesc.Format; stagingDesc.SampleDesc.Count = 1; stagingDesc.Usage = D3D11_USAGE_STAGING; stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; ID3D11Texture2D* pStaging = nullptr; // resolve MSAA if needed if (bbDesc.SampleDesc.Count > 1) { D3D11_TEXTURE2D_DESC resolveDesc = bbDesc; resolveDesc.SampleDesc.Count = 1; resolveDesc.SampleDesc.Quality = 0; resolveDesc.Usage = D3D11_USAGE_DEFAULT; resolveDesc.BindFlags = 0; resolveDesc.CPUAccessFlags = 0; ID3D11Texture2D* pResolved = nullptr; hr = g_pd3dDevice->CreateTexture2D(&resolveDesc, nullptr, &pResolved); if (SUCCEEDED(hr)) { g_pImmediateContext->ResolveSubresource(pResolved, 0, pBackBuffer, 0, bbDesc.Format); hr = g_pd3dDevice->CreateTexture2D(&stagingDesc, nullptr, &pStaging); if (SUCCEEDED(hr)) g_pImmediateContext->CopyResource(pStaging, pResolved); pResolved->Release(); } } else { hr = g_pd3dDevice->CreateTexture2D(&stagingDesc, nullptr, &pStaging); if (SUCCEEDED(hr)) g_pImmediateContext->CopyResource(pStaging, pBackBuffer); } pBackBuffer->Release(); if (!pStaging) return; D3D11_MAPPED_SUBRESOURCE mapped; hr = g_pImmediateContext->Map(pStaging, 0, D3D11_MAP_READ, 0, &mapped); if (FAILED(hr)) { pStaging->Release(); return; } // nearest-neighbor downsample to 64x64 BYTE* thumbPixels = new BYTE[THUMB_W * THUMB_H * 4]; for (int y = 0; y < THUMB_H; y++) { int srcY = y * (int)bbDesc.Height / THUMB_H; BYTE* srcRow = (BYTE*)mapped.pData + srcY * mapped.RowPitch; for (int x = 0; x < THUMB_W; x++) { int srcX = x * (int)bbDesc.Width / THUMB_W; BYTE* src = srcRow + srcX * 4; // assumes BGRA/RGBA 32bpp BYTE* dst = thumbPixels + (y * THUMB_W + x) * 4; dst[0] = src[2]; // R (from B in BGRA) dst[1] = src[1]; // G dst[2] = src[0]; // B (from R in BGRA) dst[3] = 255; // A } } g_pImmediateContext->Unmap(pStaging, 0); pStaging->Release(); // encode uncompressed PNG (IHDR + stored IDAT + IEND) int rawRowSize = 1 + THUMB_W * 3; // filter byte + RGB int rawSize = rawRowSize * THUMB_H; // single stored deflate block (rawSize fits in 16 bits) int deflateSize = 5 + rawSize; // 5-byte block header + data if (rawSize > 65535) { // shouldn't happen at 64x64 but bail just in case delete[] thumbPixels; return; } int idatDataSize = 2 + deflateSize + 4; // zlib header(2) + deflate + adler32(4) int pngSize = 8 + (12 + 13) + (12 + idatDataSize) + 12; // sig + IHDR + IDAT + IEND BYTE* png = (BYTE*)malloc(pngSize); if (!png) { delete[] thumbPixels; return; } int pos = 0; // PNG signature static const BYTE sig[8] = {0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A}; memcpy(png + pos, sig, 8); pos += 8; // big-endian write helper auto writeBE32 = [&](int offset, unsigned int val) { png[offset+0] = (val >> 24) & 0xFF; png[offset+1] = (val >> 16) & 0xFF; png[offset+2] = (val >> 8) & 0xFF; png[offset+3] = (val ) & 0xFF; }; // CRC32 for PNG chunks static unsigned int crcTable[256]; static bool crcInit = false; if (!crcInit) { for (int n = 0; n < 256; n++) { unsigned int c = n; for (int k = 0; k < 8; k++) c = (c & 1) ? 0xEDB88320 ^ (c >> 1) : c >> 1; crcTable[n] = c; } crcInit = true; } auto crc32 = [&](BYTE* data, int len) -> unsigned int { unsigned int c = 0xFFFFFFFF; for (int i = 0; i < len; i++) c = crcTable[(c ^ data[i]) & 0xFF] ^ (c >> 8); return c ^ 0xFFFFFFFF; }; // IHDR chunk writeBE32(pos, 13); pos += 4; // length int ihdrStart = pos; png[pos++]='I'; png[pos++]='H'; png[pos++]='D'; png[pos++]='R'; writeBE32(pos, THUMB_W); pos += 4; writeBE32(pos, THUMB_H); pos += 4; png[pos++] = 8; // bit depth png[pos++] = 2; // color type RGB png[pos++] = 0; // compression png[pos++] = 0; // filter png[pos++] = 0; // interlace writeBE32(pos, crc32(png + ihdrStart, 17)); pos += 4; // IDAT chunk writeBE32(pos, idatDataSize); pos += 4; int idatStart = pos; png[pos++]='I'; png[pos++]='D'; png[pos++]='A'; png[pos++]='T'; // zlib header (no compression) png[pos++] = 0x78; png[pos++] = 0x01; // Deflate stored block (final) png[pos++] = 0x01; // final block, stored png[pos++] = (rawSize ) & 0xFF; png[pos++] = (rawSize >> 8) & 0xFF; png[pos++] = ~rawSize & 0xFF; png[pos++] = (~rawSize >> 8) & 0xFF; // Raw PNG data (filter=None for each row, RGB) unsigned int adlerA = 1, adlerB = 0; for (int y = 0; y < THUMB_H; y++) { png[pos++] = 0; // filter byte = None adlerA = (adlerA + 0) % 65521; adlerB = (adlerB + adlerA) % 65521; for (int x = 0; x < THUMB_W; x++) { BYTE* px = thumbPixels + (y * THUMB_W + x) * 4; for (int c = 0; c < 3; c++) { png[pos++] = px[c]; adlerA = (adlerA + px[c]) % 65521; adlerB = (adlerB + adlerA) % 65521; } } } // Adler32 unsigned int adler = (adlerB << 16) | adlerA; writeBE32(pos, adler); pos += 4; // IDAT CRC writeBE32(pos, crc32(png + idatStart, 4 + idatDataSize)); pos += 4; // IEND chunk writeBE32(pos, 0); pos += 4; int iendStart = pos; png[pos++]='I'; png[pos++]='E'; png[pos++]='N'; png[pos++]='D'; writeBE32(pos, crc32(png + iendStart, 4)); pos += 4; delete[] thumbPixels; m_ThumbnailBuffer.m_type = ImageFileBuffer::e_typePNG; m_ThumbnailBuffer.m_pBuffer = png; m_ThumbnailBuffer.m_bufferSize = pos; } } void CConsoleMinecraftApp::GetSaveThumbnail(PBYTE *pbData,DWORD *pdwSize) { // On a save caused by a create world, the thumbnail capture won't have happened if (m_ThumbnailBuffer.Allocated()) { if (pbData) { *pbData = new BYTE[m_ThumbnailBuffer.GetBufferSize()]; *pdwSize = m_ThumbnailBuffer.GetBufferSize(); memcpy(*pbData, m_ThumbnailBuffer.GetBufferPointer(), *pdwSize); } m_ThumbnailBuffer.Release(); } else { // No capture happened (e.g. first save on world creation) leave thumbnail as nullptr if (pbData) *pbData = nullptr; if (pdwSize) *pdwSize = 0; } } void CConsoleMinecraftApp::ReleaseSaveThumbnail() { } void CConsoleMinecraftApp::GetScreenshot(int iPad,PBYTE *pbData,DWORD *pdwSize) { } void CConsoleMinecraftApp::TemporaryCreateGameStart() { ////////////////////////////////////////////////////////////////////////////////////////////// From CScene_Main::OnInit app.setLevelGenerationOptions(nullptr); // From CScene_Main::RunPlayGame Minecraft *pMinecraft=Minecraft::GetInstance(); app.ReleaseSaveThumbnail(); ProfileManager.SetLockedProfile(0); extern wchar_t g_Win64UsernameW[17]; pMinecraft->user->name = g_Win64UsernameW; app.ApplyGameSettingsChanged(0); ////////////////////////////////////////////////////////////////////////////////////////////// From CScene_MultiGameJoinLoad::OnInit MinecraftServer::resetFlags(); // From CScene_MultiGameJoinLoad::OnNotifyPressEx app.SetTutorialMode( false ); app.SetCorruptSaveDeleted(false); ////////////////////////////////////////////////////////////////////////////////////////////// From CScene_MultiGameCreate::CreateGame app.ClearTerrainFeaturePosition(); wstring wWorldName = L"TestWorld"; StorageManager.ResetSaveData(); StorageManager.SetSaveTitle(wWorldName.c_str()); bool isFlat = false; int64_t seedValue = 0; // BiomeSource::findSeed(isFlat?LevelType::lvl_flat:LevelType::lvl_normal); // 4J - was (new Random())->nextLong() - now trying to actually find a seed to suit our requirements NetworkGameInitData *param = new NetworkGameInitData(); param->seed = seedValue; param->saveData = nullptr; app.SetGameHostOption(eGameHostOption_Difficulty,0); app.SetGameHostOption(eGameHostOption_FriendsOfFriends,0); app.SetGameHostOption(eGameHostOption_Gamertags,1); app.SetGameHostOption(eGameHostOption_BedrockFog,1); app.SetGameHostOption(eGameHostOption_GameType,GameType::CREATIVE->getId() ); // LevelSettings::GAMETYPE_SURVIVAL app.SetGameHostOption(eGameHostOption_LevelType, 0 ); app.SetGameHostOption(eGameHostOption_Structures, 1 ); app.SetGameHostOption(eGameHostOption_BonusChest, 0 ); app.SetGameHostOption(eGameHostOption_PvP, 1); app.SetGameHostOption(eGameHostOption_TrustPlayers, 1 ); app.SetGameHostOption(eGameHostOption_FireSpreads, 1 ); app.SetGameHostOption(eGameHostOption_TNT, 1 ); app.SetGameHostOption(eGameHostOption_HostCanFly, 1); app.SetGameHostOption(eGameHostOption_HostCanChangeHunger, 1); app.SetGameHostOption(eGameHostOption_HostCanBeInvisible, 1 ); param->settings = app.GetGameHostOption( eGameHostOption_All ); g_NetworkManager.FakeLocalPlayerJoined(); LoadingInputParams *loadingParams = new LoadingInputParams(); loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; loadingParams->lpParam = static_cast(param); // Reset the autosave time app.SetAutosaveTimerTime(); C4JThread* thread = new C4JThread(loadingParams->func, loadingParams->lpParam, "RunNetworkGame"); thread->Run(); } int CConsoleMinecraftApp::GetLocalTMSFileIndex(WCHAR *wchTMSFile,bool bFilenameIncludesExtension,eFileExtensionType eEXT) { return -1; } int CConsoleMinecraftApp::LoadLocalTMSFile(WCHAR *wchTMSFile) { return -1; } int CConsoleMinecraftApp::LoadLocalTMSFile(WCHAR *wchTMSFile, eFileExtensionType eExt) { return -1; } void CConsoleMinecraftApp::FreeLocalTMSFiles(eTMSFileType eType) { }