Merge remote-tracking branch 'origin/main' into feature/dedicated-server

# Conflicts:
#	Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp
#	Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp
#	Minecraft.Client/Windows64/Network/WinsockNetLayer.h
This commit is contained in:
kuwacom 2026-03-05 08:31:55 +09:00
commit 7137c44d7a
57 changed files with 2585 additions and 1106 deletions

View file

@ -17,6 +17,7 @@ function(configure_msvc_target target)
$<$<AND:$<NOT:$<CONFIG:Release>>,$<COMPILE_LANGUAGE:C,CXX>>:/W3>
$<$<AND:$<CONFIG:Release>,$<COMPILE_LANGUAGE:C,CXX>>:/W0>
$<$<COMPILE_LANGUAGE:C,CXX>:/MP>
$<$<COMPILE_LANGUAGE:C,CXX>:/FS>
$<$<COMPILE_LANGUAGE:CXX>:/EHsc>
$<$<AND:$<CONFIG:Release>,$<COMPILE_LANGUAGE:C,CXX>>:/GL /O2 /Oi /GT /GF>
)

View file

@ -52,7 +52,7 @@ Chunk::Chunk(Level *level, LevelRenderer::rteMap &globalRenderableTileEntities,
: globalRenderableTileEntities( &globalRenderableTileEntities ), globalRenderableTileEntities_cs(&globalRenderableTileEntities_cs)
{
clipChunk->visible = false;
bb = NULL;
bb = nullptr;
id = 0;
this->level = level;
@ -101,15 +101,15 @@ void Chunk::setPos(int x, int y, int z)
float g = 6.0f;
// 4J - changed to just set the value rather than make a new one, if we've already created storage
if( bb == NULL )
if( !bb )
{
bb = AABB::newPermanent(-g, -g, -g, XZSIZE+g, SIZE+g, XZSIZE+g);
bb = shared_ptr<AABB>(AABB::newPermanent(-g, -g, -g, XZSIZE+g, SIZE+g, XZSIZE+g));
}
else
{
else
{
// 4J MGH - bounds are relative to the position now, so the AABB will be setup already, either above, or from the tesselator bounds.
// bb->set(-g, -g, -g, SIZE+g, SIZE+g, SIZE+g);
}
}
clipChunk->aabb[0] = bb->x0 + x;
clipChunk->aabb[1] = bb->y0 + y;
clipChunk->aabb[2] = bb->z0 + z;
@ -154,6 +154,7 @@ void Chunk::translateToPos()
Chunk::Chunk()
{
bb = nullptr;
}
void Chunk::makeCopyForRebuild(Chunk *source)
@ -998,7 +999,7 @@ int Chunk::getList(int layer)
void Chunk::cull(Culler *culler)
{
clipChunk->visible = culler->isVisible(bb);
clipChunk->visible = culler->isVisible(bb.get());
}
void Chunk::renderBB()
@ -1027,10 +1028,7 @@ void Chunk::clearDirty()
#endif
}
Chunk::~Chunk()
{
delete bb;
}
Chunk::~Chunk() = default;
bool Chunk::emptyFlagSet(int layer)
{

View file

@ -46,11 +46,11 @@ public:
int xRender, yRender, zRender;
int xRenderOffs, yRenderOffs, zRenderOffs;
int xm, ym, zm;
AABB *bb;
int xm, ym, zm;
shared_ptr<AABB> bb;
ClipChunk *clipChunk;
int id;
int id;
//public:
// vector<shared_ptr<TileEntity> > renderableTileEntities; // 4J - removed

View file

@ -361,17 +361,24 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame,
_HostGame( localUsersMask, publicSlots, privateSlots );
#ifdef _WINDOWS64
int port = (g_Win64MultiplayerPort > 0) ? g_Win64MultiplayerPort : WIN64_NET_DEFAULT_PORT;
const char *bindIP = g_Win64MultiplayerIP;
if (bindIP != NULL && bindIP[0] == 0)
bindIP = NULL;
int port = WIN64_NET_DEFAULT_PORT;
const char* bindIp = NULL;
if (g_Win64DedicatedServer)
{
if (g_Win64DedicatedServerPort > 0)
port = g_Win64DedicatedServerPort;
if (g_Win64DedicatedServerBindIP[0] != 0)
bindIp = g_Win64DedicatedServerBindIP;
}
if (!WinsockNetLayer::IsActive())
WinsockNetLayer::HostGame(bindIP, port);
WinsockNetLayer::HostGame(port, bindIp);
const wchar_t* hostName = IQNet::m_player[0].m_gamertag;
unsigned int settings = app.GetGameHostOption(eGameHostOption_All);
WinsockNetLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION);
if (WinsockNetLayer::IsActive())
{
const wchar_t* hostName = IQNet::m_player[0].m_gamertag;
unsigned int settings = app.GetGameHostOption(eGameHostOption_All);
WinsockNetLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION);
}
#endif
//#endif
}

View file

@ -58,7 +58,7 @@ bool ChoiceTask::isCompleted()
#ifdef _WINDOWS64
if (!m_bConfirmMappingComplete &&
(InputManager.GetValue(xboxPad, m_iConfirmMapping) > 0
|| KMInput.IsKeyDown(VK_RETURN)))
|| g_KBMInput.IsKeyDown(VK_RETURN)))
#else
if (!m_bConfirmMappingComplete &&
InputManager.GetValue(xboxPad, m_iConfirmMapping) > 0)
@ -70,7 +70,7 @@ bool ChoiceTask::isCompleted()
#ifdef _WINDOWS64
if (!m_bCancelMappingComplete &&
(InputManager.GetValue(xboxPad, m_iCancelMapping) > 0
|| KMInput.IsKeyDown('B')))
|| g_KBMInput.IsKeyDown('B')))
#else
if (!m_bCancelMappingComplete &&
InputManager.GetValue(xboxPad, m_iCancelMapping) > 0)

View file

@ -7,7 +7,7 @@
#include "TutorialConstraints.h"
#include "InfoTask.h"
#include "..\..\..\Minecraft.World\Material.h"
#include "..\..\KeyboardMouseInput.h"
#include "..\..\Windows64\KeyboardMouseInput.h"
InfoTask::InfoTask(Tutorial *tutorial, int descriptionId, int promptId /*= -1*/, bool requiresUserInput /*= false*/,
int iMapping /*= 0*/, ETelemetryChallenges telemetryEvent /*= eTelemetryTutorial_NoEvent*/)
@ -67,7 +67,7 @@ bool InfoTask::isCompleted()
if(!current)
{
#ifdef _WINDOWS64
if (InputManager.GetValue(pMinecraft->player->GetXboxPad(), (*it).first) > 0 || KMInput.IsKeyDown(VK_SPACE))
if (InputManager.GetValue(pMinecraft->player->GetXboxPad(), (*it).first) > 0 || g_KBMInput.IsKeyDown(VK_SPACE))
#else
if( InputManager.GetValue(pMinecraft->player->GetXboxPad(), (*it).first) > 0)
#endif

View file

@ -13,6 +13,12 @@
#include <pad.h>
#endif
#ifdef _WINDOWS64
#include "..\..\Windows64\KeyboardMouseInput.h"
SavedInventoryCursorPos g_savedInventoryCursorPos = { 0.0f, 0.0f, false };
#endif
IUIScene_AbstractContainerMenu::IUIScene_AbstractContainerMenu()
{
m_menu = NULL;
@ -474,6 +480,34 @@ void IUIScene_AbstractContainerMenu::onMouseTick()
}
#endif
#ifdef _WINDOWS64
if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
{
int deltaX = g_KBMInput.GetMouseDeltaX();
int deltaY = g_KBMInput.GetMouseDeltaY();
extern HWND g_hWnd;
RECT rc;
GetClientRect(g_hWnd, &rc);
int winW = rc.right - rc.left;
int winH = rc.bottom - rc.top;
if (winW > 0 && winH > 0)
{
float scaleX = (float)getMovieWidth() / (float)winW;
float scaleY = (float)getMovieHeight() / (float)winH;
vPointerPos.x += (float)deltaX * scaleX;
vPointerPos.y += (float)deltaY * scaleY;
}
if (deltaX != 0 || deltaY != 0)
{
bStickInput = true;
}
}
#endif
// Determine which slot the pointer is currently over.
ESceneSection eSectionUnderPointer = eSectionNone;
int iNewSlotX = -1;
@ -694,7 +728,11 @@ void IUIScene_AbstractContainerMenu::onMouseTick()
// If there is no stick input, and we are over a slot, then snap pointer to slot centre.
// 4J - TomK - only if this particular component allows so!
if(!m_bPointerDrivenByMouse && CanHaveFocus(eSectionUnderPointer))
#ifdef _WINDOWS64
if((g_KBMInput.IsMouseGrabbed() || !g_KBMInput.IsKBMActive()) && CanHaveFocus(eSectionUnderPointer))
#else
if(CanHaveFocus(eSectionUnderPointer))
#endif
{
vPointerPos.x = vSnapPos.x;
vPointerPos.y = vSnapPos.y;
@ -1329,7 +1367,7 @@ bool IUIScene_AbstractContainerMenu::handleKeyDown(int iPad, int iAction, bool b
// Standard left click
buttonNum = 0;
if (KMInput.IsKeyDown(VK_SHIFT))
if (g_KBMInput.IsKeyDown(VK_LSHIFT))
{
{
validKeyPress = TRUE;

View file

@ -1,5 +1,15 @@
#pragma once
#ifdef _WINDOWS64
struct SavedInventoryCursorPos
{
float x;
float y;
bool hasSavedPos;
};
extern SavedInventoryCursorPos g_savedInventoryCursorPos;
#endif
// Uncomment to enable tap input detection to jump 1 slot. Doesn't work particularly well yet, and I feel the system does not need it.
// Would probably be required if we decide to slow down the pointer movement.
// 4J Stu - There was a request to be able to navigate the scenes with the dpad, so I have used much of the TAP_DETECTION
@ -265,4 +275,6 @@ protected:
public:
virtual int getPad() = 0;
virtual int getMovieWidth() = 0;
virtual int getMovieHeight() = 0;
};

View file

@ -210,7 +210,6 @@ void IUIScene_CreativeMenu::staticCtor()
ITEM_AUX(Tile::woolCarpet_Id,13) // Green
ITEM_AUX(Tile::woolCarpet_Id,12) // Brown
#if 0
ITEM_AUX(Tile::stained_glass_Id,14) // Red
ITEM_AUX(Tile::stained_glass_Id,1) // Orange
ITEM_AUX(Tile::stained_glass_Id,4) // Yellow
@ -244,7 +243,6 @@ void IUIScene_CreativeMenu::staticCtor()
ITEM_AUX(Tile::stained_glass_pane_Id,15) // Black
ITEM_AUX(Tile::stained_glass_pane_Id,13) // Green
ITEM_AUX(Tile::stained_glass_pane_Id,12) // Brown
#endif
#ifndef _CONTENT_PACKAGE
DEF(eCreativeInventory_ArtToolsDecorations)
@ -265,40 +263,6 @@ void IUIScene_CreativeMenu::staticCtor()
BuildFirework(list, FireworksItem::TYPE_CREEPER, DyePowderItem::BLUE, 1, true, false);
BuildFirework(list, FireworksItem::TYPE_STAR, DyePowderItem::YELLOW, 1, false, false);
BuildFirework(list, FireworksItem::TYPE_BIG, DyePowderItem::WHITE, 1, true, true);
ITEM_AUX(Tile::stained_glass_Id,14) // Red
ITEM_AUX(Tile::stained_glass_Id,1) // Orange
ITEM_AUX(Tile::stained_glass_Id,4) // Yellow
ITEM_AUX(Tile::stained_glass_Id,5) // Lime
ITEM_AUX(Tile::stained_glass_Id,3) // Light Blue
ITEM_AUX(Tile::stained_glass_Id,9) // Cyan
ITEM_AUX(Tile::stained_glass_Id,11) // Blue
ITEM_AUX(Tile::stained_glass_Id,10) // Purple
ITEM_AUX(Tile::stained_glass_Id,2) // Magenta
ITEM_AUX(Tile::stained_glass_Id,6) // Pink
ITEM_AUX(Tile::stained_glass_Id,0) // White
ITEM_AUX(Tile::stained_glass_Id,8) // Light Gray
ITEM_AUX(Tile::stained_glass_Id,7) // Gray
ITEM_AUX(Tile::stained_glass_Id,15) // Black
ITEM_AUX(Tile::stained_glass_Id,13) // Green
ITEM_AUX(Tile::stained_glass_Id,12) // Brown
ITEM_AUX(Tile::stained_glass_pane_Id,14) // Red
ITEM_AUX(Tile::stained_glass_pane_Id,1) // Orange
ITEM_AUX(Tile::stained_glass_pane_Id,4) // Yellow
ITEM_AUX(Tile::stained_glass_pane_Id,5) // Lime
ITEM_AUX(Tile::stained_glass_pane_Id,3) // Light Blue
ITEM_AUX(Tile::stained_glass_pane_Id,9) // Cyan
ITEM_AUX(Tile::stained_glass_pane_Id,11) // Blue
ITEM_AUX(Tile::stained_glass_pane_Id,10) // Purple
ITEM_AUX(Tile::stained_glass_pane_Id,2) // Magenta
ITEM_AUX(Tile::stained_glass_pane_Id,6) // Pink
ITEM_AUX(Tile::stained_glass_pane_Id,0) // White
ITEM_AUX(Tile::stained_glass_pane_Id,8) // Light Gray
ITEM_AUX(Tile::stained_glass_pane_Id,7) // Gray
ITEM_AUX(Tile::stained_glass_pane_Id,15) // Black
ITEM_AUX(Tile::stained_glass_pane_Id,13) // Green
ITEM_AUX(Tile::stained_glass_pane_Id,12) // Brown
}
#endif
@ -882,7 +846,7 @@ void IUIScene_CreativeMenu::TabSpec::populateMenu(AbstractContainerMenu *menu, i
for(; currentGroup < m_staticGroupsCount; ++currentGroup)
{
int size = categoryGroups[m_staticGroupsA[currentGroup]].size();
if( currentIndex + size < startIndex)
if( currentIndex + size <= startIndex)
{
currentIndex += size;
continue;
@ -932,7 +896,7 @@ void IUIScene_CreativeMenu::TabSpec::populateMenu(AbstractContainerMenu *menu, i
for(; currentGroup < m_debugGroupsCount; ++currentGroup)
{
int size = categoryGroups[m_debugGroupsA[currentGroup]].size();
if( currentIndex + size < startIndex)
if( currentIndex + size <= startIndex)
{
currentIndex += size;
continue;
@ -973,7 +937,9 @@ unsigned int IUIScene_CreativeMenu::TabSpec::getPageCount()
#ifndef _CONTENT_PACKAGE
if(app.DebugArtToolsOn())
{
return (int)ceil((float)(m_staticItems + m_debugItems) / m_staticPerPage);
int totalItems = m_staticItems + m_debugItems;
const int totalRows = (totalItems + columns - 1) / columns;
return std::max<int>(1, totalRows - rows + 1);
}
else
#endif
@ -1132,7 +1098,15 @@ void IUIScene_CreativeMenu::handleAdditionalKeyPress(int iAction)
}
break;
case ACTION_MENU_OTHER_STICK_DOWN:
++m_tabPage[m_curTab];
{
int pageStep = TabSpec::rows;
#ifdef _WINDOWS64
if (g_KBMInput.WasMouseWheelConsumed())
{
pageStep = 1;
}
#endif
m_tabPage[m_curTab] += pageStep;
if(m_tabPage[m_curTab] >= specs[m_curTab]->getPageCount())
{
m_tabPage[m_curTab] = specs[m_curTab]->getPageCount() - 1;
@ -1141,9 +1115,18 @@ void IUIScene_CreativeMenu::handleAdditionalKeyPress(int iAction)
{
switchTab(m_curTab);
}
}
break;
case ACTION_MENU_OTHER_STICK_UP:
--m_tabPage[m_curTab];
{
int pageStep = TabSpec::rows;
#ifdef _WINDOWS64
if (g_KBMInput.WasMouseWheelConsumed())
{
pageStep = 1;
}
#endif
m_tabPage[m_curTab] -= pageStep;
if(m_tabPage[m_curTab] < 0)
{
m_tabPage[m_curTab] = 0;
@ -1152,6 +1135,7 @@ void IUIScene_CreativeMenu::handleAdditionalKeyPress(int iAction)
{
switchTab(m_curTab);
}
}
break;
}
}

View file

@ -42,7 +42,6 @@ bool UIControl::setupControl(UIScene *scene, IggyValuePath *parent, const string
return res;
}
#ifdef __PSVITA__
void UIControl::UpdateControl()
{
F64 fx, fy, fwidth, fheight;
@ -55,7 +54,6 @@ void UIControl::UpdateControl()
m_width = (S32)Math::round(fwidth);
m_height = (S32)Math::round(fheight);
}
#endif // __PSVITA__
void UIControl::ReInit()
{

View file

@ -61,8 +61,8 @@ public:
UIControl();
virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName);
#ifdef __PSVITA__
void UpdateControl();
#ifdef __PSVITA__
void setHidden(bool bHidden) {m_bHidden=bHidden;}
bool getHidden(void) {return m_bHidden;}
#endif

View file

@ -2,6 +2,7 @@
#include "UIController.h"
#include "UI.h"
#include "UIScene.h"
#include "UIControl_Slider.h"
#include "..\..\..\Minecraft.World\StringHelpers.h"
#include "..\..\LocalPlayer.h"
#include "..\..\DLCTexturePack.h"
@ -11,6 +12,9 @@
#include "..\..\EnderDragonRenderer.h"
#include "..\..\MultiPlayerLocalPlayer.h"
#include "UIFontData.h"
#ifdef _WINDOWS64
#include "..\..\Windows64\KeyboardMouseInput.h"
#endif
#ifdef __PSVITA__
#include <message_dialog.h>
#endif
@ -52,6 +56,21 @@ bool UIController::ms_bReloadSkinCSInitialised = false;
DWORD UIController::m_dwTrialTimerLimitSecs=DYNAMIC_CONFIG_DEFAULT_TRIAL_TIME;
#ifdef _WINDOWS64
static UIControl_Slider *FindSliderById(UIScene *pScene, int sliderId)
{
vector<UIControl *> *controls = pScene->GetControls();
if (!controls) return NULL;
for (size_t i = 0; i < controls->size(); ++i)
{
UIControl *ctrl = (*controls)[i];
if (ctrl && ctrl->getControlType() == UIControl::eSlider && ctrl->getId() == sliderId)
return (UIControl_Slider *)ctrl;
}
return NULL;
}
#endif
static void RADLINK WarningCallback(void *user_callback_data, Iggy *player, IggyResult code, const char *message)
{
//enum IggyResult{ IGGY_RESULT_SUCCESS = 0, IGGY_RESULT_Warning_None = 0,
@ -216,6 +235,10 @@ UIController::UIController()
m_currentRenderViewport = C4JRender::VIEWPORT_TYPE_FULLSCREEN;
m_bCustomRenderPosition = false;
m_winUserIndex = 0;
m_mouseDraggingSliderScene = eUIScene_COUNT;
m_mouseDraggingSliderId = -1;
m_lastHoverMouseX = -1;
m_lastHoverMouseY = -1;
m_accumulatedTicks = 0;
m_lastUiSfx = 0;
@ -761,6 +784,168 @@ void UIController::tickInput()
else
#endif
{
#ifdef _WINDOWS64
if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
{
UIScene *pScene = NULL;
for (int grp = 0; grp < eUIGroup_COUNT && !pScene; ++grp)
{
pScene = m_groups[grp]->GetTopScene(eUILayer_Debug);
if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Tooltips);
if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Error);
if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Alert);
if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Popup);
if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Fullscreen);
if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Scene);
}
if (pScene && pScene->getMovie())
{
Iggy *movie = pScene->getMovie();
int rawMouseX = g_KBMInput.GetMouseX();
int rawMouseY = g_KBMInput.GetMouseY();
F32 mouseX = (F32)rawMouseX;
F32 mouseY = (F32)rawMouseY;
extern HWND g_hWnd;
if (g_hWnd)
{
RECT rc;
GetClientRect(g_hWnd, &rc);
int winW = rc.right - rc.left;
int winH = rc.bottom - rc.top;
if (winW > 0 && winH > 0)
{
mouseX = mouseX * (m_fScreenWidth / (F32)winW);
mouseY = mouseY * (m_fScreenHeight / (F32)winH);
}
}
// Only update hover focus when the mouse has actually moved,
// so that mouse-wheel scrolling can change list selection
// without the hover immediately snapping focus back.
bool mouseMoved = (rawMouseX != m_lastHoverMouseX || rawMouseY != m_lastHoverMouseY);
m_lastHoverMouseX = rawMouseX;
m_lastHoverMouseY = rawMouseY;
if (mouseMoved)
{
IggyFocusHandle currentFocus = IGGY_FOCUS_NULL;
IggyFocusableObject focusables[64];
S32 numFocusables = 0;
IggyPlayerGetFocusableObjects(movie, &currentFocus, focusables, 64, &numFocusables);
if (numFocusables > 0 && numFocusables <= 64)
{
IggyFocusHandle hitObject = IGGY_FOCUS_NULL;
for (S32 i = 0; i < numFocusables; ++i)
{
if (mouseX >= focusables[i].x0 && mouseX <= focusables[i].x1 &&
mouseY >= focusables[i].y0 && mouseY <= focusables[i].y1)
{
hitObject = focusables[i].object;
break;
}
}
if (hitObject != currentFocus)
{
IggyPlayerSetFocusRS(movie, hitObject, 0);
}
}
}
// Convert mouse to scene/movie coordinates for slider hit testing
F32 sceneMouseX = mouseX;
F32 sceneMouseY = mouseY;
{
S32 displayWidth = 0, displayHeight = 0;
pScene->GetParentLayer()->getRenderDimensions(displayWidth, displayHeight);
if (displayWidth > 0 && displayHeight > 0)
{
sceneMouseX = mouseX * ((F32)pScene->getRenderWidth() / (F32)displayWidth);
sceneMouseY = mouseY * ((F32)pScene->getRenderHeight() / (F32)displayHeight);
}
}
// Get main panel offset (controls are positioned relative to it)
S32 panelOffsetX = 0, panelOffsetY = 0;
UIControl *pMainPanel = pScene->GetMainPanel();
if (pMainPanel)
{
pMainPanel->UpdateControl();
panelOffsetX = pMainPanel->getXPos();
panelOffsetY = pMainPanel->getYPos();
}
bool leftPressed = g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT);
bool leftDown = leftPressed || g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT);
if (m_mouseDraggingSliderScene != eUIScene_COUNT && m_mouseDraggingSliderScene != pScene->getSceneType())
{
m_mouseDraggingSliderScene = eUIScene_COUNT;
m_mouseDraggingSliderId = -1;
}
if (leftPressed)
{
vector<UIControl *> *controls = pScene->GetControls();
if (controls)
{
for (size_t i = 0; i < controls->size(); ++i)
{
UIControl *ctrl = (*controls)[i];
if (!ctrl || ctrl->getControlType() != UIControl::eSlider || !ctrl->getVisible())
continue;
UIControl_Slider *pSlider = (UIControl_Slider *)ctrl;
pSlider->UpdateControl();
S32 cx = pSlider->getXPos() + panelOffsetX;
S32 cy = pSlider->getYPos() + panelOffsetY;
S32 cw = pSlider->GetRealWidth();
S32 ch = pSlider->getHeight();
if (cw <= 0 || ch <= 0)
continue;
if (sceneMouseX >= cx && sceneMouseX <= cx + cw && sceneMouseY >= cy && sceneMouseY <= cy + ch)
{
m_mouseDraggingSliderScene = pScene->getSceneType();
m_mouseDraggingSliderId = pSlider->getId();
break;
}
}
}
}
if (leftDown && m_mouseDraggingSliderScene == pScene->getSceneType() && m_mouseDraggingSliderId >= 0)
{
UIControl_Slider *pSlider = FindSliderById(pScene, m_mouseDraggingSliderId);
if (pSlider && pSlider->getVisible())
{
pSlider->UpdateControl();
S32 sliderX = pSlider->getXPos() + panelOffsetX;
S32 sliderWidth = pSlider->GetRealWidth();
if (sliderWidth > 0)
{
float fNewSliderPos = (sceneMouseX - (float)sliderX) / (float)sliderWidth;
if (fNewSliderPos < 0.0f) fNewSliderPos = 0.0f;
if (fNewSliderPos > 1.0f) fNewSliderPos = 1.0f;
pSlider->SetSliderTouchPos(fNewSliderPos);
}
}
else
{
m_mouseDraggingSliderScene = eUIScene_COUNT;
m_mouseDraggingSliderId = -1;
}
}
else if (!leftDown)
{
m_mouseDraggingSliderScene = eUIScene_COUNT;
m_mouseDraggingSliderId = -1;
}
}
}
#endif
handleInput();
++m_accumulatedTicks;
}
@ -995,28 +1180,59 @@ void UIController::handleKeyPress(unsigned int iPad, unsigned int key)
released = InputManager.ButtonReleased(iPad,key); // Toggle
#ifdef _WINDOWS64
// Keyboard menu input for player 0
if (iPad == 0)
{
bool kbDown = false, kbPressed = false, kbReleased = false;
switch(key)
int vk = 0;
switch (key)
{
case ACTION_MENU_UP: kbDown = KMInput.IsKeyDown(VK_UP); kbPressed = KMInput.IsKeyPressed(VK_UP); kbReleased = KMInput.IsKeyReleased(VK_UP); break;
case ACTION_MENU_DOWN: kbDown = KMInput.IsKeyDown(VK_DOWN); kbPressed = KMInput.IsKeyPressed(VK_DOWN); kbReleased = KMInput.IsKeyReleased(VK_DOWN); break;
case ACTION_MENU_LEFT: kbDown = KMInput.IsKeyDown(VK_LEFT); kbPressed = KMInput.IsKeyPressed(VK_LEFT); kbReleased = KMInput.IsKeyReleased(VK_LEFT); break;
case ACTION_MENU_RIGHT: kbDown = KMInput.IsKeyDown(VK_RIGHT); kbPressed = KMInput.IsKeyPressed(VK_RIGHT); kbReleased = KMInput.IsKeyReleased(VK_RIGHT); break;
case ACTION_MENU_OK: kbDown = KMInput.IsKeyDown(VK_RETURN); kbPressed = KMInput.IsKeyPressed(VK_RETURN); kbReleased = KMInput.IsKeyReleased(VK_RETURN); break;
case ACTION_MENU_A: kbDown = KMInput.IsKeyDown(VK_RETURN); kbPressed = KMInput.IsKeyPressed(VK_RETURN); kbReleased = KMInput.IsKeyReleased(VK_RETURN); break;
case ACTION_MENU_CANCEL: kbDown = KMInput.IsKeyDown(VK_ESCAPE); kbPressed = KMInput.IsKeyPressed(VK_ESCAPE); kbReleased = KMInput.IsKeyReleased(VK_ESCAPE); break;
case ACTION_MENU_B: kbDown = KMInput.IsKeyDown(VK_ESCAPE); kbPressed = KMInput.IsKeyPressed(VK_ESCAPE); kbReleased = KMInput.IsKeyReleased(VK_ESCAPE); break;
case ACTION_MENU_PAUSEMENU: kbDown = KMInput.IsKeyDown(VK_ESCAPE); kbPressed = KMInput.IsKeyPressed(VK_ESCAPE); kbReleased = KMInput.IsKeyReleased(VK_ESCAPE); break;
case ACTION_MENU_LEFT_SCROLL: kbDown = KMInput.IsKeyDown('Q'); kbPressed = KMInput.IsKeyPressed('Q'); kbReleased = KMInput.IsKeyReleased('Q'); break;
case ACTION_MENU_RIGHT_SCROLL: kbDown = KMInput.IsKeyDown('E'); kbPressed = KMInput.IsKeyPressed('E'); kbReleased = KMInput.IsKeyReleased('E'); break;
case ACTION_MENU_QUICK_MOVE: kbDown = KMInput.IsKeyDown(VK_SHIFT); kbPressed = KMInput.IsKeyPressed(VK_SHIFT); kbReleased = KMInput.IsKeyReleased(VK_SHIFT); break;
case ACTION_MENU_OK: case ACTION_MENU_A: vk = VK_RETURN; break;
case ACTION_MENU_CANCEL: case ACTION_MENU_B: vk = VK_ESCAPE; break;
case ACTION_MENU_UP: vk = VK_UP; break;
case ACTION_MENU_DOWN: vk = VK_DOWN; break;
case ACTION_MENU_LEFT: vk = VK_LEFT; break;
case ACTION_MENU_RIGHT: vk = VK_RIGHT; break;
case ACTION_MENU_X: vk = 'R'; break;
case ACTION_MENU_Y: vk = VK_TAB; break;
case ACTION_MENU_LEFT_SCROLL: vk = 'Q'; break;
case ACTION_MENU_RIGHT_SCROLL: vk = 'E'; break;
case ACTION_MENU_PAGEUP: vk = VK_PRIOR; break;
case ACTION_MENU_PAGEDOWN: vk = VK_NEXT; break;
}
if (vk != 0)
{
if (g_KBMInput.IsKeyPressed(vk)) { pressed = true; down = true; }
if (g_KBMInput.IsKeyReleased(vk)) { released = true; down = false; }
if (!pressed && !released && g_KBMInput.IsKeyDown(vk)) { down = true; }
}
if ((key == ACTION_MENU_OK || key == ACTION_MENU_A) && !g_KBMInput.IsMouseGrabbed())
{
if (m_mouseDraggingSliderId < 0)
{
if (g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT)) { pressed = true; down = true; }
if (g_KBMInput.IsMouseButtonReleased(KeyboardMouseInput::MOUSE_LEFT)) { released = true; down = false; }
if (!pressed && !released && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT)) { down = true; }
}
}
// Scroll wheel for list scrolling — only consume the wheel value when the
// action key actually matches, so the other direction isn't lost.
if (!g_KBMInput.IsMouseGrabbed() && (key == ACTION_MENU_OTHER_STICK_UP || key == ACTION_MENU_OTHER_STICK_DOWN))
{
int wheel = g_KBMInput.PeekMouseWheel();
if (key == ACTION_MENU_OTHER_STICK_UP && wheel > 0)
{
g_KBMInput.ConsumeMouseWheel();
pressed = true;
down = true;
}
else if (key == ACTION_MENU_OTHER_STICK_DOWN && wheel < 0)
{
g_KBMInput.ConsumeMouseWheel();
pressed = true;
down = true;
}
}
pressed = pressed || kbPressed;
released = released || kbReleased;
down = down || kbDown;
}
#endif
@ -3138,4 +3354,4 @@ void UIController::SendTouchInput(unsigned int iPad, unsigned int key, bool bPre
}
#endif
#endif

View file

@ -158,6 +158,10 @@ private:
vector<QueuedMessageBoxData *> m_queuedMessageBoxData;
unsigned int m_winUserIndex;
EUIScene m_mouseDraggingSliderScene;
int m_mouseDraggingSliderId;
int m_lastHoverMouseX;
int m_lastHoverMouseY;
//bool m_bSysUIShowing;
bool m_bSystemUIShowing;
C4JThread *m_reloadSkinThread;

View file

@ -37,9 +37,7 @@ private:
public:
UIGroup(EUIGroup group, int iPad);
#ifdef __PSVITA__
EUIGroup GetGroup() {return m_group;}
#endif
UIComponent_Tooltips *getTooltips() { return m_tooltips; }
UIComponent_TutorialPopup *getTutorialPopup() { return m_tutorialPopup; }
UIScene_HUD *getHUD() { return m_hud; }

View file

@ -107,8 +107,10 @@ public:
int getRenderHeight() { return m_renderHeight; }
#ifdef __PSVITA__
UILayer *GetParentLayer() {return m_parentLayer;}
EUIGroup GetParentLayerGroup() {return m_parentLayer->m_parentGroup->GetGroup();}
#endif
#if defined(__PSVITA__) || defined(_WINDOWS64)
UILayer *GetParentLayer() {return m_parentLayer;}
vector<UIControl *> *GetControls() {return &m_controls;}
#endif

View file

@ -28,17 +28,6 @@ UIScene_AbstractContainerMenu::UIScene_AbstractContainerMenu(int iPad, UILayer *
ui.OverrideSFX(m_iPad,ACTION_MENU_DOWN,true);
m_bIgnoreInput=false;
#ifdef _WINDOWS64
m_bMouseDragSlider=false;
m_bHasMousePosition = false;
m_lastMouseX = 0;
m_lastMouseY = 0;
for (int btn = 0; btn < 3; btn++)
{
KMInput.ConsumeMousePress(btn);
}
#endif
}
UIScene_AbstractContainerMenu::~UIScene_AbstractContainerMenu()
@ -49,6 +38,16 @@ UIScene_AbstractContainerMenu::~UIScene_AbstractContainerMenu()
void UIScene_AbstractContainerMenu::handleDestroy()
{
app.DebugPrintf("UIScene_AbstractContainerMenu::handleDestroy\n");
#ifdef _WINDOWS64
g_savedInventoryCursorPos.x = m_pointerPos.x;
g_savedInventoryCursorPos.y = m_pointerPos.y;
g_savedInventoryCursorPos.hasSavedPos = true;
g_KBMInput.SetScreenCursorHidden(false);
g_KBMInput.SetCursorHiddenForUI(false);
#endif
Minecraft *pMinecraft = Minecraft::GetInstance();
if( pMinecraft->localgameModes[m_iPad] != NULL )
{
@ -84,6 +83,10 @@ void UIScene_AbstractContainerMenu::InitDataAssociations(int iPad, AbstractConta
void UIScene_AbstractContainerMenu::PlatformInitialize(int iPad, int startIndex)
{
#ifdef _WINDOWS64
g_KBMInput.SetScreenCursorHidden(true);
g_KBMInput.SetCursorHiddenForUI(true);
#endif
m_labelInventory.init( app.GetString(IDS_INVENTORY) );
@ -168,6 +171,19 @@ void UIScene_AbstractContainerMenu::PlatformInitialize(int iPad, int startIndex)
//m_pointerControl->SetPosition( &vPointerPos );
m_pointerPos = vPointerPos;
#ifdef _WINDOWS64
if (g_savedInventoryCursorPos.hasSavedPos)
{
m_pointerPos.x = g_savedInventoryCursorPos.x;
m_pointerPos.y = g_savedInventoryCursorPos.y;
if (m_pointerPos.x < m_fPointerMinX) m_pointerPos.x = m_fPointerMinX;
if (m_pointerPos.x > m_fPointerMaxX) m_pointerPos.x = m_fPointerMaxX;
if (m_pointerPos.y < m_fPointerMinY) m_pointerPos.y = m_fPointerMinY;
if (m_pointerPos.y > m_fPointerMaxY) m_pointerPos.y = m_fPointerMaxY;
}
#endif
IggyEvent mouseEvent;
S32 width, height;
m_parentLayer->getRenderDimensions(width, height);
@ -190,139 +206,15 @@ void UIScene_AbstractContainerMenu::tick()
{
UIScene::tick();
#ifdef _WINDOWS64
bool mouseActive = (m_iPad == 0 && !KMInput.IsCaptured());
bool drivePointerFromMouse = false;
float rawMouseMovieX = 0, rawMouseMovieY = 0;
int scrollDelta = 0;
// Map Windows mouse position to the virtual pointer in movie coordinates
if (mouseActive)
{
RECT clientRect;
GetClientRect(KMInput.GetHWnd(), &clientRect);
int clientWidth = clientRect.right;
int clientHeight = clientRect.bottom;
if (clientWidth > 0 && clientHeight > 0)
{
int mouseX = KMInput.GetMouseX();
int mouseY = KMInput.GetMouseY();
bool mouseMoved = !m_bHasMousePosition || mouseX != m_lastMouseX || mouseY != m_lastMouseY;
m_bHasMousePosition = true;
m_lastMouseX = mouseX;
m_lastMouseY = mouseY;
scrollDelta = KMInput.ConsumeScrollDelta();
// Convert mouse position to movie coordinates using the movie/client ratio
float mx = (float)mouseX * ((float)m_movieWidth / (float)clientWidth) - (float)m_controlMainPanel.getXPos();
float my = (float)mouseY * ((float)m_movieHeight / (float)clientHeight) - (float)m_controlMainPanel.getYPos();
rawMouseMovieX = mx;
rawMouseMovieY = my;
// Once the mouse has taken over the container cursor, keep following the OS cursor
// until explicit controller input takes ownership back.
drivePointerFromMouse = m_bPointerDrivenByMouse || mouseMoved || KMInput.IsMouseDown(0) || KMInput.IsMouseDown(1) || KMInput.IsMouseDown(2) || scrollDelta != 0;
if (drivePointerFromMouse)
{
m_bPointerDrivenByMouse = true;
m_eCurrTapState = eTapStateNoInput;
m_pointerPos.x = mx;
m_pointerPos.y = my;
}
}
}
#endif
onMouseTick();
#ifdef _WINDOWS64
// Dispatch mouse clicks AFTER onMouseTick() has updated m_eCurrSection from the new pointer position
if (mouseActive)
{
if (KMInput.ConsumeMousePress(0))
{
if (m_eCurrSection == eSectionInventoryCreativeSlider)
{
// Scrollbar click: use raw mouse position (onMouseTick may have snapped m_pointerPos)
m_bMouseDragSlider = true;
m_pointerPos.x = rawMouseMovieX;
m_pointerPos.y = rawMouseMovieY;
handleOtherClicked(m_iPad, eSectionInventoryCreativeSlider, 0, false);
}
else
{
handleKeyDown(m_iPad, ACTION_MENU_A, false);
}
}
else if (m_bMouseDragSlider && KMInput.IsMouseDown(0))
{
// Continue scrollbar drag: update scroll position from current mouse Y
m_pointerPos.x = rawMouseMovieX;
m_pointerPos.y = rawMouseMovieY;
handleOtherClicked(m_iPad, eSectionInventoryCreativeSlider, 0, false);
}
if (!KMInput.IsMouseDown(0))
m_bMouseDragSlider = false;
if (KMInput.ConsumeMousePress(1))
{
handleKeyDown(m_iPad, ACTION_MENU_X, false);
}
if (KMInput.ConsumeMousePress(2))
{
handleKeyDown(m_iPad, ACTION_MENU_Y, false);
}
// Mouse scroll wheel for page scrolling
if (scrollDelta > 0)
{
handleKeyDown(m_iPad, ACTION_MENU_OTHER_STICK_UP, false);
}
else if (scrollDelta < 0)
{
handleKeyDown(m_iPad, ACTION_MENU_OTHER_STICK_DOWN, false);
}
// ESC to close — must be last since it may destroy this scene
if (KMInput.ConsumeKeyPress(VK_ESCAPE))
{
handleKeyDown(m_iPad, ACTION_MENU_B, false);
return;
}
}
#endif
IggyEvent mouseEvent;
S32 width, height;
m_parentLayer->getRenderDimensions(width, height);
#ifdef _WINDOWS64
S32 x, y;
if (mouseActive && m_bPointerDrivenByMouse)
{
// Send raw mouse position directly as Iggy event to avoid coordinate round-trip errors
// Scale mouse client coords to the Iggy display space (which was set to getRenderDimensions())
RECT clientRect;
GetClientRect(KMInput.GetHWnd(), &clientRect);
float mouseMovieX = (float)KMInput.GetMouseX() * ((float)m_movieWidth / (float)clientRect.right);
float mouseMovieY = (float)KMInput.GetMouseY() * ((float)m_movieHeight / (float)clientRect.bottom);
float mouseLocalX = mouseMovieX - (float)m_controlMainPanel.getXPos();
float mouseLocalY = mouseMovieY - (float)m_controlMainPanel.getYPos();
S32 x = (S32)(m_pointerPos.x * ((float)width / m_movieWidth));
S32 y = (S32)(m_pointerPos.y * ((float)height / m_movieHeight));
x = (S32)(mouseLocalX * ((float)width / m_movieWidth));
y = (S32)(mouseLocalY * ((float)height / m_movieHeight));
}
else
{
x = (S32)(m_pointerPos.x * ((float)width / m_movieWidth));
y = (S32)(m_pointerPos.y * ((float)height / m_movieHeight));
}
#else
S32 x = m_pointerPos.x*((float)width/m_movieWidth);
S32 y = m_pointerPos.y*((float)height/m_movieHeight);
#endif
IggyMakeEventMouseMove( &mouseEvent, x, y);
// 4J Stu - This seems to be broken on Durango, so do it ourself

View file

@ -10,12 +10,6 @@ class UIScene_AbstractContainerMenu : public UIScene, public virtual IUIScene_Ab
private:
ESceneSection m_focusSection;
bool m_bIgnoreInput;
#ifdef _WINDOWS64
bool m_bMouseDragSlider;
bool m_bHasMousePosition;
int m_lastMouseX;
int m_lastMouseY;
#endif
protected:
UIControl m_controlMainPanel;
@ -42,6 +36,8 @@ public:
virtual void handleDestroy();
int getPad() { return m_iPad; }
int getMovieWidth() { return m_movieWidth; }
int getMovieHeight() { return m_movieHeight; }
bool getIgnoreInput() { return m_bIgnoreInput; }
void setIgnoreInput(bool bVal) { m_bIgnoreInput=bVal; }

View file

@ -1085,6 +1085,7 @@ void UIScene_CreateWorldMenu::CreateGame(UIScene_CreateWorldMenu* pClass, DWORD
__int64 seedValue = 0;
NetworkGameInitData *param = new NetworkGameInitData();
param->levelName = wWorldName;
if (wSeed.length() != 0)
{

View file

@ -39,17 +39,37 @@ UIScene_DebugOverlay::UIScene_DebugOverlay(int iPad, void *initData, UILayer *pa
m_buttonSetNight.init(L"Set Night", eControl_SetNight);
m_buttonListItems.init(eControl_Items);
// Sort items alphabetically
std::vector<std::pair<std::wstring, unsigned int>> sortedItems;
for (size_t i = 0; i < Item::items.length; ++i)
{
if (Item::items[i] != NULL)
{
sortedItems.emplace_back(std::wstring(app.GetString(Item::items[i]->getDescriptionId())), i);
}
}
for (size_t i = 1; i < sortedItems.size(); ++i)
{
auto key = sortedItems[i];
int j = i - 1;
while (j >= 0 && sortedItems[j].first > key.first)
{
sortedItems[j + 1] = sortedItems[j];
--j;
}
sortedItems[j + 1] = key;
}
// Populate the list in sorted order
int listId = 0;
for(unsigned int i = 0; i < Item::items.length; ++i)
{
if(Item::items[i] != NULL)
{
m_itemIds.push_back(i);
m_buttonListItems.addItem(app.GetString(Item::items[i]->getDescriptionId()), listId);
++listId;
}
}
for (const auto& entry : sortedItems)
{
m_itemIds.push_back(entry.second);
m_buttonListItems.addItem(entry.first.c_str(), listId);
++listId;
}
m_buttonListEnchantments.init(eControl_Enchantments);

View file

@ -238,7 +238,7 @@ UIScene_LoadMenu::UIScene_LoadMenu(int iPad, void *initData, UILayer *parentLaye
#endif
m_bShowTimer = true;
}
#if defined(_DURANGO)
#if defined(_DURANGO)
m_labelGameName.init(params->saveDetails->UTF16SaveName);
#else
wchar_t wSaveName[128];
@ -246,6 +246,16 @@ UIScene_LoadMenu::UIScene_LoadMenu(int iPad, void *initData, UILayer *parentLaye
mbstowcs(wSaveName, params->saveDetails->UTF8SaveName, strlen(params->saveDetails->UTF8SaveName)+1); // plus null
m_labelGameName.init(wSaveName);
#endif
#endif
#ifdef _WINDOWS64
if (params->saveDetails != NULL && params->saveDetails->UTF8SaveName[0] != '\0')
{
wchar_t wSaveName[128];
ZeroMemory(wSaveName, sizeof(wSaveName));
mbstowcs(wSaveName, params->saveDetails->UTF8SaveName, 127);
m_levelName = wstring(wSaveName);
m_labelGameName.init(m_levelName);
}
#endif
}
@ -1574,6 +1584,7 @@ void UIScene_LoadMenu::StartGameFromSave(UIScene_LoadMenu* pClass, DWORD dwLocal
param->saveData = NULL;
param->levelGen = pClass->m_levelGen;
param->texturePackId = pClass->m_MoreOptionsParams.dwTexturePack;
param->levelName = pClass->m_levelName;
Minecraft *pMinecraft = Minecraft::GetInstance();
pMinecraft->skins->selectTexturePackById(pClass->m_MoreOptionsParams.dwTexturePack);

View file

@ -59,6 +59,7 @@ private:
bool m_bIsCorrupt;
bool m_bThumbnailGetFailed;
__int64 m_seed;
wstring m_levelName;
#ifdef __PS3__
std::vector<SonyCommerce::ProductInfo>*m_pvProductInfo;

View file

@ -25,6 +25,98 @@
#include "message_dialog.h"
#endif
#ifdef _WINDOWS64
#include "..\..\..\Minecraft.World\NbtIo.h"
#include "..\..\..\Minecraft.World\compression.h"
static wstring ReadLevelNameFromSaveFile(const wstring& filePath)
{
HANDLE hFile = CreateFileW(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (hFile == INVALID_HANDLE_VALUE) return L"";
DWORD fileSize = GetFileSize(hFile, NULL);
if (fileSize < 12 || fileSize == INVALID_FILE_SIZE) { CloseHandle(hFile); return L""; }
unsigned char *rawData = new unsigned char[fileSize];
DWORD bytesRead = 0;
if (!ReadFile(hFile, rawData, fileSize, &bytesRead, NULL) || bytesRead != fileSize)
{
CloseHandle(hFile);
delete[] rawData;
return L"";
}
CloseHandle(hFile);
unsigned char *saveData = NULL;
unsigned int saveSize = 0;
bool freeSaveData = false;
if (*(unsigned int*)rawData == 0)
{
// Compressed format: bytes 0-3=0, bytes 4-7=decompressed size, bytes 8+=compressed data
unsigned int decompSize = *(unsigned int*)(rawData + 4);
if (decompSize == 0 || decompSize > 128 * 1024 * 1024)
{
delete[] rawData;
return L"";
}
saveData = new unsigned char[decompSize];
Compression::getCompression()->Decompress(saveData, &decompSize, rawData + 8, fileSize - 8);
saveSize = decompSize;
freeSaveData = true;
}
else
{
saveData = rawData;
saveSize = fileSize;
}
wstring result = L"";
if (saveSize >= 12)
{
unsigned int headerOffset = *(unsigned int*)saveData;
unsigned int numEntries = *(unsigned int*)(saveData + 4);
const unsigned int entrySize = sizeof(FileEntrySaveData);
if (headerOffset < saveSize && numEntries > 0 && numEntries < 10000 &&
headerOffset + numEntries * entrySize <= saveSize)
{
FileEntrySaveData *table = (FileEntrySaveData *)(saveData + headerOffset);
for (unsigned int i = 0; i < numEntries; i++)
{
if (wcscmp(table[i].filename, L"level.dat") == 0)
{
unsigned int off = table[i].startOffset;
unsigned int len = table[i].length;
if (off >= 12 && off + len <= saveSize && len > 0 && len < 4 * 1024 * 1024)
{
byteArray ba;
ba.data = (byte*)(saveData + off);
ba.length = len;
CompoundTag *root = NbtIo::decompress(ba);
if (root != NULL)
{
CompoundTag *dataTag = root->getCompound(L"Data");
if (dataTag != NULL)
result = dataTag->getString(L"LevelName");
delete root;
}
}
break;
}
}
}
}
if (freeSaveData) delete[] saveData;
delete[] rawData;
// "world" is the engine default — it means no real name was ever set, so
// return empty to let the caller fall back to the save filename (timestamp).
if (result == L"world") result = L"";
return result;
}
#endif
#ifdef SONY_REMOTE_STORAGE_DOWNLOAD
unsigned long UIScene_LoadOrJoinMenu::m_ulFileSize=0L;
@ -159,7 +251,7 @@ UIScene_LoadOrJoinMenu::UIScene_LoadOrJoinMenu(int iPad, void *initData, UILayer
}
#endif
#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) || defined(_DURANGO)
#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) || defined(_DURANGO) || defined(_WINDOWS64)
// Always clear the saves when we enter this menu
StorageManager.ClearSavesInfo();
#endif
@ -614,6 +706,22 @@ void UIScene_LoadOrJoinMenu::tick()
m_saveDetails = new SaveListDetails[m_pSaveDetails->iSaveC];
m_iSaveDetailsCount = m_pSaveDetails->iSaveC;
#ifdef _WINDOWS64
// Build sorted index array (newest-first by filename timestamp YYYYMMDDHHMMSS)
int *sortedIdx = new int[m_pSaveDetails->iSaveC];
for (int si = 0; si < (int)m_pSaveDetails->iSaveC; ++si) sortedIdx[si] = si;
for (int si = 1; si < (int)m_pSaveDetails->iSaveC; ++si)
{
int key = sortedIdx[si];
int sj = si - 1;
while (sj >= 0 && strcmp(m_pSaveDetails->SaveInfoA[sortedIdx[sj]].UTF8SaveFilename, m_pSaveDetails->SaveInfoA[key].UTF8SaveFilename) < 0)
{
sortedIdx[sj + 1] = sortedIdx[sj];
--sj;
}
sortedIdx[sj + 1] = key;
}
#endif
for(unsigned int i = 0; i < m_pSaveDetails->iSaveC; ++i)
{
#if defined(_XBOX_ONE)
@ -627,14 +735,40 @@ void UIScene_LoadOrJoinMenu::tick()
m_saveDetails[i].saveId = i;
memcpy(m_saveDetails[i].UTF16SaveName, m_pSaveDetails->SaveInfoA[i].UTF16SaveTitle, 128);
memcpy(m_saveDetails[i].UTF16SaveFilename, m_pSaveDetails->SaveInfoA[i].UTF16SaveFilename, MAX_SAVEFILENAME_LENGTH);
#else
#ifdef _WINDOWS64
{
int origIdx = sortedIdx[i];
wchar_t wFilename[MAX_SAVEFILENAME_LENGTH];
ZeroMemory(wFilename, sizeof(wFilename));
mbstowcs(wFilename, m_pSaveDetails->SaveInfoA[origIdx].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH - 1);
wstring filePath = wstring(L"Windows64\\GameHDD\\") + wstring(wFilename) + wstring(L"\\saveData.ms");
wstring levelName = ReadLevelNameFromSaveFile(filePath);
if (!levelName.empty())
{
m_buttonListSaves.addItem(levelName, wstring(L""));
wcstombs(m_saveDetails[i].UTF8SaveName, levelName.c_str(), 127);
m_saveDetails[i].UTF8SaveName[127] = '\0';
}
else
{
m_buttonListSaves.addItem(m_pSaveDetails->SaveInfoA[origIdx].UTF8SaveTitle, L"");
memcpy(m_saveDetails[i].UTF8SaveName, m_pSaveDetails->SaveInfoA[origIdx].UTF8SaveTitle, 128);
}
m_saveDetails[i].saveId = origIdx;
memcpy(m_saveDetails[i].UTF8SaveFilename, m_pSaveDetails->SaveInfoA[origIdx].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH);
}
#else
m_buttonListSaves.addItem(m_pSaveDetails->SaveInfoA[i].UTF8SaveTitle, L"");
m_saveDetails[i].saveId = i;
memcpy(m_saveDetails[i].UTF8SaveName, m_pSaveDetails->SaveInfoA[i].UTF8SaveTitle, 128);
m_saveDetails[i].saveId = i;
memcpy(m_saveDetails[i].UTF8SaveFilename, m_pSaveDetails->SaveInfoA[i].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH);
#endif
#endif
}
#ifdef _WINDOWS64
delete[] sortedIdx;
#endif
m_controlSavesTimer.setVisible( false );
// set focus on the first button
@ -650,7 +784,11 @@ void UIScene_LoadOrJoinMenu::tick()
app.DebugPrintf("Requesting the first thumbnail\n");
// set the save to load
PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo();
#ifdef _WINDOWS64
C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[m_saveDetails[m_iRequestingThumbnailId].saveId],&LoadSaveDataThumbnailReturned,this);
#else
C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[(int)m_iRequestingThumbnailId],&LoadSaveDataThumbnailReturned,this);
#endif
if(eLoadStatus!=C4JStorage::ESaveGame_GetSaveThumbnail)
{
@ -713,7 +851,11 @@ void UIScene_LoadOrJoinMenu::tick()
app.DebugPrintf("Requesting another thumbnail\n");
// set the save to load
PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo();
#ifdef _WINDOWS64
C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[m_saveDetails[m_iRequestingThumbnailId].saveId],&LoadSaveDataThumbnailReturned,this);
#else
C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[(int)m_iRequestingThumbnailId],&LoadSaveDataThumbnailReturned,this);
#endif
if(eLoadStatus!=C4JStorage::ESaveGame_GetSaveThumbnail)
{
// something went wrong
@ -1175,6 +1317,14 @@ void UIScene_LoadOrJoinMenu::handleInput(int iPad, int key, bool repeat, bool pr
sendInputToMovie(key, repeat, pressed, released);
handled = true;
break;
case ACTION_MENU_OTHER_STICK_UP:
sendInputToMovie(ACTION_MENU_UP, repeat, pressed, released);
handled = true;
break;
case ACTION_MENU_OTHER_STICK_DOWN:
sendInputToMovie(ACTION_MENU_DOWN, repeat, pressed, released);
handled = true;
break;
}
}
@ -1323,7 +1473,7 @@ void UIScene_LoadOrJoinMenu::handlePress(F64 controlId, F64 childId)
LoadMenuInitData *params = new LoadMenuInitData();
params->iPad = m_iPad;
// need to get the iIndex from the list item, since the position in the list doesn't correspond to the GetSaveGameInfo list because of sorting
params->iSaveGameInfoIndex=((int)childId)-m_iDefaultButtonsC;
params->iSaveGameInfoIndex=m_saveDetails[((int)childId)-m_iDefaultButtonsC].saveId;
//params->pbSaveRenamed=&m_bSaveRenamed;
params->levelGen = NULL;
params->saveDetails = &m_saveDetails[ ((int)childId)-m_iDefaultButtonsC ];

View file

@ -4,6 +4,33 @@
#include "..\..\Minecraft.h"
#include "..\..\GameRenderer.h"
namespace
{
const int FOV_MIN = 70;
const int FOV_MAX = 110;
const int FOV_SLIDER_MAX = 100;
int clampFov(int value)
{
if (value < FOV_MIN) return FOV_MIN;
if (value > FOV_MAX) return FOV_MAX;
return value;
}
int fovToSliderValue(float fov)
{
int clampedFov = clampFov((int)(fov + 0.5f));
return ((clampedFov - FOV_MIN) * FOV_SLIDER_MAX) / (FOV_MAX - FOV_MIN);
}
int sliderValueToFov(int sliderValue)
{
if (sliderValue < 0) sliderValue = 0;
if (sliderValue > FOV_SLIDER_MAX) sliderValue = FOV_SLIDER_MAX;
return FOV_MIN + ((sliderValue * (FOV_MAX - FOV_MIN)) / FOV_SLIDER_MAX);
}
}
UIScene_SettingsGraphicsMenu::UIScene_SettingsGraphicsMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer)
{
// Setup all the Iggy references we need for this scene
@ -22,8 +49,9 @@ UIScene_SettingsGraphicsMenu::UIScene_SettingsGraphicsMenu(int iPad, void *initD
swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_GAMMA ),app.GetGameSettings(m_iPad,eGameSetting_Gamma));
m_sliderGamma.init(TempString,eControl_Gamma,0,100,app.GetGameSettings(m_iPad,eGameSetting_Gamma));
swprintf((WCHAR*)TempString, 256, L"FOV: %d%%", (int)pMinecraft->gameRenderer->GetFovVal());
m_sliderFOV.init(TempString, eControl_FOV, 70, 110, (int)pMinecraft->gameRenderer->GetFovVal());
int initialFov = clampFov((int)(pMinecraft->gameRenderer->GetFovVal() + 0.5f));
swprintf((WCHAR*)TempString, 256, L"FOV: %d", initialFov);
m_sliderFOV.init(TempString, eControl_FOV, 0, FOV_SLIDER_MAX, fovToSliderValue((float)initialFov));
swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_INTERFACEOPACITY ),app.GetGameSettings(m_iPad,eGameSetting_InterfaceOpacity));
m_sliderInterfaceOpacity.init(TempString,eControl_InterfaceOpacity,0,100,app.GetGameSettings(m_iPad,eGameSetting_InterfaceOpacity));
@ -150,10 +178,12 @@ void UIScene_SettingsGraphicsMenu::handleSliderMove(F64 sliderId, F64 currentVal
case eControl_FOV:
{
m_sliderFOV.handleSliderMove(value);
Minecraft* pMinecraft = Minecraft::GetInstance();
pMinecraft->gameRenderer->SetFovVal((float)currentValue);
int fovValue = sliderValueToFov(value);
pMinecraft->gameRenderer->SetFovVal((float)fovValue);
WCHAR TempString[256];
swprintf((WCHAR*)TempString, 256, L"FOV: %d%%", (int)currentValue);
swprintf((WCHAR*)TempString, 256, L"FOV: %d", fovValue);
m_sliderFOV.setLabel(TempString);
}
break;

View file

@ -54,13 +54,13 @@ void EntityTracker::addEntity(shared_ptr<Entity> e)
else if (e->instanceof(eTYPE_THROWNEXPBOTTLE)) addEntity(e, 16 * 4, 10, true);
else if (e->instanceof(eTYPE_FIREWORKS_ROCKET)) addEntity(e, 16 * 4, 10, true);
else if (e->instanceof(eTYPE_ITEMENTITY)) addEntity(e, 16 * 4, 20, true);
else if (e->instanceof(eTYPE_MINECART)) addEntity(e, 16 * 5, 3, true);
else if (e->instanceof(eTYPE_BOAT)) addEntity(e, 16 * 5, 3, true);
else if (e->instanceof(eTYPE_SQUID)) addEntity(e, 16 * 4, 3, true);
else if (e->instanceof(eTYPE_WITHERBOSS)) addEntity(e, 16 * 5, 3, false);
else if (e->instanceof(eTYPE_BAT)) addEntity(e, 16 * 5, 3, false);
else if (dynamic_pointer_cast<Creature>(e)!=NULL) addEntity(e, 16 * 5, 3, true);
else if (e->instanceof(eTYPE_ENDERDRAGON)) addEntity(e, 16 * 10, 3, true);
else if (e->instanceof(eTYPE_MINECART)) addEntity(e, 16 * 5, 1, true);
else if (e->instanceof(eTYPE_BOAT)) addEntity(e, 16 * 5, 1, true);
else if (e->instanceof(eTYPE_SQUID)) addEntity(e, 16 * 4, 1, true);
else if (e->instanceof(eTYPE_WITHERBOSS)) addEntity(e, 16 * 5, 1, false);
else if (e->instanceof(eTYPE_BAT)) addEntity(e, 16 * 5, 1, false);
else if (dynamic_pointer_cast<Creature>(e)!=NULL) addEntity(e, 16 * 5, 1, true);
else if (e->instanceof(eTYPE_ENDERDRAGON)) addEntity(e, 16 * 10, 1, true);
else if (e->instanceof(eTYPE_PRIMEDTNT)) addEntity(e, 16 * 10, 10, true);
else if (e->instanceof(eTYPE_FALLINGTILE)) addEntity(e, 16 * 10, 20, true);
else if (e->instanceof(eTYPE_HANGING_ENTITY)) addEntity(e, 16 * 10, INT_MAX, false);
@ -245,4 +245,4 @@ shared_ptr<TrackedEntity> EntityTracker::getTracker(shared_ptr<Entity> e)
return it->second;
}
return nullptr;
}
}

View file

@ -7,20 +7,21 @@
#include "Input.h"
#include "..\Minecraft.Client\LocalPlayer.h"
#include "Options.h"
#ifdef _WINDOWS64
#include "Windows64\KeyboardMouseInput.h"
#endif
Input::Input()
{
xa = 0;
ya = 0;
sprintForward = 0;
wasJumping = false;
jumping = false;
sneaking = false;
usingKeyboardMovement = false;
sprinting = false;
lReset = false;
rReset = false;
m_gamepadSneaking = false;
}
void Input::tick(LocalPlayer *player)
@ -32,43 +33,43 @@ void Input::tick(LocalPlayer *player)
Minecraft *pMinecraft=Minecraft::GetInstance();
int iPad=player->GetXboxPad();
float controllerXA = 0.0f;
float controllerYA = 0.0f;
// 4J-PB minecraft movement seems to be the wrong way round, so invert x!
if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LEFT) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_RIGHT) )
xa = -InputManager.GetJoypadStick_LX(iPad);
else
xa = 0.0f;
controllerXA = -InputManager.GetJoypadStick_LX(iPad);
if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_FORWARD) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_BACKWARD) )
ya = InputManager.GetJoypadStick_LY(iPad);
else
ya = 0.0f;
sprintForward = ya;
usingKeyboardMovement = false;
controllerYA = InputManager.GetJoypadStick_LY(iPad);
float kbXA = 0.0f;
float kbYA = 0.0f;
#ifdef _WINDOWS64
// WASD movement (combine with gamepad)
if (iPad == 0 && KMInput.IsCaptured())
if (iPad == 0 && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
{
float kbX = 0.0f, kbY = 0.0f;
if (KMInput.IsKeyDown('W')) { kbY += 1.0f; sprintForward += 1.0f; usingKeyboardMovement = true; }
if (KMInput.IsKeyDown('S')) { kbY -= 1.0f; sprintForward -= 1.0f; usingKeyboardMovement = true; }
if (KMInput.IsKeyDown('A')) { kbX += 1.0f; usingKeyboardMovement = true; } // inverted like gamepad
if (KMInput.IsKeyDown('D')) { kbX -= 1.0f; usingKeyboardMovement = true; }
// Normalize diagonal
if (kbX != 0.0f && kbY != 0.0f) { kbX *= 0.707f; kbY *= 0.707f; }
if (pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LEFT) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_RIGHT))
xa = max(min(xa + kbX, 1.0f), -1.0f);
if (pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_FORWARD) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_BACKWARD))
ya = max(min(ya + kbY, 1.0f), -1.0f);
if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LEFT) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_RIGHT) )
kbXA = g_KBMInput.GetMoveX();
if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_FORWARD) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_BACKWARD) )
kbYA = g_KBMInput.GetMoveY();
}
#endif
sprintForward = max(min(sprintForward, 1.0f), -1.0f);
if (kbXA != 0.0f || kbYA != 0.0f)
{
xa = kbXA;
ya = kbYA;
}
else
{
xa = controllerXA;
ya = controllerYA;
}
#ifndef _CONTENT_PACKAGE
if (app.GetFreezePlayers())
{
xa = ya = 0.0f;
sprintForward = 0.0f;
player->abilities.flying = true;
}
#endif
@ -80,7 +81,6 @@ void Input::tick(LocalPlayer *player)
lReset = true;
}
xa = ya = 0.0f;
sprintForward = 0.0f;
}
// 4J: In flying mode, don't actually toggle sneaking (unless we're riding in which case we need to sneak to dismount)
@ -88,15 +88,46 @@ void Input::tick(LocalPlayer *player)
{
if((player->ullButtonsPressed&(1LL<<MINECRAFT_ACTION_SNEAK_TOGGLE)) && pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_SNEAK_TOGGLE))
{
m_gamepadSneaking=!m_gamepadSneaking;
sneaking=!sneaking;
}
}
sneaking = m_gamepadSneaking;
#ifdef _WINDOWS64
// Keyboard hold-to-sneak (overrides gamepad toggle)
if (iPad == 0 && KMInput.IsCaptured() && KMInput.IsKeyDown(VK_SHIFT) && !player->abilities.flying)
sneaking = true;
if (iPad == 0 && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
{
// Left Shift = sneak (hold to crouch)
if (pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_SNEAK_TOGGLE))
{
if (!player->abilities.flying)
{
sneaking = g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_SNEAK);
}
}
// Ctrl + forward = sprint (hold to sprint)
if (!player->abilities.flying)
{
bool ctrlHeld = g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_SPRINT);
bool movingForward = (kbYA > 0.0f);
if (ctrlHeld && movingForward)
{
sprinting = true;
}
else
{
sprinting = false;
}
}
else
{
sprinting = false;
}
}
else if (iPad == 0)
{
sprinting = false;
}
#endif
if(sneaking)
@ -109,6 +140,7 @@ void Input::tick(LocalPlayer *player)
float tx = 0.0f;
float ty = 0.0f;
if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LOOK_LEFT) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LOOK_RIGHT) )
tx = InputManager.GetJoypadStick_RX(iPad)*(((float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_InGame))/100.0f); // apply sensitivity to look
if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LOOK_UP) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LOOK_DOWN) )
@ -132,47 +164,52 @@ void Input::tick(LocalPlayer *player)
}
tx = ty = 0.0f;
}
player->interpolateTurn(tx * abs(tx) * turnSpeed, ty * abs(ty) * turnSpeed);
float turnX = tx * abs(tx) * turnSpeed;
float turnY = ty * abs(ty) * turnSpeed;
#ifdef _WINDOWS64
// Mouse look is now handled per-frame in Minecraft::applyFrameMouseLook()
// to eliminate the 20Hz tick delay. Only flush any remaining delta here
// as a safety measure.
if (iPad == 0 && KMInput.IsCaptured())
if (iPad == 0 && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
{
float rawDx, rawDy;
KMInput.ConsumeMouseDelta(rawDx, rawDy);
// Delta should normally be 0 since applyFrameMouseLook() already consumed it
if (rawDx != 0.0f || rawDy != 0.0f)
float mouseSensitivity = ((float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_InGame)) / 100.0f;
float mouseLookScale = 5.0f;
float mx = g_KBMInput.GetLookX(mouseSensitivity * mouseLookScale);
float my = g_KBMInput.GetLookY(mouseSensitivity * mouseLookScale);
if ( app.GetGameSettings(iPad,eGameSetting_ControlInvertLook) )
{
float mouseSensitivity = ((float)app.GetGameSettings(iPad, eGameSetting_Sensitivity_InGame)) / 100.0f;
float mdx = rawDx * mouseSensitivity;
float mdy = -rawDy * mouseSensitivity;
if (app.GetGameSettings(iPad, eGameSetting_ControlInvertLook))
mdy = -mdy;
player->interpolateTurn(mdx, mdy);
my = -my;
}
turnX += mx;
turnY += my;
}
#endif
player->interpolateTurn(turnX, turnY);
//jumping = controller.isButtonPressed(0);
unsigned int jump = InputManager.GetValue(iPad, MINECRAFT_ACTION_JUMP);
if( jump > 0 && pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_JUMP) )
bool kbJump = false;
#ifdef _WINDOWS64
kbJump = (iPad == 0) && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive() && g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_JUMP);
#endif
if( (jump > 0 || kbJump) && pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_JUMP) )
jumping = true;
else
jumping = false;
#ifdef _WINDOWS64
// Keyboard jump (Space)
if (iPad == 0 && KMInput.IsCaptured() && KMInput.IsKeyDown(VK_SPACE) && pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_JUMP))
jumping = true;
#endif
#ifndef _CONTENT_PACKAGE
if (app.GetFreezePlayers()) jumping = false;
#endif
#ifdef _WINDOWS64
if (iPad == 0 && g_KBMInput.IsKeyPressed(VK_ESCAPE) && g_KBMInput.IsMouseGrabbed())
{
g_KBMInput.SetMouseGrabbed(false);
}
#endif
//OutputDebugString("INPUT: End input tick\n");
}

View file

@ -6,20 +6,17 @@ class Input
public:
float xa;
float ya;
float sprintForward;
bool wasJumping;
bool jumping;
bool sneaking;
bool usingKeyboardMovement;
Input(); // 4J - added
bool sprinting;
Input();
virtual void tick(LocalPlayer *player);
private:
bool lReset;
bool rReset;
bool m_gamepadSneaking;
};

View file

@ -548,7 +548,7 @@ void ItemInHandRenderer::render(float a)
glPushMatrix();
float d = 0.8f;
#if defined __ORBIS__ || defined __PS3__
#if defined __ORBIS__ || defined __PS3__ || defined _WINDOWS64
static const float swingPowFactor = 1.0f;
#else
static const float swingPowFactor = 4.0f; // 4J added, to slow the swing down when nearest the player for avoiding luminance flash issues

View file

@ -487,11 +487,11 @@ void LivingEntityRenderer::renderNameTag(shared_ptr<LivingEntity> mob, const wst
Font *font = getFont();
float size = 1.60f;
float s = 1 / 60.0f * size;
constexpr float size = 1.60f;
constexpr float s = 1 / 60.0f * size;
glPushMatrix();
glTranslatef((float) x + 0, (float) y + 2.3f, (float) z);
glTranslatef(static_cast<float>(x) + 0, static_cast<float>(y) + mob->bbHeight + 0.5f, static_cast<float>(z));
glNormal3f(0, 1, 0);
glRotatef(-this->entityRenderDispatcher->playerRotY, 0, 1, 0);

View file

@ -251,13 +251,10 @@ void LocalPlayer::aiStep()
if (changingDimensionDelay > 0) changingDimensionDelay--;
bool wasJumping = input->jumping;
float runTreshold = 0.8f;
float sprintForward = input->sprintForward;
bool wasRunning = sprintForward >= runTreshold;
bool wasRunning = input->ya >= runTreshold;
//input->tick( dynamic_pointer_cast<Player>( shared_from_this() ) );
// 4J-PB - make it a localplayer
input->tick( this );
sprintForward = input->sprintForward;
if (isUsingItem() && !isRiding())
{
input->xa *= 0.2f;
@ -281,25 +278,9 @@ void LocalPlayer::aiStep()
// world with low food, then reload it in creative.
if(abilities.mayfly || isAllowedToFly() ) enoughFoodToSprint = true;
bool forwardEnoughToTriggerSprint = sprintForward >= runTreshold;
bool forwardReturnedToDeadzone = sprintForward == 0.0f;
bool forwardEnoughToContinueSprint = sprintForward >= runTreshold;
#ifdef _WINDOWS64
if (GetXboxPad() == 0 && input->usingKeyboardMovement)
{
forwardEnoughToContinueSprint = sprintForward > 0.0f;
}
#endif
#ifdef _WINDOWS64
// Keyboard sprint: Ctrl held while moving forward
if (GetXboxPad() == 0 && input->usingKeyboardMovement && KMInput.IsKeyDown(VK_CONTROL) && sprintForward > 0.0f &&
enoughFoodToSprint && !isUsingItem() && !hasEffect(MobEffect::blindness) && onGround)
{
if (!isSprinting()) setSprinting(true);
}
#endif
bool forwardEnoughToTriggerSprint = input->ya >= runTreshold;
bool forwardReturnedToDeadzone = input->ya == 0.0f;
bool forwardEnoughToContinueSprint = input->ya >= runTreshold;
// 4J - altered this slightly to make sure that the joypad returns to below returnTreshold in between registering two movements up to runThreshold
if (onGround && !isSprinting() && enoughFoodToSprint && !isUsingItem() && !hasEffect(MobEffect::blindness))
@ -327,6 +308,12 @@ void LocalPlayer::aiStep()
}
}
if (isSneaking()) sprintTriggerTime = 0;
#ifdef _WINDOWS64
if (input->sprinting && onGround && enoughFoodToSprint && !isUsingItem() && !hasEffect(MobEffect::blindness) && !isSneaking())
{
setSprinting(true);
}
#endif
// 4J-PB - try not stopping sprint on collision
//if (isSprinting() && (input->ya < runTreshold || horizontalCollision || !enoughFoodToSprint))
if (isSprinting() && (!forwardEnoughToContinueSprint || !enoughFoodToSprint || isSneaking() || isUsingItem()))

View file

@ -1545,6 +1545,7 @@ if not exist "$(TargetDir)\savedata" mkdir "$(TargetDir)\savedata"</Command>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<ShowIncludes>false</ShowIncludes>
<AdditionalOptions>/FS %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -1773,7 +1774,7 @@ xcopy /q /y /i /s /e $(ProjectDir)DurangoMedia\CU $(LayoutDir)Image\Loose\CU</C
<IntrinsicFunctions>true</IntrinsicFunctions>
<EnableFiberSafeOptimizations>true</EnableFiberSafeOptimizations>
<StringPooling>true</StringPooling>
<AdditionalOptions>/Ob3</AdditionalOptions>
<AdditionalOptions>/FS /Ob3 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -48677,4 +48678,4 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
<ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup>
</Project>
</Project>

View file

@ -19,4 +19,4 @@
<LocalDebuggerWorkingDirectory>$(SolutionDir)$(Platform)\$(Configuration)\</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>
</Project>

View file

@ -502,6 +502,13 @@ void Minecraft::setScreen(Screen *screen)
this->screen->removed();
}
#ifdef _WINDOWS64
if (screen != NULL && g_KBMInput.IsMouseGrabbed())
{
g_KBMInput.SetMouseGrabbed(false);
}
#endif
//4J Gordon: Do not force a stats save here
/*if (dynamic_cast<TitleScreen *>(screen)!=NULL)
{
@ -1184,11 +1191,11 @@ void Minecraft::applyFrameMouseLook()
int iPad = localplayers[i]->GetXboxPad();
if (iPad != 0) continue; // Mouse only applies to pad 0
if (!KMInput.IsCaptured()) continue;
if (!g_KBMInput.IsMouseGrabbed()) continue;
if (localgameModes[iPad] == NULL) continue;
float rawDx, rawDy;
KMInput.ConsumeMouseDelta(rawDx, rawDy);
g_KBMInput.ConsumeMouseDelta(rawDx, rawDy);
if (rawDx == 0.0f && rawDy == 0.0f) continue;
float mouseSensitivity = ((float)app.GetGameSettings(iPad, eGameSetting_Sensitivity_InGame)) / 100.0f;
@ -1450,14 +1457,54 @@ void Minecraft::run_middle()
// Keyboard/mouse button presses for player 0
if (i == 0)
{
if (KMInput.ConsumeKeyPress(VK_ESCAPE)) localplayers[i]->ullButtonsPressed |= 1LL<<MINECRAFT_ACTION_PAUSEMENU;
if (KMInput.ConsumeKeyPress('E')) localplayers[i]->ullButtonsPressed |= 1LL<<MINECRAFT_ACTION_INVENTORY;
if (KMInput.ConsumeKeyPress('Q')) localplayers[i]->ullButtonsPressed |= 1LL<<MINECRAFT_ACTION_DROP;
if (KMInput.ConsumeKeyPress('C')) localplayers[i]->ullButtonsPressed |= 1LL<<MINECRAFT_ACTION_CRAFTING;
if (KMInput.ConsumeKeyPress(VK_F5)) localplayers[i]->ullButtonsPressed |= 1LL<<MINECRAFT_ACTION_RENDER_THIRD_PERSON;
if (g_KBMInput.IsKBMActive())
{
if(g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT))
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_ACTION;
if(g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_RIGHT))
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_USE;
if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_INVENTORY))
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_INVENTORY;
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))
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_CRAFTING;
for (int slot = 0; slot < 9; slot++)
{
if (g_KBMInput.IsKeyPressed('1' + slot))
{
if (localplayers[i]->inventory)
localplayers[i]->inventory->selected = slot;
}
}
}
// Utility keys always work regardless of KBM active state
if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_PAUSE) && !ui.IsTutorialVisible(i))
{
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_PAUSEMENU;
app.DebugPrintf("PAUSE PRESSED (keyboard) - ipad = %d\n",i);
}
if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_THIRD_PERSON))
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_RENDER_THIRD_PERSON;
if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_DEBUG_INFO))
{
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_GAME_INFO;
}
// In flying mode, Shift held = sneak/descend
if (localplayers[i]->abilities.flying && KMInput.IsKeyDown(VK_SHIFT) && !ui.GetMenuDisplayed(i))
localplayers[i]->ullButtonsPressed |= 1LL<<MINECRAFT_ACTION_SNEAK_TOGGLE;
if(g_KBMInput.IsKBMActive() && g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_SNEAK))
{
if (localplayers[i]->abilities.flying && !ui.GetMenuDisplayed(i))
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_SNEAK_TOGGLE;
}
}
#endif
@ -2277,8 +2324,21 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
}
}
#ifdef _WINDOWS64
if ((screen != NULL || ui.GetMenuDisplayed(iPad)) && g_KBMInput.IsMouseGrabbed())
{
g_KBMInput.SetMouseGrabbed(false);
}
#endif
if (screen == NULL && !ui.GetMenuDisplayed(iPad) )
{
#ifdef _WINDOWS64
if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsWindowFocused())
{
g_KBMInput.SetMouseGrabbed(true);
}
#endif
// 4J-PB - add some tooltips if required
int iA=-1, iB=-1, iX, iY=IDS_CONTROLS_INVENTORY, iLT=-1, iRT=-1, iLB=-1, iRB=-1, iLS=-1, iRS=-1;
@ -3432,26 +3492,9 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
}
#ifdef _WINDOWS64
// Mouse scroll wheel for hotbar
if (iPad == 0)
if (iPad == 0 && wheel == 0 && g_KBMInput.IsKBMActive())
{
int kbWheel = KMInput.ConsumeScrollDelta();
if (kbWheel > 0 && gameMode->isInputAllowed(MINECRAFT_ACTION_LEFT_SCROLL)) wheel += 1;
else if (kbWheel < 0 && gameMode->isInputAllowed(MINECRAFT_ACTION_RIGHT_SCROLL)) wheel -= 1;
// 1-9 keys for direct hotbar selection
if (gameMode->isInputAllowed(MINECRAFT_ACTION_LEFT_SCROLL))
{
for (int k = '1'; k <= '9'; k++)
{
if (KMInput.ConsumeKeyPress(k))
{
player->inventory->selected = k - '1';
app.SetOpacityTimer(iPad);
break;
}
}
}
wheel = g_KBMInput.GetMouseWheel();
}
#endif
if (wheel != 0)
@ -3485,33 +3528,20 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
player->handleMouseClick(0);
player->lastClickTick[0] = ticks;
}
#ifdef _WINDOWS64
else if (iPad == 0 && KMInput.IsCaptured() && KMInput.ConsumeMousePress(0))
{
player->handleMouseClick(0);
player->lastClickTick[0] = ticks;
}
#endif
if (InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION) && ticks - player->lastClickTick[0] >= timer->ticksPerSecond / 4)
#ifdef _WINDOWS64
bool actionHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION) || (iPad == 0 && g_KBMInput.IsKBMActive() && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT));
#else
bool actionHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION);
#endif
if (actionHeld && ticks - player->lastClickTick[0] >= timer->ticksPerSecond / 4)
{
//printf("MINECRAFT_ACTION_ACTION ButtonDown");
player->handleMouseClick(0);
player->lastClickTick[0] = ticks;
}
#ifdef _WINDOWS64
else if (iPad == 0 && KMInput.IsCaptured() && KMInput.IsMouseDown(0) && ticks - player->lastClickTick[0] >= timer->ticksPerSecond / 4)
{
player->handleMouseClick(0);
player->lastClickTick[0] = ticks;
}
#endif
if(InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION)
#ifdef _WINDOWS64
|| (iPad == 0 && KMInput.IsCaptured() && KMInput.IsMouseDown(0))
#endif
)
if(actionHeld)
{
player->handleMouseDown(0, true );
}
@ -3530,25 +3560,21 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
lastClickTick = ticks;
}
*/
#ifdef _WINDOWS64
bool useHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE) || (iPad == 0 && g_KBMInput.IsKBMActive() && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_RIGHT));
#else
bool useHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE);
#endif
if( player->isUsingItem() )
{
if(!InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE)
#ifdef _WINDOWS64
&& !(iPad == 0 && KMInput.IsCaptured() && KMInput.IsMouseDown(1))
#endif
) gameMode->releaseUsingItem(player);
if(!useHeld) gameMode->releaseUsingItem(player);
}
else if( gameMode->isInputAllowed(MINECRAFT_ACTION_USE) )
{
#ifdef _WINDOWS64
bool useButtonDown = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE) || (iPad == 0 && KMInput.IsCaptured() && KMInput.IsMouseDown(1));
#else
bool useButtonDown = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE);
#endif
if( player->abilities.instabuild )
{
// 4J - attempt to handle click in special creative mode fashion if possible (used for placing blocks at regular intervals)
bool didClick = player->creativeModeHandleMouseClick(1, useButtonDown );
bool didClick = player->creativeModeHandleMouseClick(1, useHeld );
// If this handler has put us in lastClick_oldRepeat mode then it is because we aren't placing blocks - behave largely as the code used to
if( player->lastClickState == LocalPlayer::lastClick_oldRepeat )
{
@ -3560,7 +3586,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
else
{
// Otherwise just the original game code for handling autorepeat
if (useButtonDown && ticks - player->lastClickTick[1] >= timer->ticksPerSecond / 4)
if (useHeld && ticks - player->lastClickTick[1] >= timer->ticksPerSecond / 4)
{
player->handleMouseClick(1);
player->lastClickTick[1] = ticks;
@ -3576,7 +3602,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
bool firstClick = ( player->lastClickTick[1] == 0 );
bool autoRepeat = ticks - player->lastClickTick[1] >= timer->ticksPerSecond / 4;
if ( player->isRiding() || player->isSprinting() || player->isSleeping() ) autoRepeat = false;
if (useButtonDown )
if (useHeld )
{
// If the player has just exited a bed, then delay the time before a repeat key is allowed without releasing
if(player->isSleeping() ) player->lastClickTick[1] = ticks + (timer->ticksPerSecond * 2);

View file

@ -27,6 +27,14 @@
#include "..\Minecraft.World\Pos.h"
#include "..\Minecraft.World\System.h"
#include "..\Minecraft.World\StringHelpers.h"
#include "..\Minecraft.World\net.minecraft.world.entity.item.h"
#include "..\Minecraft.World\net.minecraft.world.item.h"
#include "..\Minecraft.World\net.minecraft.world.item.enchantment.h"
#include "..\Minecraft.World\net.minecraft.world.damagesource.h"
#ifdef _WINDOWS64
#include "Windows64\Network\WinsockNetLayer.h"
#endif
#include <sstream>
#ifdef SPLIT_SAVES
#include "..\Minecraft.World\ConsoleSaveFileSplit.h"
#endif
@ -78,6 +86,462 @@ bool MinecraftServer::s_slowQueuePacketSent = false;
unordered_map<wstring, int> MinecraftServer::ironTimers;
static bool ShouldUseDedicatedServerProperties()
{
#ifdef _WINDOWS64
return g_Win64DedicatedServer;
#else
return false;
#endif
}
static int GetDedicatedServerInt(Settings *settings, const wchar_t *key, int defaultValue)
{
return (ShouldUseDedicatedServerProperties() && settings != NULL) ? settings->getInt(key, defaultValue) : defaultValue;
}
static bool GetDedicatedServerBool(Settings *settings, const wchar_t *key, bool defaultValue)
{
return (ShouldUseDedicatedServerProperties() && settings != NULL) ? settings->getBoolean(key, defaultValue) : defaultValue;
}
static wstring GetDedicatedServerString(Settings *settings, const wchar_t *key, const wstring &defaultValue)
{
return (ShouldUseDedicatedServerProperties() && settings != NULL) ? settings->getString(key, defaultValue) : defaultValue;
}
static void PrintConsoleLine(const wchar_t *prefix, const wstring &message)
{
wprintf(L"%ls%ls\n", prefix, message.c_str());
fflush(stdout);
}
static bool TryParseIntValue(const wstring &text, int &value)
{
std::wistringstream stream(text);
stream >> value;
return !stream.fail() && stream.eof();
}
static vector<wstring> SplitConsoleCommand(const wstring &command)
{
vector<wstring> tokens;
std::wistringstream stream(command);
wstring token;
while (stream >> token)
{
tokens.push_back(token);
}
return tokens;
}
static wstring JoinConsoleCommandTokens(const vector<wstring> &tokens, size_t startIndex)
{
wstring joined;
for (size_t i = startIndex; i < tokens.size(); ++i)
{
if (!joined.empty()) joined += L" ";
joined += tokens[i];
}
return joined;
}
static shared_ptr<ServerPlayer> FindPlayerByName(PlayerList *playerList, const wstring &name)
{
if (playerList == NULL) return nullptr;
for (size_t i = 0; i < playerList->players.size(); ++i)
{
shared_ptr<ServerPlayer> player = playerList->players[i];
if (player != NULL && equalsIgnoreCase(player->getName(), name))
{
return player;
}
}
return nullptr;
}
static void SetAllLevelTimes(MinecraftServer *server, int value)
{
for (unsigned int i = 0; i < server->levels.length; ++i)
{
if (server->levels[i] != NULL)
{
server->levels[i]->setDayTime(value);
}
}
}
static bool ExecuteConsoleCommand(MinecraftServer *server, const wstring &rawCommand)
{
if (server == NULL)
return false;
wstring command = trimString(rawCommand);
if (command.empty())
return true;
if (command[0] == L'/')
{
command = trimString(command.substr(1));
}
vector<wstring> tokens = SplitConsoleCommand(command);
if (tokens.empty())
return true;
const wstring action = toLower(tokens[0]);
PlayerList *playerList = server->getPlayers();
if (action == L"help" || action == L"?")
{
server->info(L"Commands: help, stop, list, say <message>, save-all, time <set day|night|ticks|add ticks>, weather <clear|rain|thunder> [seconds], tp <player> <target>, give <player> <itemId> [amount] [aux], enchant <player> <enchantId> [level], kill <player>");
return true;
}
if (action == L"stop")
{
server->info(L"Stopping server...");
MinecraftServer::HaltServer();
return true;
}
if (action == L"list")
{
wstring playerNames = (playerList != NULL) ? playerList->getPlayerNames() : L"";
if (playerNames.empty()) playerNames = L"(none)";
server->info(L"Players (" + _toString((playerList != NULL) ? playerList->getPlayerCount() : 0) + L"): " + playerNames);
return true;
}
if (action == L"say")
{
if (tokens.size() < 2)
{
server->warn(L"Usage: say <message>");
return false;
}
wstring message = L"[Server] " + JoinConsoleCommandTokens(tokens, 1);
if (playerList != NULL)
{
playerList->broadcastAll(shared_ptr<ChatPacket>(new ChatPacket(message)));
}
server->info(message);
return true;
}
if (action == L"save-all")
{
if (playerList != NULL)
{
playerList->saveAll(NULL, false);
}
server->info(L"World saved.");
return true;
}
if (action == L"time")
{
if (tokens.size() < 2)
{
server->warn(L"Usage: time set <day|night|ticks> | time add <ticks>");
return false;
}
if (toLower(tokens[1]) == L"add")
{
if (tokens.size() < 3)
{
server->warn(L"Usage: time add <ticks>");
return false;
}
int delta = 0;
if (!TryParseIntValue(tokens[2], delta))
{
server->warn(L"Invalid tick value: " + tokens[2]);
return false;
}
for (unsigned int i = 0; i < server->levels.length; ++i)
{
if (server->levels[i] != NULL)
{
server->levels[i]->setDayTime(server->levels[i]->getDayTime() + delta);
}
}
server->info(L"Added " + _toString(delta) + L" ticks.");
return true;
}
wstring timeValue = toLower(tokens[1]);
if (timeValue == L"set")
{
if (tokens.size() < 3)
{
server->warn(L"Usage: time set <day|night|ticks>");
return false;
}
timeValue = toLower(tokens[2]);
}
int targetTime = 0;
if (timeValue == L"day")
{
targetTime = 0;
}
else if (timeValue == L"night")
{
targetTime = 12500;
}
else if (!TryParseIntValue(timeValue, targetTime))
{
server->warn(L"Invalid time value: " + timeValue);
return false;
}
SetAllLevelTimes(server, targetTime);
server->info(L"Time set to " + _toString(targetTime) + L".");
return true;
}
if (action == L"weather")
{
if (tokens.size() < 2)
{
server->warn(L"Usage: weather <clear|rain|thunder> [seconds]");
return false;
}
int durationSeconds = 600;
if (tokens.size() >= 3 && !TryParseIntValue(tokens[2], durationSeconds))
{
server->warn(L"Invalid duration: " + tokens[2]);
return false;
}
if (server->levels[0] == NULL)
{
server->warn(L"The overworld is not loaded.");
return false;
}
LevelData *levelData = server->levels[0]->getLevelData();
int duration = durationSeconds * SharedConstants::TICKS_PER_SECOND;
levelData->setRainTime(duration);
levelData->setThunderTime(duration);
wstring weather = toLower(tokens[1]);
if (weather == L"clear")
{
levelData->setRaining(false);
levelData->setThundering(false);
}
else if (weather == L"rain")
{
levelData->setRaining(true);
levelData->setThundering(false);
}
else if (weather == L"thunder")
{
levelData->setRaining(true);
levelData->setThundering(true);
}
else
{
server->warn(L"Usage: weather <clear|rain|thunder> [seconds]");
return false;
}
server->info(L"Weather set to " + weather + L".");
return true;
}
if (action == L"tp" || action == L"teleport")
{
if (tokens.size() < 3)
{
server->warn(L"Usage: tp <player> <target>");
return false;
}
shared_ptr<ServerPlayer> subject = FindPlayerByName(playerList, tokens[1]);
shared_ptr<ServerPlayer> destination = FindPlayerByName(playerList, tokens[2]);
if (subject == NULL)
{
server->warn(L"Unknown player: " + tokens[1]);
return false;
}
if (destination == NULL)
{
server->warn(L"Unknown player: " + tokens[2]);
return false;
}
if (subject->level->dimension->id != destination->level->dimension->id || !subject->isAlive())
{
server->warn(L"Teleport failed because the players are not in the same dimension or the source player is dead.");
return false;
}
subject->ride(nullptr);
subject->connection->teleport(destination->x, destination->y, destination->z, destination->yRot, destination->xRot);
server->info(L"Teleported " + subject->getName() + L" to " + destination->getName() + L".");
return true;
}
if (action == L"give")
{
if (tokens.size() < 3)
{
server->warn(L"Usage: give <player> <itemId> [amount] [aux]");
return false;
}
shared_ptr<ServerPlayer> player = FindPlayerByName(playerList, tokens[1]);
if (player == NULL)
{
server->warn(L"Unknown player: " + tokens[1]);
return false;
}
int itemId = 0;
int amount = 1;
int aux = 0;
if (!TryParseIntValue(tokens[2], itemId))
{
server->warn(L"Invalid item id: " + tokens[2]);
return false;
}
if (tokens.size() >= 4 && !TryParseIntValue(tokens[3], amount))
{
server->warn(L"Invalid amount: " + tokens[3]);
return false;
}
if (tokens.size() >= 5 && !TryParseIntValue(tokens[4], aux))
{
server->warn(L"Invalid aux value: " + tokens[4]);
return false;
}
if (itemId <= 0 || Item::items[itemId] == NULL)
{
server->warn(L"Unknown item id: " + _toString(itemId));
return false;
}
if (amount <= 0)
{
server->warn(L"Amount must be positive.");
return false;
}
shared_ptr<ItemInstance> itemInstance(new ItemInstance(itemId, amount, aux));
shared_ptr<ItemEntity> drop = player->drop(itemInstance);
if (drop != NULL)
{
drop->throwTime = 0;
}
server->info(L"Gave item " + _toString(itemId) + L" x" + _toString(amount) + L" to " + player->getName() + L".");
return true;
}
if (action == L"enchant")
{
if (tokens.size() < 3)
{
server->warn(L"Usage: enchant <player> <enchantId> [level]");
return false;
}
shared_ptr<ServerPlayer> player = FindPlayerByName(playerList, tokens[1]);
if (player == NULL)
{
server->warn(L"Unknown player: " + tokens[1]);
return false;
}
int enchantmentId = 0;
int enchantmentLevel = 1;
if (!TryParseIntValue(tokens[2], enchantmentId))
{
server->warn(L"Invalid enchantment id: " + tokens[2]);
return false;
}
if (tokens.size() >= 4 && !TryParseIntValue(tokens[3], enchantmentLevel))
{
server->warn(L"Invalid enchantment level: " + tokens[3]);
return false;
}
shared_ptr<ItemInstance> selectedItem = player->getSelectedItem();
if (selectedItem == NULL)
{
server->warn(L"The player is not holding an item.");
return false;
}
Enchantment *enchantment = Enchantment::enchantments[enchantmentId];
if (enchantment == NULL)
{
server->warn(L"Unknown enchantment id: " + _toString(enchantmentId));
return false;
}
if (!enchantment->canEnchant(selectedItem))
{
server->warn(L"That enchantment cannot be applied to the selected item.");
return false;
}
if (enchantmentLevel < enchantment->getMinLevel()) enchantmentLevel = enchantment->getMinLevel();
if (enchantmentLevel > enchantment->getMaxLevel()) enchantmentLevel = enchantment->getMaxLevel();
if (selectedItem->hasTag())
{
ListTag<CompoundTag> *enchantmentTags = selectedItem->getEnchantmentTags();
if (enchantmentTags != NULL)
{
for (int i = 0; i < enchantmentTags->size(); i++)
{
int type = enchantmentTags->get(i)->getShort((wchar_t *)ItemInstance::TAG_ENCH_ID);
if (Enchantment::enchantments[type] != NULL && !Enchantment::enchantments[type]->isCompatibleWith(enchantment))
{
server->warn(L"That enchantment conflicts with an existing enchantment on the selected item.");
return false;
}
}
}
}
selectedItem->enchant(enchantment, enchantmentLevel);
server->info(L"Enchanted " + player->getName() + L"'s held item with " + _toString(enchantmentId) + L" " + _toString(enchantmentLevel) + L".");
return true;
}
if (action == L"kill")
{
if (tokens.size() < 2)
{
server->warn(L"Usage: kill <player>");
return false;
}
shared_ptr<ServerPlayer> player = FindPlayerByName(playerList, tokens[1]);
if (player == NULL)
{
server->warn(L"Unknown player: " + tokens[1]);
return false;
}
player->hurt(DamageSource::outOfWorld, 3.4e38f);
server->info(L"Killed " + player->getName() + L".");
return true;
}
server->warn(L"Unknown command: " + command);
return false;
}
MinecraftServer::MinecraftServer()
{
// 4J - added initialisers
@ -107,12 +571,14 @@ MinecraftServer::MinecraftServer()
forceGameType = false;
commandDispatcher = new ServerCommandDispatcher();
InitializeCriticalSection(&m_consoleInputCS);
DispenserBootstrap::bootStrap();
}
MinecraftServer::~MinecraftServer()
{
DeleteCriticalSection(&m_consoleInputCS);
}
bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DWORD initSettings, bool findSeed)
@ -150,6 +616,15 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW
#endif
settings = new Settings(new File(L"server.properties"));
app.SetGameHostOption(eGameHostOption_Difficulty, GetDedicatedServerInt(settings, L"difficulty", app.GetGameHostOption(eGameHostOption_Difficulty)));
app.SetGameHostOption(eGameHostOption_GameType, GetDedicatedServerInt(settings, L"gamemode", app.GetGameHostOption(eGameHostOption_GameType)));
app.SetGameHostOption(eGameHostOption_Structures, GetDedicatedServerBool(settings, L"generate-structures", app.GetGameHostOption(eGameHostOption_Structures) > 0) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_BonusChest, GetDedicatedServerBool(settings, L"bonus-chest", app.GetGameHostOption(eGameHostOption_BonusChest) > 0) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_PvP, GetDedicatedServerBool(settings, L"pvp", app.GetGameHostOption(eGameHostOption_PvP) > 0) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_TrustPlayers, GetDedicatedServerBool(settings, L"trust-players", app.GetGameHostOption(eGameHostOption_TrustPlayers) > 0) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_FireSpreads, GetDedicatedServerBool(settings, L"fire-spreads", app.GetGameHostOption(eGameHostOption_FireSpreads) > 0) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_TNT, GetDedicatedServerBool(settings, L"tnt", app.GetGameHostOption(eGameHostOption_TNT) > 0) ? 1 : 0);
app.DebugPrintf("\n*** SERVER SETTINGS ***\n");
app.DebugPrintf("ServerSettings: host-friends-only is %s\n",(app.GetGameHostOption(eGameHostOption_FriendsOfFriends)>0)?"on":"off");
app.DebugPrintf("ServerSettings: game-type is %s\n",(app.GetGameHostOption(eGameHostOption_GameType)==0)?"Survival Mode":"Creative Mode");
@ -165,15 +640,15 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW
//localIp = settings->getString(L"server-ip", L"");
//onlineMode = settings->getBoolean(L"online-mode", true);
//motd = settings->getString(L"motd", L"A Minecraft Server");
//motd.replace('§', '$');
//motd.replace('<EFBFBD>', '$');
setAnimals(settings->getBoolean(L"spawn-animals", true));
setNpcsEnabled(settings->getBoolean(L"spawn-npcs", true));
setPvpAllowed(app.GetGameHostOption( eGameHostOption_PvP )>0?true:false); // settings->getBoolean(L"pvp", true);
setAnimals(GetDedicatedServerBool(settings, L"spawn-animals", true));
setNpcsEnabled(GetDedicatedServerBool(settings, L"spawn-npcs", true));
setPvpAllowed(app.GetGameHostOption( eGameHostOption_PvP )>0?true:false);
// 4J Stu - We should never have hacked clients flying when they shouldn't be like the PC version, so enable flying always
// Fix for #46612 - TU5: Code: Multiplayer: A client can be banned for flying when accidentaly being blown by dynamite
setFlightAllowed(true); //settings->getBoolean(L"allow-flight", false);
setFlightAllowed(GetDedicatedServerBool(settings, L"allow-flight", true));
// 4J Stu - Enabling flight to stop it kicking us when we use it
#ifdef _DEBUG_MENUS_ENABLED
@ -219,8 +694,8 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW
__int64 levelNanoTime = System::nanoTime();
wstring levelName = settings->getString(L"level-name", L"world");
wstring levelTypeString;
wstring levelName = (initData && !initData->levelName.empty()) ? initData->levelName : GetDedicatedServerString(settings, L"level-name", L"world");
wstring levelTypeString;
bool gameRuleUseFlatWorld = false;
if(app.getLevelGenerationOptions() != NULL)
@ -229,11 +704,11 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW
}
if(gameRuleUseFlatWorld || app.GetGameHostOption(eGameHostOption_LevelType)>0)
{
levelTypeString = settings->getString(L"level-type", L"flat");
levelTypeString = GetDedicatedServerString(settings, L"level-type", L"flat");
}
else
{
levelTypeString = settings->getString(L"level-type",L"default");
levelTypeString = GetDedicatedServerString(settings, L"level-type",L"default");
}
LevelType *pLevelType = LevelType::getLevelType(levelTypeString);
@ -254,7 +729,7 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW
#endif
}
setMaxBuildHeight(settings->getInt(L"max-build-height", Level::maxBuildHeight));
setMaxBuildHeight(GetDedicatedServerInt(settings, L"max-build-height", Level::maxBuildHeight));
setMaxBuildHeight(((getMaxBuildHeight() + 8) / 16) * 16);
setMaxBuildHeight(Mth::clamp(getMaxBuildHeight(), 64, Level::maxBuildHeight));
//settings->setProperty(L"max-build-height", maxBuildHeight);
@ -403,7 +878,7 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring
// 4J TODO - free levels here if there are already some?
levels = ServerLevelArray(3);
int gameTypeId = settings->getInt(L"gamemode", app.GetGameHostOption(eGameHostOption_GameType));//LevelSettings::GAMETYPE_SURVIVAL);
int gameTypeId = GetDedicatedServerInt(settings, L"gamemode", app.GetGameHostOption(eGameHostOption_GameType));//LevelSettings::GAMETYPE_SURVIVAL);
GameType *gameType = LevelSettings::validateGameType(gameTypeId);
app.DebugPrintf("Default game type: %d\n" , gameTypeId);
@ -502,7 +977,7 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring
#if DEBUG_SERVER_DONT_SPAWN_MOBS
levels[i]->setSpawnSettings(false, false);
#else
levels[i]->setSpawnSettings(settings->getBoolean(L"spawn-monsters", true), animals);
levels[i]->setSpawnSettings(GetDedicatedServerBool(settings, L"spawn-monsters", true), animals);
#endif
levels[i]->getLevelData()->setGameType(gameType);
@ -590,7 +1065,7 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring
for (int i = 0; i < levels.length ; i++)
{
// logger.info("Preparing start region for level " + i);
if (i == 0 || settings->getBoolean(L"allow-nether", true))
if (i == 0 || GetDedicatedServerBool(settings, L"allow-nether", true))
{
ServerLevel *level = levels[i];
if(levelChunksNeedConverted)
@ -1332,7 +1807,7 @@ void MinecraftServer::run(__int64 seed, void *lpParameter)
MinecraftServer::setTimeOfDayAtEndOfTick = false;
for (unsigned int i = 0; i < levels.length; i++)
{
if (i == 0 || settings->getBoolean(L"allow-nether", true))
if (i == 0 || GetDedicatedServerBool(settings, L"allow-nether", true))
{
ServerLevel *level = levels[i];
level->setDayTime( MinecraftServer::setTimeOfDay );
@ -1707,17 +2182,23 @@ void MinecraftServer::tick()
void MinecraftServer::handleConsoleInput(const wstring& msg, ConsoleInputSource *source)
{
EnterCriticalSection(&m_consoleInputCS);
consoleInput.push_back(new ConsoleInput(msg, source));
LeaveCriticalSection(&m_consoleInputCS);
}
void MinecraftServer::handleConsoleInputs()
{
while (consoleInput.size() > 0)
vector<ConsoleInput *> pendingInputs;
EnterCriticalSection(&m_consoleInputCS);
pendingInputs.swap(consoleInput);
LeaveCriticalSection(&m_consoleInputCS);
for (size_t i = 0; i < pendingInputs.size(); ++i)
{
AUTO_VAR(it, consoleInput.begin());
ConsoleInput *input = *it;
consoleInput.erase(it);
// commands->handleCommand(input); // 4J - removed - TODO - do we want equivalent of console commands?
ConsoleInput *input = pendingInputs[i];
ExecuteConsoleCommand(this, input->msg);
delete input;
}
}
@ -1750,10 +2231,12 @@ File *MinecraftServer::getFile(const wstring& name)
void MinecraftServer::info(const wstring& string)
{
PrintConsoleLine(L"[INFO] ", string);
}
void MinecraftServer::warn(const wstring& string)
{
PrintConsoleLine(L"[WARN] ", string);
}
wstring MinecraftServer::getConsoleName()

View file

@ -44,6 +44,7 @@ typedef struct _NetworkGameInitData
unsigned int xzSize;
unsigned char hellScale;
ESavePlatform savePlatform;
wstring levelName;
_NetworkGameInitData()
{
@ -105,6 +106,7 @@ private:
// vector<Tickable *> tickables = new ArrayList<Tickable>(); // 4J - removed
CommandDispatcher *commandDispatcher;
vector<ConsoleInput *> consoleInput; // 4J - was synchronizedList - TODO - investigate
CRITICAL_SECTION m_consoleInputCS;
public:
bool onlineMode;
bool animals;

View file

@ -370,7 +370,9 @@ bool MultiPlayerGameMode::useItemOn(shared_ptr<Player> player, Level *level, sha
// are meant to be directly caused by this. If we don't do this, then the sounds never happen as the tile's use method is only called on the
// server, and that won't allow any sounds that are directly made, or broadcast back level events to us that would make the sound, since we are
// the source of the event.
if( ( t > 0 ) && ( !bTestUseOnly ) && player->isAllowedToUse(Tile::tiles[t]) )
// ---------------------------------------------------------------------------------
// Only call soundOnly version if we didn't already call the tile's use method above
if( !didSomething && ( t > 0 ) && ( !bTestUseOnly ) && player->isAllowedToUse(Tile::tiles[t]) )
{
Tile::tiles[t]->use(level, x, y, z, player, face, clickX, clickY, clickZ, true);
}

View file

@ -53,14 +53,12 @@ PlayerList::PlayerList(MinecraftServer *server)
//int viewDistance = server->settings->getInt(L"view-distance", 10);
maxPlayers = server->settings->getInt(L"max-players", 20);
doWhiteList = false;
#ifdef _WINDOWS64
maxPlayers = MINECRAFT_NET_MAX_PLAYERS;
#else
maxPlayers = server->settings->getInt(L"max-players", 20);
#endif
doWhiteList = false;
InitializeCriticalSection(&m_kickPlayersCS);
InitializeCriticalSection(&m_closePlayersCS);
}

View file

@ -110,13 +110,13 @@ void Screen::updateEvents()
// Poll mouse button state and dispatch click/release events
for (int btn = 0; btn < 3; btn++)
{
if (KMInput.ConsumeMousePress(btn))
if (g_KBMInput.IsMouseButtonPressed(btn))
{
int xm = Mouse::getX() * width / minecraft->width;
int ym = height - Mouse::getY() * height / minecraft->height - 1;
mouseClicked(xm, ym, btn);
}
if (KMInput.ConsumeMouseRelease(btn))
if (g_KBMInput.IsMouseButtonReleased(btn))
{
int xm = Mouse::getX() * width / minecraft->width;
int ym = height - Mouse::getY() * height / minecraft->height - 1;
@ -127,7 +127,7 @@ void Screen::updateEvents()
// Poll keyboard events
for (int vk = 0; vk < 256; vk++)
{
if (KMInput.ConsumeKeyPress(vk))
if (g_KBMInput.IsKeyPressed(vk))
{
// Map Windows virtual key to the Keyboard constants used by Screen::keyPressed
int mappedKey = -1;
@ -144,7 +144,7 @@ void Screen::updateEvents()
else if (vk >= 'A' && vk <= 'Z')
{
ch = (wchar_t)(vk - 'A' + L'a');
if (KMInput.IsKeyDown(VK_SHIFT)) ch = (wchar_t)vk;
if (g_KBMInput.IsKeyDown(VK_LSHIFT) || g_KBMInput.IsKeyDown(VK_RSHIFT)) ch = (wchar_t)vk;
}
else if (vk >= '0' && vk <= '9') ch = (wchar_t)vk;
else if (vk == VK_SPACE) ch = L' ';

View file

@ -1,18 +1,90 @@
#include "stdafx.h"
#include "Settings.h"
#include "..\Minecraft.World\File.h"
#include "..\Minecraft.World\StringHelpers.h"
#include <fstream>
static wstring ParsePropertyText(const string &text)
{
return trimString(convStringToWstring(text));
}
static bool TryParseBoolean(const wstring &text, bool defaultValue)
{
wstring lowered = toLower(trimString(text));
if (lowered == L"true" || lowered == L"1" || lowered == L"yes" || lowered == L"on")
return true;
if (lowered == L"false" || lowered == L"0" || lowered == L"no" || lowered == L"off")
return false;
return defaultValue;
}
// 4J - TODO - serialise/deserialise from file
Settings::Settings(File *file)
{
if (file != NULL)
{
filePath = file->getPath();
}
if (filePath.empty())
return;
std::ifstream stream(wstringtofilename(filePath), std::ios::in | std::ios::binary);
if (!stream.is_open())
return;
string line;
while (std::getline(stream, line))
{
if (!line.empty() && line[line.size() - 1] == '\r')
line.erase(line.size() - 1);
if (line.size() >= 3 &&
(unsigned char)line[0] == 0xEF &&
(unsigned char)line[1] == 0xBB &&
(unsigned char)line[2] == 0xBF)
{
line.erase(0, 3);
}
size_t commentPos = line.find_first_of("#;");
if (commentPos != string::npos && line.find_first_not_of(" \t") == commentPos)
continue;
size_t separatorPos = line.find('=');
if (separatorPos == string::npos)
continue;
wstring key = ParsePropertyText(line.substr(0, separatorPos));
if (key.empty())
continue;
wstring value = ParsePropertyText(line.substr(separatorPos + 1));
properties[key] = value;
}
}
void Settings::generateNewProperties()
{
saveProperties();
}
void Settings::saveProperties()
{
if (filePath.empty())
return;
std::ofstream stream(wstringtofilename(filePath), std::ios::out | std::ios::binary | std::ios::trunc);
if (!stream.is_open())
return;
stream << "# MinecraftConsoles dedicated server properties\r\n";
for (unordered_map<wstring, wstring>::const_iterator it = properties.begin(); it != properties.end(); ++it)
{
string key = string(wstringtochararray(it->first));
string value = string(wstringtochararray(it->second));
stream << key << "=" << value << "\r\n";
}
}
wstring Settings::getString(const wstring& key, const wstring& defaultValue)
@ -39,17 +111,17 @@ bool Settings::getBoolean(const wstring& key, bool defaultValue)
{
if(properties.find(key) == properties.end())
{
properties[key] = _toString<bool>(defaultValue);
properties[key] = defaultValue ? L"true" : L"false";
saveProperties();
}
MemSect(35);
bool retval = _fromString<bool>(properties[key]);
bool retval = TryParseBoolean(properties[key], defaultValue);
MemSect(0);
return retval;
}
void Settings::setBooleanAndSave(const wstring& key, bool value)
{
properties[key] = _toString<bool>(value);
properties[key] = value ? L"true" : L"false";
saveProperties();
}

View file

@ -7,8 +7,8 @@ class Settings
// public static Logger logger = Logger.getLogger("Minecraft");
// private Properties properties = new Properties();
private:
unordered_map<wstring,wstring> properties; // 4J - TODO was Properties type, will need to implement something we can serialise/deserialise too
//File *file;
unordered_map<wstring,wstring> properties;
wstring filePath;
public:
Settings(File *file);
@ -18,4 +18,4 @@ public:
int getInt(const wstring& key, int defaultValue);
bool getBoolean(const wstring& key, bool defaultValue);
void setBooleanAndSave(const wstring& key, bool value);
};
};

View file

@ -973,6 +973,9 @@ void gdraw_D3D1X_(SetRendertargetSize)(S32 w, S32 h)
void gdraw_D3D1X_(SetTileOrigin)(ID3D1X(RenderTargetView) *main_rt, ID3D1X(DepthStencilView) *main_ds, ID3D1X(ShaderResourceView) *non_msaa_rt, S32 x, S32 y)
{
if (!gdraw) return; // AAR - saftey check because windows calls resize early
D3D1X_(RENDER_TARGET_VIEW_DESC) desc;
if (gdraw->frame_done) {

View file

@ -3,118 +3,176 @@
#ifdef _WINDOWS64
#include "KeyboardMouseInput.h"
#include <cmath>
KeyboardMouseInput KMInput;
KeyboardMouseInput g_KBMInput;
KeyboardMouseInput::KeyboardMouseInput()
: m_mouseDeltaXAccum(0.0f)
, m_mouseDeltaYAccum(0.0f)
, m_scrollDeltaAccum(0)
, m_captured(false)
, m_hWnd(NULL)
, m_initialized(false)
, m_mouseX(0)
, m_mouseY(0)
extern HWND g_hWnd;
// Forward declaration
static void ClipCursorToWindow(HWND hWnd);
static bool IsModifierKeyDown(const bool* keyState, int vkCode)
{
memset(m_keyState, 0, sizeof(m_keyState));
memset(m_keyStatePrev, 0, sizeof(m_keyStatePrev));
memset(m_mouseButtons, 0, sizeof(m_mouseButtons));
memset(m_mouseButtonsPrev, 0, sizeof(m_mouseButtonsPrev));
memset(m_keyPressedAccum, 0, sizeof(m_keyPressedAccum));
memset(m_mousePressedAccum, 0, sizeof(m_mousePressedAccum));
memset(m_mouseReleasedAccum, 0, sizeof(m_mouseReleasedAccum));
}
KeyboardMouseInput::~KeyboardMouseInput()
{
if (m_captured)
switch (vkCode)
{
SetCapture(false);
case VK_SHIFT:
return keyState[VK_LSHIFT] || keyState[VK_RSHIFT];
case VK_CONTROL:
return keyState[VK_LCONTROL] || keyState[VK_RCONTROL];
case VK_MENU:
return keyState[VK_LMENU] || keyState[VK_RMENU];
default:
return false;
}
}
void KeyboardMouseInput::Init(HWND hWnd)
void KeyboardMouseInput::Init()
{
m_hWnd = hWnd;
m_initialized = true;
memset(m_keyDown, 0, sizeof(m_keyDown));
memset(m_keyDownPrev, 0, sizeof(m_keyDownPrev));
memset(m_keyPressedAccum, 0, sizeof(m_keyPressedAccum));
memset(m_keyReleasedAccum, 0, sizeof(m_keyReleasedAccum));
memset(m_keyPressed, 0, sizeof(m_keyPressed));
memset(m_keyReleased, 0, sizeof(m_keyReleased));
memset(m_mouseButtonDown, 0, sizeof(m_mouseButtonDown));
memset(m_mouseButtonDownPrev, 0, sizeof(m_mouseButtonDownPrev));
memset(m_mouseBtnPressedAccum, 0, sizeof(m_mouseBtnPressedAccum));
memset(m_mouseBtnReleasedAccum, 0, sizeof(m_mouseBtnReleasedAccum));
memset(m_mouseBtnPressed, 0, sizeof(m_mouseBtnPressed));
memset(m_mouseBtnReleased, 0, sizeof(m_mouseBtnReleased));
m_mouseX = 0;
m_mouseY = 0;
m_mouseDeltaX = 0;
m_mouseDeltaY = 0;
m_mouseDeltaAccumX = 0;
m_mouseDeltaAccumY = 0;
m_mouseWheelAccum = 0;
m_mouseWheelConsumed = false;
m_mouseGrabbed = false;
m_cursorHiddenForUI = false;
m_windowFocused = true;
m_hasInput = false;
m_kbmActive = true;
m_screenWantsCursorHidden = false;
// Register for raw mouse input
RAWINPUTDEVICE rid;
rid.usUsagePage = HID_USAGE_PAGE_GENERIC;
rid.usUsage = HID_USAGE_GENERIC_MOUSE;
rid.usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
rid.usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE
rid.dwFlags = 0;
rid.hwndTarget = hWnd;
rid.hwndTarget = g_hWnd;
RegisterRawInputDevices(&rid, 1, sizeof(rid));
}
void KeyboardMouseInput::ClearAllState()
{
memset(m_keyDown, 0, sizeof(m_keyDown));
memset(m_keyDownPrev, 0, sizeof(m_keyDownPrev));
memset(m_keyPressedAccum, 0, sizeof(m_keyPressedAccum));
memset(m_keyReleasedAccum, 0, sizeof(m_keyReleasedAccum));
memset(m_keyPressed, 0, sizeof(m_keyPressed));
memset(m_keyReleased, 0, sizeof(m_keyReleased));
memset(m_mouseButtonDown, 0, sizeof(m_mouseButtonDown));
memset(m_mouseButtonDownPrev, 0, sizeof(m_mouseButtonDownPrev));
memset(m_mouseBtnPressedAccum, 0, sizeof(m_mouseBtnPressedAccum));
memset(m_mouseBtnReleasedAccum, 0, sizeof(m_mouseBtnReleasedAccum));
memset(m_mouseBtnPressed, 0, sizeof(m_mouseBtnPressed));
memset(m_mouseBtnReleased, 0, sizeof(m_mouseBtnReleased));
m_mouseDeltaX = 0;
m_mouseDeltaY = 0;
m_mouseDeltaAccumX = 0;
m_mouseDeltaAccumY = 0;
m_mouseWheelAccum = 0;
m_mouseWheelConsumed = false;
}
void KeyboardMouseInput::Tick()
{
// Keep cursor pinned to center while captured
if (m_captured)
CenterCursor();
}
memcpy(m_keyDownPrev, m_keyDown, sizeof(m_keyDown));
memcpy(m_mouseButtonDownPrev, m_mouseButtonDown, sizeof(m_mouseButtonDown));
void KeyboardMouseInput::EndFrame()
{
// Advance previous state for next frame's edge detection.
// Must be called AFTER all per-frame consumers have read IsKeyPressed/Released etc.
memcpy(m_keyStatePrev, m_keyState, sizeof(m_keyState));
memcpy(m_mouseButtonsPrev, m_mouseButtons, sizeof(m_mouseButtons));
}
memcpy(m_keyPressed, m_keyPressedAccum, sizeof(m_keyPressedAccum));
memcpy(m_keyReleased, m_keyReleasedAccum, sizeof(m_keyReleasedAccum));
memset(m_keyPressedAccum, 0, sizeof(m_keyPressedAccum));
memset(m_keyReleasedAccum, 0, sizeof(m_keyReleasedAccum));
void KeyboardMouseInput::OnKeyDown(WPARAM vk)
{
if (vk < 256)
memcpy(m_mouseBtnPressed, m_mouseBtnPressedAccum, sizeof(m_mouseBtnPressedAccum));
memcpy(m_mouseBtnReleased, m_mouseBtnReleasedAccum, sizeof(m_mouseBtnReleasedAccum));
memset(m_mouseBtnPressedAccum, 0, sizeof(m_mouseBtnPressedAccum));
memset(m_mouseBtnReleasedAccum, 0, sizeof(m_mouseBtnReleasedAccum));
m_mouseDeltaX = m_mouseDeltaAccumX;
m_mouseDeltaY = m_mouseDeltaAccumY;
m_mouseDeltaAccumX = 0;
m_mouseDeltaAccumY = 0;
m_mouseWheelConsumed = false;
m_hasInput = (m_mouseDeltaX != 0 || m_mouseDeltaY != 0 || m_mouseWheelAccum != 0);
if (!m_hasInput)
{
if (!m_keyState[vk]) m_keyPressedAccum[vk] = true;
m_keyState[vk] = true;
}
}
void KeyboardMouseInput::OnKeyUp(WPARAM vk)
{
if (vk < 256)
{
m_keyState[vk] = false;
}
}
void KeyboardMouseInput::OnRawMouseInput(LPARAM lParam)
{
if (!m_captured) return;
UINT dwSize = 0;
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));
BYTE* lpb = (BYTE*)alloca(dwSize);
if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize)
return;
RAWINPUT* raw = (RAWINPUT*)lpb;
if (raw->header.dwType == RIM_TYPEMOUSE)
{
if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE)
for (int i = 0; i < MAX_KEYS; i++)
{
m_mouseDeltaXAccum += (float)raw->data.mouse.lLastX;
m_mouseDeltaYAccum += (float)raw->data.mouse.lLastY;
if (m_keyDown[i]) { m_hasInput = true; break; }
}
}
}
void KeyboardMouseInput::OnMouseButton(int button, bool down)
{
if (ui.IsPauseMenuDisplayed(ProfileManager.GetPrimaryPad())) { return; }
if (button >= 0 && button < 3)
if (!m_hasInput)
{
if (down && !m_mouseButtons[button]) m_mousePressedAccum[button] = true;
if (!down && m_mouseButtons[button]) m_mouseReleasedAccum[button] = true;
m_mouseButtons[button] = down;
for (int i = 0; i < MAX_MOUSE_BUTTONS; i++)
{
if (m_mouseButtonDown[i]) { m_hasInput = true; break; }
}
}
if ((m_mouseGrabbed || m_cursorHiddenForUI) && g_hWnd)
{
RECT rc;
GetClientRect(g_hWnd, &rc);
POINT center;
center.x = (rc.right - rc.left) / 2;
center.y = (rc.bottom - rc.top) / 2;
ClientToScreen(g_hWnd, &center);
SetCursorPos(center.x, center.y);
}
}
void KeyboardMouseInput::OnMouseWheel(int delta)
void KeyboardMouseInput::OnKeyDown(int vkCode)
{
m_scrollDeltaAccum += delta;
if (vkCode >= 0 && vkCode < MAX_KEYS)
{
if (!m_keyDown[vkCode])
m_keyPressedAccum[vkCode] = true;
m_keyDown[vkCode] = true;
}
}
void KeyboardMouseInput::OnKeyUp(int vkCode)
{
if (vkCode >= 0 && vkCode < MAX_KEYS)
{
if (m_keyDown[vkCode])
m_keyReleasedAccum[vkCode] = true;
m_keyDown[vkCode] = false;
}
}
void KeyboardMouseInput::OnMouseButtonDown(int button)
{
if (button >= 0 && button < MAX_MOUSE_BUTTONS)
{
if (!m_mouseButtonDown[button])
m_mouseBtnPressedAccum[button] = true;
m_mouseButtonDown[button] = true;
}
}
void KeyboardMouseInput::OnMouseButtonUp(int button)
{
if (button >= 0 && button < MAX_MOUSE_BUTTONS)
{
if (m_mouseButtonDown[button])
m_mouseBtnReleasedAccum[button] = true;
m_mouseButtonDown[button] = false;
}
}
void KeyboardMouseInput::OnMouseMove(int x, int y)
@ -123,139 +181,204 @@ void KeyboardMouseInput::OnMouseMove(int x, int y)
m_mouseY = y;
}
int KeyboardMouseInput::GetMouseX() const { return m_mouseX; }
int KeyboardMouseInput::GetMouseY() const { return m_mouseY; }
HWND KeyboardMouseInput::GetHWnd() const { return m_hWnd; }
void KeyboardMouseInput::ClearAllState()
void KeyboardMouseInput::OnMouseWheel(int delta)
{
memset(m_keyState, 0, sizeof(m_keyState));
memset(m_mouseButtons, 0, sizeof(m_mouseButtons));
memset(m_keyPressedAccum, 0, sizeof(m_keyPressedAccum));
memset(m_mousePressedAccum, 0, sizeof(m_mousePressedAccum));
memset(m_mouseReleasedAccum, 0, sizeof(m_mouseReleasedAccum));
m_mouseDeltaXAccum = 0.0f;
m_mouseDeltaYAccum = 0.0f;
m_scrollDeltaAccum = 0;
// Normalize from raw Windows delta (multiples of WHEEL_DELTA=120) to discrete notch counts
m_mouseWheelAccum += delta / WHEEL_DELTA;
}
// Per-frame key queries
bool KeyboardMouseInput::IsKeyDown(int vk) const
int KeyboardMouseInput::GetMouseWheel()
{
if (vk < 0 || vk >= 256) return false;
return m_keyState[vk];
int val = m_mouseWheelAccum;
if (val != 0)
m_mouseWheelConsumed = true;
m_mouseWheelAccum = 0;
return val;
}
bool KeyboardMouseInput::IsKeyPressed(int vk) const
void KeyboardMouseInput::OnRawMouseDelta(int dx, int dy)
{
if (vk < 0 || vk >= 256) return false;
return m_keyState[vk] && !m_keyStatePrev[vk];
m_mouseDeltaAccumX += dx;
m_mouseDeltaAccumY += dy;
}
bool KeyboardMouseInput::IsKeyReleased(int vk) const
bool KeyboardMouseInput::IsKeyDown(int vkCode) const
{
if (vk < 0 || vk >= 256) return false;
return !m_keyState[vk] && m_keyStatePrev[vk];
if (vkCode == VK_SHIFT || vkCode == VK_CONTROL || vkCode == VK_MENU)
return IsModifierKeyDown(m_keyDown, vkCode);
if (vkCode >= 0 && vkCode < MAX_KEYS)
return m_keyDown[vkCode];
return false;
}
// Per-frame mouse button queries
bool KeyboardMouseInput::IsMouseDown(int btn) const
bool KeyboardMouseInput::IsKeyPressed(int vkCode) const
{
if (btn < 0 || btn >= 3) return false;
return m_mouseButtons[btn];
if (vkCode == VK_SHIFT || vkCode == VK_CONTROL || vkCode == VK_MENU)
return IsModifierKeyDown(m_keyPressed, vkCode);
if (vkCode >= 0 && vkCode < MAX_KEYS)
return m_keyPressed[vkCode];
return false;
}
bool KeyboardMouseInput::IsMousePressed(int btn) const
bool KeyboardMouseInput::IsKeyReleased(int vkCode) const
{
if (btn < 0 || btn >= 3) return false;
return m_mouseButtons[btn] && !m_mouseButtonsPrev[btn];
if (vkCode == VK_SHIFT || vkCode == VK_CONTROL || vkCode == VK_MENU)
return IsModifierKeyDown(m_keyReleased, vkCode);
if (vkCode >= 0 && vkCode < MAX_KEYS)
return m_keyReleased[vkCode];
return false;
}
bool KeyboardMouseInput::IsMouseReleased(int btn) const
bool KeyboardMouseInput::IsMouseButtonDown(int button) const
{
if (btn < 0 || btn >= 3) return false;
return !m_mouseButtons[btn] && m_mouseButtonsPrev[btn];
if (button >= 0 && button < MAX_MOUSE_BUTTONS)
return m_mouseButtonDown[button];
return false;
}
// Game-tick consume methods
bool KeyboardMouseInput::ConsumeKeyPress(int vk)
bool KeyboardMouseInput::IsMouseButtonPressed(int button) const
{
if (vk < 0 || vk >= 256) return false;
bool pressed = m_keyPressedAccum[vk];
m_keyPressedAccum[vk] = false;
return pressed;
if (button >= 0 && button < MAX_MOUSE_BUTTONS)
return m_mouseBtnPressed[button];
return false;
}
bool KeyboardMouseInput::ConsumeMousePress(int btn)
bool KeyboardMouseInput::IsMouseButtonReleased(int button) const
{
if (btn < 0 || btn >= 3) return false;
bool pressed = m_mousePressedAccum[btn];
m_mousePressedAccum[btn] = false;
return pressed;
}
bool KeyboardMouseInput::ConsumeMouseRelease(int btn)
{
if (btn < 0 || btn >= 3) return false;
bool released = m_mouseReleasedAccum[btn];
m_mouseReleasedAccum[btn] = false;
return released;
if (button >= 0 && button < MAX_MOUSE_BUTTONS)
return m_mouseBtnReleased[button];
return false;
}
void KeyboardMouseInput::ConsumeMouseDelta(float &dx, float &dy)
{
dx = m_mouseDeltaXAccum;
dy = m_mouseDeltaYAccum;
m_mouseDeltaXAccum = 0.0f;
m_mouseDeltaYAccum = 0.0f;
dx = (float)m_mouseDeltaAccumX;
dy = (float)m_mouseDeltaAccumY;
m_mouseDeltaAccumX = 0;
m_mouseDeltaAccumY = 0;
}
int KeyboardMouseInput::ConsumeScrollDelta()
void KeyboardMouseInput::SetMouseGrabbed(bool grabbed)
{
int delta = m_scrollDeltaAccum;
m_scrollDeltaAccum = 0;
return delta;
}
if (m_mouseGrabbed == grabbed)
return;
// Mouse capture
void KeyboardMouseInput::SetCapture(bool capture)
{
if (capture == m_captured) return;
m_captured = capture;
if (capture)
m_mouseGrabbed = grabbed;
if (grabbed && g_hWnd)
{
ShowCursor(FALSE);
RECT rect;
GetClientRect(m_hWnd, &rect);
POINT topLeft = { rect.left, rect.top };
POINT bottomRight = { rect.right, rect.bottom };
ClientToScreen(m_hWnd, &topLeft);
ClientToScreen(m_hWnd, &bottomRight);
RECT screenRect = { topLeft.x, topLeft.y, bottomRight.x, bottomRight.y };
ClipCursor(&screenRect);
CenterCursor();
while (ShowCursor(FALSE) >= 0) {}
ClipCursorToWindow(g_hWnd);
// Flush accumulated deltas so the snap-to-center doesn't cause a jump
m_mouseDeltaXAccum = 0.0f;
m_mouseDeltaYAccum = 0.0f;
RECT rc;
GetClientRect(g_hWnd, &rc);
POINT center;
center.x = (rc.right - rc.left) / 2;
center.y = (rc.bottom - rc.top) / 2;
ClientToScreen(g_hWnd, &center);
SetCursorPos(center.x, center.y);
m_mouseDeltaAccumX = 0;
m_mouseDeltaAccumY = 0;
}
else
else if (!grabbed && !m_cursorHiddenForUI && g_hWnd)
{
ShowCursor(TRUE);
while (ShowCursor(TRUE) < 0) {}
ClipCursor(NULL);
}
}
bool KeyboardMouseInput::IsCaptured() const { return m_captured; }
void KeyboardMouseInput::CenterCursor()
void KeyboardMouseInput::SetCursorHiddenForUI(bool hidden)
{
RECT rect;
GetClientRect(m_hWnd, &rect);
POINT center = { (rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2 };
ClientToScreen(m_hWnd, &center);
SetCursorPos(center.x, center.y);
if (m_cursorHiddenForUI == hidden)
return;
m_cursorHiddenForUI = hidden;
if (hidden && g_hWnd)
{
while (ShowCursor(FALSE) >= 0) {}
ClipCursorToWindow(g_hWnd);
RECT rc;
GetClientRect(g_hWnd, &rc);
POINT center;
center.x = (rc.right - rc.left) / 2;
center.y = (rc.bottom - rc.top) / 2;
ClientToScreen(g_hWnd, &center);
SetCursorPos(center.x, center.y);
m_mouseDeltaAccumX = 0;
m_mouseDeltaAccumY = 0;
}
else if (!hidden && !m_mouseGrabbed && g_hWnd)
{
while (ShowCursor(TRUE) < 0) {}
ClipCursor(NULL);
}
}
static void ClipCursorToWindow(HWND hWnd)
{
if (!hWnd) return;
RECT rc;
GetClientRect(hWnd, &rc);
POINT topLeft = { rc.left, rc.top };
POINT bottomRight = { rc.right, rc.bottom };
ClientToScreen(hWnd, &topLeft);
ClientToScreen(hWnd, &bottomRight);
RECT clipRect = { topLeft.x, topLeft.y, bottomRight.x, bottomRight.y };
ClipCursor(&clipRect);
}
void KeyboardMouseInput::SetWindowFocused(bool focused)
{
m_windowFocused = focused;
if (focused)
{
if (m_mouseGrabbed || m_cursorHiddenForUI)
{
while (ShowCursor(FALSE) >= 0) {}
ClipCursorToWindow(g_hWnd);
}
else
{
while (ShowCursor(TRUE) < 0) {}
ClipCursor(NULL);
}
}
else
{
while (ShowCursor(TRUE) < 0) {}
ClipCursor(NULL);
}
}
float KeyboardMouseInput::GetMoveX() const
{
float x = 0.0f;
if (m_keyDown[KEY_LEFT]) x += 1.0f;
if (m_keyDown[KEY_RIGHT]) x -= 1.0f;
return x;
}
float KeyboardMouseInput::GetMoveY() const
{
float y = 0.0f;
if (m_keyDown[KEY_FORWARD]) y += 1.0f;
if (m_keyDown[KEY_BACKWARD]) y -= 1.0f;
return y;
}
float KeyboardMouseInput::GetLookX(float sensitivity) const
{
return (float)m_mouseDeltaX * sensitivity;
}
float KeyboardMouseInput::GetLookY(float sensitivity) const
{
return (float)(-m_mouseDeltaY) * sensitivity;
}
#endif // _WINDOWS64

View file

@ -4,88 +4,132 @@
#include <windows.h>
// HID usage page and usage for raw input registration
#ifndef HID_USAGE_PAGE_GENERIC
#define HID_USAGE_PAGE_GENERIC ((USHORT)0x01)
#endif
#ifndef HID_USAGE_GENERIC_MOUSE
#define HID_USAGE_GENERIC_MOUSE ((USHORT)0x02)
#endif
class KeyboardMouseInput
{
public:
KeyboardMouseInput();
~KeyboardMouseInput();
static const int MAX_KEYS = 256;
void Init(HWND hWnd);
static const int MOUSE_LEFT = 0;
static const int MOUSE_RIGHT = 1;
static const int MOUSE_MIDDLE = 2;
static const int MAX_MOUSE_BUTTONS = 3;
static const int KEY_FORWARD = 'W';
static const int KEY_BACKWARD = 'S';
static const int KEY_LEFT = 'A';
static const int KEY_RIGHT = 'D';
static const int KEY_JUMP = VK_SPACE;
static const int KEY_SNEAK = VK_LSHIFT;
static const int KEY_SPRINT = VK_CONTROL;
static const int KEY_INVENTORY = 'E';
static const int KEY_DROP = 'Q';
static const int KEY_CRAFTING = 'C';
static const int KEY_CRAFTING_ALT = 'R';
static const int KEY_CONFIRM = VK_RETURN;
static const int KEY_CANCEL = VK_ESCAPE;
static const int KEY_PAUSE = VK_ESCAPE;
static const int KEY_THIRD_PERSON = VK_F5;
static const int KEY_DEBUG_INFO = VK_F3;
void Init();
void Tick();
void EndFrame();
// Called from WndProc
void OnKeyDown(WPARAM vk);
void OnKeyUp(WPARAM vk);
void OnRawMouseInput(LPARAM lParam);
void OnMouseButton(int button, bool down);
void OnMouseWheel(int delta);
void ClearAllState();
// Per-frame edge detection (for UI / per-frame logic like Alt toggle)
bool IsKeyDown(int vk) const;
bool IsKeyPressed(int vk) const;
bool IsKeyReleased(int vk) const;
bool IsMouseDown(int btn) const;
bool IsMousePressed(int btn) const;
bool IsMouseReleased(int btn) const;
// Game-tick consume methods: accumulate across frames, clear on read.
// Use these from code that runs at game tick rate (20Hz).
bool ConsumeKeyPress(int vk);
bool ConsumeMousePress(int btn);
bool ConsumeMouseRelease(int btn);
void ConsumeMouseDelta(float &dx, float &dy);
int ConsumeScrollDelta();
// Absolute cursor position (client-area coordinates, for GUI when not captured)
void OnKeyDown(int vkCode);
void OnKeyUp(int vkCode);
void OnMouseButtonDown(int button);
void OnMouseButtonUp(int button);
void OnMouseMove(int x, int y);
int GetMouseX() const;
int GetMouseY() const;
HWND GetHWnd() const;
void OnMouseWheel(int delta);
void OnRawMouseDelta(int dx, int dy);
// Mouse capture for FPS look
void SetCapture(bool capture);
bool IsCaptured() const;
bool IsKeyDown(int vkCode) const;
bool IsKeyPressed(int vkCode) const;
bool IsKeyReleased(int vkCode) const;
bool IsMouseButtonDown(int button) const;
bool IsMouseButtonPressed(int button) const;
bool IsMouseButtonReleased(int button) const;
int GetMouseX() const { return m_mouseX; }
int GetMouseY() const { return m_mouseY; }
int GetMouseDeltaX() const { return m_mouseDeltaX; }
int GetMouseDeltaY() const { return m_mouseDeltaY; }
int GetMouseWheel();
int PeekMouseWheel() const { return m_mouseWheelAccum; }
void ConsumeMouseWheel() { if (m_mouseWheelAccum != 0) m_mouseWheelConsumed = true; m_mouseWheelAccum = 0; }
bool WasMouseWheelConsumed() const { return m_mouseWheelConsumed; }
// Per-frame delta consumption for low-latency mouse look.
// Reads and clears the raw accumulators (not the per-tick snapshot).
void ConsumeMouseDelta(float &dx, float &dy);
void SetMouseGrabbed(bool grabbed);
bool IsMouseGrabbed() const { return m_mouseGrabbed; }
void SetCursorHiddenForUI(bool hidden);
bool IsCursorHiddenForUI() const { return m_cursorHiddenForUI; }
void SetWindowFocused(bool focused);
bool IsWindowFocused() const { return m_windowFocused; }
bool HasAnyInput() const { return m_hasInput; }
void SetKBMActive(bool active) { m_kbmActive = active; }
bool IsKBMActive() const { return m_kbmActive; }
void SetScreenCursorHidden(bool hidden) { m_screenWantsCursorHidden = hidden; }
bool IsScreenCursorHidden() const { return m_screenWantsCursorHidden; }
float GetMoveX() const;
float GetMoveY() const;
float GetLookX(float sensitivity) const;
float GetLookY(float sensitivity) const;
private:
void CenterCursor();
bool m_keyDown[MAX_KEYS];
bool m_keyDownPrev[MAX_KEYS];
// Per-frame double-buffered state (for IsKeyPressed/Released per-frame edge detection)
bool m_keyState[256];
bool m_keyStatePrev[256];
bool m_mouseButtons[3];
bool m_mouseButtonsPrev[3];
bool m_keyPressedAccum[MAX_KEYS];
bool m_keyReleasedAccum[MAX_KEYS];
bool m_keyPressed[MAX_KEYS];
bool m_keyReleased[MAX_KEYS];
// Sticky press accumulators (persist until consumed by game tick)
bool m_keyPressedAccum[256];
bool m_mousePressedAccum[3];
bool m_mouseReleasedAccum[3];
bool m_mouseButtonDown[MAX_MOUSE_BUTTONS];
bool m_mouseButtonDownPrev[MAX_MOUSE_BUTTONS];
// Mouse delta accumulators (persist until consumed by game tick)
float m_mouseDeltaXAccum;
float m_mouseDeltaYAccum;
bool m_mouseBtnPressedAccum[MAX_MOUSE_BUTTONS];
bool m_mouseBtnReleasedAccum[MAX_MOUSE_BUTTONS];
bool m_mouseBtnPressed[MAX_MOUSE_BUTTONS];
bool m_mouseBtnReleased[MAX_MOUSE_BUTTONS];
// Scroll accumulator (persists until consumed by game tick)
int m_scrollDeltaAccum;
bool m_captured;
HWND m_hWnd;
bool m_initialized;
// Absolute cursor position in client coordinates
int m_mouseX;
int m_mouseY;
int m_mouseDeltaX;
int m_mouseDeltaY;
int m_mouseDeltaAccumX;
int m_mouseDeltaAccumY;
int m_mouseWheelAccum;
bool m_mouseWheelConsumed;
bool m_mouseGrabbed;
bool m_cursorHiddenForUI;
bool m_windowFocused;
bool m_hasInput;
bool m_kbmActive;
bool m_screenWantsCursorHidden;
};
extern KeyboardMouseInput KMInput;
extern KeyboardMouseInput g_KBMInput;
#endif // _WINDOWS64

View file

@ -50,7 +50,10 @@ std::vector<BYTE> WinsockNetLayer::s_freeSmallIds;
bool g_Win64MultiplayerHost = false;
bool g_Win64MultiplayerJoin = false;
int g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT;
char g_Win64MultiplayerIP[256] = "0.0.0.0";
char g_Win64MultiplayerIP[256] = "127.0.0.1";
bool g_Win64DedicatedServer = false;
int g_Win64DedicatedServerPort = WIN64_NET_DEFAULT_PORT;
char g_Win64DedicatedServerBindIP[256] = "";
bool WinsockNetLayer::Initialize()
{
@ -139,12 +142,7 @@ void WinsockNetLayer::Shutdown()
}
}
bool WinsockNetLayer::HostGame(int port)
{
return HostGame(NULL, port);
}
bool WinsockNetLayer::HostGame(const char *bindIp, int port)
bool WinsockNetLayer::HostGame(int port, const char* bindIp)
{
if (!s_initialized && !Initialize()) return false;
@ -164,21 +162,19 @@ bool WinsockNetLayer::HostGame(const char *bindIp, int port)
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
hints.ai_flags = (bindIp == NULL || bindIp[0] == 0) ? AI_PASSIVE : 0;
char portStr[16];
sprintf_s(portStr, "%d", port);
const char *bindNode = NULL;
if (bindIp != NULL && bindIp[0] != 0 && strcmp(bindIp, "*") != 0)
{
bindNode = bindIp;
}
int iResult = getaddrinfo(bindNode, portStr, &hints, &result);
const char* resolvedBindIp = (bindIp != NULL && bindIp[0] != 0) ? bindIp : NULL;
int iResult = getaddrinfo(resolvedBindIp, portStr, &hints, &result);
if (iResult != 0)
{
app.DebugPrintf("getaddrinfo failed for bind '%s': %d\n", bindNode ? bindNode : "0.0.0.0", iResult);
app.DebugPrintf("getaddrinfo failed for %s:%d - %d\n",
resolvedBindIp != NULL ? resolvedBindIp : "*",
port,
iResult);
return false;
}
@ -217,7 +213,9 @@ bool WinsockNetLayer::HostGame(const char *bindIp, int port)
s_acceptThread = CreateThread(NULL, 0, AcceptThreadProc, NULL, 0, NULL);
app.DebugPrintf("Win64 LAN: Hosting on %s:%d\n", bindNode ? bindNode : "0.0.0.0", port);
app.DebugPrintf("Win64 LAN: Hosting on %s:%d\n",
resolvedBindIp != NULL ? resolvedBindIp : "*",
port);
return true;
}
@ -919,4 +917,4 @@ DWORD WINAPI WinsockNetLayer::DiscoveryThreadProc(LPVOID param)
return 0;
}
#endif
#endif

View file

@ -65,8 +65,7 @@ public:
static bool Initialize();
static void Shutdown();
static bool HostGame(const char *bindIp, int port);
static bool HostGame(int port);
static bool HostGame(int port, const char* bindIp = NULL);
static bool JoinGame(const char* ip, int port);
static bool SendToSmallId(BYTE targetSmallId, const void* data, int dataSize);
@ -148,5 +147,8 @@ extern bool g_Win64MultiplayerHost;
extern bool g_Win64MultiplayerJoin;
extern int g_Win64MultiplayerPort;
extern char g_Win64MultiplayerIP[256];
extern bool g_Win64DedicatedServer;
extern int g_Win64DedicatedServerPort;
extern char g_Win64DedicatedServerBindIP[256];
#endif

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

View file

@ -48,13 +48,11 @@ void glLoadIdentity()
RenderManager.MatrixSetIdentity();
}
extern int g_iScreenWidth;
extern int g_iScreenHeight;
// AAR - Use calculated aspect ratio to support dynamic resizing
extern float g_iAspectRatio;
void gluPerspective(float fovy, float aspect, float zNear, float zFar)
{
float dynamicAspect = (float)g_iScreenWidth / (float)g_iScreenHeight;
RenderManager.MatrixPerspective(fovy, dynamicAspect, zNear, zFar);
RenderManager.MatrixPerspective(fovy, g_iAspectRatio, zNear, zFar);
}
void glOrtho(float left,float right,float bottom,float top,float zNear,float zFar)

View file

@ -3,41 +3,98 @@
#ifdef _WINDOWS64
#include "Windows64\KeyboardMouseInput.h"
static const int s_keyToVK[] = {
'A', // KEY_A = 0
'B', // KEY_B = 1
'C', // KEY_C = 2
'D', // KEY_D = 3
'E', // KEY_E = 4
'F', // KEY_F = 5
'G', // KEY_G = 6
'H', // KEY_H = 7
'I', // KEY_I = 8
'J', // KEY_J = 9
'K', // KEY_K = 10
'L', // KEY_L = 11
'M', // KEY_M = 12
'N', // KEY_N = 13
'O', // KEY_O = 14
'P', // KEY_P = 15
'Q', // KEY_Q = 16
'R', // KEY_R = 17
'S', // KEY_S = 18
'T', // KEY_T = 19
'U', // KEY_U = 20
'V', // KEY_V = 21
'W', // KEY_W = 22
'X', // KEY_X = 23
'Y', // KEY_Y = 24
'Z', // KEY_Z = 25
VK_SPACE, // KEY_SPACE = 26
VK_LSHIFT, // KEY_LSHIFT = 27
VK_ESCAPE, // KEY_ESCAPE = 28
VK_BACK, // KEY_BACK = 29
VK_RETURN, // KEY_RETURN = 30
VK_RSHIFT, // KEY_RSHIFT = 31
VK_UP, // KEY_UP = 32
VK_DOWN, // KEY_DOWN = 33
VK_TAB, // KEY_TAB = 34
'1', // KEY_1 = 35
'2', // KEY_2 = 36
'3', // KEY_3 = 37
'4', // KEY_4 = 38
'5', // KEY_5 = 39
'6', // KEY_6 = 40
'7', // KEY_7 = 41
'8', // KEY_8 = 42
'9', // KEY_9 = 43
VK_F1, // KEY_F1 = 44
VK_F3, // KEY_F3 = 45
VK_F4, // KEY_F4 = 46
VK_F5, // KEY_F5 = 47
VK_F6, // KEY_F6 = 48
VK_F8, // KEY_F8 = 49
VK_F9, // KEY_F9 = 50
VK_F11, // KEY_F11 = 51
VK_ADD, // KEY_ADD = 52
VK_SUBTRACT,// KEY_SUBTRACT = 53
VK_LEFT, // KEY_LEFT = 54
VK_RIGHT, // KEY_RIGHT = 55
};
static const int s_keyToVKCount = sizeof(s_keyToVK) / sizeof(s_keyToVK[0]);
int Keyboard::toVK(int keyConst)
{
if (keyConst >= 0 && keyConst < s_keyToVKCount)
return s_keyToVK[keyConst];
return 0;
}
bool Keyboard::isKeyDown(int keyCode)
{
int vk = toVK(keyCode);
if (vk > 0)
return g_KBMInput.IsKeyDown(vk);
return false;
}
int Mouse::getX()
{
return KMInput.GetMouseX();
return g_KBMInput.GetMouseX();
}
int Mouse::getY()
{
// Return Y in bottom-up coordinates (OpenGL convention, matching original Java LWJGL Mouse)
extern HWND g_hWnd;
RECT rect;
GetClientRect(KMInput.GetHWnd(), &rect);
return (rect.bottom - 1) - KMInput.GetMouseY();
GetClientRect(g_hWnd, &rect);
return (rect.bottom - 1) - g_KBMInput.GetMouseY();
}
bool Mouse::isButtonDown(int button)
{
return KMInput.IsMouseDown(button);
}
bool Keyboard::isKeyDown(int key)
{
// Map Keyboard constants to Windows virtual key codes
if (key == Keyboard::KEY_LSHIFT) return KMInput.IsKeyDown(VK_LSHIFT);
if (key == Keyboard::KEY_RSHIFT) return KMInput.IsKeyDown(VK_RSHIFT);
if (key == Keyboard::KEY_ESCAPE) return KMInput.IsKeyDown(VK_ESCAPE);
if (key == Keyboard::KEY_RETURN) return KMInput.IsKeyDown(VK_RETURN);
if (key == Keyboard::KEY_BACK) return KMInput.IsKeyDown(VK_BACK);
if (key == Keyboard::KEY_SPACE) return KMInput.IsKeyDown(VK_SPACE);
if (key == Keyboard::KEY_TAB) return KMInput.IsKeyDown(VK_TAB);
if (key == Keyboard::KEY_UP) return KMInput.IsKeyDown(VK_UP);
if (key == Keyboard::KEY_DOWN) return KMInput.IsKeyDown(VK_DOWN);
if (key == Keyboard::KEY_LEFT) return KMInput.IsKeyDown(VK_LEFT);
if (key == Keyboard::KEY_RIGHT) return KMInput.IsKeyDown(VK_RIGHT);
if (key >= Keyboard::KEY_A && key <= Keyboard::KEY_Z)
return KMInput.IsKeyDown('A' + (key - Keyboard::KEY_A));
return false;
return g_KBMInput.IsMouseButtonDown(button);
}
#endif

View file

@ -187,12 +187,13 @@ public:
static void create() {}
static void destroy() {}
#ifdef _WINDOWS64
static bool isKeyDown(int key);
static bool isKeyDown(int keyCode);
#else
static bool isKeyDown(int) {return false;}
static bool isKeyDown(int) { return false; }
#endif
static wstring getKeyName(int) { return L"KEYNAME"; }
static void enableRepeatEvents(bool) {}
static const int KEY_A = 0;
static const int KEY_B = 1;
static const int KEY_C = 2;
@ -228,8 +229,32 @@ public:
static const int KEY_UP = 32;
static const int KEY_DOWN = 33;
static const int KEY_TAB = 34;
static const int KEY_LEFT = 35;
static const int KEY_RIGHT = 36;
static const int KEY_1 = 35;
static const int KEY_2 = 36;
static const int KEY_3 = 37;
static const int KEY_4 = 38;
static const int KEY_5 = 39;
static const int KEY_6 = 40;
static const int KEY_7 = 41;
static const int KEY_8 = 42;
static const int KEY_9 = 43;
static const int KEY_F1 = 44;
static const int KEY_F3 = 45;
static const int KEY_F4 = 46;
static const int KEY_F5 = 47;
static const int KEY_F6 = 48;
static const int KEY_F8 = 49;
static const int KEY_F9 = 50;
static const int KEY_F11 = 51;
static const int KEY_ADD = 52;
static const int KEY_SUBTRACT = 53;
static const int KEY_LEFT = 54;
static const int KEY_RIGHT = 55;
#ifdef _WINDOWS64
// Map LWJGL-style key constant to Windows VK code
static int toVK(int keyConst);
#endif
};
class Mouse

View file

@ -749,7 +749,7 @@ void ConsoleSaveFileOriginal::Flush(bool autosave, bool updateThumbnail )
PBYTE pbDataSaveImage=NULL;
DWORD dwDataSizeSaveImage=0;
#if ( defined _XBOX || defined _DURANGO )
#if ( defined _XBOX || defined _DURANGO || defined _WINDOWS64 )
app.GetSaveThumbnail(&pbThumbnailData,&dwThumbnailDataSize);
#elif ( defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ )
app.GetSaveThumbnail(&pbThumbnailData,&dwThumbnailDataSize,&pbDataSaveImage,&dwDataSizeSaveImage);

View file

@ -479,7 +479,7 @@ void EntityHorse::createInventory()
void EntityHorse::updateEquipment()
{
if (!level->isClientSide)
if (level && !level->isClientSide)
{
setSaddled(inventory->getItem(INV_SLOT_SADDLE) != NULL);
if (canWearArmor())

View file

@ -1257,6 +1257,7 @@
<UseFullPaths>false</UseFullPaths>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<AdditionalOptions>/FS %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -1355,7 +1356,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<EnableFiberSafeOptimizations>true</EnableFiberSafeOptimizations>
<StringPooling>true</StringPooling>
<AdditionalOptions>/Ob3</AdditionalOptions>
<AdditionalOptions>/FS /Ob3 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -4957,4 +4958,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View file

@ -34,7 +34,7 @@ bool NearestAttackableTargetGoal::DistComp::operator() (shared_ptr<Entity> e1, s
double distSqr2 = source->distanceToSqr(e2);
if (distSqr1 < distSqr2) return true;
if (distSqr1 > distSqr2) return false;
return true;
return false;
}
NearestAttackableTargetGoal::NearestAttackableTargetGoal(PathfinderMob *mob, const type_info& targetType, int randomInterval, bool mustSee, bool mustReach /*= false*/, EntitySelector *entitySelector /* =NULL */)

View file

@ -20,10 +20,11 @@ This project contains the source code of Minecraft Legacy Console Edition v1.6.0
- Fixed compilation and execution in both Debug and Release mode on Windows using Visual Studio 2022
- Added support for keyboard and mouse input
- Added fullscreen mode support (toggle using F11)
- Disabled V-Sync for better performance
- (WIP) Disabled V-Sync for better performance
- Added a high-resolution timer path on Windows for smoother high-FPS gameplay timing
- Device's screen resolution will be used as the game resolution instead of using a fixed resolution (1920x1080)
- LAN Multiplayer & Discovery
- Added persistent username system via "username.txt"
## Multiplayer
@ -38,17 +39,25 @@ This feature is based on [LCEMP](https://github.com/LCEMP/LCEMP/)
### Launch Arguments
| Argument | Description |
|--------------------|----------------------------------------------------------------------------------------------------------------|
| `-name <username>` | Sets your in-game username |
| `-ip <address>` | Manually connect to an IP if LAN advertising does not work or if the server cannot be discovered automatically |
| `-port <port>` | Override the default port if it was changed in the source |
| Argument | Description |
|--------------------|-----------------------------------------------------------------------------------------------------|
| `-name <username>` | Sets your in-game username |
| `-server` | Launches a headless server instead of the client |
| `-ip <address>` | Client mode: manually connect to an IP. Server mode: override the bind IP from `server.properties` |
| `-port <port>` | Client mode: override the join port. Server mode: override the listen port from `server.properties` |
Example:
```
Minecraft.Client.exe -name Steve -ip 192.168.0.25 -port 25565
```
Headless server example:
```
Minecraft.Client.exe -server -ip 0.0.0.0 -port 25565
```
The headless server also reads and writes `server.properties` in the working directory. If `-ip` / `-port` are omitted in `-server` mode, it falls back to `server-ip` / `server-port` from that file. Dedicated-server host options such as `trust-players`, `pvp`, `fire-spreads`, `tnt`, `difficulty`, `gamemode`, `spawn-animals`, and `spawn-npcs` are persisted there as well.
## Controls (Keyboard & Mouse)
- **Movement**: `W` `A` `S` `D`
@ -61,7 +70,6 @@ Minecraft.Client.exe -name Steve -ip 192.168.0.25 -port 25565
- **Toggle View (FPS/TPS)**: `F5`
- **Fullscreen**: `F11`
- **Pause Menu**: `Esc`
- **Toggle Mouse Capture**: `Left Alt` (for debugging)
- **Attack / Destroy**: `Left Click`
- **Use / Place**: `Right Click`
- **Select Item**: `Mouse Wheel` or keys `1` to `9`