Merge remote-tracking branch 'itsRevela/main' into upstream-merge

# Conflicts:
#	Minecraft.Client/Common/Audio/SoundNames.cpp
#	Minecraft.Client/SheepRenderer.cpp
#	Minecraft.World/SoundTypes.h
#	README.md
#	cmake/CopyAssets.cmake
This commit is contained in:
George V. 2026-04-13 17:54:36 +03:00
commit 734f186cd3
No known key found for this signature in database
GPG key ID: 1DB61094F2DD4982
56 changed files with 558 additions and 423 deletions

View file

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.24)
project(MinecraftConsoles LANGUAGES C CXX RC ASM_MASM)
project(LCE-Revelations LANGUAGES C CXX RC ASM_MASM)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

View file

@ -128,8 +128,6 @@ set(ASSET_FOLDER_PAIRS
"${CMAKE_CURRENT_SOURCE_DIR}/music" "music"
"${CMAKE_CURRENT_SOURCE_DIR}/Common/Media" "Common/Media"
"${CMAKE_CURRENT_SOURCE_DIR}/Common/res" "Common/res"
"${CMAKE_CURRENT_SOURCE_DIR}/Common/Trial" "Common/Trial"
"${CMAKE_CURRENT_SOURCE_DIR}/Common/Tutorial" "Common/Tutorial"
"${CMAKE_CURRENT_SOURCE_DIR}/${PLATFORM_NAME}Media" "${PLATFORM_NAME}Media"
)
setup_asset_folder_copy(Minecraft.Client "${ASSET_FOLDER_PAIRS}")

View file

@ -12,6 +12,7 @@ const wstring ChatScreen::allowedChars = SharedConstants::acceptableLetters;
vector<wstring> ChatScreen::s_chatHistory;
int ChatScreen::s_historyIndex = -1;
wstring ChatScreen::s_historyDraft;
int ChatScreen::s_chatIndex = 0;
bool ChatScreen::isAllowedChatChar(wchar_t c)
{
@ -28,6 +29,8 @@ ChatScreen::ChatScreen()
frame = 0;
cursorIndex = 0;
s_historyIndex = -1;
ChatScreen::s_chatIndex = 0;
}
void ChatScreen::init()
@ -89,6 +92,20 @@ void ChatScreen::handleHistoryDown()
applyHistoryMessage();
}
int ChatScreen::getChatIndex()
{
return ChatScreen::s_chatIndex;
}
void ChatScreen::correctChatIndex(int newChatIndex) {
ChatScreen::s_chatIndex = newChatIndex;
}
void ChatScreen::setWheelValue(int wheel) {
ChatScreen::s_chatIndex += wheel;
if (ChatScreen::s_chatIndex < 0) ChatScreen::s_chatIndex = 0;
}
void ChatScreen::keyPressed(wchar_t ch, int eventKey)
{
if (eventKey == Keyboard::KEY_ESCAPE)
@ -140,7 +157,7 @@ void ChatScreen::keyPressed(wchar_t ch, int eventKey)
cursorIndex--;
return;
}
if (isAllowedChatChar(ch) && static_cast<int>(message.length()) < SharedConstants::maxChatLength)
if (isAllowedChatChar(ch) && static_cast<int>(message.length()) < SharedConstants::maxVisibleLength)
{
message.insert(cursorIndex, 1, ch);
cursorIndex++;

View file

@ -16,6 +16,7 @@ private:
static std::vector<wstring> s_chatHistory;
static int s_historyIndex;
static wstring s_historyDraft;
static int s_chatIndex;
static const wstring allowedChars;
static bool isAllowedChatChar(wchar_t c);
@ -28,6 +29,9 @@ public:
virtual void handleHistoryUp();
virtual void handleHistoryDown();
static int getChatIndex();
static void correctChatIndex(int newChatIndex);
static void setWheelValue(int wheel);
protected:
void keyPressed(wchar_t ch, int eventKey);
public:

View file

@ -66,6 +66,7 @@
#include "../Minecraft.World/DurangoStats.h"
#include "../Minecraft.World/GenericStats.h"
#endif
#include <regex>
namespace
{
@ -1607,17 +1608,35 @@ void ClientConnection::handleChat(shared_ptr<ChatPacket> packet)
bool replaceEntitySource = false;
bool replaceItem = false;
static std::wregex IDS_Pattern(LR"(\{\*IDS_(\d+)\*\})"); //maybe theres a better way to do translateable IDS
int stringArgsSize = packet->m_stringArgs.size();
wstring playerDisplayName = L"";
wstring sourceDisplayName = L"";
// On platforms other than Xbox One this just sets display name to gamertag
if (packet->m_stringArgs.size() >= 1) playerDisplayName = GetDisplayNameByGamertag(packet->m_stringArgs[0]);
if (packet->m_stringArgs.size() >= 2) sourceDisplayName = GetDisplayNameByGamertag(packet->m_stringArgs[1]);
if (stringArgsSize >= 1) playerDisplayName = GetDisplayNameByGamertag(packet->m_stringArgs[0]);
if (stringArgsSize >= 2) sourceDisplayName = GetDisplayNameByGamertag(packet->m_stringArgs[1]);
switch(packet->m_messageType)
{
case ChatPacket::e_ChatCustom:
message = (packet->m_stringArgs.size() >= 1) ? packet->m_stringArgs[0] : L"";
case ChatPacket::e_ChatActionBar:
if (stringArgsSize >= 1) {
message = packet->m_stringArgs[0];
std::wsmatch match;
while (std::regex_search(message, match, IDS_Pattern)) {
message = replaceAll(message, match[0], app.GetString(std::stoi(match[1].str())));
}
message = app.EscapeHTMLString(message); //do this to enforce escaped string
message = app.FormatChatMessage(message); //this needs to be last cause it converts colors to html colors that would have been escaped
} else {
message = L"empty message";
}
displayOnGui = (packet->m_messageType == ChatPacket::e_ChatCustom);
break;
case ChatPacket::e_ChatBedOccupied:
message = app.GetString(IDS_TILE_BED_OCCUPIED);
@ -1967,7 +1986,7 @@ void ClientConnection::handleChat(shared_ptr<ChatPacket> packet)
if(replacePlayer)
{
message = replaceAll(message,L"{*PLAYER*}",playerDisplayName);
message = replaceAll(message,L"{*PLAYER*}", playerDisplayName);
}
if(replaceEntitySource)
@ -2002,7 +2021,9 @@ void ClientConnection::handleChat(shared_ptr<ChatPacket> packet)
// flag that a message is a death message
bool bIsDeathMessage = (packet->m_messageType>=ChatPacket::e_ChatDeathInFire) && (packet->m_messageType<=ChatPacket::e_ChatDeathIndirectMagicItem);
if( displayOnGui ) minecraft->gui->addMessage(message,m_userIndex, bIsDeathMessage);
if( displayOnGui ) minecraft->gui->addMessage(message, m_userIndex, bIsDeathMessage);
if (!displayOnGui && !message.empty()) minecraft->gui->setActionBarMessage(message);
}
void ClientConnection::handleAnimate(shared_ptr<AnimatePacket> packet)

View file

@ -270,6 +270,8 @@ const WCHAR *ConsoleSoundEngine::wchSoundNames[eSoundType_MAX]=
L"item.armor.equip_generic4",
L"item.armor.equip_generic5",
L"item.armor.equip_generic6"
L"damage.critical", //eSoundType_DAMAGE_CRITICAL,
};

View file

@ -6786,6 +6786,87 @@ wstring CMinecraftApp::FormatHTMLString(int iPad, const wstring &desc, int shado
return text;
}
//found list of html escapes at https://stackoverflow.com/questions/7381974/which-characters-need-to-be-escaped-in-html
wstring CMinecraftApp::EscapeHTMLString(const wstring& desc)
{
static std::unordered_map<wchar_t, wchar_t*> replacementMap = {
{L'&', L"&amp;"},
{L'<', L"&lt;"},
{L'>', L"&gt;"},
{L'\"', L"&quot;"},
{L'\'', L"&#39;"},
};
wstring finalString = L"";
for (int i = 0; i < desc.size(); i++) {
wchar_t _char = desc[i];
auto it = replacementMap.find(_char);
if (it != replacementMap.end()) finalString += it->second;
else finalString += _char;
}
return finalString;
}
wstring CMinecraftApp::FormatChatMessage(const wstring& desc, bool applyColor)
{
static std::wstring_view colorFormatString = L"<font color=\"#%08x\" shadowcolor=\"#%08x\">";
wstring results = desc;
wchar_t replacements[64];
swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_0), 0xFFFFFFFF);
results = replaceAll(results, L"§0", replacements);
swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_1), 0xFFFFFFFF);
results = replaceAll(results, L"§1", replacements);
swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_2), 0xFFFFFFFF);
results = replaceAll(results, L"§2", replacements);
swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_3), 0xFFFFFFFF);
results = replaceAll(results, L"§3", replacements);
swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_4), 0xFFFFFFFF);
results = replaceAll(results, L"§4", replacements);
swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_5), 0xFFFFFFFF);
results = replaceAll(results, L"§5", replacements);
swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_6), 0xFFFFFFFF);
results = replaceAll(results, L"§6", replacements);
swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_7), 0xFFFFFFFF);
results = replaceAll(results, L"§7", replacements);
swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_8), 0xFFFFFFFF);
results = replaceAll(results, L"§8", replacements);
swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_9), 0xFFFFFFFF);
results = replaceAll(results, L"§9", replacements);
swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_a), 0xFFFFFFFF);
results = replaceAll(results, L"§a", replacements);
swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_b), 0xFFFFFFFF);
results = replaceAll(results, L"§b", replacements);
swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_c), 0xFFFFFFFF);
results = replaceAll(results, L"§c", replacements);
swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_d), 0xFFFFFFFF);
results = replaceAll(results, L"§d", replacements);
swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_e), 0xFFFFFFFF);
results = replaceAll(results, L"§e", replacements);
swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_f), 0xFFFFFFFF);
results = replaceAll(results, L"§f", replacements);
return results;
}
wstring CMinecraftApp::GetActionReplacement(int iPad, unsigned char ucAction)
{
unsigned int input = InputManager.GetGameJoypadMaps(InputManager.GetJoypadMapVal(iPad) ,ucAction);

View file

@ -567,7 +567,9 @@ public:
int GetHTMLColour(eMinecraftColour colour);
int GetHTMLColor(eMinecraftColour colour) { return GetHTMLColour(colour); }
int GetHTMLFontSize(EHTMLFontSize size);
wstring FormatHTMLString(int iPad, const wstring &desc, int shadowColour = 0xFFFFFFFF);
wstring FormatHTMLString(int iPad, const wstring& desc, int shadowColour = 0xFFFFFFFF);
wstring EscapeHTMLString(const wstring &desc);
wstring FormatChatMessage(const wstring& desc, bool applyColor = true);
wstring GetActionReplacement(int iPad, unsigned char ucAction);
wstring GetVKReplacement(unsigned int uiVKey);
wstring GetIconReplacement(unsigned int uiIcon);

View file

@ -204,6 +204,12 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
ProfileManager.SetDeferredSignoutEnabled(true);
#endif
// Clear any stale cancel flag latched by the previous join's progress
// UI teardown, otherwise the next join's first tick insta-closes.
EnterCriticalSection(&bCancelRequestedCS);
g_NetworkManager.m_bCancelRequested = false;
LeaveCriticalSection(&bCancelRequestedCS);
int64_t seed = 0;
bool dedicatedNoLocalHostPlayer = false;
if (lpParameter != nullptr)

View file

@ -134,7 +134,14 @@ void CPlatformNetworkManagerStub::NotifyPlayerLeaving(IQNetPlayer* pQNetPlayer)
if (socket != nullptr)
{
if (m_pIQNet->IsHost())
{
g_NetworkManager.CloseConnection(networkPlayer);
// Propagate the TCP drop to the game Socket so any orphaned
// PendingConnection at this smallId cleans up before its login
// timer fires and leaks a DisconnectPacket to the reused slot.
socket->close(true);
}
}
if (m_pIQNet->IsHost())

View file

@ -33,6 +33,7 @@ IUIScene_AbstractContainerMenu::IUIScene_AbstractContainerMenu()
m_pointerPos.y = 0.0f;
m_bPointerDrivenByMouse = false;
m_iLastMouseTickTimeNs = -1;
}
IUIScene_AbstractContainerMenu::~IUIScene_AbstractContainerMenu()
@ -311,6 +312,21 @@ void IUIScene_AbstractContainerMenu::handleEnchantButton(int slot, int iPad) {
void IUIScene_AbstractContainerMenu::onMouseTick()
{
// Frame-rate independent cursor input, normalized to a 60Hz reference frame.
const int64_t kRefFrameNs = 1000000000LL / 60;
const int64_t kMinDeltaNs = 1000000LL;
const int64_t kMaxDeltaNs = 100000000LL;
int64_t iNowNs = System::nanoTime();
float fFrameScale = 1.0f;
if ( m_iLastMouseTickTimeNs > 0 )
{
int64_t iDeltaNs = iNowNs - m_iLastMouseTickTimeNs;
if ( iDeltaNs < kMinDeltaNs ) iDeltaNs = kMinDeltaNs;
if ( iDeltaNs > kMaxDeltaNs ) iDeltaNs = kMaxDeltaNs;
fFrameScale = static_cast<float>(iDeltaNs) / static_cast<float>(kRefFrameNs);
}
m_iLastMouseTickTimeNs = iNowNs;
Minecraft *pMinecraft = Minecraft::GetInstance();
if( pMinecraft->localgameModes[getPad()] != nullptr)
{
@ -467,10 +483,10 @@ void IUIScene_AbstractContainerMenu::onMouseTick()
// The SD/splitscreen scenes are approximately 0.6 times the size of the fullscreen on
if(!RenderManager.IsHiDef() || app.GetLocalPlayerCount() > 1) fInputScale *= 0.6f;
fInputX *= fInputScale;
fInputY *= fInputScale;
fInputX *= fInputScale * fFrameScale;
fInputY *= fInputScale * fFrameScale;
#ifdef USE_POINTER_ACCEL
#ifdef USE_POINTER_ACCEL
m_fPointerAccelX += fInputX / 50.0f;
m_fPointerAccelY += fInputY / 50.0f;
@ -1317,36 +1333,8 @@ void IUIScene_AbstractContainerMenu::onMouseTick()
vPointerPos.x -= m_fPointerImageOffsetX;
vPointerPos.y -= m_fPointerImageOffsetY;
// Update pointer position.
// 4J-PB - do not allow sub pixel positions or we get broken lines in box edges
// problem here when sensitivity is low - we'll be moving a sub pixel size, so it'll clamp, and we'll never move. In that case, move 1 pixel
if(fInputDirX!=0.0f)
{
if(fInputDirX==1.0f)
{
vPointerPos.x+=0.999999f;
}
else
{
vPointerPos.x-=0.999999f;
}
}
if(fInputDirY!=0.0f)
{
if(fInputDirY==1.0f)
{
vPointerPos.y+=0.999999f;
}
else
{
vPointerPos.y-=0.999999f;
}
}
vPointerPos.x = static_cast<float>(floor(vPointerPos.x + 0.5f));
vPointerPos.y = static_cast<float>(floor(vPointerPos.y + 0.5f));
// Keep sub-pixel float state so deltas <1px accumulate across frames; the renderer
// truncates to integer pixels when emitting the Iggy mouse event.
m_pointerPos = vPointerPos;
adjustPointerForSafeZone();

View file

@ -146,6 +146,8 @@ protected:
int m_iConsectiveInputTicks;
int64_t m_iLastMouseTickTimeNs;
// Used for detecting quick "taps" in a direction, should jump cursor to next slot.
enum ETapState
{

View file

@ -1,5 +1,6 @@
#include "stdafx.h"
#include "UIController.h"
#include <ChatScreen.h>
#include "UI.h"
#include "UIScene.h"
#include "UIControl_Slider.h"
@ -1459,6 +1460,9 @@ void UIController::handleKeyPress(unsigned int iPad, unsigned int key)
}
#endif
if (key == 4) ChatScreen::setWheelValue(1);
if (key == 5) ChatScreen::setWheelValue(-1);
if(pressed) app.DebugPrintf("Pressed %d\n",key);
if(released) app.DebugPrintf("Released %d\n",key);
// Repeat handling

View file

@ -490,18 +490,21 @@ SCreditTextItemDef UIScene_Credits::gs_aCreditDefs[MAX_CREDIT_STRINGS] =
#endif
{L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"MinecraftConsoles", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eExtraLargeText},
{L"LCE-Revelations", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eExtraLargeText},
{L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"Project Maintainers", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eLargeText},
{L"smartcmd", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"codeHusky", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"mattsumi", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"itsRevela", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"Former Maintainers", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eLargeText},
{L"smartcmd", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"Patoke", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"rtm516", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"mattsumi", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"dxf", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"la", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"Thank you to our 100+ contributors on GitHub!", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eLargeText},
{L"github.com/smartcmd/MinecraftConsoles", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"Thank you to our 120+ contributors on GitHub!", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eLargeText},
{L"github.com/MCLCE/MinecraftConsoles", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"(formerly smartcmd/MinecraftConsoles)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"Additional Thanks", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eLargeText},

View file

@ -9,6 +9,7 @@
#include "../../EnderDragonRenderer.h"
#include "../../../Minecraft.World/net.minecraft.world.inventory.h"
#include "../../../Minecraft.World/StringHelpers.h"
#include <ChatScreen.h>
UIScene_HUD::UIScene_HUD(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer)
{
@ -23,8 +24,10 @@ UIScene_HUD::UIScene_HUD(int iPad, void *initData, UILayer *parentLayer) : UISce
for(unsigned int i = 0; i < CHAT_LINES_COUNT; ++i)
{
m_labelChatText[i].init(L"");
IggyValueSetBooleanRS(m_labelChatText[i].getIggyValuePath(), 0, "m_bUseHtmlText", true);
}
m_labelJukebox.init(L"");
IggyValueSetBooleanRS(m_labelJukebox.getIggyValuePath(), 0, "m_bUseHtmlText", true);
addTimer(0, 100);
}
@ -787,16 +790,31 @@ void UIScene_HUD::render(S32 width, S32 height, C4JRender::eViewportType viewpor
void UIScene_HUD::handleTimerComplete(int id)
{
Minecraft *pMinecraft = Minecraft::GetInstance();
bool isChatOpen = (dynamic_cast<ChatScreen*>(pMinecraft->getScreen()) != nullptr);
bool anyVisible = false;
if(pMinecraft->localplayers[m_iPad]!= nullptr)
{
Gui *pGui = pMinecraft->gui;
//DWORD messagesToDisplay = min( CHAT_LINES_COUNT, pGui->getMessagesCount(m_iPad) );
for( unsigned int i = 0; i < CHAT_LINES_COUNT; ++i )
DWORD totalMessages = pGui->getMessagesCount(m_iPad);
DWORD messagesToDisplay = min( CHAT_LINES_COUNT, totalMessages);
DWORD maxScroll = max(0, totalMessages - messagesToDisplay);
bool canScroll = messagesToDisplay < totalMessages;
int startIndex = (canScroll && isChatOpen ? ChatScreen::getChatIndex() : 0);
if (startIndex > maxScroll) {
ChatScreen::correctChatIndex(maxScroll);
startIndex = maxScroll;
}
app.DebugPrintf("handleTimerComplete: %d | %d | %d\n", maxScroll, startIndex, totalMessages);
for( unsigned int i = 0; i < messagesToDisplay; ++i )
{
float opacity = pGui->getOpacity(m_iPad, i);
if( opacity > 0 )
unsigned int msgIndex = startIndex + i;
float opacity = pGui->getOpacity(m_iPad, msgIndex);
if( opacity > 0 || isChatOpen)
{
#if 0 // def _WINDOWS64 // Use Iggy chat until Gui::render has visual parity
// Chat drawn by Gui::render with color codes. Hides Iggy chat to avoid double chats.
@ -804,9 +822,10 @@ void UIScene_HUD::handleTimerComplete(int id)
m_labelChatText[i].setOpacity(0);
m_labelChatText[i].setLabel(L"");
#else
m_controlLabelBackground[i].setOpacity(opacity);
m_labelChatText[i].setOpacity(opacity);
m_labelChatText[i].setLabel( pGui->getMessagesCount(m_iPad) ? pGui->getMessage(m_iPad,i) : L"" );
m_controlLabelBackground[i].setOpacity((isChatOpen ? 1 : opacity));
m_labelChatText[i].setOpacity((isChatOpen ? 1 : opacity));
m_labelChatText[i].setLabel(pGui->getMessage(m_iPad, msgIndex));
#endif
anyVisible = true;
}

View file

@ -11,7 +11,7 @@ private:
bool m_bSplitscreen;
protected:
UIControl_Label m_labelChatText[CHAT_LINES_COUNT];
UIControl_HTMLLabel m_labelChatText[CHAT_LINES_COUNT];
UIControl_Label m_labelJukebox;
UIControl m_controlLabelBackground[CHAT_LINES_COUNT];
UIControl_Label m_labelDisplayName;

View file

@ -86,7 +86,7 @@ void EntityTracker::addEntity(shared_ptr<Entity> e, int range, int updateInterva
{
assert(false); // Entity already tracked
}
if( e->entityId >= 2048 )
if( e->entityId >= 16384 )
{
__debugbreak();
}

View file

@ -404,6 +404,8 @@ void Font::draw(const wstring &str, bool dropShadow, int initialColor)
t->begin();
t->color(currentColor & 0x00ffffff, (currentColor >> 24) & 255);
bool prev = t->setMipmapEnable(false); // Disable mipmapping for fonts, and save previous enabled value to be restored later - Botch
for (int i = 0; i < static_cast<int>(cleanStr.length()); ++i)
{
// Map character
@ -481,6 +483,8 @@ void Font::draw(const wstring &str, bool dropShadow, int initialColor)
}
}
t->setMipmapEnable(prev); //Reinstates previously used enabled value - Botch
t->end();
}

View file

@ -1209,7 +1209,20 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
// Disable the depth test so the text shows on top of the paperdoll
glDisable(GL_DEPTH_TEST);
#ifdef _WINDOWS64
float scaleWidth = (g_rScreenWidth / 1920.0f);
float scaleHeight = (g_rScreenHeight / 1080.0f);
float scale = min(scaleWidth, scaleHeight); //stop stretching
if (scale < 0.5f) scale = 0.5f; // force minimum scale
if (scale > 1.2f) // resolutions over 1296 pixels tall
{
scale = scale - 0.33f; // tame overscaling on 1440p
}
glScalef(scale, scale, 1);
#endif
// Loop through the lines and draw them all on screen
int yPos = debugTop;
for (const auto &line : lines)
@ -1218,6 +1231,9 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
yPos += 10;
}
#ifdef _WINDOWS64
glScalef(1, 1, 1);
#endif
// Restore the depth test
glEnable(GL_DEPTH_TEST);
@ -1579,6 +1595,13 @@ float Gui::getOpacity(int iPad, DWORD index)
return opacityPercentage;
}
//just like java functionality it overwrites the jukebox label
void Gui::setActionBarMessage(wstring message)
{
overlayMessageString = message;
overlayMessageTime = 20 * 4; //idk how long it should last, need to check java usage
}
float Gui::getJukeboxOpacity(int iPad)
{
float t = overlayMessageTime - lastTickA;
@ -1594,7 +1617,7 @@ void Gui::setNowPlaying(const wstring& string)
// overlayMessageString = L"Now playing: " + string;
overlayMessageString = app.GetString(IDS_NOWPLAYING) + string;
overlayMessageTime = 20 * 3;
animateOverlayMessageColor = true;
animateOverlayMessageColor = true; //appears to be unused, @DrPerkyLegit plans to add in later pr
}
void Gui::displayClientMessage(int messageId, int iPad)

View file

@ -17,6 +17,7 @@ private:
static const int m_iMaxMessageWidth = 280;
static ItemRenderer *itemRenderer;
vector<GuiMessage> guiMessages[XUSER_MAX_COUNT];
int chatIndex = 0;
Random *random;
Minecraft *minecraft;
@ -63,6 +64,8 @@ public:
wstring getMessage(int iPad, DWORD index) { return guiMessages[iPad].at(index).string; }
float getOpacity(int iPad, DWORD index);
void setActionBarMessage(wstring message); //uses jukebox label
wstring getJukeboxMessage(int iPad) { return overlayMessageString; }
float getJukeboxOpacity(int iPad);

View file

@ -1544,8 +1544,12 @@ void Minecraft::run_middle()
// Utility keys always work regardless of KBM active state
if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_PAUSE) && !ui.GetMenuDisplayed(i))
{
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_PAUSEMENU;
app.DebugPrintf("PAUSE PRESSED (keyboard) - ipad = %d\n",i);
if (dynamic_cast<ChatScreen*>(getScreen()) != nullptr) {
setScreen(nullptr);
} else {
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_PAUSEMENU;
app.DebugPrintf("PAUSE PRESSED (keyboard) - ipad = %d\n",i);
}
}
if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_THIRD_PERSON))

View file

@ -972,7 +972,7 @@ void PlayerConnection::handleChat(shared_ptr<ChatPacket> packet)
}
#else
wstring formatted = L"<" + player->name + L"> " + message;
server->getPlayers()->broadcastAll(shared_ptr<ChatPacket>(new ChatPacket(formatted)));
server->getPlayers()->broadcastAll(shared_ptr<ChatPacket>(new ChatPacket(app.FormatChatMessage(formatted, false))));
#endif
chatSpamTickCount += SharedConstants::TICKS_PER_SECOND;
if (chatSpamTickCount > SharedConstants::TICKS_PER_SECOND * 10)

View file

@ -161,7 +161,7 @@ void Screen::updateEvents()
static bool s_arrowFirstRepeat[2] = { false, false };
const DWORD ARROW_REPEAT_DELAY_MS = 250;
const DWORD ARROW_REPEAT_INTERVAL_MS = 50;
DWORD now = GetTickCount();
DWORD now = GetTickCount64();
// Poll keyboard events (special keys that may not come through WM_CHAR, e.g. Escape, arrows)
for (int vk = 0; vk < 256; vk++)

View file

@ -295,12 +295,12 @@ void ServerPlayer::flagEntitiesToBeRemoved(unsigned int *flags, bool *removedFou
{
*removedFound = true;
// before this left 192 bytes uninitialized!!!!!
memset(flags, 0, (2048 / 32) * sizeof(unsigned int));
memset(flags, 0, (16384 / 32) * sizeof(unsigned int));
}
for(int index : entitiesToRemove)
{
if( index < 2048 )
if( index < 16384 )
{
unsigned int i = index / 32;
unsigned int j = index % 32;

View file

@ -78,7 +78,7 @@ void Settings::saveProperties()
if (!stream.is_open())
return;
stream << "# MinecraftConsoles dedicated server properties\r\n";
stream << "# LCE-Revelations 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));

View file

@ -13,11 +13,10 @@ SheepRenderer::SheepRenderer(Model *model, Model *armor, float shadow) : MobRend
int SheepRenderer::prepareArmor(shared_ptr<LivingEntity> _sheep, int layer, float a)
{
// 4J - dynamic cast required because we aren't using templates/generics in our version
shared_ptr<Sheep> sheep = dynamic_pointer_cast<Sheep>(_sheep);
if (layer == 0 && !sheep->isSheared() &&
!sheep->isInvisibleTo(Minecraft::GetInstance()->player)) // 4J-JEV: Todo, merge with java fix (for invisible sheep armour) in '1.7.5'.
!sheep->isInvisibleTo(Minecraft::GetInstance()->player))
{
MemSect(31);
bindTexture(&SHEEP_FUR_LOCATION);
@ -25,9 +24,8 @@ int SheepRenderer::prepareArmor(shared_ptr<LivingEntity> _sheep, int layer, floa
if (sheep->hasCustomName() && sheep->getCustomName().compare(L"jeb_") == 0)
{
// easter egg...
int colorDuration = 25;
int value = (sheep->tickCount / colorDuration);
int value = (sheep->tickCount / colorDuration) + sheep->entityId;
int c1 = value % Sheep::COLOR_LENGTH;
int c2 = (value + 1) % Sheep::COLOR_LENGTH;
float subStep = ((sheep->tickCount % colorDuration) + a) / static_cast<float>(colorDuration);
@ -64,4 +62,4 @@ void SheepRenderer::render(shared_ptr<Entity> mob, double x, double y, double z,
ResourceLocation *SheepRenderer::getTextureLocation(shared_ptr<Entity> mob)
{
return &SHEEP_LOCATION;
}
}

View file

@ -3,6 +3,8 @@
#include "stdafx.h"
#include <dxgi1_4.h> // IDXGISwapChain3 for SetColorSpace1
#include <assert.h>
#include <iostream>
#include <ShellScalingApi.h>
@ -58,6 +60,13 @@ extern Renderer InternalRenderManager;
#include "Xbox/Resource.h"
// request use of dedicated GPU from AMD and Nvidia drivers
extern "C"
{
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
}
#ifdef _MSC_VER
#pragma comment(lib, "legacy_stdio_definitions.lib")
#endif
@ -471,7 +480,6 @@ ID3D11Device* g_pd3dDevice = nullptr;
ID3D11DeviceContext* g_pImmediateContext = nullptr;
IDXGISwapChain* g_pSwapChain = nullptr;
bool g_bVSync = false;
static bool g_bTearingSupported = false;
static bool g_bPendingExclusiveFullscreen = false;
static bool g_bPendingExclusiveFullscreenValue = false;
@ -546,50 +554,15 @@ static bool TakeScreenshot(wstring& outFilename)
return success;
}
// COM proxy for IDXGISwapChain — delegates all calls to the real swap chain,
// but overrides Present() to set SyncInterval=1 when VSync is enabled.
// Avoids vtable patching, which conflicts with the D3D11 debug layer.
static class SwapChainVSyncProxy : public IDXGISwapChain
{
public:
void SetTarget(IDXGISwapChain* pReal) { m_pReal = pReal; }
// IUnknown
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override { return m_pReal->QueryInterface(riid, ppvObject); }
ULONG STDMETHODCALLTYPE AddRef() override { return m_pReal->AddRef(); }
ULONG STDMETHODCALLTYPE Release() override { return m_pReal->Release(); }
// IDXGIObject
HRESULT STDMETHODCALLTYPE SetPrivateData(REFGUID Name, UINT DataSize, const void* pData) override { return m_pReal->SetPrivateData(Name, DataSize, pData); }
HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(REFGUID Name, const IUnknown* pUnknown) override { return m_pReal->SetPrivateDataInterface(Name, pUnknown); }
HRESULT STDMETHODCALLTYPE GetPrivateData(REFGUID Name, UINT* pDataSize, void* pData) override { return m_pReal->GetPrivateData(Name, pDataSize, pData); }
HRESULT STDMETHODCALLTYPE GetParent(REFIID riid, void** ppParent) override { return m_pReal->GetParent(riid, ppParent); }
// IDXGIDeviceSubObject
HRESULT STDMETHODCALLTYPE GetDevice(REFIID riid, void** ppDevice) override { return m_pReal->GetDevice(riid, ppDevice); }
// IDXGISwapChain
// NOTE: The 4J RenderManager library hardcodes SyncInterval=1 and does NOT
// dispatch Present through this proxy's vtable. VSync control is handled
// directly in the main loop (see the Present-the-frame block) instead.
HRESULT STDMETHODCALLTYPE Present(UINT SyncInterval, UINT Flags) override { return m_pReal->Present(SyncInterval, Flags); }
HRESULT STDMETHODCALLTYPE GetBuffer(UINT Buffer, REFIID riid, void** ppSurface) override { return m_pReal->GetBuffer(Buffer, riid, ppSurface); }
HRESULT STDMETHODCALLTYPE SetFullscreenState(BOOL Fullscreen, IDXGIOutput* pTarget) override { return m_pReal->SetFullscreenState(Fullscreen, pTarget); }
HRESULT STDMETHODCALLTYPE GetFullscreenState(BOOL* pFullscreen, IDXGIOutput** ppTarget) override { return m_pReal->GetFullscreenState(pFullscreen, ppTarget); }
HRESULT STDMETHODCALLTYPE GetDesc(DXGI_SWAP_CHAIN_DESC* pDesc) override { return m_pReal->GetDesc(pDesc); }
HRESULT STDMETHODCALLTYPE ResizeBuffers(UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags) override { return m_pReal->ResizeBuffers(BufferCount, Width, Height, NewFormat, SwapChainFlags); }
HRESULT STDMETHODCALLTYPE ResizeTarget(const DXGI_MODE_DESC* pNewTargetParameters) override { return m_pReal->ResizeTarget(pNewTargetParameters); }
HRESULT STDMETHODCALLTYPE GetContainingOutput(IDXGIOutput** ppOutput) override { return m_pReal->GetContainingOutput(ppOutput); }
HRESULT STDMETHODCALLTYPE GetFrameStatistics(DXGI_FRAME_STATISTICS* pStats) override { return m_pReal->GetFrameStatistics(pStats); }
HRESULT STDMETHODCALLTYPE GetLastPresentCount(UINT* pLastPresentCount) override { return m_pReal->GetLastPresentCount(pLastPresentCount); }
private:
IDXGISwapChain* m_pReal = nullptr;
} g_swapChainProxy;
ID3D11RenderTargetView* g_pRenderTargetView = nullptr;
ID3D11DepthStencilView* g_pDepthStencilView = nullptr;
ID3D11Texture2D* g_pDepthStencilBuffer = nullptr;
static const float kClearColorWhite[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
static const float kClearColorBlack[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
// True when the swap chain is in DXGI exclusive fullscreen. Lets ResizeD3D
// skip its destroy-and-recreate path, which would break exclusive ownership.
static bool g_bDxgiExclusiveFullscreen = false;
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
@ -937,33 +910,30 @@ HRESULT InitDevice()
};
UINT numFeatureLevels = ARRAYSIZE( featureLevels );
// Check tearing support before device/swap chain creation
{
IDXGIFactory5* factory5 = nullptr;
if (SUCCEEDED(CreateDXGIFactory1(__uuidof(IDXGIFactory5), (void**)&factory5)))
{
BOOL allowTearing = FALSE;
if (SUCCEEDED(factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing, sizeof(allowTearing))))
g_bTearingSupported = (allowTearing == TRUE);
factory5->Release();
}
}
// Use the legacy bitblt DISCARD swap model (SwapEffect left as default 0).
// DXGI_SWAP_EFFECT_FLIP_DISCARD gives lower latency and tearing support, but
// takes exclusive ownership of the HWND — which makes window resize via
// CreateSwapChain fail with E_ACCESSDENIED and ResizeBuffers fail with
// DXGI_ERROR_INVALID_CALL (the closed-source 4J Renderer holds hidden
// backbuffer refs we can't release). Bitblt DISCARD has no HWND lock, so
// the "destroy old, create new" resize path in ResizeD3D() works cleanly.
// VSync toggle still works via the SyncInterval parameter on Present().
// RefreshRate=0/0 so DXGI matches the current display mode. Hardcoding a
// rate would force a mode switch on SetFullscreenState, which can produce
// "input signal out of range" errors on high-refresh monitors.
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory( &sd, sizeof( sd ) );
sd.BufferCount = 2;
sd.BufferCount = 1;
sd.BufferDesc.Width = width;
sd.BufferDesc.Height = height;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 0;
sd.BufferDesc.RefreshRate.Denominator = 0;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;
sd.OutputWindow = g_hWnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;
sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
sd.Flags = g_bTearingSupported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0;
for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
{
@ -1024,8 +994,7 @@ HRESULT InitDevice()
vp.TopLeftY = 0;
g_pImmediateContext->RSSetViewports( 1, &vp );
g_swapChainProxy.SetTarget(g_pSwapChain);
RenderManager.Initialise(g_pd3dDevice, &g_swapChainProxy);
RenderManager.Initialise(g_pd3dDevice, g_pSwapChain);
PostProcesser::GetInstance().Init();
@ -1041,7 +1010,7 @@ void Render()
const float ClearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f }; //red,green,blue,alpha
g_pImmediateContext->ClearRenderTargetView( g_pRenderTargetView, ClearColor );
g_pSwapChain->Present(0, g_bTearingSupported ? DXGI_PRESENT_ALLOW_TEARING : 0);
g_pSwapChain->Present(0, 0);
}
//--------------------------------------------------------------------------------------
@ -1052,6 +1021,9 @@ static bool ResizeD3D(int newW, int newH)
if (newW <= 0 || newH <= 0) return false;
if (!g_pSwapChain) return false;
if (!g_bResizeReady) return false;
// In exclusive fullscreen the swap chain must not be recreated.
if (g_bDxgiExclusiveFullscreen)
return false;
int bbW = newW;
int bbH = newH;
@ -1081,11 +1053,11 @@ static bool ResizeD3D(int newW, int newH)
// Verify offsets by checking device and swap chain pointers
ID3D11Device** ppRM_Device = (ID3D11Device**)(pRM + 0x10);
if (*ppRM_Device != g_pd3dDevice || *ppRM_SC != (IDXGISwapChain*)&g_swapChainProxy)
if (*ppRM_Device != g_pd3dDevice || *ppRM_SC != g_pSwapChain)
{
app.DebugPrintf("[RESIZE] ERROR: RenderManager offset verification failed! "
"device=%p (expected %p) swapchain=%p (expected %p)\n",
*ppRM_Device, g_pd3dDevice, *ppRM_SC, (IDXGISwapChain*)&g_swapChainProxy);
*ppRM_Device, g_pd3dDevice, *ppRM_SC, g_pSwapChain);
return false;
}
@ -1116,61 +1088,63 @@ static bool ResizeD3D(int newW, int newH)
gdraw_D3D11_PreReset();
// Get IDXGIFactory from the existing device BEFORE destroying the old swap
// chain. If anything fails before we have a new swap chain, we abort
// without destroying the old one — leaving the Renderer in a valid
// (old-size) state.
IDXGISwapChain* pOldSwapChain = g_pSwapChain;
bool success = false;
HRESULT hr;
// Create a brand-new swap chain instead of ResizeBuffers.
// ResizeBuffers requires ALL backbuffer refs released, but the closed-source
// Renderer holds hidden refs we can't track — causing DXGI_ERROR_INVALID_CALL
// and leaving the Renderer with NULL views (black screen).
// Creating a new swap chain orphans the old backbuffer (tiny leak) but avoids
// the need to release every hidden reference.
{
IDXGIDevice* dxgiDevice = NULL;
IDXGIAdapter* dxgiAdapter = NULL;
IDXGIFactory* dxgiFactory = NULL;
hr = g_pd3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice);
if (FAILED(hr)) goto postReset;
hr = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&dxgiAdapter);
dxgiDevice->Release();
if (FAILED(hr)) goto postReset;
hr = dxgiAdapter->GetParent(__uuidof(IDXGIFactory), (void**)&dxgiFactory);
dxgiAdapter->Release();
if (FAILED(hr)) goto postReset;
IDXGIDevice* dxgiDevice = NULL;
IDXGIAdapter* dxgiAdapter = NULL;
IDXGIFactory* dxgiFactory = NULL;
hr = g_pd3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice);
if (FAILED(hr)) goto postReset;
hr = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&dxgiAdapter);
if (FAILED(hr)) { dxgiDevice->Release(); goto postReset; }
hr = dxgiAdapter->GetParent(__uuidof(IDXGIFactory), (void**)&dxgiFactory);
dxgiAdapter->Release();
dxgiDevice->Release();
if (FAILED(hr)) goto postReset;
// Create a brand-new swap chain at the target size and swap it in.
// Must use the SAME swap-chain config as InitDevice (legacy bitblt
// DISCARD model), otherwise DXGI may return E_ACCESSDENIED. The
// Renderer's old RTV/SRV/DSV are intentionally NOT released here — they
// become orphaned with the old swap chain (tiny leak, but avoids
// fighting unknown refs inside the closed-source Renderer library).
{
DXGI_SWAP_CHAIN_DESC sd = {};
sd.BufferCount = 2;
sd.BufferCount = 1;
sd.BufferDesc.Width = bbW;
sd.BufferDesc.Height = bbH;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
// RefreshRate=0/0 matches InitDevice; see comment there.
sd.BufferDesc.RefreshRate.Numerator = 0;
sd.BufferDesc.RefreshRate.Denominator = 0;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;
sd.OutputWindow = g_hWnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;
sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
sd.Flags = g_bTearingSupported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0;
IDXGISwapChain* pNewSwapChain = NULL;
hr = dxgiFactory->CreateSwapChain(g_pd3dDevice, &sd, &pNewSwapChain);
dxgiFactory->Release();
if (FAILED(hr))
if (FAILED(hr) || pNewSwapChain == NULL)
{
app.DebugPrintf("[RESIZE] CreateSwapChain FAILED hr=0x%08X\n", (unsigned)hr);
app.DebugPrintf("[RESIZE] CreateSwapChain FAILED hr=0x%08X — keeping old swap chain\n", (unsigned)hr);
goto postReset;
}
// Destroy old, install new
// New swap chain created successfully — NOW destroy the old one.
pOldSwapChain->Release();
g_pSwapChain = pNewSwapChain;
g_swapChainProxy.SetTarget(g_pSwapChain);
}
// Patch Renderer's swap chain pointer (use proxy so VSync override stays active)
*ppRM_SC = &g_swapChainProxy;
// Patch Renderer's swap chain pointer to the new raw swap chain.
*ppRM_SC = g_pSwapChain;
// Create render target views from new backbuffer
{
@ -1364,18 +1338,99 @@ void SetExclusiveFullscreen(bool enabled)
g_bPendingExclusiveFullscreenValue = enabled;
}
// Uses borderless fullscreen (ToggleFullscreen) rather than DXGI SetFullscreenState.
// With DXGI_SWAP_EFFECT_FLIP_DISCARD + DXGI_PRESENT_ALLOW_TEARING, borderless
// fullscreen gets the same direct-flip path as exclusive fullscreen on Windows 10+ —
// identical latency and uncapped FPS. True DXGI exclusive fullscreen is blocked by
// the 4J Renderer holding hidden backbuffer references that prevent ResizeBuffers.
// Enter or leave true DXGI exclusive fullscreen. With our bitblt swap chain,
// Present(SyncInterval=0) in exclusive mode produces real screen tearing via
// direct scanout (DWM is out of the pipeline). Flip mode with ALLOW_TEARING
// would also work but is blocked by the 4J Renderer's deferred context refs
// on the backbuffer, which DXGI's ResizeBuffers cannot release.
static void ApplyExclusiveFullscreen(bool enabled)
{
// Toggle into/out of borderless fullscreen if state doesn't match
if (enabled && !g_isFullscreen)
ToggleFullscreen();
else if (!enabled && g_isFullscreen)
ToggleFullscreen();
if (!g_pSwapChain)
return;
LONG styleBefore = GetWindowLong(g_hWnd, GWL_STYLE);
if (enabled)
{
// Grow the window to cover the monitor first. This fires WM_SIZE which
// runs ResizeD3D and recreates the backbuffer at monitor-native size.
// Otherwise a small windowed backbuffer would enter exclusive fullscreen
// at that smaller size and DXGI would scale it to fill the monitor,
// producing a filtered / washed-out look.
HMONITOR hMon = MonitorFromWindow(g_hWnd, MONITOR_DEFAULTTOPRIMARY);
MONITORINFO mi = {};
mi.cbSize = sizeof(mi);
if (GetMonitorInfo(hMon, &mi))
{
int monW = mi.rcMonitor.right - mi.rcMonitor.left;
int monH = mi.rcMonitor.bottom - mi.rcMonitor.top;
SetWindowLong(g_hWnd, GWL_STYLE, (styleBefore & ~WS_OVERLAPPEDWINDOW) | WS_VISIBLE);
SetWindowPos(g_hWnd, HWND_TOP,
mi.rcMonitor.left, mi.rcMonitor.top, monW, monH,
SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
// ResizeTarget pins the display mode to the backbuffer size with
// no scaling. Microsoft's pattern is ResizeTarget then
// SetFullscreenState then ResizeTarget again (see below).
DXGI_MODE_DESC targetMode = {};
targetMode.Width = monW;
targetMode.Height = monH;
targetMode.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
targetMode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
targetMode.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
g_pSwapChain->ResizeTarget(&targetMode);
}
}
HRESULT hr = g_pSwapChain->SetFullscreenState(enabled ? TRUE : FALSE, nullptr);
if (FAILED(hr))
return;
g_bDxgiExclusiveFullscreen = enabled;
if (enabled)
{
// Explicitly declare sRGB. Default for R8G8B8A8_UNORM but some drivers
// behave differently if the color space is never set.
IDXGISwapChain3* pSwapChain3 = nullptr;
if (SUCCEEDED(g_pSwapChain->QueryInterface(__uuidof(IDXGISwapChain3), (void**)&pSwapChain3)) && pSwapChain3)
{
UINT colorSpaceSupport = 0;
pSwapChain3->CheckColorSpaceSupport(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, &colorSpaceSupport);
if (colorSpaceSupport & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)
pSwapChain3->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709);
pSwapChain3->Release();
}
// Second ResizeTarget per Microsoft's recommendation to make the mode stick.
HMONITOR hMon2 = MonitorFromWindow(g_hWnd, MONITOR_DEFAULTTOPRIMARY);
MONITORINFO mi2 = {};
mi2.cbSize = sizeof(mi2);
if (GetMonitorInfo(hMon2, &mi2))
{
DXGI_MODE_DESC targetMode2 = {};
targetMode2.Width = mi2.rcMonitor.right - mi2.rcMonitor.left;
targetMode2.Height = mi2.rcMonitor.bottom - mi2.rcMonitor.top;
targetMode2.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
targetMode2.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
targetMode2.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
g_pSwapChain->ResizeTarget(&targetMode2);
}
g_isFullscreen = true;
}
else
{
// Force a real decorated windowed state on exit. DXGI would otherwise
// restore whatever state the window had before SetFullscreenState,
// which may still be borderless.
SetWindowLong(g_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE);
const int w = 1280, h = 720;
const int sw = GetSystemMetrics(SM_CXSCREEN);
const int sh = GetSystemMetrics(SM_CYSCREEN);
SetWindowPos(g_hWnd, HWND_TOP, (sw - w) / 2, (sh - h) / 2, w, h,
SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
g_isFullscreen = false;
}
}
//--------------------------------------------------------------------------------------
@ -1610,10 +1665,13 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
return 0;
}
// Restore fullscreen state from previous session
if (LoadFullscreenOption() && !g_isFullscreen || launchOptions.fullscreen)
// Restore fullscreen state from previous session. Route through the
// deferred exclusive fullscreen path so the main loop applies it on the
// first tick (safer than transitioning during init).
if ((LoadFullscreenOption() && !g_isFullscreen) || launchOptions.fullscreen)
{
ToggleFullscreen();
g_bPendingExclusiveFullscreen = true;
g_bPendingExclusiveFullscreenValue = true;
}
#if 0
@ -1722,7 +1780,14 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
continue;
}
const float* clearColor = app.GetGameStarted() ? kClearColorBlack : kClearColorWhite;
RenderManager.SetClearColour(clearColor);
RenderManager.StartFrame();
if (!app.GetGameStarted())
{
RenderManager.SetClearColour(kClearColorWhite); // set intro scene background to white
RenderManager.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
#if 0
if(pMinecraft->soundEngine->isStreamingWavebankReady() &&
!pMinecraft->soundEngine->isPlayingStreamingGameMusic() &&
@ -1904,13 +1969,13 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
RenderManager.Set_matrixDirty();
#endif
// Present the frame.
// RenderManager.Present() hardcodes SyncInterval=1 internally.
// When VSync is off, bypass it and call the swap chain directly.
if (!g_bVSync && g_bTearingSupported && g_pSwapChain)
// Present the frame. RenderManager.Present() hardcodes SyncInterval=1,
// so when VSync is off we bypass it for uncapped frames. In DXGI
// exclusive fullscreen this produces real tearing via direct scanout.
if (!g_bVSync && g_pSwapChain)
{
HRESULT hrPresent = g_pSwapChain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
// If tearing Present fails (e.g. during fullscreen transition),
HRESULT hrPresent = g_pSwapChain->Present(0, 0);
// If the direct Present fails (e.g. during fullscreen transition),
// fall back to the library's VSync'd Present for this frame.
if (FAILED(hrPresent))
RenderManager.Present();
@ -2008,11 +2073,11 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
}
#endif
// toggle fullscreen
// toggle fullscreen (DXGI exclusive via ApplyExclusiveFullscreen)
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_FULLSCREEN))
{
ToggleFullscreen();
app.SetGameSettings(ProfileManager.GetPrimaryPad(), eGameSetting_ExclusiveFullscreen, g_isFullscreen ? 1 : 0);
ApplyExclusiveFullscreen(!g_bDxgiExclusiveFullscreen);
app.SetGameSettings(ProfileManager.GetPrimaryPad(), eGameSetting_ExclusiveFullscreen, g_bDxgiExclusiveFullscreen ? 1 : 0);
}
// Apply deferred exclusive fullscreen toggle

View file

@ -1,12 +1,5 @@
set(BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Windows64/")
set(_MINECRAFT_CLIENT_WINDOWS_COMMON_RES_AUDIO
"${CMAKE_CURRENT_SOURCE_DIR}/Common/res/audio/minecraft.xsb"
"${CMAKE_CURRENT_SOURCE_DIR}/Common/res/audio/resident.xwb"
"${CMAKE_CURRENT_SOURCE_DIR}/Common/res/audio/streamed.xwb"
)
source_group("Common/res/audio" FILES ${_MINECRAFT_CLIENT_WINDOWS_COMMON_RES_AUDIO})
set(_MINECRAFT_CLIENT_WINDOWS_COMMON_AUDIO
"${CMAKE_CURRENT_SOURCE_DIR}/Common/Audio/SoundEngine.cpp"
)
@ -479,7 +472,6 @@ set(_MINECRAFT_CLIENT_WINDOWS_NET_MINECRAFT_STATS
source_group("net/minecraft/stats" FILES ${_MINECRAFT_CLIENT_WINDOWS_NET_MINECRAFT_STATS})
set(MINECRAFT_CLIENT_WINDOWS
${_MINECRAFT_CLIENT_WINDOWS_COMMON_RES_AUDIO}
${_MINECRAFT_CLIENT_WINDOWS_COMMON_AUDIO}
${_MINECRAFT_CLIENT_WINDOWS_COMMON_NETWORK}
${_MINECRAFT_CLIENT_WINDOWS_COMMON_UI}

View file

@ -735,7 +735,7 @@ int main(int argc, char **argv)
break;
}
if (autosaveRequested && app.GetXuiServerAction(kServerActionPad) == eXuiServerAction_Idle)
if (autosaveRequested && app.GetXuiServerAction(kServerActionPad) == eXuiServerAction_Idle && !ConsoleSaveFileOriginal::hasPendingBackgroundSave())
{
LogWorldIO("autosave completed");
autosaveRequested = false;
@ -749,7 +749,7 @@ int main(int argc, char **argv)
DWORD now = GetTickCount();
if ((LONG)(now - nextAutosaveTick) >= 0)
{
if (app.GetXuiServerAction(kServerActionPad) == eXuiServerAction_Idle)
if (app.GetXuiServerAction(kServerActionPad) == eXuiServerAction_Idle && !ConsoleSaveFileOriginal::hasPendingBackgroundSave())
{
LogWorldIO("requesting autosave");
app.SetXuiServerAction(kServerActionPad, eXuiServerAction_AutoSaveGame);
@ -768,16 +768,18 @@ int main(int argc, char **argv)
LogInfof("shutdown", "Dedicated server stopped");
MinecraftServer *server = MinecraftServer::getInstance();
if (server != NULL)
{
server->setSaveOnExit(true);
}
if (server != NULL)
if (server != NULL && !ConsoleSaveFileOriginal::hasPendingBackgroundSave())
{
server->setSaveOnExit(true);
LogWorldIO("requesting save before shutdown");
LogWorldIO("using saveOnExit for shutdown");
}
if (ConsoleSaveFileOriginal::hasPendingBackgroundSave())
{
LogWorldIO("Waiting for autosave to complete...");
}
MinecraftServer::HaltServer();
if (g_NetworkManager.ServerStoppedValid())

View file

@ -515,6 +515,7 @@ set(_MINECRAFT_SERVER_COMMON_ROOT
"${_MS_SRC}/../Minecraft.Client/iob_shim.asm"
"${_MS_SRC}/../Minecraft.Client/stdafx.cpp"
"${_MS_SRC}/../Minecraft.Client/stubs.cpp"
"${_MS_SRC}/../Minecraft.World/Entity.cpp"
"${_MS_SRC}/../Minecraft.World/AbstractContainerMenu.cpp"
"${_MS_SRC}/../Minecraft.World/CompoundContainer.h"
"${_MS_SRC}/../Minecraft.World/ItemEntity.cpp"

View file

@ -98,6 +98,7 @@ public:
e_ChatCommandTeleportMe,
e_ChatCommandTeleportToMe,
e_ChatActionBar,
};
public:

View file

@ -124,6 +124,7 @@ DamageSource::DamageSource(ChatPacket::EChatPacketMessage msgId, ChatPacket::ECh
_isProjectile = false;
_isMagic = false;
_isExplosion = false;
_isCritical = false;
//this->msgId = msgId;
m_msgId = msgId;
@ -153,7 +154,15 @@ DamageSource *DamageSource::bypassInvul()
_bypassInvul = true;
return this;
}
bool DamageSource::isCritical()
{
return _isCritical;
}
DamageSource *DamageSource::setIsCritical()
{
_isCritical = true;
return this;
}
DamageSource *DamageSource::setIsFire()
{
isFireSource = true;

View file

@ -47,8 +47,11 @@ private:
bool _scalesWithDifficulty;
bool _isMagic;
bool _isExplosion;
bool _isCritical;
public:
bool isCritical();
DamageSource *setIsCritical();
bool isProjectile();
DamageSource *setProjectile();
bool isExplosion();

View file

@ -27,13 +27,17 @@
const wstring Entity::RIDING_TAG = L"Riding";
int Entity::entityCounter = 2048; // 4J - changed initialiser to 2048, as we are using range 0 - 2047 as special unique smaller ids for things that need network tracked
//int Entity::entityCounter = 2048; // 4J - changed initialiser to 2048, as we are using range 0 - 2047 as special unique smaller ids for things that need network tracked
int Entity::entityCounter = 16384; //now using full range of 0 - 16383, limit is 32k but we shouldnt need that yet
DWORD Entity::tlsIdx = TlsAlloc();
// 4J - added getSmallId & freeSmallId methods
unsigned int Entity::entityIdUsedFlags[2048/32] = {0};
unsigned int Entity::entityIdWanderFlags[2048/32] = {0};
unsigned int Entity::entityIdRemovingFlags[2048/32] = {0};
//unsigned int Entity::entityIdUsedFlags[2048/32] = {0};
//unsigned int Entity::entityIdWanderFlags[2048/32] = {0};
//unsigned int Entity::entityIdRemovingFlags[2048/32] = {0};
unsigned int Entity::entityIdUsedFlags[16384/32] = {0};
unsigned int Entity::entityIdWanderFlags[16384/32] = {0};
unsigned int Entity::entityIdRemovingFlags[16384/32] = {0};
int Entity::extraWanderIds[EXTRA_WANDER_MAX] = {0};
int Entity::extraWanderTicks = 0;
int Entity::extraWanderCount = 0;
@ -65,7 +69,7 @@ int Entity::getSmallId()
}
}
for( int i = 0; i < (2048 / 32 ); i++ )
for( int i = 0; i < (16384 / 32 ); i++ )
{
unsigned int uiFlags = *puiUsedFlags;
if( uiFlags != 0xffffffff )
@ -102,7 +106,7 @@ int Entity::getSmallId()
if (entityCounter == 0x7ffffff)
{
entityCounter = 2048;
entityCounter = 16384;
}
return fallbackId;
#else
@ -116,7 +120,7 @@ void Entity::countFlagsForPIX()
{
int freecount = 0;
unsigned int *puiUsedFlags = entityIdUsedFlags;
for( int i = 0; i < (2048 / 32 ); i++ )
for( int i = 0; i < (16384 / 32 ); i++ )
{
unsigned int uiFlags = *puiUsedFlags;
if( uiFlags != 0xffffffff )
@ -134,7 +138,7 @@ void Entity::countFlagsForPIX()
puiUsedFlags++;
}
PIXAddNamedCounter(freecount,"Small Ids free");
PIXAddNamedCounter(2048 - freecount,"Small Ids used");
PIXAddNamedCounter(16384 - freecount,"Small Ids used");
}
void Entity::resetSmallId()
@ -149,7 +153,7 @@ void Entity::resetSmallId()
void Entity::freeSmallId(int index)
{
if( ( (size_t)TlsGetValue(tlsIdx) ) == 0 ) return; // Don't do anything with small ids if this isn't the server thread
if( index >= 2048 ) return; // Don't do anything if this isn't a short id
if( index >= 16384 ) return; // Don't do anything if this isn't a short id
unsigned int i = index / 32;
unsigned int j = index % 32;
@ -172,7 +176,7 @@ void Entity::useSmallIds()
void Entity::considerForExtraWandering(bool enable)
{
if( ( (size_t)TlsGetValue(tlsIdx) ) == 0 ) return; // Don't do anything with small ids if this isn't the server thread
if( entityId >= 2048 ) return; // Don't do anything if this isn't a short id
if( entityId >= 16384 ) return; // Don't do anything if this isn't a short id
unsigned int i = entityId / 32;
unsigned int j = entityId % 32;
@ -192,7 +196,7 @@ void Entity::considerForExtraWandering(bool enable)
bool Entity::isExtraWanderingEnabled()
{
if( ( (size_t)TlsGetValue(tlsIdx) ) == 0 ) return false; // Don't do anything with small ids if this isn't the server thread
if( entityId >= 2048 ) return false; // Don't do anything if this isn't a short id
if( entityId >= 16384 ) return false; // Don't do anything if this isn't a short id
for( int i = 0; i < extraWanderCount; i++ )
{
@ -224,12 +228,12 @@ void Entity::tickExtraWandering()
int entityId = 0;
if( extraWanderCount )
{
entityId = ( extraWanderIds[ extraWanderCount - 1 ] + 1 ) % 2048;
entityId = ( extraWanderIds[ extraWanderCount - 1 ] + 1 ) % 16384;
}
extraWanderCount = 0;
for( int k = 0; ( k < 2048 ) && ( extraWanderCount < EXTRA_WANDER_MAX); k++ )
for( int k = 0; ( k < 16384 ) && ( extraWanderCount < EXTRA_WANDER_MAX); k++ )
{
unsigned int i = entityId / 32;
unsigned int j = entityId % 32;
@ -241,7 +245,7 @@ void Entity::tickExtraWandering()
// printf("%d, ", entityId);
}
entityId = ( entityId + 1 ) % 2048;
entityId = ( entityId + 1 ) % 16384;
}
// printf("\n");
}
@ -261,7 +265,7 @@ void Entity::_init(bool useSmallId, Level *level)
else
{
entityId = Entity::entityCounter++;
if(entityCounter == 0x7ffffff ) entityCounter = 2048;
if(entityCounter == 0x7ffffff ) entityCounter = 16384;
}
viewScale = 1.0;

View file

@ -382,9 +382,12 @@ private:
int getSmallId();
void freeSmallId(int index);
static unsigned int entityIdUsedFlags[2048/32];
static unsigned int entityIdWanderFlags[2048/32];
static unsigned int entityIdRemovingFlags[2048/32];
//static unsigned int entityIdUsedFlags[2048/32];
//static unsigned int entityIdWanderFlags[2048/32];
//static unsigned int entityIdRemovingFlags[2048/32];
static unsigned int entityIdUsedFlags[16384/32];
static unsigned int entityIdWanderFlags[16384/32];
static unsigned int entityIdRemovingFlags[16384/32];
static int extraWanderIds[EXTRA_WANDER_MAX];
static int extraWanderCount;
static int extraWanderTicks;

View file

@ -5,6 +5,9 @@ class EntityEvent
public:
static const BYTE JUMP = 1;
static const BYTE HURT = 2;
//New
static const BYTE HURT_CRITICAL = 19;
static const BYTE DEATH_CRITICAL = 20;
static const BYTE DEATH = 3;
static const BYTE START_ATTACKING = 4;
static const BYTE STOP_ATTACKING = 5;

View file

@ -881,7 +881,12 @@ bool LivingEntity::hurt(DamageSource *source, float dmg)
if (sound)
{
level->broadcastEntityEvent(shared_from_this(), EntityEvent::HURT);
if (source->isCritical()) {
level->broadcastEntityEvent(shared_from_this(), EntityEvent::HURT_CRITICAL);
}
else {
level->broadcastEntityEvent(shared_from_this(), EntityEvent::HURT);
}
if (source != DamageSource::drown) markHurt();
if (sourceEntity != nullptr)
{
@ -904,12 +909,19 @@ bool LivingEntity::hurt(DamageSource *source, float dmg)
MemSect(31);
if (getHealth() <= 0)
{
if (sound) playSound(getDeathSound(), getSoundVolume(), getVoicePitch());
if (sound) {
//New: both death AND hurt sounds should play critical sound as well.
if (source->isCritical()) playSound(getCriticalSound(), getSoundVolume(), getVoicePitch());
playSound(getDeathSound(), getSoundVolume(), getVoicePitch());
};
die(source);
}
else
{
if (sound) playSound(getHurtSound(), getSoundVolume(), getVoicePitch());
if (sound) {
if (source->isCritical()) playSound(getCriticalSound(), getSoundVolume(), getVoicePitch());
playSound(getHurtSound(), getSoundVolume(), getVoicePitch());
}
}
MemSect(0);
@ -988,7 +1000,11 @@ void LivingEntity::die(DamageSource *source)
}
}
level->broadcastEntityEvent(shared_from_this(), EntityEvent::DEATH);
if (source->isCritical()) {
level->broadcastEntityEvent(shared_from_this(), EntityEvent::DEATH_CRITICAL);
} else {
level->broadcastEntityEvent(shared_from_this(), EntityEvent::DEATH);
}
}
void LivingEntity::dropEquipment(bool byPlayer, int playerBonusLevel)
@ -1021,7 +1037,10 @@ int LivingEntity::getHurtSound()
{
return eSoundType_DAMAGE_HURT;
}
int LivingEntity::getCriticalSound()
{
return eSoundType_DAMAGE_CRITICAL;
}
int LivingEntity::getDeathSound()
{
return eSoundType_DAMAGE_HURT;
@ -1265,7 +1284,8 @@ void LivingEntity::swing()
void LivingEntity::handleEntityEvent(byte id)
{
if (id == EntityEvent::HURT)
//These gotta be in parentheses
if ((id == EntityEvent::HURT) || (id == EntityEvent::HURT_CRITICAL))
{
walkAnimSpeed = 1.5f;
@ -1275,19 +1295,30 @@ void LivingEntity::handleEntityEvent(byte id)
MemSect(31);
// 4J-PB -added because villagers have no sounds
int iHurtSound=getHurtSound();
int iHurtSound = getHurtSound();
int iCritSound = getCriticalSound();
if(iHurtSound!=-1)
{
playSound(iHurtSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);
}
if(iCritSound!=-1 && (id == EntityEvent::HURT_CRITICAL))
{
playSound(iCritSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);
}
MemSect(0);
hurt(DamageSource::genericSource, 0);
}
else if (id == EntityEvent::DEATH)
else if ((id == EntityEvent::DEATH) || (id == EntityEvent::DEATH_CRITICAL))
{
MemSect(31);
// 4J-PB -added because villagers have no sounds
int iDeathSound=getDeathSound();
int iCritSound = getCriticalSound();
if (iCritSound != -1 && (id == EntityEvent::DEATH_CRITICAL))
{
playSound(iCritSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);
}
if(iDeathSound!=-1)
{
playSound(iDeathSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);

View file

@ -190,6 +190,7 @@ public:
virtual void knockback(shared_ptr<Entity> source, float dmg, double xd, double zd);
protected:
virtual int getCriticalSound();
virtual int getHurtSound();
virtual int getDeathSound();

View file

@ -35,7 +35,7 @@ void MoveEntityPacket::read(DataInputStream *dis) //throws IOException
void MoveEntityPacket::write(DataOutputStream *dos) //throws IOException
{
if( (id < 0 ) || (id >= 2048 ) )
if( (id < 0 ) || (id >= 16384 ) )
{
// We shouln't be tracking an entity that doesn't have a short type of id
__debugbreak();

View file

@ -19,7 +19,7 @@ MoveEntityPacketSmall::MoveEntityPacketSmall()
MoveEntityPacketSmall::MoveEntityPacketSmall(int id)
{
if( (id < 0 ) || (id >= 2048 ) )
if( (id < 0 ) || (id >= 16384 ) )
{
// We shouln't be tracking an entity that doesn't have a short type of id
__debugbreak();
@ -42,7 +42,7 @@ void MoveEntityPacketSmall::read(DataInputStream *dis) //throws IOException
void MoveEntityPacketSmall::write(DataOutputStream *dos) //throws IOException
{
if( (id < 0 ) || (id >= 2048 ) )
if( (id < 0 ) || (id >= 16384 ) )
{
// We shouln't be tracking an entity that doesn't have a short type of id
__debugbreak();
@ -99,7 +99,7 @@ void MoveEntityPacketSmall::PosRot::read(DataInputStream *dis) //throws IOExcept
void MoveEntityPacketSmall::PosRot::write(DataOutputStream *dos) //throws IOException
{
if( (id < 0 ) || (id >= 2048 ) )
if( (id < 0 ) || (id >= 16384 ) )
{
// We shouln't be tracking an entity that doesn't have a short type of id
__debugbreak();
@ -138,7 +138,7 @@ void MoveEntityPacketSmall::Pos::read(DataInputStream *dis) //throws IOException
void MoveEntityPacketSmall::Pos::write(DataOutputStream *dos) //throws IOException
{
if( (id < 0 ) || (id >= 2048 ) )
if( (id < 0 ) || (id >= 16384 ) )
{
// We shouln't be tracking an entity that doesn't have a short type of id
__debugbreak();
@ -176,7 +176,7 @@ void MoveEntityPacketSmall::Rot::read(DataInputStream *dis) //throws IOException
void MoveEntityPacketSmall::Rot::write(DataOutputStream *dos) //throws IOException
{
if( (id < 0 ) || (id >= 2048 ) )
if( (id < 0 ) || (id >= 16384 ) )
{
// We shouln't be tracking an entity that doesn't have a short type of id
__debugbreak();

View file

@ -1635,6 +1635,10 @@ void Player::attack(shared_ptr<Entity> entity)
}
DamageSource *damageSource = DamageSource::playerAttack(dynamic_pointer_cast<Player>(shared_from_this()));
if (bCrit) {
damageSource->setIsCritical();
}
bool wasHurt = entity->hurt(damageSource, dmg);
delete damageSource;
if (wasHurt)

View file

@ -20,7 +20,8 @@ class SharedConstants
static wstring readAcceptableChars();
public:
static const int maxChatLength = 100;
static const int maxChatLength = 255;
static const int maxVisibleLength = 100; //to be changed
static wstring acceptableLetters;
static const int ILLEGAL_FILE_CHARACTERS_LENGTH = 15;

View file

@ -493,6 +493,10 @@ void Socket::SocketOutputStreamNetwork::writeWithFlags(byteArray b, unsigned int
}
else
{
// Don't write on a closing socket: an orphan whose smallId has been
// recycled would otherwise leak a packet onto the new client.
if( m_socket->isClosing() ) return;
XRNM_SEND_BUFFER buffer;
buffer.pbyData = &b[offset];
buffer.dwDataSize = length;

View file

@ -260,6 +260,8 @@ enum eSOUND_TYPE
eSoundType_ITEM_ARMOR_equipGeneric5,
eSoundType_ITEM_ARMOR_equipGeneric6,
eSoundType_DAMAGE_CRITICAL,
eSoundType_MAX
};

View file

@ -16,7 +16,7 @@ $ErrorActionPreference = "Stop"
# --- Configuration ---
$RepoOwner = "itsRevela"
$RepoName = "MinecraftConsoles"
$RepoName = "LCE-Revelations"
$ReleaseTag = "Nightly"
$ReleaseDir = Join-Path $PSScriptRoot "build\Minecraft.Client\Release"
$ZipName = "LCREWindows64.zip"

View file

@ -7,16 +7,19 @@ function(setup_asset_folder_copy TARGET_NAME ASSET_FOLDER_PAIRS)
"*.xml" "*.lang"
"*.bat" "*.cmd"
"*.msscmp" "*.binka" # Old audio formats
#"*.swf" # These are built into the .arc
"*.swf"
"*.resx" #"*.loc"
"*.wav" # Unsupported audio format
"*.xui"
"*.xui" "*.xgs"
"*.xwb" "*.xsb"
"*.xap" "*.xzp"
)
# Global folder exclusions applied to every folder copy
# set(ASSET_EXCLUDE_FOLDERS
# "Graphics"
# )
set(ASSET_EXCLUDE_FOLDERS
#"Graphics"
"Gamerules"
)
# Exclude platform-specific media folders
set(PLATFORM_MEDIA_FOLDERS

View file

@ -1,6 +1,6 @@
services:
lce-revelations-dedicated-server:
image: ghcr.io/lce-hub/lce-revelations-dedicated-server:nightly
image: ghcr.io/itsrevela/lce-revelations-dedicated-server:nightly
container_name: lce-revelations-dedicated-server
restart: unless-stopped
tty: true

View file

@ -1,180 +0,0 @@
# FourKit Port Reconnaissance
Phase 1 deliverable for porting the FourKit plugin system into LCE-Revelations. This document is the source of truth for what needs to change, where, and how risky each change is. Phases 2-3 of the port consume this document directly.
## Source repos referenced
| Role | Path |
|---|---|
| Vanilla baseline | `C:\Users\revela\Documents\Minecraft\itsRevela` |
| Donor (has FourKit) | `C:\Users\revela\Documents\Minecraft\FourKit\MinecraftConsoles` |
| Target (this repo) | `C:\Users\revela\Documents\Minecraft\LCE-Hub\LCE-Revelations` |
Vanilla and donor were forked from upstream at similar points; the donor adds FourKit on top. The target is a more recent independent fork that has its own divergence (Security subsystem, OpManager, revoke-token command, additional ServerProperties fields).
## Executive summary
- **27 files** in the donor are FourKit-bearing across `Minecraft.Server/`, `Minecraft.World/`, and `Minecraft.Client/`. Of those, **23 are clean patches** (target identical to vanilla in those files), and **only 4 require manual merge work** (all in `Minecraft.Server/`).
- **7 pure-add native files** drop in unmodified.
- **1 entire managed project** (`Minecraft.Server.FourKit/`) drops in unmodified.
- **No blockers identified.** Every Fire* hook site exists in target at vanilla content; the conflicts are concentrated in build files and a small set of `Minecraft.Server/` source files where target diverged.
- **Native callback ABI is intact.** Spot-checks of player/world/inventory subsystem APIs that FourKitNatives.cpp depends on all match between donor and target.
## A. File diff matrix
### A.1 Pure adds (drop in verbatim)
| File | Notes |
|---|---|
| `Minecraft.Server/FourKitBridge.cpp` | Event firing + callback registry |
| `Minecraft.Server/FourKitBridge.h` | Public Fire* and Handle* surface |
| `Minecraft.Server/FourKitNatives.cpp` | C# → C++ callback implementations (~80 functions) |
| `Minecraft.Server/FourKitNatives.h` | Native function declarations |
| `Minecraft.Server/FourKitRuntime.cpp` | hostfxr load + .NET 10 bootstrap |
| `Minecraft.Server/FourKitRuntime.h` | Runtime init API |
| `Minecraft.Server/FourKitMappers.cpp` | Type/enum mapping helpers |
| `Minecraft.Server.FourKit/` (entire project) | Managed plugin host, Bukkit-style API, ~54 events |
### A.2 Modified files in `Minecraft.Server/` (the merge work)
| File | Donor delta | Target diverges from vanilla? | Merge class |
|---|---|---|---|
| `CMakeLists.txt` | +24 lines (FourKit dep + post-build copy) | No | CLEAN_PATCH |
| `cmake/sources/Common.cmake` | +7 FourKit sources, donor also strips OpManager/Security (donor lacks those) | No | **SELECTIVE_MERGE**: add donor's FourKit sources only; preserve target's OpManager/Security entries |
| `Windows64/ServerMain.cpp` | FourKit init/shutdown + 1 inline FireWorldSave | No | CLEAN_PATCH |
| `Console/ServerCliEngine.cpp` | HandleConsoleCommand hook + GetPluginCommandHelp integration | No | CLEAN_PATCH |
| `Console/commands/help/CliCommandHelp.cpp` | Plugin command help integration | Unknown: check before merge | LIKELY_CLEAN |
| `Console/commands/whitelist/CliCommandWhitelist.cpp` | Unknown delta | Unknown | LIKELY_CLEAN |
| `Access/Access.cpp` | Donor delta unrelated to FourKit hooks per grep | No | CLEAN_PATCH (verify) |
| `Access/Access.h` | Same | No | CLEAN_PATCH (verify) |
| `ServerLogManager.cpp` | Logger plumbing for FourKit log routing | No | CLEAN_PATCH |
| `ServerLogManager.h` | Same | No | CLEAN_PATCH |
| `ServerLogger.cpp` | Same | No | CLEAN_PATCH |
| `ServerProperties.cpp` | Donor REMOVES fields target relies on | **Yes** | **CONFLICT: KEEP TARGET** |
| `ServerProperties.h` | Same | **Yes** | **CONFLICT: KEEP TARGET** |
### A.3 Modified files in `Minecraft.World/` and `Minecraft.Client/`
The donor changed 53 files in `Minecraft.World/` and 95 files in `Minecraft.Client/` overall, but most of those are upstream churn unrelated to FourKit. The FourKit-specific subset is identified by `grep "FourKit"` and yields **23 files**, all of which are identical between vanilla and target: i.e., every single one is CLEAN_PATCH.
#### `Minecraft.World/` FourKit hook files (17, all CLEAN_PATCH)
`AbstractContainerMenu.cpp`, `CactusTile.cpp`, `CocoaTile.cpp`, `CropTile.cpp`, `EggTile.cpp`, `FireTile.cpp`, `GrassTile.cpp`, `ItemEntity.cpp`, `LiquidTileDynamic.cpp`, `LivingEntity.cpp`, `Mushroom.cpp`, `NetherWartTile.cpp`, `PistonBaseTile.cpp`, `ReedTile.cpp`, `Sapling.cpp`, `StemTile.cpp`, `ThrownEnderpearl.cpp`
#### `Minecraft.Client/` FourKit hook files (6, all CLEAN_PATCH)
`PendingConnection.cpp`, `PlayerConnection.cpp`, `ServerLevel.cpp`, `ServerPlayer.cpp`, `ServerPlayerGameMode.cpp`, `TeleportCommand.cpp`
> **Note on naming:** Despite the `Minecraft.Client/` folder name, several of these files (`PlayerConnection`, `ServerPlayer`, `ServerLevel`, etc.) are shared engine code that the dedicated server also compiles. They are valid hook sites for a server-side plugin system.
#### Build files in `Minecraft.World/` and `Minecraft.Client/`
The donor also modifies `Minecraft.World/cmake/sources/Common.cmake`, `Minecraft.Client/cmake/sources/Common.cmake`, `Minecraft.Client/cmake/sources/Windows.cmake`, and `Minecraft.Client/CMakeLists.txt`, but none of these contain FourKit references: they are upstream churn and should NOT be touched as part of the FourKit port.
## B. Fire* hook site inventory
Donor exposes Fire*/Handle* via `FourKitBridge::` (see `Minecraft.Server/FourKitBridge.h`). Hook calls appear in 26 source files across the three subprojects.
### B.1 Hook sites by subsystem
| Subsystem | Files (in donor) |
|---|---|
| Server lifecycle | `Minecraft.Server/Windows64/ServerMain.cpp` (init, shutdown, FireWorldSave) |
| Console / commands | `Minecraft.Server/Console/ServerCliEngine.cpp` (HandleConsoleCommand, GetPluginCommandHelp), `Minecraft.Server/Console/commands/help/CliCommandHelp.cpp` |
| Player connection / login | `Minecraft.Client/PlayerConnection.cpp`, `Minecraft.Client/PendingConnection.cpp` |
| Player gameplay | `Minecraft.Client/ServerPlayer.cpp`, `Minecraft.Client/ServerPlayerGameMode.cpp`, `Minecraft.Client/TeleportCommand.cpp` |
| World / level | `Minecraft.Client/ServerLevel.cpp` |
| Living entities & damage | `Minecraft.World/LivingEntity.cpp`, `Minecraft.World/ItemEntity.cpp`, `Minecraft.World/ThrownEnderpearl.cpp` |
| Block / tile (growth, burn, spread, ignite, piston) | `Minecraft.World/CactusTile.cpp`, `CocoaTile.cpp`, `CropTile.cpp`, `EggTile.cpp`, `FireTile.cpp`, `GrassTile.cpp`, `LiquidTileDynamic.cpp`, `Mushroom.cpp`, `NetherWartTile.cpp`, `PistonBaseTile.cpp`, `ReedTile.cpp`, `Sapling.cpp`, `StemTile.cpp` |
| Inventory / containers | `Minecraft.World/AbstractContainerMenu.cpp` |
### B.2 Hook insertion-site safety
Per A.3, **every file in B.1 outside of `Minecraft.Server/` is byte-identical between vanilla and target**. That means donor's hook insertions can be applied as a straight patch without adapting around target-side refactors.
In `Minecraft.Server/`, the hook-bearing files (`ServerMain.cpp`, `ServerCliEngine.cpp`, `CliCommandHelp.cpp`) are also identical or near-identical to vanilla per A.2. Net result: **the locked Phase 3 hook adaptation policy ("adapt to LCE-Revelations refactors") will not need to fire** for any hook site in the current state of the target. If the user pulls upstream changes between now and Phase 3, this assumption must be re-validated.
### B.3 Three concrete hook sites verified end-to-end (others follow same pattern)
1. **`Minecraft.Server/Windows64/ServerMain.cpp:681`**: `FourKitBridge::FireWorldSave()` inside the autosave trigger block. Target file is byte-identical to vanilla → clean insertion.
2. **`Minecraft.Server/Console/ServerCliEngine.cpp:165`**: `if (FourKitBridge::HandleConsoleCommand(normalizedLine)) return true;` early return for plugin command preprocessing. Target file byte-identical → clean insertion.
3. **`Minecraft.Server/Console/ServerCliEngine.cpp:215-246`**: `FourKitBridge::GetPluginCommandHelp(...)` integration into the help printer. Same file, clean insertion.
## C. Native callback surface (FourKitNatives.cpp ↔ engine APIs)
`FourKitNatives.cpp` implements ~80 functions that wrap engine APIs and expose them to managed code. Spot-check of the highest-risk subsystems:
| Native function | Engine API called | Target has it? | Signature drift? |
|---|---|---|---|
| `NativeSetPlayerHealth(int, float)` | `ServerPlayer::setHealth(float)` | Yes | None |
| `NativeTeleportPlayer(int, double, double, double)` | `PlayerConnection::teleport(...)` | Yes | None |
| `NativeSetTile(int, int, int, int, int, int)` | `ServerLevel::setBlock(...)` | Yes | None |
| `NativeGetPlayerAddress(int, char*, int, int*)` | `PlayerConnection` accessors | Yes | None |
| `NativeGetPlayerLatency(int)` | `PlayerConnection::latency` | Yes (field) | None |
| `NativeBanPlayer(int, char*, int)` | `Access::AddPlayerBan(...)` | Yes | None |
| `NativeSetItemMeta(int, int, char*, int)` | `ItemInstance` serialization | Yes | None |
| `NativeGetContainerContents(int, int*, int)` | `AbstractContainerMenu` traversal | Yes | None |
**Verdict:** No drift detected in critical subsystems. Phase 2's native bridge build is expected to compile against target's headers without shimming. Any drift discovered at compile time will be a Phase 2.3 finding, not a known risk.
## D. Critical hot files
| File | Vanilla LOC | Donor LOC | Target LOC | Status |
|---|---|---|---|---|
| `Minecraft.Server/Windows64/ServerMain.cpp` | 1,257 | 1,257 | 1,257 | Identical across all three. Donor's FourKit additions sit on the vanilla baseline; target has not touched it. |
| `Minecraft.Server/CMakeLists.txt` | 86 | 110 (+24) | 86 | Target is vanilla. Donor adds FourKit deps and a post-build copy step. Clean apply. |
| `Minecraft.Server/cmake/sources/Common.cmake` | 628 | 640 (+12 net; donor also strips ~16 lines of OpManager/Security) | 628 | Target is vanilla. **Selective merge:** apply donor's `FourKit*.cpp` additions, do NOT apply donor's removals. |
| `Minecraft.Server/ServerProperties.cpp` | 965 | 930 (-35) | 965 | Donor is older / leaner; target carries hardcore + security fields donor never had. **Keep target version unchanged**, do not merge donor's deletions. |
| `Minecraft.Server/ServerProperties.h` | n/a | n/a | n/a | Same logic: keep target. |
| `Minecraft.Server/Console/commands/` | 20 subdirs | 18 subdirs (no `revoketoken`) | 20 subdirs (has `revoketoken`) | Donor predates `revoketoken`; target has it. Keep target's command set. |
## E. Top-level structure of the target
| Item | Status |
|---|---|
| `samples/` at repo root | NOT FOUND: Phase 7 will create it |
| `docs/` at repo root | NOT FOUND: created by this document |
| `docker/` | EXISTS: contains `dedicated-server/{Dockerfile,entrypoint.sh}`. Phase 5 must inspect to decide whether the image builds the server from source or consumes the release zip |
| `Minecraft.Server.FourKit/` | NOT FOUND (as expected) |
## Merge strategy summary (input to Phase 2 and Phase 3)
1. **Phase 2 source drop:** Copy the 7 PURE_ADD files into `Minecraft.Server/` verbatim. Copy the entire `Minecraft.Server.FourKit/` project to repo root verbatim.
2. **Phase 2 build files:**
- `Minecraft.Server/CMakeLists.txt`: apply donor's diff verbatim (target is vanilla).
- `Minecraft.Server/cmake/sources/Common.cmake`: apply ONLY the donor's additions of `FourKit*.cpp`. Do not delete OpManager or Security entries.
3. **Phase 3 hooks (the entire surface, all CLEAN_PATCH):**
- 3 sites in `Minecraft.Server/` (`ServerMain.cpp`, `ServerCliEngine.cpp`, `CliCommandHelp.cpp`)
- 17 sites in `Minecraft.World/` (block/entity/container)
- 6 sites in `Minecraft.Client/` (player/level/connection)
- 1 lifecycle hook pair in `ServerMain.cpp` for `FourKitRuntime::Initialize` / shutdown
- Logger plumbing in `ServerLogManager.{cpp,h}` and `ServerLogger.cpp`
4. **Phase 3 conflicts to leave alone:**
- `ServerProperties.{cpp,h}`: keep target version, do not merge donor.
- `Console/commands/revoketoken/`: keep target version, donor lacks it.
- `Access/OpManager.{cpp,h}`: keep target version, donor lacks it.
- `Security/`: keep target version, donor lacks it.
5. **Spot-checks needed before merge:**
- `Console/commands/help/CliCommandHelp.cpp`: verify donor's delta is purely the plugin help integration and not entangled with anything target changed.
- `Console/commands/whitelist/CliCommandWhitelist.cpp`: confirm scope of donor's edit.
- `Access/Access.{cpp,h}`: confirm donor's delta is not security-related.
## Risks updated post-recon
| Risk | Original severity | Post-recon severity | Reason |
|---|---|---|---|
| LCE-Revelations divergence at hook sites | HIGH | **LOW** | All 26 hook-bearing source files are byte-identical between vanilla and target. |
| CMake structural divergence | HIGH | **LOW** | Target's `CMakeLists.txt` and `Common.cmake` are byte-identical to vanilla. |
| ServerMain.cpp init-order conflicts | MEDIUM | **LOW** | File is identical to vanilla. |
| ServerProperties conflict | NEW | **MEDIUM** | Donor strips fields target relies on. Mitigation: do not merge donor's version. |
| Native ABI drift in subsystem APIs | MEDIUM | **LOW** | Spot-checks all clean. Compile-time will catch any miss. |
| Donor predates target features (revoketoken, OpManager, Security) | NEW | **LOW** | Easy to preserve target's additions; donor doesn't depend on their absence. |
| Self-contained .NET 10 publish size | MEDIUM | **MEDIUM** | Unchanged. ~70-100 MB release zip growth. |
| Hot-reload absent | LOW | **LOW** | Inherited limitation, documented. |
## Open follow-ups for Phase 2
1. Confirm `Console/commands/help/CliCommandHelp.cpp` and `Console/commands/whitelist/CliCommandWhitelist.cpp` deltas are FourKit-related (currently marked LIKELY_CLEAN, not verified).
2. Confirm `Access/Access.{cpp,h}` delta scope.
3. Decide whether the donor's changes to `ServerLogManager` and `ServerLogger` are strictly additive (capture for FourKit log routing) or entangled with other refactoring.
4. Confirm during Phase 2.3 that `FourKitRuntime.cpp`'s hostfxr loader handles the case where `hostfxr.dll` is absent gracefully (server should warn and continue, not crash): this matters because the self-contained publish puts hostfxr next to the server exe, so the failure mode only fires if a user deletes it.

View file

@ -1,5 +1,5 @@
{
description = "MinecraftConsoles - Minecraft Legacy Console Edition recreation";
description = "LCE-Revelations - Minecraft Legacy Console Edition recreation";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
@ -503,7 +503,7 @@
XWIN_CACHE = "$HOME/.cache/xwin";
shellHook = ''
echo "MinecraftConsoles development shell"
echo "LCE-Revelations development shell"
echo ""
echo "Quick build (uses cached SDK):"
echo " nix build .#client # Build client package"