mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-08-20 09:57:09 +00:00
added screenshot functionality
alpha is broken tho
This commit is contained in:
parent
43d520f692
commit
e87fb36bc4
|
|
@ -862,6 +862,7 @@ enum EControllerActions
|
|||
MINECRAFT_ACTION_INVENTORY,
|
||||
MINECRAFT_ACTION_PAUSEMENU,
|
||||
MINECRAFT_ACTION_DROP,
|
||||
MINECRAFT_ACTION_DROP_ALL,
|
||||
MINECRAFT_ACTION_SNEAK_TOGGLE,
|
||||
MINECRAFT_ACTION_CRAFTING,
|
||||
MINECRAFT_ACTION_RENDER_THIRD_PERSON,
|
||||
|
|
|
|||
|
|
@ -20727,6 +20727,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
|
|||
</ClInclude>
|
||||
<ClInclude Include="Rect2i.h" />
|
||||
<ClInclude Include="ResourceLocation.h" />
|
||||
<ClInclude Include="ScreenshotManager.h" />
|
||||
<ClInclude Include="ServerCommandDispatcher.h" />
|
||||
<ClInclude Include="ServerConnection.h" />
|
||||
<ClInclude Include="ServerInterface.h" />
|
||||
|
|
@ -37951,6 +37952,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
|
|||
<ClCompile Include="RedDustParticle.cpp" />
|
||||
<ClCompile Include="RenameWorldScreen.cpp" />
|
||||
<ClCompile Include="Screen.cpp" />
|
||||
<ClCompile Include="ScreenshotManager.cpp" />
|
||||
<ClCompile Include="ScreenSizeCalculator.cpp" />
|
||||
<ClCompile Include="ScrolledSelectionList.cpp" />
|
||||
<ClCompile Include="SelectWorldScreen.cpp" />
|
||||
|
|
|
|||
|
|
@ -3793,6 +3793,9 @@
|
|||
<ClInclude Include="..\include\lce_filesystem\lce_filesystem.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ScreenshotManager.h">
|
||||
<Filter>Windows64\Source Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
|
@ -5949,6 +5952,9 @@
|
|||
<ClCompile Include="..\include\lce_filesystem\lce_filesystem.cpp">
|
||||
<Filter>include\lce_filesystem</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ScreenshotManager.cpp">
|
||||
<Filter>Windows64\Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Library Include="Xbox\4JLibs\libs\4J_Render_d.lib">
|
||||
|
|
|
|||
|
|
@ -1459,6 +1459,7 @@ void Minecraft::run_middle()
|
|||
if(InputManager.ButtonPressed(i, ACTION_MENU_GTC_PAUSE)) localplayers[i]->ullButtonsPressed|=1LL<<ACTION_MENU_GTC_PAUSE;
|
||||
#endif
|
||||
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_DROP)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_DROP;
|
||||
if (InputManager.ButtonPressed(i, MINECRAFT_ACTION_DROP_ALL)) localplayers[i]->ullButtonsPressed |= 1LL << MINECRAFT_ACTION_DROP_ALL;
|
||||
|
||||
// 4J-PB - If we're flying, the sneak needs to be held on to go down
|
||||
if(localplayers[i]->abilities.flying)
|
||||
|
|
@ -1509,20 +1510,21 @@ void Minecraft::run_middle()
|
|||
}
|
||||
}
|
||||
|
||||
if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_DROP))
|
||||
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_DROP;
|
||||
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_DROP)) {
|
||||
localplayers[i]->ullButtonsPressed |= 1LL << MINECRAFT_ACTION_DROP;
|
||||
}
|
||||
|
||||
if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_CRAFTING) || g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_CRAFTING_ALT))
|
||||
{
|
||||
if((ui.IsSceneInStack(i, eUIScene_Crafting2x2Menu) || ui.IsSceneInStack(i, eUIScene_Crafting3x3Menu) || ui.IsSceneInStack(i, eUIScene_CreativeMenu) || isClosableByEitherKey) && !isEditing)
|
||||
{
|
||||
ui.CloseUIScenes(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_CRAFTING) || g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_CRAFTING_ALT)) {
|
||||
if((ui.IsSceneInStack(i, eUIScene_Crafting2x2Menu)
|
||||
|| ui.IsSceneInStack(i, eUIScene_Crafting3x3Menu)
|
||||
|| ui.IsSceneInStack(i, eUIScene_CreativeMenu)
|
||||
|| isClosableByEitherKey)
|
||||
&& !isEditing) {
|
||||
ui.CloseUIScenes(i);
|
||||
} else {
|
||||
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_CRAFTING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int slot = 0; slot < 9; slot++)
|
||||
{
|
||||
|
|
|
|||
208
Minecraft.Client/ScreenshotManager.cpp
Normal file
208
Minecraft.Client/ScreenshotManager.cpp
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
#include "stdafx.h"
|
||||
#include "ScreenshotManager.h"
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <dxgi.h>
|
||||
#include <wincodec.h>
|
||||
#include <shlobj.h>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
// link WIC
|
||||
#pragma comment(lib, "windowscodecs.lib")
|
||||
|
||||
// defined in Windows64_Minecraft.cpp
|
||||
extern IDXGISwapChain* g_pSwapChain;
|
||||
extern ID3D11Device* g_pd3dDevice;
|
||||
extern ID3D11DeviceContext* g_pImmediateContext;
|
||||
extern int g_rScreenWidth;
|
||||
extern int g_rScreenHeight;
|
||||
|
||||
// make sure the output foldler exists
|
||||
bool ScreenshotManager::ensureScreenshotsFolder(std::wstring& outPath) {
|
||||
wchar_t exePath[MAX_PATH] = {};
|
||||
if (!GetModuleFileNameW(nullptr, exePath, _countof(exePath))) return false;
|
||||
wchar_t* lastSlash = wcsrchr(exePath, L'\\');
|
||||
if (lastSlash) *(lastSlash + 1) = L'\0';
|
||||
|
||||
std::wstring folder = exePath;
|
||||
folder += L"screenshots";
|
||||
// create if doesn't exist
|
||||
DWORD attrib = GetFileAttributesW(folder.c_str());
|
||||
if (attrib == INVALID_FILE_ATTRIBUTES || !(attrib & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
if (!CreateDirectoryW(folder.c_str(), nullptr)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
outPath = folder + L"\\";
|
||||
return true;
|
||||
}
|
||||
|
||||
// create a formatted name with the current timestamp
|
||||
std::wstring ScreenshotManager::makeTimestampedFilename() {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto t = std::chrono::system_clock::to_time_t(now);
|
||||
std::wstringstream ss;
|
||||
ss << L"screenshot_";
|
||||
std::tm tm{};
|
||||
localtime_s(&tm, &t);
|
||||
ss << std::put_time(&tm, L"%Y-%m-%d_%H-%M-%S") << ".png";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// takes a screenshot of the current frame
|
||||
bool ScreenshotManager::takeScreenshot(const std::wstring& path) {
|
||||
if (!g_pSwapChain || !g_pd3dDevice || !g_pImmediateContext) return false;
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
ID3D11Texture2D* pBack = nullptr;
|
||||
hr = g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**) &pBack);
|
||||
if (FAILED(hr) || !pBack) return false;
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
pBack->GetDesc(&desc);
|
||||
|
||||
D3D11_TEXTURE2D_DESC stagingDesc = desc;
|
||||
stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
stagingDesc.Usage = D3D11_USAGE_STAGING;
|
||||
stagingDesc.BindFlags = 0;
|
||||
stagingDesc.MiscFlags = 0;
|
||||
stagingDesc.SampleDesc.Count = 1;
|
||||
stagingDesc.SampleDesc.Quality = 0;
|
||||
ID3D11Texture2D* pStaging = nullptr;
|
||||
hr = g_pd3dDevice->CreateTexture2D(&stagingDesc, nullptr, &pStaging);
|
||||
|
||||
if (FAILED(hr) || !pStaging) {
|
||||
pBack->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
g_pImmediateContext->CopyResource(pStaging, pBack);
|
||||
|
||||
// map and read
|
||||
D3D11_MAPPED_SUBRESOURCE mapped;
|
||||
hr = g_pImmediateContext->Map(pStaging, 0, D3D11_MAP_READ, 0, &mapped);
|
||||
if (FAILED(hr)) {
|
||||
pStaging->Release();
|
||||
pBack->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
// prepare WIC
|
||||
IWICImagingFactory* pFactory = nullptr;
|
||||
|
||||
hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
if (FAILED(hr) && hr != RPC_E_CHANGED_MODE) {
|
||||
g_pImmediateContext->Unmap(pStaging, 0);
|
||||
pStaging->Release();
|
||||
pBack->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFactory));
|
||||
if (FAILED(hr)) {
|
||||
g_pImmediateContext->Unmap(pStaging, 0);
|
||||
pStaging->Release();
|
||||
pBack->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring outFolder;
|
||||
if (!ensureScreenshotsFolder(outFolder)) {
|
||||
pFactory->Release();
|
||||
g_pImmediateContext->Unmap(pStaging, 0);
|
||||
pStaging->Release();
|
||||
pBack->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring fileName = path;
|
||||
if (fileName.empty()) {
|
||||
fileName = outFolder + makeTimestampedFilename();
|
||||
} else {
|
||||
if (fileName.find(L":\\") == std::wstring::npos) {
|
||||
fileName = outFolder + fileName;
|
||||
}
|
||||
}
|
||||
|
||||
GUID wicFormat = GUID_WICPixelFormat32bppBGRA;
|
||||
|
||||
// we swizzle R / B because WIC expects BGRA for some fucking reason
|
||||
const UINT width = desc.Width;
|
||||
const UINT height = desc.Height;
|
||||
const UINT srcRowPitch = mapped.RowPitch;
|
||||
const UINT dstStride = width * 4;
|
||||
|
||||
std::vector<BYTE> imageData(dstStride * height);
|
||||
BYTE* dst = imageData.data();
|
||||
BYTE* srcBase = reinterpret_cast<BYTE*>(mapped.pData);
|
||||
for (UINT y = 0; y < height; ++y) {
|
||||
BYTE* src = srcBase + y * srcRowPitch;
|
||||
BYTE* row = dst + y * dstStride;
|
||||
for (UINT x = 0; x < width; ++x) {
|
||||
BYTE r = src[x * 4 + 0];
|
||||
BYTE g = src[x * 4 + 1];
|
||||
BYTE b = src[x * 4 + 2];
|
||||
BYTE a = src[x * 4 + 3];
|
||||
row[x * 4 + 0] = b;
|
||||
row[x * 4 + 1] = g;
|
||||
row[x * 4 + 2] = r;
|
||||
row[x * 4 + 3] = a;
|
||||
}
|
||||
}
|
||||
|
||||
// create WIC bitmap from memory
|
||||
IWICBitmap* pBitmap = nullptr;
|
||||
hr = pFactory->CreateBitmapFromMemory(width, height, wicFormat, dstStride, static_cast<UINT>(imageData.size()), imageData.data(), &pBitmap);
|
||||
if (FAILED(hr)) { pFactory->Release(); g_pImmediateContext->Unmap(pStaging, 0); pStaging->Release(); pBack->Release(); return false; }
|
||||
|
||||
// create encoder
|
||||
IWICStream* pStream = nullptr;
|
||||
IWICBitmapEncoder* pEncoder = nullptr;
|
||||
hr = pFactory->CreateStream(&pStream);
|
||||
if (FAILED(hr)) { pBitmap->Release(); pFactory->Release(); g_pImmediateContext->Unmap(pStaging, 0); pStaging->Release(); pBack->Release(); return false; }
|
||||
|
||||
hr = pStream->InitializeFromFilename(fileName.c_str(), GENERIC_WRITE);
|
||||
if (FAILED(hr)) { pStream->Release(); pBitmap->Release(); pFactory->Release(); g_pImmediateContext->Unmap(pStaging, 0); pStaging->Release(); pBack->Release(); return false; }
|
||||
|
||||
hr = pFactory->CreateEncoder(GUID_ContainerFormatPng, nullptr, &pEncoder);
|
||||
if (FAILED(hr)) { pStream->Release(); pBitmap->Release(); pFactory->Release(); g_pImmediateContext->Unmap(pStaging, 0); pStaging->Release(); pBack->Release(); return false; }
|
||||
|
||||
hr = pEncoder->Initialize(pStream, WICBitmapEncoderNoCache);
|
||||
if (FAILED(hr)) { pEncoder->Release(); pStream->Release(); pBitmap->Release(); pFactory->Release(); g_pImmediateContext->Unmap(pStaging, 0); pStaging->Release(); pBack->Release(); return false; }
|
||||
|
||||
IWICBitmapFrameEncode* pFrame = nullptr;
|
||||
IPropertyBag2* pProps = nullptr;
|
||||
hr = pEncoder->CreateNewFrame(&pFrame, &pProps);
|
||||
if (FAILED(hr)) { pEncoder->Release(); pStream->Release(); pBitmap->Release(); pFactory->Release(); g_pImmediateContext->Unmap(pStaging, 0); pStaging->Release(); pBack->Release(); return false; }
|
||||
|
||||
hr = pFrame->Initialize(pProps);
|
||||
if (FAILED(hr)) { pFrame->Release(); if (pProps) pProps->Release(); pEncoder->Release(); pStream->Release(); pBitmap->Release(); pFactory->Release(); g_pImmediateContext->Unmap(pStaging, 0); pStaging->Release(); pBack->Release(); return false; }
|
||||
|
||||
hr = pFrame->SetSize(width, height);
|
||||
if (FAILED(hr)) { pFrame->Release(); if (pProps) pProps->Release(); pEncoder->Release(); pStream->Release(); pBitmap->Release(); pFactory->Release(); g_pImmediateContext->Unmap(pStaging, 0); pStaging->Release(); pBack->Release(); return false; }
|
||||
|
||||
hr = pFrame->SetPixelFormat(&wicFormat); // prefer BGRA
|
||||
hr = pFrame->WriteSource(pBitmap, nullptr);
|
||||
hr = pFrame->Commit();
|
||||
hr = pEncoder->Commit();
|
||||
|
||||
// cleanup
|
||||
if (pProps) pProps->Release();
|
||||
pFrame->Release();
|
||||
pEncoder->Release();
|
||||
pStream->Release();
|
||||
pBitmap->Release();
|
||||
pFactory->Release();
|
||||
|
||||
g_pImmediateContext->Unmap(pStaging, 0);
|
||||
pStaging->Release();
|
||||
pBack->Release();
|
||||
|
||||
CoUninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
16
Minecraft.Client/ScreenshotManager.h
Normal file
16
Minecraft.Client/ScreenshotManager.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
class ScreenshotManager {
|
||||
public:
|
||||
static ScreenshotManager* getInstance() {
|
||||
static ScreenshotManager instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
bool ensureScreenshotsFolder(std::wstring& outPath);
|
||||
std::wstring makeTimestampedFilename();
|
||||
bool takeScreenshot(const std::wstring& path = L"");
|
||||
|
||||
private:
|
||||
ScreenshotManager() = default;
|
||||
};
|
||||
|
|
@ -48,6 +48,7 @@
|
|||
#include "Network\WinsockNetLayer.h"
|
||||
#include "Windows64_Xuid.h"
|
||||
#include "Common/UI/UI.h"
|
||||
#include "ScreenshotManager.h"
|
||||
|
||||
// Forward-declare the internal Renderer class and its global instance from 4J_Render_PC_d.lib.
|
||||
// C4JRender (RenderManager) is a stateless wrapper — all D3D state lives in InternalRenderManager.
|
||||
|
|
@ -1773,6 +1774,16 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
|||
app.SetGameSettings(primaryPad, eGameSetting_DisplayHand, displayHud ? 0 : 1);
|
||||
}
|
||||
|
||||
// F2 for screenshot - retucio
|
||||
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_SCREENSHOT)) {
|
||||
Minecraft* mc = Minecraft::GetInstance();
|
||||
if (ScreenshotManager::getInstance()->takeScreenshot() && mc->player) {
|
||||
mc->gui->addMessage(L"screenshot taken", -1, false);
|
||||
} else if (mc->player) {
|
||||
mc->gui->addMessage(L"failed to take screenshot", -1, false);
|
||||
}
|
||||
}
|
||||
|
||||
// F3 toggles onscreen debug info
|
||||
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_DEBUG_INFO))
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue