3 Commits

Author SHA1 Message Date
Revela a00b9962bb Prevent exit-without-saving loophole for hardcore players
Switch player to Adventure mode on death in hardcore mode, ban their XUID, and trigger a save action to prevent quitting without saving.
2026-03-15 04:11:03 -05:00
Revela 00fef6e9d2 smartcmd/MinecraftConsoles and itsRevela/MinecraftConsoles network packet compatibility fix:
LoginPacket.cpp:
  - read(): Extracts hardcore from bit 3 of gameType (gameType & 0x8), then masks it off (gameType &
  ~0x8)
  - write(): Encodes hardcore into gameType via gameType | (m_isHardcore ? 0x8 : 0)
  - Removed the separate readBoolean()/writeBoolean() for m_isHardcore
  - Removed trailing + sizeof(bool) from getEstimatedSize()

  RespawnPacket.cpp:
  - read(): Extracts hardcore from bit 3 of the playerGameType byte, then masks it off before passing
  to GameType::byId()
  - write(): Encodes hardcore into the playerGameType byte via getId() | (m_isHardcore ? 0x8 : 0)
  - Removed the separate readBoolean()/writeBoolean() for m_isHardcore
  - Reverted getEstimatedSize() from 14+length to 13+length
2026-03-15 04:11:03 -05:00
Revela a82e0ccf82 detailed summary of every changed file:
---
  Minecraft.Client/ClientConnection.cpp

  Purpose: Propagate hardcore flag through network level creation

  - handleLogin() (2 sites): Changed MultiPlayerLevel constructor calls from hardcoded false for the
  hardcore parameter to packet->m_isHardcore, so the client-side level correctly knows it's hardcore
  when joining a server.
  - handleRespawn(): Same change - when creating a new dimension level on respawn, uses
  packet->m_isHardcore instead of querying minecraft->level->getLevelData()->isHardcore() (which could
   be stale/wrong).

  ---
  Minecraft.Client/Common/App_Defines.h

  Purpose: Define bitmask for hardcore host option

  - Added GAME_HOST_OPTION_BITMASK_HARDCORE (0x40000000) - a new bit in the host options bitfield to
  store whether the game is hardcore.

  ---
  Minecraft.Client/Common/App_enums.h

  Purpose: Add hardcore enum value

  - Added eGameHostOption_Hardcore to the eGameHostOption enum so code can get/set the hardcore flag
  via SetGameHostOption/GetGameHostOption.

  ---
  Minecraft.Client/Common/Consoles_App.cpp

  Purpose: Implement hardcore get/set in host options bitfield

  - SetGameHostOption(): Added case eGameHostOption_Hardcore - sets or clears the
  GAME_HOST_OPTION_BITMASK_HARDCORE bit.
  - GetGameHostOption(): Added case eGameHostOption_Hardcore - returns 1 if the hardcore bit is set, 0
   otherwise.

  ---
  Minecraft.Client/Common/Consoles_App.h

  Purpose: Store save folder name for hardcore world deletion

  - Added SetCurrentSaveFolderName() and GetCurrentSaveFolderName() public methods.
  - Added wstring m_currentSaveFolderName private member - stores the save folder name so the hardcore
   death handler can find and delete the world.

  ---
  Minecraft.Client/Common/UI/IUIScene_PauseMenu.cpp

  Purpose: Delete hardcore world's save data on exit

  - Added Win64_DeleteSaveDirectory() - a recursive directory deletion helper (Windows64 only).
  - In _ExitWorld(): Before the server is torn down, captures whether this is a hardcore death exit
  (getDeleteWorldOnExit()). Tries 3 sources for the save folder name: app storage, StorageManager, and
   MinecraftServer.
  - After the server fully stops, if shouldDeleteHardcoreWorld is true, deletes the entire
  Windows64\GameHDD\<savefolder> directory.

  ---
  Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp

  Purpose: Hardcore difficulty slider in Create World menu

  - Added file-scope s_bHardcore flag to track when the slider is at position 4 (Hardcore).
  - Constructor: Extended difficulty slider range from 0-3 to 0-4, resets s_bHardcore to false.
  - handleSliderMove(): When slider value >= 4, sets s_bHardcore = true, stores actual difficulty as 3
   (Hard), and displays "Hardcore" label. Otherwise behaves normally.
  - CreateGame(): Clears the save folder name (new world), and sets eGameHostOption_Hardcore based on
  s_bHardcore.
  - Minor: Changed RequestErrorMessage to RequestAlertMessage for a content restriction dialog.

  ---
  Minecraft.Client/Common/UI/UIScene_DeathMenu.cpp

  Purpose: Hardcore death screen behavior (Iggy UI)

  - Constructor: Checks if current level is hardcore. If so, shows IDS_HARDCORE_DEATH_MESSAGE on the
  respawn button and hides it. Otherwise shows normal "Respawn" button.
  - handlePress() - Respawn: Added safeguard - if hardcore, blocks respawn entirely.
  - handlePress() - Exit Game: If hardcore and host, skips save dialog, disables save-on-exit, enables
   delete-world-on-exit, and triggers immediate world exit.

  ---
  Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp

  Purpose: Show "Difficulty: Hardcore" in Load World menu + persist hardcore through game launch

  - Static array: Expanded m_iDifficultyTitleSettingA from 4 to 5 entries, added IDS_GAMEMODE_HARDCORE
   at index 4.
  - Constructor: Initializes m_bHardcore = false. In Windows64 block: sets up thumbnail name from save
   details, and reads isHardcore from params->saveDetails. If hardcore, immediately initializes the
  difficulty slider to show "Hardcore" locked at position 4.
  - tick(): When host options are read (bHostOptionsRead block), also reads the hardcore flag and
  re-initializes the slider if needed (for console path).
  - handleSliderMove(): If m_bHardcore, locks the slider at position 4 (prevents changing difficulty).
  - StartGameFromSave(): Stores the save folder name in app for later hardcore deletion. Sets
  eGameHostOption_Hardcore from m_bHardcore.

  ---
  Minecraft.Client/Common/UI/UIScene_LoadMenu.h

  Purpose: Declare hardcore member

  - Expanded m_iDifficultyTitleSettingA from [4] to [5].
  - Added bool m_bHardcore private member.

  ---
  Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp

  Purpose: Read hardcore flag from level.dat when building the save list

  - ReadLevelNameFromSaveFile(): Added optional bool *outHardcore parameter. Inside the NBT Data
  compound tag parsing, if outHardcore is non-null, reads dataTag->getBoolean(L"hardcore").
  - Save enumeration block (Windows64): Passes &saveHardcore to ReadLevelNameFromSaveFile and stores
  the result in m_saveDetails[i].isHardcore.

  ---
  Minecraft.Client/Common/UI/UIStructs.h

  Purpose: Add isHardcore to save list details struct

  - Added bool isHardcore field to _SaveListDetails struct.
  - Initialized to false in the constructor.

  ---
  Minecraft.Client/Common/XUI/XUI_Death.cpp

  Purpose: Hardcore death screen behavior (XUI/Xbox UI)

  - Mirror of the Iggy UIScene_DeathMenu.cpp changes but for the XUI rendering path.
  - OnInit(): Checks isHardcore(), hides respawn button and shows death message if true.
  - OnNotifyPressEx() - Exit Game: If hardcore and host, skips save, flags world for deletion, exits
  immediately.
  - OnNotifyPressEx() - Respawn: Safeguard to block respawn in hardcore.

  ---
  Minecraft.Client/Gui.cpp

  Purpose: Syntax fix

  - Fixed lines.push_back(L"" → lines.push_back(L"") - missing closing quote/paren in debug terrain
  feature display.

  ---
  Minecraft.Client/MinecraftServer.cpp

  Purpose: Server-side hardcore support

  - Constructor: Initializes m_deleteWorldOnExit = false.
  - loadLevel(): Captures the save folder name from StorageManager into m_saveFolderName for later use
   in hardcore world deletion.
  - isHardcore(): Changed from always returning false to returning
  app.GetGameHostOption(eGameHostOption_Hardcore) > 0 - this is the key change that makes the server
  actually report hardcore mode.

  ---
  Minecraft.Client/MinecraftServer.h

  Purpose: Declare hardcore-related members

  - Added bool m_deleteWorldOnExit and wstring m_saveFolderName private members.
  - Added setDeleteWorldOnExit(), getDeleteWorldOnExit(), and getSaveFolderName() public methods.

  ---
  Minecraft.Client/PlayerConnection.h

  Purpose: Thread-safety fix for kicked flag

  - Changed m_bWasKicked from bool to std::atomic<bool> (initialized with {false}).
  - Changed setWasKicked()/getWasKicked() to use .store()/.load() - fixes a race condition where the
  kicked flag is set on one thread and read on another.

  ---
  Minecraft.Client/PlayerList.cpp

  Purpose: Hardcore multiplayer - ban, respawn as Adventure, thread-safe bans

  - Constructor/Destructor: Added InitializeCriticalSection/DeleteCriticalSection for m_banCS.
  - placeNewPlayer(): Passes isHardcore() flag to the LoginPacket constructor so clients joining know
  it's hardcore.
  - respawn(): After respawn in hardcore, forces the player into Adventure mode (spectate-like: can
  look around but not interact). Sends GameEventPacket to sync client.
  - respawn() and toggleDimension() (2 sites): Pass isHardcore() to RespawnPacket constructor.
  - isXuidBanned(): Wrapped m_bannedXuids iteration with EnterCriticalSection/LeaveCriticalSection for
   thread safety.
  - banXuid() (new): Thread-safe method to add a player's XUID to the ban list - used when a player
  dies in hardcore multiplayer.

  ---
  Minecraft.Client/PlayerList.h

  Purpose: Declare ban-related additions

  - Added CRITICAL_SECTION m_banCS to protect m_bannedXuids.
  - Added void banXuid(PlayerUID xuid) public method.

  ---
  Minecraft.Client/SelectWorldScreen.cpp

  Purpose: Show [Hardcore] badge in Java-style world list

  - In renderItem(): If levelSummary->isHardcore(), appends  [Hardcore] to the world name display.

  ---
  Minecraft.Client/ServerPlayer.cpp

  Purpose: Hardcore death behavior on server

  - die(): If the level is hardcore, switches the dead player to Adventure mode (so they can't
  interact if somehow respawned).
  - Minor: Two comment lines changed // → /// (no functional change).

  ---
  Minecraft.Client/Windows64Media/strings.h

  Purpose: String IDs for hardcore UI text

  - Added 8 new string IDs (2286-2293): IDS_GAMEMODE_HARDCORE, IDS_HARDCORE, IDS_HARDCORE_TOOLTIP,
  IDS_HARDCORE_WARNING_TITLE, IDS_HARDCORE_WARNING_TEXT, IDS_HARDCORE_DEATH_MESSAGE,
  IDS_LABEL_HARDCORE, IDS_GAMEOPTION_HARDCORE.

  ---
  Minecraft.World/ConsoleSaveFileOriginal.cpp

  Purpose: Capture save folder name after first save (for new worlds)

  - SaveSaveDataCallback() (Windows64 only): After a successful save, if the app doesn't yet know the
  save folder name, attempts to capture it via StorageManager or by scanning Windows64\GameHDD\ for
  the newest folder. This handles the case where a newly-created hardcore world hasn't been saved yet
  when the folder name is needed.

  ---
  Minecraft.World/DisconnectPacket.h

  Purpose: Hardcore disconnect reason

  - Added eDisconnect_HardcoreDeath to the disconnect reason enum - used when kicking a player who
  died in hardcore multiplayer.

  ---
  Minecraft.World/LoginPacket.cpp & LoginPacket.h

  Purpose: Serialize hardcore flag in login packet

  - Added bool m_isHardcore member, initialized to false in both constructors.
  - Server→Client constructor now accepts bool isHardcore = false parameter.
  - read(): Reads m_isHardcore from the stream.
  - write(): Writes m_isHardcore to the stream.
  - getEstimatedSize(): Added sizeof(bool) for the new field.

  ---
  Minecraft.World/RespawnPacket.cpp & RespawnPacket.h

  Purpose: Serialize hardcore flag in respawn packet

  - Added bool m_isHardcore member, initialized to false.
  - Constructor now accepts bool isHardcore = false parameter.
  - read()/write(): Serialize m_isHardcore via readBoolean()/writeBoolean().
  - getEstimatedSize(): Changed from 13 to 14 bytes to account for the new boolean.
2026-03-15 04:10:57 -05:00
46 changed files with 1255 additions and 635 deletions
+3 -3
View File
@@ -342,7 +342,7 @@ void ClientConnection::handleLogin(shared_ptr<LoginPacket> packet)
Level *dimensionLevel = minecraft->getLevel( packet->dimension );
if( dimensionLevel == nullptr )
{
level = new MultiPlayerLevel(this, new LevelSettings(packet->seed, GameType::byId(packet->gameType), false, false, packet->m_newSeaLevel, packet->m_pLevelType, packet->m_xzSize, packet->m_hellScale), packet->dimension, packet->difficulty);
level = new MultiPlayerLevel(this, new LevelSettings(packet->seed, GameType::byId(packet->gameType), false, packet->m_isHardcore, packet->m_newSeaLevel, packet->m_pLevelType, packet->m_xzSize, packet->m_hellScale), packet->dimension, packet->difficulty);
// 4J Stu - We want to share the SavedDataStorage between levels
int otherDimensionId = packet->dimension == 0 ? -1 : 0;
@@ -411,7 +411,7 @@ void ClientConnection::handleLogin(shared_ptr<LoginPacket> packet)
activeLevel = minecraft->getLevel(otherDimensionId);
}
MultiPlayerLevel *dimensionLevel = new MultiPlayerLevel(this, new LevelSettings(packet->seed, GameType::byId(packet->gameType), false, false, packet->m_newSeaLevel, packet->m_pLevelType, packet->m_xzSize, packet->m_hellScale), packet->dimension, packet->difficulty);
MultiPlayerLevel *dimensionLevel = new MultiPlayerLevel(this, new LevelSettings(packet->seed, GameType::byId(packet->gameType), false, packet->m_isHardcore, packet->m_newSeaLevel, packet->m_pLevelType, packet->m_xzSize, packet->m_hellScale), packet->dimension, packet->difficulty);
dimensionLevel->savedDataStorage = activeLevel->savedDataStorage;
@@ -2870,7 +2870,7 @@ void ClientConnection::handleRespawn(shared_ptr<RespawnPacket> packet)
MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->getLevel( packet->dimension );
if( dimensionLevel == nullptr )
{
dimensionLevel = new MultiPlayerLevel(this, new LevelSettings(packet->mapSeed, packet->playerGameType, false, minecraft->level->getLevelData()->isHardcore(), packet->m_newSeaLevel, packet->m_pLevelType, packet->m_xzSize, packet->m_hellScale), packet->dimension, packet->difficulty);
dimensionLevel = new MultiPlayerLevel(this, new LevelSettings(packet->mapSeed, packet->playerGameType, false, packet->m_isHardcore, packet->m_newSeaLevel, packet->m_pLevelType, packet->m_xzSize, packet->m_hellScale), packet->dimension, packet->difficulty);
// 4J Stu - We want to shared the savedDataStorage between both levels
//if( dimensionLevel->savedDataStorage != nullptr )
+1
View File
@@ -45,6 +45,7 @@
#define GAME_HOST_OPTION_BITMASK_DOTILEDROPS 0x08000000
#define GAME_HOST_OPTION_BITMASK_NATURALREGEN 0x10000000
#define GAME_HOST_OPTION_BITMASK_DODAYLIGHTCYCLE 0x20000000
#define GAME_HOST_OPTION_BITMASK_HARDCORE 0x40000000 // 4J Added - for hardcore mode
#define GAME_HOST_OPTION_BITMASK_ALL 0xFFFFFFFF
#define GAME_HOST_OPTION_BITMASK_WORLDSIZE_BITSHIFT 20
+1
View File
@@ -648,6 +648,7 @@ enum eGameHostOption
eGameHostOption_DoTileDrops,
eGameHostOption_NaturalRegeneration,
eGameHostOption_DoDaylightCycle,
eGameHostOption_Hardcore, // 4J Added - for hardcore mode
};
// 4J-PB - If any new DLC items are added to the TMSFiles, this array needs updated
+13
View File
@@ -8084,6 +8084,16 @@ void CMinecraftApp::SetGameHostOption(unsigned int &uiHostSettings, eGameHostOpt
uiHostSettings&=~GAME_HOST_OPTION_BITMASK_WORLDSIZE;
uiHostSettings|=(GAME_HOST_OPTION_BITMASK_WORLDSIZE & (uiVal<<GAME_HOST_OPTION_BITMASK_WORLDSIZE_BITSHIFT));
break;
case eGameHostOption_Hardcore: // 4J Added - for hardcore mode
if(uiVal!=0)
{
uiHostSettings |= GAME_HOST_OPTION_BITMASK_HARDCORE;
}
else
{
uiHostSettings &= ~GAME_HOST_OPTION_BITMASK_HARDCORE;
}
break;
case eGameHostOption_All:
uiHostSettings=uiVal;
break;
@@ -8185,6 +8195,9 @@ unsigned int CMinecraftApp::GetGameHostOption(unsigned int uiHostSettings, eGame
case eGameHostOption_DoDaylightCycle:
return !(uiHostSettings&GAME_HOST_OPTION_BITMASK_DODAYLIGHTCYCLE);
break;
case eGameHostOption_Hardcore: // 4J Added - for hardcore mode
return (uiHostSettings&GAME_HOST_OPTION_BITMASK_HARDCORE) ? 1 : 0;
break;
}
return false;
+5
View File
@@ -805,6 +805,10 @@ public:
void SetCorruptSaveDeleted(bool bVal) {m_bCorruptSaveDeleted=bVal;}
bool GetCorruptSaveDeleted(void) {return m_bCorruptSaveDeleted;}
// 4J Added: Store save folder name for hardcore world deletion on Win64
void SetCurrentSaveFolderName(const wstring& name) { m_currentSaveFolderName = name; }
const wstring& GetCurrentSaveFolderName() const { return m_currentSaveFolderName; }
void EnterSaveNotificationSection();
void LeaveSaveNotificationSection();
private:
@@ -826,6 +830,7 @@ private:
CRITICAL_SECTION csAdditionalSkinBoxes;
CRITICAL_SECTION csAnimOverrideBitmask;
bool m_bCorruptSaveDeleted;
wstring m_currentSaveFolderName; // 4J Added: for hardcore world deletion on Win64
DWORD m_dwAdditionalModelParts[XUSER_MAX_COUNT];
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -410,11 +410,72 @@ int IUIScene_PauseMenu::ExitWorldThreadProc( void* lpParameter )
return S_OK;
}
#ifdef _WINDOWS64
static bool Win64_DeleteSaveDirectory(const wchar_t* wPath)
{
wchar_t wSearch[MAX_PATH];
swprintf_s(wSearch, MAX_PATH, L"%s\\*", wPath);
WIN32_FIND_DATAW fd;
HANDLE hFind = FindFirstFileW(wSearch, &fd);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
if (wcscmp(fd.cFileName, L".") == 0 || wcscmp(fd.cFileName, L"..") == 0) continue;
wchar_t wChild[MAX_PATH];
swprintf_s(wChild, MAX_PATH, L"%s\\%s", wPath, fd.cFileName);
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
Win64_DeleteSaveDirectory(wChild);
else
DeleteFileW(wChild);
} while (FindNextFileW(hFind, &fd));
FindClose(hFind);
}
return RemoveDirectoryW(wPath) != 0;
}
#endif // _WINDOWS64
// This function performs the meat of exiting from a level. It should be called from a thread other than the main thread.
void IUIScene_PauseMenu::_ExitWorld(LPVOID lpParameter)
{
Minecraft *pMinecraft=Minecraft::GetInstance();
// 4J Added: Capture hardcore delete info before the server is destroyed
#ifdef _WINDOWS64
bool shouldDeleteHardcoreWorld = false;
wstring hardcoreSaveFolderName;
if (MinecraftServer::getInstance() != nullptr && MinecraftServer::getInstance()->getDeleteWorldOnExit())
{
shouldDeleteHardcoreWorld = true;
// Try 1: Use the save folder name stored by UIScene_LoadMenu::StartGameFromSave (works for existing saves)
hardcoreSaveFolderName = app.GetCurrentSaveFolderName();
if (!hardcoreSaveFolderName.empty())
{
app.DebugPrintf("Hardcore mode: save folder from app = '%ls'\n", hardcoreSaveFolderName.c_str());
}
// Try 2: StorageManager (may work for new saves after first autosave)
if (hardcoreSaveFolderName.empty())
{
char szSaveFolder[MAX_SAVEFILENAME_LENGTH] = {};
StorageManager.GetSaveUniqueFilename(szSaveFolder);
if (szSaveFolder[0] != '\0')
{
wchar_t wSaveFolder[MAX_SAVEFILENAME_LENGTH] = {};
mbstowcs(wSaveFolder, szSaveFolder, MAX_SAVEFILENAME_LENGTH - 1);
hardcoreSaveFolderName = wSaveFolder;
app.DebugPrintf("Hardcore mode: save folder from StorageManager = '%s'\n", szSaveFolder);
}
}
// Try 3: Stored during loadLevel
if (hardcoreSaveFolderName.empty())
{
hardcoreSaveFolderName = MinecraftServer::getInstance()->getSaveFolderName();
app.DebugPrintf("Hardcore mode: save folder from server = '%ls'\n", hardcoreSaveFolderName.c_str());
}
MinecraftServer::getInstance()->setDeleteWorldOnExit(false);
}
#endif
int exitReasonStringId = pMinecraft->progressRenderer->getCurrentTitle();
int exitReasonTitleId = IDS_CONNECTION_LOST;
@@ -625,6 +686,17 @@ void IUIScene_PauseMenu::_ExitWorld(LPVOID lpParameter)
{
Sleep(1);
}
// 4J Added: Hardcore mode — delete world save data now that the server is fully stopped
#ifdef _WINDOWS64
if (shouldDeleteHardcoreWorld && !hardcoreSaveFolderName.empty())
{
wchar_t wFolderPath[MAX_PATH] = {};
swprintf_s(wFolderPath, MAX_PATH, L"Windows64\\GameHDD\\%s", hardcoreSaveFolderName.c_str());
app.DebugPrintf("Hardcore mode: Deleting world save folder '%ls'\n", wFolderPath);
Win64_DeleteSaveDirectory(wFolderPath);
}
#endif
pMinecraft->setLevel(nullptr,exitReasonStringId,nullptr,saveStats);
TelemetryManager->Flush();
@@ -1,7 +1,6 @@
#include "stdafx.h"
#include "UI.h"
#include "UIControl_TextInput.h"
#include "..\..\Screen.h"
UIControl_TextInput::UIControl_TextInput()
{
@@ -212,31 +211,6 @@ UIControl_TextInput::EDirectEditResult UIControl_TextInput::tickDirectEdit()
}
}
// Paste from clipboard
if (g_KBMInput.IsKeyPressed('V') && g_KBMInput.IsKeyDown(VK_CONTROL))
{
wstring pasted = Screen::getClipboard();
wstring sanitized;
sanitized.reserve(pasted.length());
for (wchar_t pc : pasted)
{
if (pc >= 0x20) // Keep printable characters
{
if (m_iCharLimit > 0 && (m_editBuffer.length() + sanitized.length()) >= (size_t)m_iCharLimit)
break;
sanitized += pc;
}
}
if (!sanitized.empty())
{
m_editBuffer.insert(m_iCursorPos, sanitized);
m_iCursorPos += (int)sanitized.length();
changed = true;
}
}
// Arrow keys, Home, End, Delete for cursor movement
if (g_KBMInput.IsKeyPressed(VK_LEFT) && m_iCursorPos > 0)
{
@@ -26,6 +26,8 @@
#define GAME_CREATE_ONLINE_TIMER_ID 0
#define GAME_CREATE_ONLINE_TIMER_TIME 100
static bool s_bHardcore = false; // 4J Added: tracks when difficulty slider is at Hardcore position (file-scope to avoid header layout changes)
int UIScene_CreateWorldMenu::m_iDifficultyTitleSettingA[4]=
{
IDS_DIFFICULTY_TITLE_PEACEFUL,
@@ -59,8 +61,9 @@ UIScene_CreateWorldMenu::UIScene_CreateWorldMenu(int iPad, void *initData, UILay
WCHAR TempString[256];
swprintf( (WCHAR *)TempString, 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ),app.GetString(m_iDifficultyTitleSettingA[app.GetGameSettings(m_iPad,eGameSetting_Difficulty)]));
m_sliderDifficulty.init(TempString,eControl_Difficulty,0,3,app.GetGameSettings(m_iPad,eGameSetting_Difficulty));
m_sliderDifficulty.init(TempString,eControl_Difficulty,0,4,app.GetGameSettings(m_iPad,eGameSetting_Difficulty));
s_bHardcore = false;
m_MoreOptionsParams.bGenerateOptions=TRUE;
m_MoreOptionsParams.bStructures=TRUE;
m_MoreOptionsParams.bFlatWorld=FALSE;
@@ -654,8 +657,13 @@ void UIScene_CreateWorldMenu::handleSliderMove(F64 sliderId, F64 currentValue)
case eControl_Difficulty:
m_sliderDifficulty.handleSliderMove(value);
app.SetGameSettings(m_iPad,eGameSetting_Difficulty,value);
swprintf( (WCHAR *)TempString, 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ),app.GetString(m_iDifficultyTitleSettingA[value]));
// 4J Added: Difficulty value 4 = Hardcore (store actual difficulty as Hard, track hardcore separately)
s_bHardcore = (value >= 4);
app.SetGameSettings(m_iPad, eGameSetting_Difficulty, s_bHardcore ? 3 : value);
if (value >= 4)
swprintf( (WCHAR *)TempString, 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ), L"Hardcore");
else
swprintf( (WCHAR *)TempString, 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ),app.GetString(m_iDifficultyTitleSettingA[value]));
m_sliderDifficulty.setLabel(TempString);
break;
}
@@ -823,7 +831,7 @@ void UIScene_CreateWorldMenu::checkStateAndStartGame()
// 4J Stu - This is a bit messy and is due to the library incorrectly returning false for IsSignedInLive if the npAvailability isn't SCE_OK
UINT uiIDA[1];
uiIDA[0]=IDS_OK;
ui.RequestErrorMessage(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPadNotSignedInLive);
ui.RequestAlertMessage(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPadNotSignedInLive);
}
else
{
@@ -1113,6 +1121,10 @@ void UIScene_CreateWorldMenu::CreateGame(UIScene_CreateWorldMenu* pClass, DWORD
StorageManager.ResetSaveData();
// Make our next save default to the name of the level
StorageManager.SetSaveTitle((wchar_t *)wWorldName.c_str());
#ifdef _WINDOWS64
// New world — save folder doesn't exist yet, clear for now (will be set after first autosave)
app.SetCurrentSaveFolderName(L"");
#endif
wstring wSeed;
if(!pClass->m_MoreOptionsParams.seed.empty() )
@@ -1179,7 +1191,9 @@ void UIScene_CreateWorldMenu::CreateGame(UIScene_CreateWorldMenu* pClass, DWORD
Minecraft *pMinecraft = Minecraft::GetInstance();
pMinecraft->skins->selectTexturePackById(pClass->m_MoreOptionsParams.dwTexturePack);
app.SetGameHostOption(eGameHostOption_Difficulty,Minecraft::GetInstance()->options->difficulty);
// 4J Added: If hardcore was selected on difficulty slider, set difficulty to Hard and enable hardcore flag
app.SetGameHostOption(eGameHostOption_Difficulty, Minecraft::GetInstance()->options->difficulty);
app.SetGameHostOption(eGameHostOption_Hardcore, s_bHardcore ? 1 : 0);
app.SetGameHostOption(eGameHostOption_FriendsOfFriends,pClass->m_MoreOptionsParams.bAllowFriendsOfFriends);
app.SetGameHostOption(eGameHostOption_Gamertags,app.GetGameSettings(pClass->m_iPad,eGameSetting_GamertagsVisible)?1:0);
@@ -3,24 +3,43 @@
#include "UIScene_DeathMenu.h"
#include "IUIScene_PauseMenu.h"
#include "..\..\Minecraft.h"
#include "..\..\MultiPlayerLevel.h"
#include "..\..\MultiplayerLocalPlayer.h"
#include "..\..\MinecraftServer.h"
#include "..\..\..\Minecraft.World\net.minecraft.world.level.storage.h"
UIScene_DeathMenu::UIScene_DeathMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer)
{
// Setup all the Iggy references we need for this scene
initialiseMovie();
m_buttonRespawn.init(app.GetString(IDS_RESPAWN),eControl_Respawn);
m_buttonExitGame.init(app.GetString(IDS_EXIT_GAME),eControl_ExitGame);
m_labelTitle.setLabel(app.GetString(IDS_YOU_DIED));
// 4J Added: In hardcore mode, disable respawn and show hardcore death message
Minecraft *pMC = Minecraft::GetInstance();
bool isHardcore = false;
if (pMC != nullptr && pMC->level != nullptr)
{
isHardcore = pMC->level->getLevelData()->isHardcore();
}
if (isHardcore)
{
m_buttonRespawn.init(app.GetString(IDS_HARDCORE_DEATH_MESSAGE), eControl_Respawn);
m_buttonRespawn.setVisible(false);
}
else
{
m_buttonRespawn.init(app.GetString(IDS_RESPAWN), eControl_Respawn);
}
m_bIgnoreInput = false;
Minecraft *pMinecraft = Minecraft::GetInstance();
if(pMinecraft != nullptr && pMinecraft->localgameModes[iPad] != nullptr )
if(pMC != nullptr && pMC->localgameModes[iPad] != nullptr )
{
TutorialMode *gameMode = static_cast<TutorialMode *>(pMinecraft->localgameModes[iPad]);
TutorialMode *gameMode = static_cast<TutorialMode *>(pMC->localgameModes[iPad]);
// This just allows it to be shown
gameMode->getTutorial()->showTutorialPopup(false);
@@ -84,8 +103,16 @@ void UIScene_DeathMenu::handlePress(F64 controlId, F64 childId)
switch(static_cast<int>(controlId))
{
case eControl_Respawn:
m_bIgnoreInput = true;
app.SetAction(m_iPad,eAppAction_Respawn);
{
// 4J Added: Safeguard - don't respawn in hardcore mode
Minecraft *pMC = Minecraft::GetInstance();
if (pMC != nullptr && pMC->level != nullptr && pMC->level->getLevelData()->isHardcore())
{
break;
}
m_bIgnoreInput = true;
app.SetAction(m_iPad,eAppAction_Respawn);
}
#ifdef _DURANGO
//InputManager.SetEnabledGtcButtons(_360_GTC_MENU|_360_GTC_PAUSE|_360_GTC_VIEW);
#endif
@@ -109,7 +136,16 @@ void UIScene_DeathMenu::handlePress(F64 controlId, F64 childId)
playTime = static_cast<int>(pMinecraft->localplayers[m_iPad]->getSessionTimer());
}
TelemetryManager->RecordLevelExit(m_iPad, eSen_LevelExitStatus_Failed);
// 4J Added: Hardcore mode — skip save dialog, exit without saving, delete world
if (pMinecraft->level != nullptr && pMinecraft->level->getLevelData()->isHardcore() && g_NetworkManager.IsHost())
{
MinecraftServer::getInstance()->setSaveOnExit(false);
MinecraftServer::getInstance()->setDeleteWorldOnExit(true);
app.SetAction(m_iPad, eAppAction_ExitWorld);
break;
}
#if defined (_XBOX_ONE) || defined(__ORBIS__)
if(g_NetworkManager.IsHost() && StorageManager.GetSaveDisabled())
{
@@ -1,7 +1,6 @@
#include "stdafx.h"
#include "UI.h"
#include "UIScene_Keyboard.h"
#include "..\..\Screen.h"
#ifdef _WINDOWS64
// Global buffer that stores the text entered in the native keyboard scene.
@@ -225,38 +224,6 @@ void UIScene_Keyboard::tick()
}
}
// Paste from clipboard
if (g_KBMInput.IsKeyPressed('V') && g_KBMInput.IsKeyDown(VK_CONTROL))
{
wstring pasted = Screen::getClipboard();
wstring sanitized;
sanitized.reserve(pasted.length());
for (wchar_t pc : pasted)
{
if (pc >= 0x20) // Keep printable characters
{
if (static_cast<int>(m_win64TextBuffer.length() + sanitized.length()) >= m_win64MaxChars)
break;
sanitized += pc;
}
}
if (!sanitized.empty())
{
if (m_bPCMode)
{
m_win64TextBuffer.insert(m_iCursorPos, sanitized);
m_iCursorPos += (int)sanitized.length();
}
else
{
m_win64TextBuffer += sanitized;
}
changed = true;
}
}
if (m_bPCMode)
{
// Arrow keys, Home, End, Delete for cursor movement
@@ -24,12 +24,13 @@
#define CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME 50
#endif
int UIScene_LoadMenu::m_iDifficultyTitleSettingA[4]=
int UIScene_LoadMenu::m_iDifficultyTitleSettingA[5]=
{
IDS_DIFFICULTY_TITLE_PEACEFUL,
IDS_DIFFICULTY_TITLE_EASY,
IDS_DIFFICULTY_TITLE_NORMAL,
IDS_DIFFICULTY_TITLE_HARD
IDS_DIFFICULTY_TITLE_HARD,
IDS_GAMEMODE_HARDCORE
};
int UIScene_LoadMenu::LoadSaveDataThumbnailReturned(LPVOID lpParam,PBYTE pbThumbnail,DWORD dwThumbnailBytes)
@@ -110,6 +111,7 @@ UIScene_LoadMenu::UIScene_LoadMenu(int iPad, void *initData, UILayer *parentLaye
m_bThumbnailGetFailed = false;
m_seed = 0;
m_bIsCorrupt = false;
m_bHardcore = false;
m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad);
// 4J-PB - read the settings for the online flag. We'll only save this setting if the user changed it.
@@ -257,6 +259,30 @@ UIScene_LoadMenu::UIScene_LoadMenu(int iPad, void *initData, UILayer *parentLaye
m_levelName = wstring(wSaveName);
m_labelGameName.init(m_levelName);
}
if (params->saveDetails != nullptr)
{
// Set thumbnail name from save filename (needed for texture display in tick)
wchar_t wFilename[MAX_SAVEFILENAME_LENGTH];
ZeroMemory(wFilename, sizeof(wFilename));
mbstowcs(wFilename, params->saveDetails->UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH - 1);
m_thumbnailName = wFilename;
if (params->saveDetails->pbThumbnailData && params->saveDetails->dwThumbnailSize > 0)
{
m_pbThumbnailData = params->saveDetails->pbThumbnailData;
m_uiThumbnailSize = params->saveDetails->dwThumbnailSize;
m_bSaveThumbnailReady = true;
m_bRetrievingSaveThumbnail = false;
}
m_bHardcore = params->saveDetails->isHardcore;
if (m_bHardcore)
{
WCHAR TempString[256];
swprintf((WCHAR *)TempString, 256, L"%ls: %ls", app.GetString(IDS_SLIDER_DIFFICULTY), L"Hardcore");
m_sliderDifficulty.init(TempString, eControl_Difficulty, 0, 4, 4);
}
}
#endif
}
@@ -546,6 +572,14 @@ void UIScene_LoadMenu::tick()
{
m_MoreOptionsParams.bAllowFriendsOfFriends = TRUE;
}
m_bHardcore = app.GetGameHostOption(uiHostOptions, eGameHostOption_Hardcore) > 0;
if (m_bHardcore)
{
WCHAR TempString[256];
swprintf( (WCHAR *)TempString, 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ), L"Hardcore");
m_sliderDifficulty.init(TempString, eControl_Difficulty, 0, 4, 4);
}
}
Minecraft *pMinecraft = Minecraft::GetInstance();
@@ -950,10 +984,15 @@ void UIScene_LoadMenu::handleSliderMove(F64 sliderId, F64 currentValue)
switch(static_cast<int>(sliderId))
{
case eControl_Difficulty:
if (m_bHardcore)
{
m_sliderDifficulty.handleSliderMove(4);
break;
}
m_sliderDifficulty.handleSliderMove(value);
app.SetGameSettings(m_iPad,eGameSetting_Difficulty,value);
swprintf( (WCHAR *)TempString, 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ),app.GetString(m_iDifficultyTitleSettingA[value]));
swprintf( (WCHAR *)TempString, 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ),app.GetString(m_iDifficultyTitleSettingA[value]));
m_sliderDifficulty.setLabel(TempString);
break;
}
@@ -1167,7 +1206,7 @@ void UIScene_LoadMenu::LaunchGame(void)
#if TO_BE_IMPLEMENTED
if(eLoadStatus==C4JStorage::ELoadGame_DeviceRemoved)
{
// disable saving
// disable saving
StorageManager.SetSaveDisabled(true);
StorageManager.SetSaveDeviceSelected(m_iPad,false);
UINT uiIDA[1];
@@ -1580,6 +1619,24 @@ void UIScene_LoadMenu::StartGameFromSave(UIScene_LoadMenu* pClass, DWORD dwLocal
PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo();
#ifdef _WINDOWS64
// 4J Added: Store save folder name for potential hardcore world deletion
app.DebugPrintf("StartGameFromSave: pSaveDetails=%p, levelGen=%p, saveInfoIndex=%d\n", pSaveDetails, pClass->m_levelGen, pClass->m_iSaveGameInfoIndex);
if (pSaveDetails != nullptr && pClass->m_levelGen == nullptr)
{
app.DebugPrintf("StartGameFromSave: UTF8SaveFilename='%s'\n", pSaveDetails->SaveInfoA[(int)pClass->m_iSaveGameInfoIndex].UTF8SaveFilename);
wchar_t wFolder[MAX_SAVEFILENAME_LENGTH] = {};
mbstowcs(wFolder, pSaveDetails->SaveInfoA[(int)pClass->m_iSaveGameInfoIndex].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH - 1);
app.SetCurrentSaveFolderName(wFolder);
app.DebugPrintf("StartGameFromSave: stored folder name '%ls'\n", wFolder);
}
else
{
app.DebugPrintf("StartGameFromSave: no save details or is levelGen, clearing folder name\n");
app.SetCurrentSaveFolderName(L"");
}
#endif
NetworkGameInitData *param = new NetworkGameInitData();
param->seed = pClass->m_seed;
param->saveData = nullptr;
@@ -1612,6 +1669,7 @@ void UIScene_LoadMenu::StartGameFromSave(UIScene_LoadMenu* pClass, DWORD dwLocal
app.SetGameHostOption(eGameHostOption_DoTileDrops, pClass->m_MoreOptionsParams.bDoTileDrops);
app.SetGameHostOption(eGameHostOption_NaturalRegeneration, pClass->m_MoreOptionsParams.bNaturalRegeneration);
app.SetGameHostOption(eGameHostOption_DoDaylightCycle, pClass->m_MoreOptionsParams.bDoDaylightCycle);
app.SetGameHostOption(eGameHostOption_Hardcore, pClass->m_bHardcore ? 1 : 0);
#ifdef _LARGE_WORLDS
app.SetGameHostOption(eGameHostOption_WorldSize, pClass->m_MoreOptionsParams.worldSize+1 ); // 0 is GAME_HOST_OPTION_WORLDSIZE_UNKNOWN
@@ -15,7 +15,7 @@ private:
eControl_OnlineGame,
};
static int m_iDifficultyTitleSettingA[4];
static int m_iDifficultyTitleSettingA[5];
UIControl m_controlMainPanel;
UIControl_Label m_labelGameName, m_labelSeed, m_labelCreatedMode;
@@ -71,6 +71,7 @@ private:
wstring m_thumbnailName;
bool m_bRebuildTouchBoxes;
bool m_bHardcore;
public:
UIScene_LoadMenu(int iPad, void *initData, UILayer *parentLayer);
@@ -29,7 +29,7 @@
#include "..\..\..\Minecraft.World\NbtIo.h"
#include "..\..\..\Minecraft.World\compression.h"
static wstring ReadLevelNameFromSaveFile(const wstring& filePath)
static wstring ReadLevelNameFromSaveFile(const wstring& filePath, bool *outHardcore = nullptr)
{
// Check for a worldname.txt sidecar written by the rename feature first
size_t slashPos = filePath.rfind(L'\\');
@@ -124,7 +124,11 @@ static wstring ReadLevelNameFromSaveFile(const wstring& filePath)
{
CompoundTag *dataTag = root->getCompound(L"Data");
if (dataTag != nullptr)
{
result = dataTag->getString(L"LevelName");
if (outHardcore)
*outHardcore = dataTag->getBoolean(L"hardcore");
}
delete root;
}
}
@@ -775,7 +779,9 @@ void UIScene_LoadOrJoinMenu::tick()
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);
bool saveHardcore = false;
wstring levelName = ReadLevelNameFromSaveFile(filePath, &saveHardcore);
m_saveDetails[i].isHardcore = saveHardcore;
if (!levelName.empty())
{
m_buttonListSaves.addItem(levelName, wstring(L""));
@@ -368,7 +368,7 @@ void UIScene_MainMenu::handlePress(F64 controlId, F64 childId)
UINT uiIDA[2];
uiIDA[0]=IDS_CANCEL;
uiIDA[1]=IDS_OK;
ui.RequestErrorMessage(IDS_WINDOWS_EXIT, IDS_WARNING_ARCADE_TEXT, uiIDA, 2, XUSER_INDEX_ANY,&UIScene_MainMenu::ExitGameReturned,this);
ui.RequestErrorMessage(IDS_WARNING_ARCADE_TITLE, IDS_WARNING_ARCADE_TEXT, uiIDA, 2, XUSER_INDEX_ANY,&UIScene_MainMenu::ExitGameReturned,this);
}
else
{
+3
View File
@@ -247,11 +247,14 @@ typedef struct _SaveListDetails
#endif
#endif
bool isHardcore;
_SaveListDetails()
{
saveId = 0;
pbThumbnailData = nullptr;
dwThumbnailSize = 0;
isHardcore = false;
#ifdef _DURANGO
ZeroMemory(UTF16SaveName,sizeof(wchar_t)*128);
ZeroMemory(UTF16SaveFilename,sizeof(wchar_t)*MAX_SAVEFILENAME_LENGTH);
+36 -2
View File
@@ -11,6 +11,7 @@
#include "..\..\..\Minecraft.World\Entity.h"
#include "..\..\..\Minecraft.Client\MultiplayerLocalPlayer.h"
#include "..\..\..\Minecraft.World\Level.h"
#include "..\..\..\Minecraft.World\net.minecraft.world.level.storage.h"
#include "..\..\..\Minecraft.World\ChunkSource.h"
#include "..\..\..\Minecraft.Client\ProgressRenderer.h"
#include "..\..\..\Minecraft.Client\GameRenderer.h"
@@ -18,6 +19,7 @@
#include "..\..\..\Minecraft.World\Pos.h"
#include "..\..\..\Minecraft.World\Dimension.h"
#include "..\..\Minecraft.h"
#include "..\..\MinecraftServer.h"
#include "..\..\Options.h"
#include "..\..\LocalPlayer.h"
#include "..\..\..\Minecraft.World\compression.h"
@@ -37,9 +39,26 @@ HRESULT CScene_Death::OnInit( XUIMessageInit* pInitData, BOOL& bHandled )
}
XuiControlSetText(m_Title,app.GetString(IDS_YOU_DIED));
XuiControlSetText(m_Buttons[BUTTON_DEATH_RESPAWN],app.GetString(IDS_RESPAWN));
XuiControlSetText(m_Buttons[BUTTON_DEATH_EXITGAME],app.GetString(IDS_EXIT_GAME));
// 4J Added: In hardcore mode, disable respawn and show hardcore death message
Minecraft *pMinecraft = Minecraft::GetInstance();
bool isHardcore = false;
if (pMinecraft != nullptr && pMinecraft->level != nullptr)
{
isHardcore = pMinecraft->level->getLevelData()->isHardcore();
}
if (isHardcore)
{
XuiControlSetText(m_Buttons[BUTTON_DEATH_RESPAWN], app.GetString(IDS_HARDCORE_DEATH_MESSAGE));
XuiElementSetShow(m_Buttons[BUTTON_DEATH_RESPAWN], FALSE);
}
else
{
XuiControlSetText(m_Buttons[BUTTON_DEATH_RESPAWN], app.GetString(IDS_RESPAWN));
}
// Display the tooltips
ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT);
@@ -110,7 +129,16 @@ HRESULT CScene_Death::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNoti
playTime = static_cast<int>(pMinecraft->localplayers[pNotifyPressData->UserIndex]->getSessionTimer());
}
TelemetryManager->RecordLevelExit(pNotifyPressData->UserIndex, eSen_LevelExitStatus_Failed);
// 4J Added: Hardcore mode — skip save dialog, exit without saving, delete world
if (pMinecraft->level != nullptr && pMinecraft->level->getLevelData()->isHardcore() && g_NetworkManager.IsHost())
{
MinecraftServer::getInstance()->setSaveOnExit(false);
MinecraftServer::getInstance()->setDeleteWorldOnExit(true);
app.SetAction(pNotifyPressData->UserIndex, eAppAction_ExitWorld);
break;
}
if(StorageManager.GetSaveDisabled())
{
uiIDA[0]=IDS_CONFIRM_CANCEL;
@@ -172,6 +200,12 @@ HRESULT CScene_Death::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNoti
break;
case BUTTON_DEATH_RESPAWN:
{
// 4J Added: Safeguard - don't respawn in hardcore mode
Minecraft *pMC = Minecraft::GetInstance();
if (pMC != nullptr && pMC->level != nullptr && pMC->level->getLevelData()->isHardcore())
{
break;
}
m_bIgnoreInput = true;
app.SetAction(pNotifyPressData->UserIndex,eAppAction_Respawn);
}
+1 -1
View File
@@ -392,7 +392,7 @@ float GameRenderer::getFov(float a, bool applyEffects)
float fov = m_fov;//70;
if (applyEffects)
{
//fov += mc->options->fov * 40;
fov += mc->options->fov * 40;
fov *= oFov[playerIdx] + (this->fov[playerIdx] - oFov[playerIdx]) * a;
}
if (player->getHealth() <= 0)
+204
View File
@@ -854,6 +854,210 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
// font.draw(str, x + 1, y, 0xffffff);
// }
#ifndef _FINAL_BUILD
MemSect(31);
// temporarily render overlay at all times so version is more obvious in bug reports
// we can turn this off once things stabilize
if (true)// minecraft->options->renderDebug && minecraft->player != nullptr && minecraft->level != nullptr)
{
const int debugLeft = 1;
const int debugTop = 1;
const float maxContentWidth = 1200.f;
const float maxContentHeight = 420.f;
float scale = static_cast<float>(screenWidth - debugLeft - 8) / maxContentWidth;
float scaleV = static_cast<float>(screenHeight - debugTop - 80) / maxContentHeight;
if (scaleV < scale) scale = scaleV;
if (scale > 1.f) scale = 1.f;
if (scale < 0.5f) scale = 0.5f;
glPushMatrix();
glTranslatef(static_cast<float>(debugLeft), static_cast<float>(debugTop), 0.f);
glScalef(scale, scale, 1.f);
glTranslatef(static_cast<float>(-debugLeft), static_cast<float>(-debugTop), 0.f);
vector<wstring> lines;
// Only add version/branch lines if the watermark toggle is enabled
if (ClientConstants::SHOW_VERSION_WATERMARK) {
lines.push_back(ClientConstants::VERSION_STRING);
lines.push_back(ClientConstants::BRANCH_STRING);
}
if (minecraft->options->renderDebug && minecraft->player != nullptr && minecraft->level != nullptr)
{
lines.push_back(minecraft->fpsString);
lines.push_back(L"E: " + std::to_wstring(minecraft->level->getAllEntities().size())); // Could maybe use entity::shouldRender to work out how many are rendered but thats like expensive
// TODO Add server information with packet counts - once multiplayer is more stable
int renderDistance = app.GetGameSettings(iPad, eGameSetting_RenderDistance);
// Calculate the chunk sections using 16 * (2n + 1)^2
lines.push_back(L"C: " + std::to_wstring(16 * (2 * renderDistance + 1) * (2 * renderDistance + 1)) + L" D: " + std::to_wstring(renderDistance));
lines.push_back(minecraft->gatherStats4()); // Chunk Cache
// Dimension
wstring dimension = L"unknown";
switch (minecraft->player->dimension)
{
case -1:
dimension = L"minecraft:the_nether";
break;
case 0:
dimension = L"minecraft:overworld";
break;
case 1:
dimension = L"minecraft:the_end";
break;
}
lines.push_back(dimension);
lines.push_back(L""); // Spacer
// Players block pos
int xBlockPos = Mth::floor(minecraft->player->x);
int yBlockPos = Mth::floor(minecraft->player->y);
int zBlockPos = Mth::floor(minecraft->player->z);
// Chunk player is in
int xChunkPos = xBlockPos >> 4;
int yChunkPos = yBlockPos >> 4;
int zChunkPos = zBlockPos >> 4;
// Players offset within the chunk
int xChunkOffset = xBlockPos & 15;
int yChunkOffset = yBlockPos & 15;
int zChunkOffset = zBlockPos & 15;
// Format the position like java with limited decumal places
WCHAR posString[44]; // Allows upto 7 digit positions (+-9_999_999)
swprintf(posString, 44, L"%.3f / %.5f / %.3f", minecraft->player->x, minecraft->player->y, minecraft->player->z);
lines.push_back(L"XYZ: " + std::wstring(posString));
lines.push_back(L"Block: " + std::to_wstring(static_cast<int>(xBlockPos)) + L" " + std::to_wstring(static_cast<int>(yBlockPos)) + L" " + std::to_wstring(static_cast<int>(zBlockPos)));
lines.push_back(L"Chunk: " + std::to_wstring(xChunkOffset) + L" " + std::to_wstring(yChunkOffset) + L" " + std::to_wstring(zChunkOffset) + L" in " + std::to_wstring(xChunkPos) + L" " + std::to_wstring(yChunkPos) + L" " + std::to_wstring(zChunkPos));
// Wrap the yRot to 360 then adjust to (-180 to 180) range to match java
float yRotDisplay = fmod(minecraft->player->yRot, 360.0f);
if (yRotDisplay > 180.0f)
{
yRotDisplay -= 360.0f;
}
if (yRotDisplay < -180.0f)
{
yRotDisplay += 360.0f;
}
// Generate the angle string in the format "yRot / xRot" with one decimal place, similar to java edition
WCHAR angleString[16];
swprintf(angleString, 16, L"%.1f / %.1f", yRotDisplay, minecraft->player->xRot);
// Work out the named direction
int direction = Mth::floor(minecraft->player->yRot * 4.0f / 360.0f + 0.5) & 0x3;
wstring cardinalDirection;
switch (direction)
{
case 0:
cardinalDirection = L"south";
break;
case 1:
cardinalDirection = L"west";
break;
case 2:
cardinalDirection = L"north";
break;
case 3:
cardinalDirection = L"east";
break;
}
lines.push_back(L"Facing: " + cardinalDirection + L" (" + angleString + L")");
// We have to limit y to 256 as we don't get any information past that
if (minecraft->level != NULL && minecraft->level->hasChunkAt(xBlockPos, fmod(yBlockPos, 256), zBlockPos))
{
LevelChunk *chunkAt = minecraft->level->getChunkAt(xBlockPos, zBlockPos);
if (chunkAt != NULL)
{
int skyLight = chunkAt->getBrightness(LightLayer::Sky, xChunkOffset, yChunkOffset, zChunkOffset);
int blockLight = chunkAt->getBrightness(LightLayer::Block, xChunkOffset, yChunkOffset, zChunkOffset);
int maxLight = fmax(skyLight, blockLight);
lines.push_back(L"Light: " + std::to_wstring(maxLight) + L" (" + std::to_wstring(skyLight) + L" sky, " + std::to_wstring(blockLight) + L" block)");
lines.push_back(L"CH S: " + std::to_wstring(chunkAt->getHeightmap(xChunkOffset, zChunkOffset)));
Biome *biome = chunkAt->getBiome(xChunkOffset, zChunkOffset, minecraft->level->getBiomeSource());
lines.push_back(L"Biome: " + biome->m_name + L" (" + std::to_wstring(biome->id) + L")");
lines.push_back(L"Difficulty: " + std::to_wstring(minecraft->level->difficulty) + L" (Day " + std::to_wstring(minecraft->level->getGameTime() / Level::TICKS_PER_DAY) + L")");
}
}
// This is all LCE only stuff, it was never on java
lines.push_back(L""); // Spacer
lines.push_back(L"Seed: " + std::to_wstring(minecraft->level->getLevelData()->getSeed()));
lines.push_back(minecraft->gatherStats1()); // Time to autosave
lines.push_back(minecraft->gatherStats2()); // Empty currently - CPlatformNetworkManagerStub::GatherStats()
lines.push_back(minecraft->gatherStats3()); // RTT
}
#ifdef _DEBUG // Only show terrain features in debug builds not release
// TERRAIN FEATURES
if (minecraft->level->dimension->id == 0)
{
wstring wfeature[eTerrainFeature_Count];
wfeature[eTerrainFeature_Stronghold] = L"Stronghold: ";
wfeature[eTerrainFeature_Mineshaft] = L"Mineshaft: ";
wfeature[eTerrainFeature_Village] = L"Village: ";
wfeature[eTerrainFeature_Ravine] = L"Ravine: ";
float maxW = static_cast<float>(screenWidth - debugLeft - 8) / scale;
float maxWForContent = maxW - static_cast<float>(font->width(L"..."));
bool truncated[eTerrainFeature_Count] = {};
for (size_t i = 0; i < app.m_vTerrainFeatures.size(); i++)
{
FEATURE_DATA *pFeatureData = app.m_vTerrainFeatures[i];
int type = pFeatureData->eTerrainFeature;
if (type < eTerrainFeature_Stronghold || type > eTerrainFeature_Ravine)
{
continue;
}
if (truncated[type])
{
continue;
}
wstring itemInfo = L"[" + std::to_wstring(pFeatureData->x * 16) + L", " + std::to_wstring(pFeatureData->z * 16) + L"] ";
if (font->width(wfeature[type] + itemInfo) <= maxWForContent)
{
wfeature[type] += itemInfo;
}
else
{
wfeature[type] += L"...";
truncated[type] = true;
}
}
lines.push_back(L""); // Add a spacer line
for (int i = eTerrainFeature_Stronghold; i <= static_cast<int>(eTerrainFeature_Ravine); i++)
{
lines.push_back(wfeature[i]);
}
lines.push_back(L"");
}
#endif
// Loop through the lines and draw them all on screen
int yPos = debugTop;
for (const auto &line : lines)
{
drawString(font, line, debugLeft, yPos, 0xffffff);
yPos += 10;
}
glPopMatrix();
}
MemSect(0);
#endif
lastTickA = a;
// 4J Stu - This is now displayed in a xui scene
#if 0
+256 -265
View File
@@ -561,6 +561,7 @@ MinecraftServer::MinecraftServer()
m_serverPausedEvent = new C4JThread::Event;
m_saveOnExit = false;
m_deleteWorldOnExit = false;
m_suspending = false;
m_ugcPlayersVersion = 0;
@@ -657,7 +658,7 @@ bool MinecraftServer::initServer(int64_t seed, NetworkGameInitData *initData, DW
setFlightAllowed(GetDedicatedServerBool(settings, L"allow-flight", true));
// 4J Stu - Enabling flight to stop it kicking us when we use it
#if (defined _DEBUG_MENUS_ENABLED && defined _DEBUG)
#ifdef _DEBUG_MENUS_ENABLED
setFlightAllowed(true);
#endif
@@ -887,6 +888,15 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring
// }
ProgressRenderer *mcprogress = Minecraft::GetInstance()->progressRenderer;
// 4J Added - store save folder name for potential hardcore world deletion
{
char szSaveFolder[MAX_SAVEFILENAME_LENGTH] = {};
StorageManager.GetSaveUniqueFilename(szSaveFolder);
wchar_t wSaveFolder[MAX_SAVEFILENAME_LENGTH] = {};
mbstowcs(wSaveFolder, szSaveFolder, MAX_SAVEFILENAME_LENGTH - 1);
m_saveFolderName = wSaveFolder;
}
// 4J TODO - free levels here if there are already some?
levels = ServerLevelArray(3);
@@ -1642,7 +1652,7 @@ bool MinecraftServer::isNetherEnabled()
bool MinecraftServer::isHardcore()
{
return false;
return app.GetGameHostOption(eGameHostOption_Hardcore) > 0;
}
int MinecraftServer::getOperatorUserPermissionLevel()
@@ -1715,345 +1725,330 @@ void MinecraftServer::setPlayerIdleTimeout(int playerIdleTimeout)
extern int c0a, c0b, c1a, c1b, c1c, c2a, c2b;
void MinecraftServer::run(int64_t seed, void *lpParameter)
{
NetworkGameInitData *initData = nullptr;
DWORD initSettings = 0;
bool findSeed = false;
NetworkGameInitData *initData = nullptr;
DWORD initSettings = 0;
bool findSeed = false;
if(lpParameter != nullptr)
{
initData = static_cast<NetworkGameInitData *>(lpParameter);
initSettings = app.GetGameHostOption(eGameHostOption_All);
findSeed = initData->findSeed;
m_texturePackId = initData->texturePackId;
}
// try { // 4J - removed try/catch/finally
bool didInit = false;
{
initData = static_cast<NetworkGameInitData *>(lpParameter);
initSettings = app.GetGameHostOption(eGameHostOption_All);
findSeed = initData->findSeed;
m_texturePackId = initData->texturePackId;
}
// try { // 4J - removed try/catch/finally
bool didInit = false;
if (initServer(seed, initData, initSettings,findSeed))
{
didInit = true;
ServerLevel *levelNormalDimension = levels[0];
// 4J-PB - Set the Stronghold position in the leveldata if there isn't one in there
Minecraft *pMinecraft = Minecraft::GetInstance();
{
didInit = true;
ServerLevel *levelNormalDimension = levels[0];
// 4J-PB - Set the Stronghold position in the leveldata if there isn't one in there
Minecraft *pMinecraft = Minecraft::GetInstance();
LevelData *pLevelData=levelNormalDimension->getLevelData();
if(pLevelData && pLevelData->getHasStronghold()==false)
{
{
int x,z;
if(app.GetTerrainFeaturePosition(eTerrainFeature_Stronghold,&x,&z))
{
pLevelData->setXStronghold(x);
pLevelData->setZStronghold(z);
pLevelData->setHasStronghold();
}
}
{
pLevelData->setXStronghold(x);
pLevelData->setZStronghold(z);
pLevelData->setHasStronghold();
}
}
int64_t lastTime = getCurrentTimeMillis();
int64_t unprocessedTime = 0;
while (running && !s_bServerHalted)
{
int64_t now = getCurrentTimeMillis();
int64_t lastTime = getCurrentTimeMillis();
int64_t unprocessedTime = 0;
while (running && !s_bServerHalted)
{
int64_t now = getCurrentTimeMillis();
// 4J Stu - When we pause the server, we don't want to count that as time passed
// 4J Stu - TU-1 hotifx - Remove this line. We want to make sure that we tick connections at the proper rate when paused
// 4J Stu - When we pause the server, we don't want to count that as time passed
// 4J Stu - TU-1 hotifx - Remove this line. We want to make sure that we tick connections at the proper rate when paused
//Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost
//if(m_isServerPaused) lastTime = now;
int64_t passedTime = now - lastTime;
if (passedTime > MS_PER_TICK * 40)
{
// logger.warning("Can't keep up! Did the system time change, or is the server overloaded?");
passedTime = MS_PER_TICK * 40;
}
if (passedTime < 0)
{
// logger.warning("Time ran backwards! Did the system time change?");
passedTime = 0;
}
unprocessedTime += passedTime;
lastTime = now;
int64_t passedTime = now - lastTime;
if (passedTime > MS_PER_TICK * 40)
{
// logger.warning("Can't keep up! Did the system time change, or is the server overloaded?");
passedTime = MS_PER_TICK * 40;
}
if (passedTime < 0)
{
// logger.warning("Time ran backwards! Did the system time change?");
passedTime = 0;
}
unprocessedTime += passedTime;
lastTime = now;
// 4J Added ability to pause the server
// 4J Added ability to pause the server
if( !m_isServerPaused )
{
bool didTick = false;
if (levels[0]->allPlayersAreSleeping())
{
tick();
unprocessedTime = 0;
}
else
{
// int tickcount = 0;
// int64_t beforeall = System::currentTimeMillis();
while (unprocessedTime > MS_PER_TICK)
{
unprocessedTime -= MS_PER_TICK;
chunkPacketManagement_PreTick();
// int64_t before = System::currentTimeMillis();
tick();
// int64_t after = System::currentTimeMillis();
// PIXReportCounter(L"Server time",(float)(after-before));
{
bool didTick = false;
if (levels[0]->allPlayersAreSleeping())
{
tick();
unprocessedTime = 0;
}
else
{
// int tickcount = 0;
// int64_t beforeall = System::currentTimeMillis();
while (unprocessedTime > MS_PER_TICK)
{
unprocessedTime -= MS_PER_TICK;
chunkPacketManagement_PreTick();
// int64_t before = System::currentTimeMillis();
tick();
// int64_t after = System::currentTimeMillis();
// PIXReportCounter(L"Server time",(float)(after-before));
chunkPacketManagement_PostTick();
}
// int64_t afterall = System::currentTimeMillis();
// PIXReportCounter(L"Server time all",(float)(afterall-beforeall));
// PIXReportCounter(L"Server ticks",(float)tickcount);
}
}
else
{
// 4J Stu - TU1-hotfix
chunkPacketManagement_PostTick();
}
// int64_t afterall = System::currentTimeMillis();
// PIXReportCounter(L"Server time all",(float)(afterall-beforeall));
// PIXReportCounter(L"Server ticks",(float)tickcount);
}
}
else
{
// 4J Stu - TU1-hotfix
//Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost
// The connections should tick at the same frequency even when paused
while (unprocessedTime > MS_PER_TICK)
{
unprocessedTime -= MS_PER_TICK;
// Keep ticking the connections to stop them timing out
connection->tick();
}
}
// The connections should tick at the same frequency even when paused
while (unprocessedTime > MS_PER_TICK)
{
unprocessedTime -= MS_PER_TICK;
// Keep ticking the connections to stop them timing out
connection->tick();
}
}
if(MinecraftServer::setTimeAtEndOfTick)
{
MinecraftServer::setTimeAtEndOfTick = false;
for (unsigned int i = 0; i < levels.length; i++)
{
// if (i == 0 || settings->getBoolean(L"allow-nether", true)) // 4J removed - we always have nether
{
ServerLevel *level = levels[i];
{
MinecraftServer::setTimeAtEndOfTick = false;
for (unsigned int i = 0; i < levels.length; i++)
{
// if (i == 0 || settings->getBoolean(L"allow-nether", true)) // 4J removed - we always have nether
{
ServerLevel *level = levels[i];
level->setGameTime( MinecraftServer::setTime );
}
}
}
}
}
}
if(MinecraftServer::setTimeOfDayAtEndOfTick)
{
MinecraftServer::setTimeOfDayAtEndOfTick = false;
for (unsigned int i = 0; i < levels.length; i++)
{
if (i == 0 || GetDedicatedServerBool(settings, L"allow-nether", true))
{
ServerLevel *level = levels[i];
{
MinecraftServer::setTimeOfDayAtEndOfTick = false;
for (unsigned int i = 0; i < levels.length; i++)
{
if (i == 0 || GetDedicatedServerBool(settings, L"allow-nether", true))
{
ServerLevel *level = levels[i];
level->setDayTime( MinecraftServer::setTimeOfDay );
}
}
}
}
}
}
// Process delayed actions
eXuiServerAction eAction;
LPVOID param;
// Process delayed actions
eXuiServerAction eAction;
LPVOID param;
for(int i=0;i<XUSER_MAX_COUNT;i++)
{
eAction = app.GetXuiServerAction(i);
param = app.GetXuiServerActionParam(i);
{
eAction = app.GetXuiServerAction(i);
param = app.GetXuiServerActionParam(i);
switch(eAction)
{
case eXuiServerAction_AutoSaveGame:
#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(MINECRAFT_SERVER_BUILD)
{
{
case eXuiServerAction_AutoSaveGame:
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXBeginNamedEvent(0, "Autosave");
{
PIXBeginNamedEvent(0,"Autosave");
// Get the frequency of the timer
LARGE_INTEGER qwTicksPerSec, qwTime, qwNewTime, qwDeltaTime;
float fElapsedTime = 0.0f;
QueryPerformanceFrequency(&qwTicksPerSec);
float fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart;
// Get the frequency of the timer
LARGE_INTEGER qwTicksPerSec, qwTime, qwNewTime, qwDeltaTime;
float fElapsedTime = 0.0f;
QueryPerformanceFrequency( &qwTicksPerSec );
float fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart;
// Save the start time
QueryPerformanceCounter(&qwTime);
#endif
// Save the start time
QueryPerformanceCounter( &qwTime );
if (players != nullptr)
{
players->saveAll(nullptr);
}
if (players != nullptr)
{
players->saveAll(nullptr);
}
for (unsigned int j = 0; j < levels.length; j++)
{
for (unsigned int j = 0; j < levels.length; j++)
{
if( s_bServerHalted ) break;
// 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
// with the data from the nethers leveldata.
// Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
ServerLevel *level = levels[levels.length - 1 - j];
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXBeginNamedEvent(0, "Saving level %d", levels.length - 1 - j);
#endif
level->save(false, nullptr, true);
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXEndNamedEvent();
#endif
}
if (!s_bServerHalted)
{
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXBeginNamedEvent(0, "Saving game rules");
#endif
saveGameRules();
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXEndNamedEvent();
// 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
// with the data from the nethers leveldata.
// Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
ServerLevel *level = levels[levels.length - 1 - j];
PIXBeginNamedEvent(0, "Saving level %d",levels.length - 1 - j);
level->save(false, nullptr, true);
PIXEndNamedEvent();
}
if( !s_bServerHalted )
{
PIXBeginNamedEvent(0,"Saving game rules");
saveGameRules();
PIXEndNamedEvent();
PIXBeginNamedEvent(0, "Save to disc");
#endif
levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, true);
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXEndNamedEvent();
#endif
}
PIXBeginNamedEvent(0,"Save to disc");
levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, true);
PIXEndNamedEvent();
}
PIXEndNamedEvent();
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXEndNamedEvent();
QueryPerformanceCounter(&qwNewTime);
qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart;
fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart));
app.DebugPrintf("Autosave: Elapsed time %f\n", fElapsedTime);
QueryPerformanceCounter( &qwNewTime );
qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart;
fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart));
app.DebugPrintf("Autosave: Elapsed time %f\n", fElapsedTime);
}
break;
#endif
}
break;
#endif
case eXuiServerAction_SaveGame:
app.EnterSaveNotificationSection();
if (players != nullptr)
{
players->saveAll(Minecraft::GetInstance()->progressRenderer);
}
case eXuiServerAction_SaveGame:
app.EnterSaveNotificationSection();
if (players != nullptr)
{
players->saveAll(Minecraft::GetInstance()->progressRenderer);
}
players->broadcastAll(std::make_shared<UpdateProgressPacket>(20));
players->broadcastAll(std::make_shared<UpdateProgressPacket>(20));
for (unsigned int j = 0; j < levels.length; j++)
{
for (unsigned int j = 0; j < levels.length; j++)
{
if( s_bServerHalted ) break;
// 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
// with the data from the nethers leveldata.
// Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
ServerLevel *level = levels[levels.length - 1 - j];
// 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
// with the data from the nethers leveldata.
// Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
ServerLevel *level = levels[levels.length - 1 - j];
level->save(true, Minecraft::GetInstance()->progressRenderer, (eAction==eXuiServerAction_AutoSaveGame));
players->broadcastAll(std::make_shared<UpdateProgressPacket>(33 + (j * 33)));
}
players->broadcastAll(std::make_shared<UpdateProgressPacket>(33 + (j * 33)));
}
if( !s_bServerHalted )
{
saveGameRules();
{
saveGameRules();
levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, (eAction==eXuiServerAction_AutoSaveGame));
}
app.LeaveSaveNotificationSection();
break;
case eXuiServerAction_DropItem:
// Find the player, and drop the id at their feet
{
shared_ptr<ServerPlayer> player = players->players.at(0);
}
app.LeaveSaveNotificationSection();
break;
case eXuiServerAction_DropItem:
// Find the player, and drop the id at their feet
{
shared_ptr<ServerPlayer> player = players->players.at(0);
size_t id = (size_t) param;
player->drop(std::make_shared<ItemInstance>(id, 1, 0));
}
break;
case eXuiServerAction_SpawnMob:
{
shared_ptr<ServerPlayer> player = players->players.at(0);
eINSTANCEOF factory = static_cast<eINSTANCEOF>((size_t)param);
player->drop(std::make_shared<ItemInstance>(id, 1, 0));
}
break;
case eXuiServerAction_SpawnMob:
{
shared_ptr<ServerPlayer> player = players->players.at(0);
eINSTANCEOF factory = static_cast<eINSTANCEOF>((size_t)param);
shared_ptr<Mob> mob = dynamic_pointer_cast<Mob>(EntityIO::newByEnumType(factory,player->level ));
mob->moveTo(player->x+1, player->y, player->z+1, player->level->random->nextFloat() * 360, 0);
mob->setDespawnProtected(); // 4J added, default to being protected against despawning (has to be done after initial position is set)
player->level->addEntity(mob);
}
break;
case eXuiServerAction_PauseServer:
player->level->addEntity(mob);
}
break;
case eXuiServerAction_PauseServer:
m_isServerPaused = ( (size_t) param == TRUE );
if( m_isServerPaused )
{
m_serverPausedEvent->Set();
}
break;
case eXuiServerAction_ToggleRain:
{
bool isRaining = levels[0]->getLevelData()->isRaining();
levels[0]->getLevelData()->setRaining(!isRaining);
levels[0]->getLevelData()->setRainTime(levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + Level::TICKS_PER_DAY / 2);
}
break;
case eXuiServerAction_ToggleThunder:
{
bool isThundering = levels[0]->getLevelData()->isThundering();
levels[0]->getLevelData()->setThundering(!isThundering);
levels[0]->getLevelData()->setThunderTime(levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + Level::TICKS_PER_DAY / 2);
}
break;
case eXuiServerAction_ServerSettingChanged_Gamertags:
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_OPTIONS, app.GetGameHostOption(eGameHostOption_Gamertags)));
break;
case eXuiServerAction_ServerSettingChanged_BedrockFog:
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS, app.GetGameHostOption(eGameHostOption_All)));
break;
{
m_serverPausedEvent->Set();
}
break;
case eXuiServerAction_ToggleRain:
{
bool isRaining = levels[0]->getLevelData()->isRaining();
levels[0]->getLevelData()->setRaining(!isRaining);
levels[0]->getLevelData()->setRainTime(levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + Level::TICKS_PER_DAY / 2);
}
break;
case eXuiServerAction_ToggleThunder:
{
bool isThundering = levels[0]->getLevelData()->isThundering();
levels[0]->getLevelData()->setThundering(!isThundering);
levels[0]->getLevelData()->setThunderTime(levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + Level::TICKS_PER_DAY / 2);
}
break;
case eXuiServerAction_ServerSettingChanged_Gamertags:
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_OPTIONS, app.GetGameHostOption(eGameHostOption_Gamertags)));
break;
case eXuiServerAction_ServerSettingChanged_BedrockFog:
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS, app.GetGameHostOption(eGameHostOption_All)));
break;
case eXuiServerAction_ServerSettingChanged_Difficulty:
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_DIFFICULTY, Minecraft::GetInstance()->options->difficulty));
break;
case eXuiServerAction_ExportSchematic:
case eXuiServerAction_ServerSettingChanged_Difficulty:
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_DIFFICULTY, Minecraft::GetInstance()->options->difficulty));
break;
case eXuiServerAction_ExportSchematic:
#ifndef _CONTENT_PACKAGE
app.EnterSaveNotificationSection();
app.EnterSaveNotificationSection();
//players->broadcastAll( shared_ptr<UpdateProgressPacket>( new UpdateProgressPacket(20) ) );
if( !s_bServerHalted )
{
ConsoleSchematicFile::XboxSchematicInitParam *initData = static_cast<ConsoleSchematicFile::XboxSchematicInitParam *>(param);
{
ConsoleSchematicFile::XboxSchematicInitParam *initData = static_cast<ConsoleSchematicFile::XboxSchematicInitParam *>(param);
#ifdef _XBOX
File targetFileDir(File::pathRoot + File::pathSeparator + L"Schematics");
File targetFileDir(File::pathRoot + File::pathSeparator + L"Schematics");
#else
File targetFileDir(L"Schematics");
File targetFileDir(L"Schematics");
#endif
if(!targetFileDir.exists()) targetFileDir.mkdir();
wchar_t filename[128];
wchar_t filename[128];
swprintf(filename,128,L"%ls%dx%dx%d.sch",initData->name,(initData->endX - initData->startX + 1), (initData->endY - initData->startY + 1), (initData->endZ - initData->startZ + 1));
File dataFile = File( targetFileDir, wstring(filename) );
if(dataFile.exists()) dataFile._delete();
FileOutputStream fos = FileOutputStream(dataFile);
DataOutputStream dos = DataOutputStream(&fos);
ConsoleSchematicFile::generateSchematicFile(&dos, levels[0], initData->startX, initData->startY, initData->startZ, initData->endX, initData->endY, initData->endZ, initData->bSaveMobs, initData->compressionType);
dos.close();
FileOutputStream fos = FileOutputStream(dataFile);
DataOutputStream dos = DataOutputStream(&fos);
ConsoleSchematicFile::generateSchematicFile(&dos, levels[0], initData->startX, initData->startY, initData->startZ, initData->endX, initData->endY, initData->endZ, initData->bSaveMobs, initData->compressionType);
dos.close();
delete initData;
}
app.LeaveSaveNotificationSection();
delete initData;
}
app.LeaveSaveNotificationSection();
#endif
break;
case eXuiServerAction_SetCameraLocation:
break;
case eXuiServerAction_SetCameraLocation:
#ifndef _CONTENT_PACKAGE
{
DebugSetCameraPosition *pos = static_cast<DebugSetCameraPosition *>(param);
{
DebugSetCameraPosition *pos = static_cast<DebugSetCameraPosition *>(param);
app.DebugPrintf( "DEBUG: Player=%i\n", pos->player );
app.DebugPrintf( "DEBUG: Teleporting to pos=(%f.2, %f.2, %f.2), looking at=(%f.2,%f.2)\n",
pos->m_camX, pos->m_camY, pos->m_camZ,
pos->m_camX, pos->m_camY, pos->m_camZ,
pos->m_yRot, pos->m_elev
);
shared_ptr<ServerPlayer> player = players->players.at(pos->player);
shared_ptr<ServerPlayer> player = players->players.at(pos->player);
player->debug_setPosition( pos->m_camX, pos->m_camY, pos->m_camZ,
pos->m_yRot, pos->m_elev );
// Doesn't work
// Doesn't work
//player->setYHeadRot(pos->m_yRot);
//player->absMoveTo(pos->m_camX, pos->m_camY, pos->m_camZ, pos->m_yRot, pos->m_elev);
}
}
#endif
break;
}
break;
}
app.SetXuiServerAction(i,eXuiServerAction_Idle);
}
}
Sleep(1);
}
}
Sleep(1);
}
}
//else
//{
//{
// while (running)
// {
// {
// handleConsoleInputs();
// Sleep(10);
// Sleep(10);
// }
//}
#if 0
@@ -2080,9 +2075,9 @@ void MinecraftServer::run(int64_t seed, void *lpParameter)
}
#endif
// 4J Stu - Stop the server when the loops complete, as the finally would do
stopServer(didInit);
stopped = true;
// 4J Stu - Stop the server when the loops complete, as the finally would do
stopServer(didInit);
stopped = true;
}
void MinecraftServer::broadcastStartSavingPacket()
@@ -2390,9 +2385,6 @@ bool MinecraftServer::chunkPacketManagement_CanSendTo(INetworkPlayer *player)
{
if( player == nullptr ) return false;
#ifdef MINECRAFT_SERVER_BUILD
return true;
#else
int time = GetTickCount();
DWORD currentPlayerCount = g_NetworkManager.GetPlayerCount();
if( currentPlayerCount == 0 ) return false;
@@ -2404,7 +2396,6 @@ bool MinecraftServer::chunkPacketManagement_CanSendTo(INetworkPlayer *player)
}
return false;
#endif
}
void MinecraftServer::chunkPacketManagement_DidSendTo(INetworkPlayer *player)
+5
View File
@@ -265,6 +265,8 @@ private:
private:
// 4J Added
bool m_saveOnExit;
bool m_deleteWorldOnExit; // 4J Added - for hardcore mode world deletion
wstring m_saveFolderName; // 4J Added - stored for hardcore world deletion
bool m_suspending;
public:
@@ -278,6 +280,9 @@ public:
void chunkPacketManagement_PostTick();
void setSaveOnExit(bool save) { m_saveOnExit = save; s_bSaveOnExitAnswered = true; }
void setDeleteWorldOnExit(bool del) { m_deleteWorldOnExit = del; }
bool getDeleteWorldOnExit() const { return m_deleteWorldOnExit; }
const wstring& getSaveFolderName() const { return m_saveFolderName; }
void Suspend();
bool IsSuspending();
+4 -3
View File
@@ -1,6 +1,7 @@
#include "ConsoleInputSource.h"
#include "..\Minecraft.World\PacketListener.h"
#include "..\Minecraft.World\JavaIntHash.h"
#include <atomic>
class MinecraftServer;
class Connection;
@@ -130,8 +131,8 @@ public:
void setShowOnMaps(bool bVal);
void setWasKicked() { m_bWasKicked = true; }
bool getWasKicked() { return m_bWasKicked; }
void setWasKicked() { m_bWasKicked.store(true); }
bool getWasKicked() { return m_bWasKicked.load(); }
// 4J Added
bool hasClientTickedOnce() { return m_bHasClientTickedOnce; }
@@ -140,5 +141,5 @@ private:
bool m_bCloseOnTick;
vector<wstring> m_texturesRequested;
bool m_bWasKicked;
std::atomic<bool> m_bWasKicked{false};
};
+43 -3
View File
@@ -67,6 +67,7 @@ PlayerList::PlayerList(MinecraftServer *server)
int rawMax = server->settings->getInt(L"max-players", 8);
maxPlayers = static_cast<unsigned int>(Mth::clamp(rawMax, 1, MINECRAFT_NET_MAX_PLAYERS));
doWhiteList = false;
InitializeCriticalSection(&m_banCS);
InitializeCriticalSection(&m_kickPlayersCS);
InitializeCriticalSection(&m_closePlayersCS);
}
@@ -80,6 +81,7 @@ PlayerList::~PlayerList()
player->gameMode = nullptr;
}
DeleteCriticalSection(&m_banCS);
DeleteCriticalSection(&m_kickPlayersCS);
DeleteCriticalSection(&m_closePlayersCS);
}
@@ -271,7 +273,8 @@ bool PlayerList::placeNewPlayer(Connection *connection, shared_ptr<ServerPlayer>
static_cast<BYTE>(playerIndex), level->useNewSeaLevel(),
player->getAllPlayerGamePrivileges(),
level->getLevelData()->getXZSize(),
level->getLevelData()->getHellScale()));
level->getLevelData()->getHellScale(),
level->getLevelData()->isHardcore()));
playerConnection->send(std::make_shared<SetSpawnPositionPacket>(spawnPos->x, spawnPos->y, spawnPos->z));
playerConnection->send(std::make_shared<PlayerAbilitiesPacket>(&player->abilities));
playerConnection->send(std::make_shared<SetCarriedItemPacket>(player->inventory->selected));
@@ -702,6 +705,13 @@ shared_ptr<ServerPlayer> PlayerList::respawn(shared_ptr<ServerPlayer> serverPlay
// necessary)
updatePlayerGameMode(player, serverPlayer, level);
// 4J Added: Hardcore mode — force Adventure mode on respawn
if (server->getLevel(0)->getLevelData()->isHardcore())
{
player->gameMode->setGameModeForPlayer(GameType::ADVENTURE);
player->connection->send(std::make_shared<GameEventPacket>(GameEventPacket::CHANGE_GAME_MODE, GameType::ADVENTURE->getId()));
}
if(serverPlayer->wonGame && targetDimension == oldDimension && serverPlayer->getHealth() > 0)
{
// If the player is still alive and respawning to the same dimension, they are just being added back from someone else viewing the Win screen
@@ -739,7 +749,7 @@ shared_ptr<ServerPlayer> PlayerList::respawn(shared_ptr<ServerPlayer> serverPlay
player->connection->send( std::make_shared<RespawnPacket>( static_cast<char>(player->dimension), player->level->getSeed(), player->level->getMaxBuildHeight(),
player->gameMode->getGameModeForPlayer(), level->difficulty, level->getLevelData()->getGenerator(),
player->level->useNewSeaLevel(), player->entityId, level->getLevelData()->getXZSize(), level->getLevelData()->getHellScale() ) );
player->level->useNewSeaLevel(), player->entityId, level->getLevelData()->getXZSize(), level->getLevelData()->getHellScale(), level->getLevelData()->isHardcore() ) );
player->connection->teleport(player->x, player->y, player->z, player->yRot, player->xRot);
player->connection->send( std::make_shared<SetExperiencePacket>( player->experienceProgress, player->totalExperience, player->experienceLevel) );
@@ -856,7 +866,7 @@ void PlayerList::toggleDimension(shared_ptr<ServerPlayer> player, int targetDime
player->connection->send(std::make_shared<RespawnPacket>(static_cast<char>(player->dimension), newLevel->getSeed(), newLevel->getMaxBuildHeight(),
player->gameMode->getGameModeForPlayer(), newLevel->difficulty, newLevel->getLevelData()->getGenerator(),
newLevel->useNewSeaLevel(), player->entityId, newLevel->getLevelData()->getXZSize(), newLevel->getLevelData()->getHellScale()));
newLevel->useNewSeaLevel(), player->entityId, newLevel->getLevelData()->getXZSize(), newLevel->getLevelData()->getHellScale(), newLevel->getLevelData()->isHardcore()));
oldLevel->removeEntityImmediately(player);
player->removed = false;
@@ -1670,6 +1680,7 @@ bool PlayerList::isXuidBanned(PlayerUID xuid)
bool banned = false;
EnterCriticalSection(&m_banCS);
for(PlayerUID it : m_bannedXuids)
{
if( ProfileManager.AreXUIDSEqual( xuid, it ) )
@@ -1678,6 +1689,7 @@ bool PlayerList::isXuidBanned(PlayerUID xuid)
break;
}
}
LeaveCriticalSection(&m_banCS);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (!banned && g_Win64DedicatedServer)
@@ -1689,6 +1701,34 @@ bool PlayerList::isXuidBanned(PlayerUID xuid)
return banned;
}
void PlayerList::banXuid(PlayerUID xuid)
{
// 4J Added - for hardcore mode ban-on-death
// Ban a player's XUID. Used when a player dies in a hardcore multiplayer world.
if(xuid == INVALID_XUID) return;
EnterCriticalSection(&m_banCS);
// Check if already banned
bool alreadyBanned = false;
for(PlayerUID it : m_bannedXuids)
{
if( ProfileManager.AreXUIDSEqual( xuid, it ) )
{
alreadyBanned = true;
break;
}
}
if(!alreadyBanned)
{
m_bannedXuids.push_back(xuid);
app.DebugPrintf("PlayerList::banXuid - Player XUID banned for hardcore death\n");
}
LeaveCriticalSection(&m_banCS);
}
// AP added for Vita so the range can be increased once the level starts
void PlayerList::setViewDistance(int newViewDistance)
{
+2
View File
@@ -31,6 +31,7 @@ private:
// 4J Added
vector<PlayerUID> m_bannedXuids;
CRITICAL_SECTION m_banCS; // 4J Added - protects m_bannedXuids for concurrent access
deque<BYTE> m_smallIdsToKick;
CRITICAL_SECTION m_kickPlayersCS;
deque<BYTE> m_smallIdsToClose;
@@ -135,6 +136,7 @@ public:
void closePlayerConnectionBySmallId(BYTE networkSmallId);
void queueSmallIdForRecycle(BYTE smallId);
bool isXuidBanned(PlayerUID xuid);
void banXuid(PlayerUID xuid); // 4J Added - for hardcore mode ban-on-death
// AP added for Vita so the range can be increased once the level starts
void setViewDistance(int newViewDistance);
};
+1 -1
View File
@@ -204,7 +204,7 @@ void Screen::updateEvents()
// Map to Screen::keyPressed
int mappedKey = -1;
wchar_t ch = 0;
if (vk == VK_ESCAPE) mappedKey = Keyboard::KEY_ESCAPE;
if (vk == VK_ESCAPE) mappedKey = Keyboard::KEY_ESCAPE;
else if (vk == VK_RETURN) mappedKey = Keyboard::KEY_RETURN;
else if (vk == VK_BACK) mappedKey = Keyboard::KEY_BACK;
else if (vk == VK_UP) mappedKey = Keyboard::KEY_UP;
+6
View File
@@ -303,6 +303,12 @@ void SelectWorldScreen::WorldSelectionList::renderItem(int i, int x, int y, int
info = parent->conversionLang + L" " + info;
}
// 4J Added: Show [Hardcore] badge for hardcore worlds
if (levelSummary->isHardcore())
{
name = name + L" [Hardcore]";
}
parent->drawString(parent->font, name, x + 2, y + 1, 0xffffff);
parent->drawString(parent->font, id, x + 2, y + 12, 0x808080);
parent->drawString(parent->font, info, x + 2, y + 12 + 10, 0x808080);
+28 -22
View File
@@ -146,8 +146,7 @@ void ServerPlayer::flagEntitiesToBeRemoved(unsigned int *flags, bool *removedFou
if( ( *removedFound ) == false )
{
*removedFound = true;
// before this left 192 bytes uninitialized!!!!!
memset(flags, 0, (2048 / 32) * sizeof(unsigned int));
memset(flags, 0, 2048/32);
}
for(int index : entitiesToRemove)
@@ -377,9 +376,6 @@ void ServerPlayer::doChunkSendingTick(bool dontDelayChunks)
// connection->done);
// }
#ifdef MINECRAFT_SERVER_BUILD
if (dontDelayChunks || (canSendToPlayer && !connection->done))
#else
if( dontDelayChunks ||
(canSendToPlayer &&
#ifdef _XBOX_ONE
@@ -394,22 +390,21 @@ void ServerPlayer::doChunkSendingTick(bool dontDelayChunks)
#endif
//(tickCount - lastBrupSendTickCount) > (connection->getNetworkPlayer()->GetCurrentRtt()>>4) &&
!connection->done) )
#endif
{
lastBrupSendTickCount = tickCount;
okToSend = true;
MinecraftServer::chunkPacketManagement_DidSendTo(connection->getNetworkPlayer());
{
lastBrupSendTickCount = tickCount;
okToSend = true;
MinecraftServer::chunkPacketManagement_DidSendTo(connection->getNetworkPlayer());
// static unordered_map<wstring,int64_t> mapLastTime;
// int64_t thisTime = System::currentTimeMillis();
// int64_t lastTime = mapLastTime[connection->getNetworkPlayer()->GetUID().toString()];
// app.DebugPrintf(" - OK to send (%d ms since last)\n", thisTime - lastTime);
// mapLastTime[connection->getNetworkPlayer()->GetUID().toString()] = thisTime;
}
else
{
// app.DebugPrintf(" - <NOT OK>\n");
}
// static unordered_map<wstring,int64_t> mapLastTime;
// int64_t thisTime = System::currentTimeMillis();
// int64_t lastTime = mapLastTime[connection->getNetworkPlayer()->GetUID().toString()];
// app.DebugPrintf(" - OK to send (%d ms since last)\n", thisTime - lastTime);
// mapLastTime[connection->getNetworkPlayer()->GetUID().toString()] = thisTime;
}
else
{
// app.DebugPrintf(" - <NOT OK>\n");
}
}
if (okToSend)
@@ -569,6 +564,17 @@ void ServerPlayer::die(DamageSource *source)
{
server->getPlayers()->broadcastAll(getCombatTracker()->getDeathMessagePacket());
// 4J Added: Hardcore mode — switch to Adventure mode on death (can look but not break/place blocks)
if (level->getLevelData()->isHardcore())
{
setGameMode(GameType::ADVENTURE);
// Ban this player's XUID and force-save so the host
// cannot circumvent the death by quitting without saving.
server->getPlayers()->banXuid(getOnlineXuid());
app.SetXuiServerAction(ProfileManager.GetPrimaryPad(), eXuiServerAction_SaveGame);
}
if (!level->getGameRules()->getBoolean(GameRules::RULE_KEEPINVENTORY))
{
inventory->dropAll();
@@ -1609,9 +1615,9 @@ bool ServerPlayer::hasPermission(EGameCommand command)
//
// // 4J - Don't need
// //if (server.isSingleplayer() && server.getSingleplayerName().equals(name))
// //{
/// //{
// // server.setDifficulty(packet.getDifficulty());
// //}
/// //}
//}
int ServerPlayer::getViewDistance()
@@ -25,20 +25,12 @@ public:
static const int KEY_DROP = 'Q';
static const int KEY_CRAFTING = 'C';
static const int KEY_CRAFTING_ALT = 'R';
static const int KEY_CHAT = 'T';
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_TOGGLE_HUD = VK_F1;
static const int KEY_THIRD_PERSON = VK_F5;
static const int KEY_DEBUG_INFO = VK_F3;
static const int KEY_DEBUG_MENU = VK_F4;
static const int KEY_THIRD_PERSON = VK_F5;
static const int KEY_DEBUG_CONSOLE = VK_F6;
static const int KEY_HOST_SETTINGS = VK_F8;
static const int KEY_FULLSCREEN = VK_F11;
// todo: implement and shi
static const int KEY_SCREENSHOT = VK_F2;
void Init();
void Tick();
@@ -121,6 +121,7 @@ static WINDOWPLACEMENT g_wpPrev = { sizeof(g_wpPrev) };
struct Win64LaunchOptions
{
int screenMode;
bool serverMode;
bool fullscreen;
};
@@ -206,9 +207,13 @@ static Win64LaunchOptions ParseLaunchOptions()
{
Win64LaunchOptions options = {};
options.screenMode = 0;
options.serverMode = false;
g_Win64MultiplayerJoin = false;
g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT;
g_Win64DedicatedServer = false;
g_Win64DedicatedServerPort = WIN64_NET_DEFAULT_PORT;
g_Win64DedicatedServerBindIP[0] = 0;
int argc = 0;
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
@@ -221,6 +226,17 @@ static Win64LaunchOptions ParseLaunchOptions()
options.screenMode = argv[1][0] - L'0';
}
for (int i = 1; i < argc; ++i)
{
if (_wcsicmp(argv[i], L"-server") == 0)
{
options.serverMode = true;
break;
}
}
g_Win64DedicatedServer = options.serverMode;
for (int i = 1; i < argc; ++i)
{
if (_wcsicmp(argv[i], L"-name") == 0 && (i + 1) < argc)
@@ -231,8 +247,15 @@ static Win64LaunchOptions ParseLaunchOptions()
{
char ipBuf[256];
CopyWideArgToAnsi(argv[++i], ipBuf, sizeof(ipBuf));
strncpy_s(g_Win64MultiplayerIP, sizeof(g_Win64MultiplayerIP), ipBuf, _TRUNCATE);
g_Win64MultiplayerJoin = true;
if (options.serverMode)
{
strncpy_s(g_Win64DedicatedServerBindIP, sizeof(g_Win64DedicatedServerBindIP), ipBuf, _TRUNCATE);
}
else
{
strncpy_s(g_Win64MultiplayerIP, sizeof(g_Win64MultiplayerIP), ipBuf, _TRUNCATE);
g_Win64MultiplayerJoin = true;
}
}
else if (_wcsicmp(argv[i], L"-port") == 0 && (i + 1) < argc)
{
@@ -240,7 +263,10 @@ static Win64LaunchOptions ParseLaunchOptions()
const long port = wcstol(argv[++i], &endPtr, 10);
if (endPtr != argv[i] && *endPtr == 0 && port > 0 && port <= 65535)
{
g_Win64MultiplayerPort = static_cast<int>(port);
if (options.serverMode)
g_Win64DedicatedServerPort = static_cast<int>(port);
else
g_Win64MultiplayerPort = static_cast<int>(port);
}
}
else if (_wcsicmp(argv[i], L"-fullscreen") == 0)
@@ -251,6 +277,36 @@ static Win64LaunchOptions ParseLaunchOptions()
return options;
}
static BOOL WINAPI HeadlessServerCtrlHandler(DWORD ctrlType)
{
switch (ctrlType)
{
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
case CTRL_CLOSE_EVENT:
case CTRL_SHUTDOWN_EVENT:
app.m_bShutdown = true;
MinecraftServer::HaltServer();
return TRUE;
default:
return FALSE;
}
}
static void SetupHeadlessServerConsole()
{
if (AllocConsole())
{
FILE* stream = nullptr;
freopen_s(&stream, "CONIN$", "r", stdin);
freopen_s(&stream, "CONOUT$", "w", stdout);
freopen_s(&stream, "CONOUT$", "w", stderr);
SetConsoleTitleA("Minecraft Server");
}
SetConsoleCtrlHandler(HeadlessServerCtrlHandler, TRUE);
}
void DefineActions(void)
{
// The app needs to define the actions required, and the possible mappings for these
@@ -1294,6 +1350,161 @@ static Minecraft* InitialiseMinecraftRuntime()
return pMinecraft;
}
static int HeadlessServerConsoleThreadProc(void* lpParameter)
{
UNREFERENCED_PARAMETER(lpParameter);
std::string line;
while (!app.m_bShutdown)
{
if (!std::getline(std::cin, line))
{
if (std::cin.eof())
{
break;
}
std::cin.clear();
Sleep(50);
continue;
}
wstring command = trimString(convStringToWstring(line));
if (command.empty())
continue;
MinecraftServer* server = MinecraftServer::getInstance();
if (server != nullptr)
{
server->handleConsoleInput(command, server);
}
}
return 0;
}
static int RunHeadlessServer()
{
SetupHeadlessServerConsole();
Settings serverSettings(new File(L"server.properties"));
const wstring configuredBindIp = serverSettings.getString(L"server-ip", L"");
const char* bindIp = "*";
if (g_Win64DedicatedServerBindIP[0] != 0)
{
bindIp = g_Win64DedicatedServerBindIP;
}
else if (!configuredBindIp.empty())
{
bindIp = wstringtochararray(configuredBindIp);
}
const int port = g_Win64DedicatedServerPort > 0 ? g_Win64DedicatedServerPort : serverSettings.getInt(L"server-port", WIN64_NET_DEFAULT_PORT);
printf("Starting headless server on %s:%d\n", bindIp, port);
fflush(stdout);
const Minecraft* pMinecraft = InitialiseMinecraftRuntime();
if (pMinecraft == nullptr)
{
fprintf(stderr, "Failed to initialise the Minecraft runtime.\n");
return 1;
}
app.SetGameHostOption(eGameHostOption_Difficulty, serverSettings.getInt(L"difficulty", 1));
app.SetGameHostOption(eGameHostOption_Gamertags, 1);
app.SetGameHostOption(eGameHostOption_GameType, serverSettings.getInt(L"gamemode", 0));
app.SetGameHostOption(eGameHostOption_LevelType, 0);
app.SetGameHostOption(eGameHostOption_Structures, serverSettings.getBoolean(L"generate-structures", true) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_BonusChest, serverSettings.getBoolean(L"bonus-chest", false) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_PvP, serverSettings.getBoolean(L"pvp", true) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_TrustPlayers, serverSettings.getBoolean(L"trust-players", true) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_FireSpreads, serverSettings.getBoolean(L"fire-spreads", true) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_TNT, serverSettings.getBoolean(L"tnt", true) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_HostCanFly, 1);
app.SetGameHostOption(eGameHostOption_HostCanChangeHunger, 1);
app.SetGameHostOption(eGameHostOption_HostCanBeInvisible, 1);
app.SetGameHostOption(eGameHostOption_MobGriefing, 1);
app.SetGameHostOption(eGameHostOption_KeepInventory, 0);
app.SetGameHostOption(eGameHostOption_DoMobSpawning, 1);
app.SetGameHostOption(eGameHostOption_DoMobLoot, 1);
app.SetGameHostOption(eGameHostOption_DoTileDrops, 1);
app.SetGameHostOption(eGameHostOption_NaturalRegeneration, 1);
app.SetGameHostOption(eGameHostOption_DoDaylightCycle, 1);
MinecraftServer::resetFlags();
g_NetworkManager.HostGame(0, false, true, MINECRAFT_NET_MAX_PLAYERS, 0);
if (!WinsockNetLayer::IsActive())
{
fprintf(stderr, "Failed to bind the server socket on %s:%d.\n", bindIp, port);
return 1;
}
g_NetworkManager.FakeLocalPlayerJoined();
NetworkGameInitData* param = new NetworkGameInitData();
param->seed = 0;
param->settings = app.GetGameHostOption(eGameHostOption_All);
g_NetworkManager.ServerStoppedCreate(true);
g_NetworkManager.ServerReadyCreate(true);
C4JThread* thread = new C4JThread(&CGameNetworkManager::ServerThreadProc, param, "Server", 256 * 1024);
thread->SetProcessor(CPU_CORE_SERVER);
thread->Run();
g_NetworkManager.ServerReadyWait();
g_NetworkManager.ServerReadyDestroy();
if (MinecraftServer::serverHalted())
{
fprintf(stderr, "The server halted during startup.\n");
g_NetworkManager.LeaveGame(false);
return 1;
}
app.SetGameStarted(true);
g_NetworkManager.DoWork();
printf("Server ready on %s:%d\n", bindIp, port);
printf("Type 'help' for server commands.\n");
fflush(stdout);
C4JThread* consoleThread = new C4JThread(&HeadlessServerConsoleThreadProc, nullptr, "Server console", 128 * 1024);
consoleThread->Run();
MSG msg = { 0 };
while (WM_QUIT != msg.message && !app.m_bShutdown && !MinecraftServer::serverHalted())
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
continue;
}
app.UpdateTime();
ProfileManager.Tick();
StorageManager.Tick();
RenderManager.Tick();
ui.tick();
g_NetworkManager.DoWork();
app.HandleXuiActions();
Sleep(10);
}
printf("Stopping server...\n");
fflush(stdout);
app.m_bShutdown = true;
MinecraftServer::HaltServer();
g_NetworkManager.LeaveGame(false);
return 0;
}
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
@@ -1355,8 +1566,11 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
const Win64LaunchOptions launchOptions = ParseLaunchOptions();
ApplyScreenMode(launchOptions.screenMode);
// Ensure uid.dat exists from startup (before any multiplayer/login path).
Win64Xuid::ResolvePersistentXuid();
// Ensure uid.dat exists from startup in client mode (before any multiplayer/login path).
if (!launchOptions.serverMode)
{
Win64Xuid::ResolvePersistentXuid();
}
// If no username, let's fall back
if (g_Win64Username[0] == 0)
@@ -1437,7 +1651,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
if (!InitInstance (hInstance, launchOptions.serverMode ? SW_HIDE : nCmdShow))
{
return FALSE;
}
@@ -1456,6 +1670,13 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
ToggleFullscreen();
}
if (launchOptions.serverMode)
{
const int serverResult = RunHeadlessServer();
CleanupDevice();
return serverResult;
}
#if 0
// Main message loop
MSG msg = {0};
@@ -1765,7 +1986,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
}
// F1 toggles the HUD
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_TOGGLE_HUD))
if (g_KBMInput.IsKeyPressed(VK_F1))
{
const int primaryPad = ProfileManager.GetPrimaryPad();
const unsigned char displayHud = app.GetGameSettings(primaryPad, eGameSetting_DisplayHUD);
@@ -1774,7 +1995,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
}
// F3 toggles onscreen debug info
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_DEBUG_INFO))
if (g_KBMInput.IsKeyPressed(VK_F3))
{
if (const Minecraft* pMinecraft = Minecraft::GetInstance())
{
@@ -1787,7 +2008,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
#ifdef _DEBUG_MENUS_ENABLED
// F6 Open debug console
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_DEBUG_CONSOLE))
if (g_KBMInput.IsKeyPressed(VK_F6))
{
static bool s_debugConsole = false;
s_debugConsole = !s_debugConsole;
@@ -1795,14 +2016,14 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
}
#endif
// toggle fullscreen
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_FULLSCREEN))
// F11 Toggle fullscreen
if (g_KBMInput.IsKeyPressed(VK_F11))
{
ToggleFullscreen();
}
// TAB opens game info menu. - Vvis :3 - Updated by detectiveren
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_HOST_SETTINGS) && !ui.GetMenuDisplayed(0))
if (g_KBMInput.IsKeyPressed(VK_TAB) && !ui.GetMenuDisplayed(0))
{
if (Minecraft* pMinecraft = Minecraft::GetInstance())
{
@@ -1814,7 +2035,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
}
// Open chat
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_CHAT) && app.GetGameStarted() && !ui.GetMenuDisplayed(0) && pMinecraft->screen == NULL)
if (g_KBMInput.IsKeyPressed('T') && app.GetGameStarted() && !ui.GetMenuDisplayed(0) && pMinecraft->screen == NULL)
{
g_KBMInput.ClearCharBuffer();
pMinecraft->setScreen(new ChatScreen());
Binary file not shown.
@@ -8811,7 +8811,4 @@ All Ender Chests in a world are linked. Items placed into an Ender Chest are acc
<data name="IDS_TOOLTIPS_CURE">
<value>Cure</value>
</data>
<data name="IDS_WINDOWS_EXIT">
<value>Exit Minecraft</value>
</data>
</root>
+133 -126
View File
@@ -1,7 +1,7 @@
#pragma once
// Auto-generated by StringTable builder — do not edit manually.
// Source language: en-US
// Total strings: 2287
// Total strings: 2286
#define IDS_NULL 0
#define IDS_OK 1
@@ -2165,128 +2165,135 @@
#define IDS_TUTORIAL_TASK_MINECART_PUSHING 2159
#define IDS_CONNECTION_FAILED_NO_SD_SPLITSCREEN 2160
#define IDS_TOOLTIPS_CURE 2161
#define IDS_WINDOWS_EXIT 2162
#define IDS_LANG_SYSTEM 2163
#define IDS_LANG_ENGLISH 2164
#define IDS_LANG_GERMAN 2165
#define IDS_LANG_SPANISH 2166
#define IDS_LANG_SPANISH_SPAIN 2167
#define IDS_LANG_SPANISH_LATIN_AMERICA 2168
#define IDS_LANG_FRENCH 2169
#define IDS_LANG_ITALIAN 2170
#define IDS_LANG_PORTUGUESE 2171
#define IDS_LANG_PORTUGUESE_PORTUGAL 2172
#define IDS_LANG_PORTUGUESE_BRAZIL 2173
#define IDS_LANG_JAPANESE 2174
#define IDS_LANG_KOREAN 2175
#define IDS_LANG_CHINESE_TRADITIONAL 2176
#define IDS_LANG_CHINESE_SIMPLIFIED 2177
#define IDS_LANG_DANISH 2178
#define IDS_LANG_FINISH 2179
#define IDS_LANG_DUTCH 2180
#define IDS_LANG_POLISH 2181
#define IDS_LANG_RUSSIAN 2182
#define IDS_LANG_SWEDISH 2183
#define IDS_LANG_NORWEGIAN 2184
#define IDS_LANG_GREEK 2185
#define IDS_LANG_TURKISH 2186
#define IDS_LEADERBOARD_KILLS_EASY 2187
#define IDS_LEADERBOARD_KILLS_NORMAL 2188
#define IDS_LEADERBOARD_KILLS_HARD 2189
#define IDS_LEADERBOARD_MINING_BLOCKS_PEACEFUL 2190
#define IDS_LEADERBOARD_MINING_BLOCKS_EASY 2191
#define IDS_LEADERBOARD_MINING_BLOCKS_NORMAL 2192
#define IDS_LEADERBOARD_MINING_BLOCKS_HARD 2193
#define IDS_LEADERBOARD_FARMING_PEACEFUL 2194
#define IDS_LEADERBOARD_FARMING_EASY 2195
#define IDS_LEADERBOARD_FARMING_NORMAL 2196
#define IDS_LEADERBOARD_FARMING_HARD 2197
#define IDS_LEADERBOARD_TRAVELLING_PEACEFUL 2198
#define IDS_LEADERBOARD_TRAVELLING_EASY 2199
#define IDS_LEADERBOARD_TRAVELLING_NORMAL 2200
#define IDS_LEADERBOARD_TRAVELLING_HARD 2201
#define IDS_TIPS_GAMETIP_0 2202
#define IDS_TIPS_GAMETIP_1 2203
#define IDS_TIPS_GAMETIP_48 2204
#define IDS_TIPS_GAMETIP_44 2205
#define IDS_TIPS_GAMETIP_45 2206
#define IDS_TIPS_TRIVIA_4 2207
#define IDS_TIPS_TRIVIA_17 2208
#define IDS_HOW_TO_PLAY_MULTIPLAYER 2209
#define IDS_HOW_TO_PLAY_SOCIALMEDIA 2210
#define IDS_HOW_TO_PLAY_CREATIVE 2211
#define IDS_TUTORIAL_TASK_FLY 2212
#define IDS_TOOLTIPS_SELECTDEVICE 2213
#define IDS_TOOLTIPS_CHANGEDEVICE 2214
#define IDS_TOOLTIPS_VIEW_GAMERCARD 2215
#define IDS_TOOLTIPS_VIEW_GAMERPROFILE 2216
#define IDS_TOOLTIPS_INVITE_PARTY 2217
#define IDS_CONFIRM_START_CREATIVE 2218
#define IDS_CONFIRM_START_SAVEDINCREATIVE 2219
#define IDS_CONFIRM_START_SAVEDINCREATIVE_CONTINUE 2220
#define IDS_CONFIRM_START_HOST_PRIVILEGES 2221
#define IDS_CONNECTION_LOST_LIVE 2222
#define IDS_CONNECTION_LOST_LIVE_NO_EXIT 2223
#define IDS_AWARD_AVATAR1 2224
#define IDS_AWARD_AVATAR2 2225
#define IDS_AWARD_AVATAR3 2226
#define IDS_AWARD_THEME 2227
#define IDS_UNLOCK_ACHIEVEMENT_TEXT 2228
#define IDS_UNLOCK_AVATAR_TEXT 2229
#define IDS_UNLOCK_GAMERPIC_TEXT 2230
#define IDS_UNLOCK_THEME_TEXT 2231
#define IDS_UNLOCK_ACCEPT_INVITE 2232
#define IDS_UNLOCK_GUEST_TEXT 2233
#define IDS_LEADERBOARD_GAMERTAG 2234
#define IDS_GROUPNAME_POTIONS_480 2235
#define IDS_RETURNEDTOTITLESCREEN_TEXT 2236
#define IDS_TRIALOVER_TEXT 2237
#define IDS_FATAL_ERROR_TEXT 2238
#define IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT 2239
#define IDS_NO_MULTIPLAYER_PRIVILEGE_HOST_TEXT 2240
#define IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL 2241
#define IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL 2242
#define IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_REMOTE 2243
#define IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_CREATE 2244
#define IDS_SAVE_ICON_MESSAGE 2245
#define IDS_GAMEOPTION_HOST_PRIVILEGES 2246
#define IDS_CHECKBOX_DISPLAY_SPLITSCREENGAMERTAGS 2247
#define IDS_ACHIEVEMENTS 2248
#define IDS_LABEL_GAMERTAGS 2249
#define IDS_IN_GAME_GAMERTAGS 2250
#define IDS_SOCIAL_DEFAULT_DESCRIPTION 2251
#define IDS_TITLE_UPDATE_NAME 2252
#define IDS_PLATFORM_NAME 2253
#define IDS_BACK_BUTTON 2254
#define IDS_HOST_OPTION_DISABLES_ACHIEVEMENTS 2255
#define IDS_KICK_PLAYER_DESCRIPTION 2256
#define IDS_USING_TRIAL_TEXUREPACK_WARNING 2257
#define IDS_WORLD_SIZE_TITLE_SMALL 2258
#define IDS_WORLD_SIZE_TITLE_MEDIUM 2259
#define IDS_WORLD_SIZE_TITLE_LARGE 2260
#define IDS_WORLD_SIZE_TITLE_CLASSIC 2261
#define IDS_WORLD_SIZE 2262
#define IDS_GAMEOPTION_WORLD_SIZE 2263
#define IDS_DISABLE_SAVING 2264
#define IDS_GAMEOPTION_DISABLE_SAVING 2265
#define IDS_RICHPRESENCE_GAMESTATE 2266
#define IDS_RICHPRESENCE_IDLE 2267
#define IDS_RICHPRESENCE_MENUS 2268
#define IDS_RICHPRESENCE_MULTIPLAYER 2269
#define IDS_RICHPRESENCE_MULTIPLAYEROFFLINE 2270
#define IDS_RICHPRESENCE_MULTIPLAYER_1P 2271
#define IDS_RICHPRESENCE_MULTIPLAYER_1POFFLINE 2272
#define IDS_RICHPRESENCESTATE_BLANK 2273
#define IDS_RICHPRESENCESTATE_RIDING_PIG 2274
#define IDS_RICHPRESENCESTATE_RIDING_MINECART 2275
#define IDS_RICHPRESENCESTATE_BOATING 2276
#define IDS_RICHPRESENCESTATE_FISHING 2277
#define IDS_RICHPRESENCESTATE_CRAFTING 2278
#define IDS_RICHPRESENCESTATE_FORGING 2279
#define IDS_RICHPRESENCESTATE_NETHER 2280
#define IDS_RICHPRESENCESTATE_CD 2281
#define IDS_RICHPRESENCESTATE_MAP 2282
#define IDS_RICHPRESENCESTATE_ENCHANTING 2283
#define IDS_RICHPRESENCESTATE_BREWING 2284
#define IDS_RICHPRESENCESTATE_ANVIL 2285
#define IDS_RICHPRESENCESTATE_TRADING 2286
#define IDS_LANG_SYSTEM 2162
#define IDS_LANG_ENGLISH 2163
#define IDS_LANG_GERMAN 2164
#define IDS_LANG_SPANISH 2165
#define IDS_LANG_SPANISH_SPAIN 2166
#define IDS_LANG_SPANISH_LATIN_AMERICA 2167
#define IDS_LANG_FRENCH 2168
#define IDS_LANG_ITALIAN 2169
#define IDS_LANG_PORTUGUESE 2170
#define IDS_LANG_PORTUGUESE_PORTUGAL 2171
#define IDS_LANG_PORTUGUESE_BRAZIL 2172
#define IDS_LANG_JAPANESE 2173
#define IDS_LANG_KOREAN 2174
#define IDS_LANG_CHINESE_TRADITIONAL 2175
#define IDS_LANG_CHINESE_SIMPLIFIED 2176
#define IDS_LANG_DANISH 2177
#define IDS_LANG_FINISH 2178
#define IDS_LANG_DUTCH 2179
#define IDS_LANG_POLISH 2180
#define IDS_LANG_RUSSIAN 2181
#define IDS_LANG_SWEDISH 2182
#define IDS_LANG_NORWEGIAN 2183
#define IDS_LANG_GREEK 2184
#define IDS_LANG_TURKISH 2185
#define IDS_LEADERBOARD_KILLS_EASY 2186
#define IDS_LEADERBOARD_KILLS_NORMAL 2187
#define IDS_LEADERBOARD_KILLS_HARD 2188
#define IDS_LEADERBOARD_MINING_BLOCKS_PEACEFUL 2189
#define IDS_LEADERBOARD_MINING_BLOCKS_EASY 2190
#define IDS_LEADERBOARD_MINING_BLOCKS_NORMAL 2191
#define IDS_LEADERBOARD_MINING_BLOCKS_HARD 2192
#define IDS_LEADERBOARD_FARMING_PEACEFUL 2193
#define IDS_LEADERBOARD_FARMING_EASY 2194
#define IDS_LEADERBOARD_FARMING_NORMAL 2195
#define IDS_LEADERBOARD_FARMING_HARD 2196
#define IDS_LEADERBOARD_TRAVELLING_PEACEFUL 2197
#define IDS_LEADERBOARD_TRAVELLING_EASY 2198
#define IDS_LEADERBOARD_TRAVELLING_NORMAL 2199
#define IDS_LEADERBOARD_TRAVELLING_HARD 2200
#define IDS_TIPS_GAMETIP_0 2201
#define IDS_TIPS_GAMETIP_1 2202
#define IDS_TIPS_GAMETIP_48 2203
#define IDS_TIPS_GAMETIP_44 2204
#define IDS_TIPS_GAMETIP_45 2205
#define IDS_TIPS_TRIVIA_4 2206
#define IDS_TIPS_TRIVIA_17 2207
#define IDS_HOW_TO_PLAY_MULTIPLAYER 2208
#define IDS_HOW_TO_PLAY_SOCIALMEDIA 2209
#define IDS_HOW_TO_PLAY_CREATIVE 2210
#define IDS_TUTORIAL_TASK_FLY 2211
#define IDS_TOOLTIPS_SELECTDEVICE 2212
#define IDS_TOOLTIPS_CHANGEDEVICE 2213
#define IDS_TOOLTIPS_VIEW_GAMERCARD 2214
#define IDS_TOOLTIPS_VIEW_GAMERPROFILE 2215
#define IDS_TOOLTIPS_INVITE_PARTY 2216
#define IDS_CONFIRM_START_CREATIVE 2217
#define IDS_CONFIRM_START_SAVEDINCREATIVE 2218
#define IDS_CONFIRM_START_SAVEDINCREATIVE_CONTINUE 2219
#define IDS_CONFIRM_START_HOST_PRIVILEGES 2220
#define IDS_CONNECTION_LOST_LIVE 2221
#define IDS_CONNECTION_LOST_LIVE_NO_EXIT 2222
#define IDS_AWARD_AVATAR1 2223
#define IDS_AWARD_AVATAR2 2224
#define IDS_AWARD_AVATAR3 2225
#define IDS_AWARD_THEME 2226
#define IDS_UNLOCK_ACHIEVEMENT_TEXT 2227
#define IDS_UNLOCK_AVATAR_TEXT 2228
#define IDS_UNLOCK_GAMERPIC_TEXT 2229
#define IDS_UNLOCK_THEME_TEXT 2230
#define IDS_UNLOCK_ACCEPT_INVITE 2231
#define IDS_UNLOCK_GUEST_TEXT 2232
#define IDS_LEADERBOARD_GAMERTAG 2233
#define IDS_GROUPNAME_POTIONS_480 2234
#define IDS_RETURNEDTOTITLESCREEN_TEXT 2235
#define IDS_TRIALOVER_TEXT 2236
#define IDS_FATAL_ERROR_TEXT 2237
#define IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT 2238
#define IDS_NO_MULTIPLAYER_PRIVILEGE_HOST_TEXT 2239
#define IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL 2240
#define IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL 2241
#define IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_REMOTE 2242
#define IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_CREATE 2243
#define IDS_SAVE_ICON_MESSAGE 2244
#define IDS_GAMEOPTION_HOST_PRIVILEGES 2245
#define IDS_CHECKBOX_DISPLAY_SPLITSCREENGAMERTAGS 2246
#define IDS_ACHIEVEMENTS 2247
#define IDS_LABEL_GAMERTAGS 2248
#define IDS_IN_GAME_GAMERTAGS 2249
#define IDS_SOCIAL_DEFAULT_DESCRIPTION 2250
#define IDS_TITLE_UPDATE_NAME 2251
#define IDS_PLATFORM_NAME 2252
#define IDS_BACK_BUTTON 2253
#define IDS_HOST_OPTION_DISABLES_ACHIEVEMENTS 2254
#define IDS_KICK_PLAYER_DESCRIPTION 2255
#define IDS_USING_TRIAL_TEXUREPACK_WARNING 2256
#define IDS_WORLD_SIZE_TITLE_SMALL 2257
#define IDS_WORLD_SIZE_TITLE_MEDIUM 2258
#define IDS_WORLD_SIZE_TITLE_LARGE 2259
#define IDS_WORLD_SIZE_TITLE_CLASSIC 2260
#define IDS_WORLD_SIZE 2261
#define IDS_GAMEOPTION_WORLD_SIZE 2262
#define IDS_DISABLE_SAVING 2263
#define IDS_GAMEOPTION_DISABLE_SAVING 2264
#define IDS_RICHPRESENCE_GAMESTATE 2265
#define IDS_RICHPRESENCE_IDLE 2266
#define IDS_RICHPRESENCE_MENUS 2267
#define IDS_RICHPRESENCE_MULTIPLAYER 2268
#define IDS_RICHPRESENCE_MULTIPLAYEROFFLINE 2269
#define IDS_RICHPRESENCE_MULTIPLAYER_1P 2270
#define IDS_RICHPRESENCE_MULTIPLAYER_1POFFLINE 2271
#define IDS_RICHPRESENCESTATE_BLANK 2272
#define IDS_RICHPRESENCESTATE_RIDING_PIG 2273
#define IDS_RICHPRESENCESTATE_RIDING_MINECART 2274
#define IDS_RICHPRESENCESTATE_BOATING 2275
#define IDS_RICHPRESENCESTATE_FISHING 2276
#define IDS_RICHPRESENCESTATE_CRAFTING 2277
#define IDS_RICHPRESENCESTATE_FORGING 2278
#define IDS_RICHPRESENCESTATE_NETHER 2279
#define IDS_RICHPRESENCESTATE_CD 2280
#define IDS_RICHPRESENCESTATE_MAP 2281
#define IDS_RICHPRESENCESTATE_ENCHANTING 2282
#define IDS_RICHPRESENCESTATE_BREWING 2283
#define IDS_RICHPRESENCESTATE_ANVIL 2284
#define IDS_RICHPRESENCESTATE_TRADING 2285
#define IDS_GAMEMODE_HARDCORE 2286
#define IDS_HARDCORE 2287
#define IDS_HARDCORE_TOOLTIP 2288
#define IDS_HARDCORE_WARNING_TITLE 2289
#define IDS_HARDCORE_WARNING_TEXT 2290
#define IDS_HARDCORE_DEATH_MESSAGE 2291
#define IDS_LABEL_HARDCORE 2292
#define IDS_GAMEOPTION_HARDCORE 2293
+1 -1
View File
@@ -30,7 +30,7 @@ void Bat::defineSynchedData()
float Bat::getSoundVolume()
{
return 0.8f;
return 0.1f;
}
float Bat::getVoicePitch()
@@ -838,6 +838,51 @@ int ConsoleSaveFileOriginal::SaveSaveDataCallback(LPVOID lpParam,bool bRes)
{
ConsoleSaveFile *pClass=static_cast<ConsoleSaveFile *>(lpParam);
#ifdef _WINDOWS64
// 4J Added: After save completes, capture the save folder name for hardcore world deletion
if (bRes && app.GetCurrentSaveFolderName().empty())
{
// Try 1: Ask the library for the folder name
char szSaveFolder[MAX_SAVEFILENAME_LENGTH] = {};
if (StorageManager.GetSaveUniqueFilename(szSaveFolder) && szSaveFolder[0] != '\0')
{
wchar_t wFolder[MAX_SAVEFILENAME_LENGTH] = {};
mbstowcs(wFolder, szSaveFolder, MAX_SAVEFILENAME_LENGTH - 1);
app.SetCurrentSaveFolderName(wFolder);
app.DebugPrintf("SaveSaveDataCallback: captured save folder '%s'\n", szSaveFolder);
}
// Try 2: Scan GameHDD for the newest folder — right after save, it's guaranteed to be ours
if (app.GetCurrentSaveFolderName().empty())
{
WIN32_FIND_DATAW fd;
HANDLE hFind = FindFirstFileW(L"Windows64\\GameHDD\\*", &fd);
if (hFind != INVALID_HANDLE_VALUE)
{
FILETIME newestTime = {};
wchar_t newestFolder[MAX_PATH] = {};
do
{
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
wcscmp(fd.cFileName, L".") != 0 && wcscmp(fd.cFileName, L"..") != 0)
{
if (CompareFileTime(&fd.ftLastWriteTime, &newestTime) > 0)
{
newestTime = fd.ftLastWriteTime;
wcscpy_s(newestFolder, MAX_PATH, fd.cFileName);
}
}
} while (FindNextFileW(hFind, &fd));
FindClose(hFind);
if (newestFolder[0] != L'\0')
{
app.SetCurrentSaveFolderName(newestFolder);
app.DebugPrintf("SaveSaveDataCallback: captured save folder via scan '%ls'\n", newestFolder);
}
}
}
}
#endif
return 0;
}
+1 -1
View File
@@ -72,7 +72,7 @@ void Cow::playStepSound(int xt, int yt, int zt, int t)
float Cow::getSoundVolume()
{
return 1.f;
return 0.4f;
}
int Cow::getDeathLoot()
+2
View File
@@ -50,6 +50,7 @@ public:
#ifdef _XBOX_ONE
eDisconnect_ExitedGame,
#endif
eDisconnect_HardcoreDeath, // 4J Added - for hardcore mode multiplayer ban-on-death
};
// 4J Stu - The reason was a string, but we need to send a non-locale specific reason
@@ -71,3 +72,4 @@ public:
};
-11
View File
@@ -96,20 +96,9 @@ int Entity::getSmallId()
puiUsedFlags++;
}
#ifdef MINECRAFT_SERVER_BUILD
// in mc server dedi, a server with 8+ playerrs can cause this to go wack
int fallbackId = Entity::entityCounter++;
if (entityCounter == 0x7ffffff)
{
entityCounter = 2048;
}
return fallbackId;
#else
app.DebugPrintf("Out of small entity Ids... possible leak?\n");
__debugbreak();
return -1;
#endif
}
void Entity::countFlagsForPIX()
-3
View File
@@ -37,7 +37,6 @@ void FireworksRecipe::ReleaseThreadStorage()
void FireworksRecipe::setResultItem(shared_ptr<ItemInstance> item)
{
ThreadStorage *tls = static_cast<ThreadStorage *>(TlsGetValue(tlsIdx));
if (tls == nullptr) tls = tlsDefault;
tls->resultItem = item;
}
@@ -270,7 +269,6 @@ bool FireworksRecipe::matches(shared_ptr<CraftingContainer> craftSlots, Level *l
shared_ptr<ItemInstance> FireworksRecipe::assemble(shared_ptr<CraftingContainer> craftSlots)
{
ThreadStorage *tls = static_cast<ThreadStorage *>(TlsGetValue(tlsIdx));
if (tls == nullptr) tls = tlsDefault;
return tls->resultItem->copy();
//return resultItem->copy();
}
@@ -283,7 +281,6 @@ int FireworksRecipe::size()
const ItemInstance *FireworksRecipe::getResultItem()
{
ThreadStorage *tls = static_cast<ThreadStorage *>(TlsGetValue(tlsIdx));
if (tls == nullptr) tls = tlsDefault;
return tls->resultItem.get();
//return resultItem.get();
}
-5
View File
@@ -16,12 +16,7 @@ using namespace std;
// 4J Stu - This value should be big enough that we don't get any crashes causes by memory overwrites,
// however it does seem way too large for what is actually needed. Needs further investigation
#ifdef MINECRAFT_SERVER_BUILD
// fixes a crash when 8+ players are present
#define LEVEL_CHUNKS_TO_UPDATE_MAX (32*32*8)
#else
#define LEVEL_CHUNKS_TO_UPDATE_MAX (19*19*8)
#endif
class Vec3;
class ChunkSource;
+7 -2
View File
@@ -34,6 +34,7 @@ LoginPacket::LoginPacket()
m_uiGamePrivileges = 0;
m_xzSize = LEVEL_MAX_WIDTH;
m_hellScale = HELL_LEVEL_MAX_SCALE;
m_isHardcore = false;
}
// Client -> Server
@@ -63,10 +64,11 @@ LoginPacket::LoginPacket(const wstring& userName, int clientVersion, PlayerUID o
m_uiGamePrivileges = 0;
m_xzSize = LEVEL_MAX_WIDTH;
m_hellScale = HELL_LEVEL_MAX_SCALE;
m_isHardcore = false;
}
// Server -> Client
LoginPacket::LoginPacket(const wstring& userName, int clientVersion, LevelType *pLevelType, int64_t seed, int gameType, char dimension, BYTE mapHeight, BYTE maxPlayers, char difficulty, INT multiplayerInstanceId, BYTE playerIndex, bool newSeaLevel, unsigned int uiGamePrivileges, int xzSize, int hellScale)
LoginPacket::LoginPacket(const wstring& userName, int clientVersion, LevelType *pLevelType, int64_t seed, int gameType, char dimension, BYTE mapHeight, BYTE maxPlayers, char difficulty, INT multiplayerInstanceId, BYTE playerIndex, bool newSeaLevel, unsigned int uiGamePrivileges, int xzSize, int hellScale, bool isHardcore)
{
this->userName = userName;
this->clientVersion = clientVersion;
@@ -92,6 +94,7 @@ LoginPacket::LoginPacket(const wstring& userName, int clientVersion, LevelType *
m_uiGamePrivileges = uiGamePrivileges;
m_xzSize = xzSize;
m_hellScale = hellScale;
m_isHardcore = isHardcore;
}
void LoginPacket::read(DataInputStream *dis) //throws IOException
@@ -106,6 +109,8 @@ void LoginPacket::read(DataInputStream *dis) //throws IOException
}
seed = dis->readLong();
gameType = dis->readInt();
m_isHardcore = (gameType & 0x8) != 0;
gameType = gameType & ~0x8;
dimension = dis->readByte();
mapHeight = dis->readByte();
maxPlayers = dis->readByte();
@@ -144,7 +149,7 @@ void LoginPacket::write(DataOutputStream *dos) //throws IOException
writeUtf(m_pLevelType->getGeneratorName(), dos);
}
dos->writeLong(seed);
dos->writeInt(gameType);
dos->writeInt(gameType | (m_isHardcore ? 0x8 : 0));
dos->writeByte(dimension);
dos->writeByte(mapHeight);
dos->writeByte(maxPlayers);
+2 -1
View File
@@ -24,6 +24,7 @@ public:
unsigned int m_uiGamePrivileges;
int m_xzSize; // 4J Added
int m_hellScale; // 4J Added
bool m_isHardcore; // 4J Added - for hardcore mode
// 1.8.2
int gameType;
@@ -31,7 +32,7 @@ public:
BYTE maxPlayers;
LoginPacket();
LoginPacket(const wstring& userName, int clientVersion, LevelType *pLevelType, int64_t seed, int gameType, char dimension, BYTE mapHeight, BYTE maxPlayers, char difficulty, INT m_multiplayerInstanceId, BYTE playerIndex, bool newSeaLevel, unsigned int uiGamePrivileges, int xzSize, int hellScale); // Server -> Client
LoginPacket(const wstring& userName, int clientVersion, LevelType *pLevelType, int64_t seed, int gameType, char dimension, BYTE mapHeight, BYTE maxPlayers, char difficulty, INT m_multiplayerInstanceId, BYTE playerIndex, bool newSeaLevel, unsigned int uiGamePrivileges, int xzSize, int hellScale, bool isHardcore = false); // Server -> Client
LoginPacket(const wstring& userName, int clientVersion, PlayerUID offlineXuid, PlayerUID onlineXuid, bool friendsOnlyUGC, DWORD ugcPlayersVersion, DWORD skinId, DWORD capeId, bool isGuest); // Client -> Server
virtual void read(DataInputStream *dis);
+7 -3
View File
@@ -17,9 +17,10 @@ RespawnPacket::RespawnPacket()
m_newEntityId = 0;
m_xzSize = LEVEL_MAX_WIDTH;
m_hellScale = HELL_LEVEL_MAX_SCALE;
m_isHardcore = false;
}
RespawnPacket::RespawnPacket(char dimension, int64_t mapSeed, int mapHeight, GameType *playerGameType, char difficulty, LevelType *pLevelType, bool newSeaLevel, int newEntityId, int xzSize, int hellScale)
RespawnPacket::RespawnPacket(char dimension, int64_t mapSeed, int mapHeight, GameType *playerGameType, char difficulty, LevelType *pLevelType, bool newSeaLevel, int newEntityId, int xzSize, int hellScale, bool isHardcore)
{
this->dimension = dimension;
this->mapSeed = mapSeed;
@@ -31,6 +32,7 @@ RespawnPacket::RespawnPacket(char dimension, int64_t mapSeed, int mapHeight, Gam
this->m_newEntityId = newEntityId;
m_xzSize = xzSize;
m_hellScale = hellScale;
m_isHardcore = isHardcore;
app.DebugPrintf("RespawnPacket - Difficulty = %d\n",difficulty);
}
@@ -43,7 +45,9 @@ void RespawnPacket::handle(PacketListener *listener)
void RespawnPacket::read(DataInputStream *dis) //throws IOException
{
dimension = dis->readByte();
playerGameType = GameType::byId(dis->readByte());
int rawGameType = dis->readByte();
m_isHardcore = (rawGameType & 0x8) != 0;
playerGameType = GameType::byId(rawGameType & ~0x8);
mapHeight = dis->readShort();
wstring typeName = readUtf(dis, 16);
m_pLevelType = LevelType::getLevelType(typeName);
@@ -66,7 +70,7 @@ void RespawnPacket::read(DataInputStream *dis) //throws IOException
void RespawnPacket::write(DataOutputStream *dos) //throws IOException
{
dos->writeByte(dimension);
dos->writeByte(playerGameType->getId());
dos->writeByte(playerGameType->getId() | (m_isHardcore ? 0x8 : 0));
dos->writeShort(mapHeight);
if (m_pLevelType == nullptr)
{
+2 -1
View File
@@ -19,9 +19,10 @@ public:
int m_newEntityId;
int m_xzSize; // 4J Added
int m_hellScale; // 4J Added
bool m_isHardcore; // 4J Added - for hardcore mode
RespawnPacket();
RespawnPacket(char dimension, int64_t mapSeed, int mapHeight, GameType *playerGameType, char difficulty, LevelType *pLevelType, bool newSeaLevel, int newEntityId, int xzSize, int hellScale);
RespawnPacket(char dimension, int64_t mapSeed, int mapHeight, GameType *playerGameType, char difficulty, LevelType *pLevelType, bool newSeaLevel, int newEntityId, int xzSize, int hellScale, bool isHardcore = false);
virtual void handle(PacketListener *listener);
virtual void read(DataInputStream *dis);
+1 -77
View File
@@ -30,65 +30,6 @@ ensure_persist_file() {
ln -sfn "${persist_path}" "${runtime_path}"
}
wait_for_xvfb_ready() {
local display="$1"
local xvfb_pid="$2"
local wait_seconds="${XVFB_WAIT_SECONDS:-10}"
local wait_ticks=$((wait_seconds * 10))
local display_number="${display#:}"
display_number="${display_number%%.*}"
if [ -z "${display_number}" ] || ! [[ "${display_number}" =~ ^[0-9]+$ ]]; then
echo "[error] Invalid DISPLAY format for Xvfb wait: ${display}" >&2
return 1
fi
local socket_path="/tmp/.X11-unix/X${display_number}"
local elapsed=0
while [ "${elapsed}" -lt "${wait_ticks}" ]; do
if ! kill -0 "${xvfb_pid}" 2>/dev/null; then
echo "[error] Xvfb exited before the display became ready." >&2
if [ -f /tmp/xvfb.log ]; then
echo "[error] ---- /tmp/xvfb.log ----" >&2
tail -n 200 /tmp/xvfb.log >&2 || true
echo "[error] ----------------------" >&2
fi
return 1
fi
if [ -S "${socket_path}" ]; then
# Keep a short extra delay so Wine does not race display handshake.
sleep 0.2
if kill -0 "${xvfb_pid}" 2>/dev/null && [ -S "${socket_path}" ]; then
return 0
fi
fi
# One more liveness check after the ready probe branch.
if ! kill -0 "${xvfb_pid}" 2>/dev/null; then
echo "[error] Xvfb exited during display readiness probe." >&2
if [ -f /tmp/xvfb.log ]; then
echo "[error] ---- /tmp/xvfb.log ----" >&2
tail -n 200 /tmp/xvfb.log >&2 || true
echo "[error] ----------------------" >&2
fi
return 1
fi
sleep 0.1
elapsed=$((elapsed + 1))
done
echo "[error] Timed out waiting for Xvfb display ${display}." >&2
if [ -f /tmp/xvfb.log ]; then
echo "[error] ---- /tmp/xvfb.log ----" >&2
tail -n 200 /tmp/xvfb.log >&2 || true
echo "[error] ----------------------" >&2
fi
return 1
}
if [ ! -d "$SERVER_DIR" ]; then
echo "[error] Server directory not found: $SERVER_DIR" >&2
exit 1
@@ -137,25 +78,8 @@ fi
if [ -z "${DISPLAY:-}" ]; then
export DISPLAY="${XVFB_DISPLAY:-:99}"
XVFB_SCREEN="${XVFB_SCREEN:-64x64x16}"
DISPLAY_NUMBER="${DISPLAY#:}"
DISPLAY_NUMBER="${DISPLAY_NUMBER%%.*}"
if [ -z "${DISPLAY_NUMBER}" ] || ! [[ "${DISPLAY_NUMBER}" =~ ^[0-9]+$ ]]; then
echo "[error] Invalid XVFB_DISPLAY format: ${DISPLAY}" >&2
exit 1
fi
XVFB_SOCKET="/tmp/.X11-unix/X${DISPLAY_NUMBER}"
XVFB_LOCK="/tmp/.X${DISPLAY_NUMBER}-lock"
# The check is performed assuming the same container will be restarted.
if [ -S "${XVFB_SOCKET}" ] || [ -e "${XVFB_LOCK}" ]; then
echo "[warn] Removing stale Xvfb state for ${DISPLAY} before startup." >&2
rm -f "${XVFB_SOCKET}" "${XVFB_LOCK}"
fi
Xvfb "${DISPLAY}" -nolisten tcp -screen 0 "${XVFB_SCREEN}" >/tmp/xvfb.log 2>&1 &
XVFB_PID=$!
wait_for_xvfb_ready "${DISPLAY}" "${XVFB_PID}"
echo "[info] Xvfb ready on ${DISPLAY} (pid=${XVFB_PID}, screen=${XVFB_SCREEN})"
else
echo "[info] Using existing DISPLAY=${DISPLAY}; skipping Xvfb startup"
sleep 0.2
fi
args=(