Files
LCE-Revelations/Minecraft.Client/MinecraftServer.h
Revela b28c0026af 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-13 06:56:46 -05:00

288 lines
9.1 KiB
C++

#pragma once
#include "ConsoleInputSource.h"
#include "..\Minecraft.World\ArrayWithLength.h"
#include "..\Minecraft.World\SharedConstants.h"
#include "..\Minecraft.World\C4JThread.h"
class ServerConnection;
class Settings;
class PlayerList;
class EntityTracker;
class ConsoleInput;
class ConsoleCommands;
class LevelStorageSource;
class ChunkSource;
class INetworkPlayer;
class LevelRuleset;
class LevelType;
class ProgressRenderer;
class CommandDispatcher;
#if defined(_WINDOWS64)
#define MINECRAFT_SERVER_SLOW_QUEUE_DELAY 0 // Removed slow queue because at large player counts, chunks stopped appearing
#else
#define MINECRAFT_SERVER_SLOW_QUEUE_DELAY 250
#endif
#if defined _XBOX_ONE || defined _XBOX || defined __ORBIS__ || defined __PS3__ || defined __PSVITA__
#define _ACK_CHUNK_SEND_THROTTLING
#endif
typedef struct _LoadSaveDataThreadParam
{
LPVOID data;
int64_t fileSize;
const wstring saveName;
_LoadSaveDataThreadParam(LPVOID data, int64_t filesize, const wstring &saveName) : data( data ), fileSize( filesize ), saveName( saveName ) {}
} LoadSaveDataThreadParam;
typedef struct _NetworkGameInitData
{
int64_t seed;
LoadSaveDataThreadParam *saveData;
DWORD settings;
LevelGenerationOptions *levelGen;
DWORD texturePackId;
bool findSeed;
unsigned int xzSize;
unsigned char hellScale;
ESavePlatform savePlatform;
wstring levelName;
_NetworkGameInitData()
{
seed = 0;
saveData = nullptr;
settings = 0;
levelGen = nullptr;
texturePackId = 0;
findSeed = false;
xzSize = LEVEL_LEGACY_WIDTH;
hellScale = HELL_LEVEL_LEGACY_SCALE;
savePlatform = SAVE_FILE_PLATFORM_LOCAL;
}
} NetworkGameInitData;
using namespace std;
// 4J Stu - 1.0.1 updates the server to implement the ServerInterface class, but I don't think we will use any of the functions that defines so not implementing here
class MinecraftServer : public ConsoleInputSource
{
public:
static const wstring VERSION;
static const int TICK_STATS_SPAN = SharedConstants::TICKS_PER_SECOND * 5;
// static Logger logger = Logger.getLogger("Minecraft");
static unordered_map<wstring, int> ironTimers;
private:
static const int DEFAULT_MINECRAFT_PORT = 25565;
static const int MS_PER_TICK = 1000 / SharedConstants::TICKS_PER_SECOND;
// 4J Stu - Added 1.0.1, Not needed
//wstring localIp;
//int port;
public:
ServerConnection *connection;
Settings *settings;
ServerLevelArray levels;
private:
PlayerList *players;
// 4J Stu - Added 1.0.1, Not needed
//long[] tickTimes = new long[TICK_STATS_SPAN];
//long[][] levelTickTimes;
private:
ConsoleCommands *commands;
bool running;
bool m_bLoaded;
public:
bool stopped;
int tickCount;
public:
wstring progressStatus;
int progress;
private:
// vector<Tickable *> tickables = new ArrayList<Tickable>(); // 4J - removed
CommandDispatcher *commandDispatcher;
vector<ConsoleInput *> consoleInput; // 4J - was synchronizedList - TODO - investigate
CRITICAL_SECTION m_consoleInputCS;
public:
bool onlineMode;
bool animals;
bool npcs;
bool pvp;
bool allowFlight;
wstring motd;
int maxBuildHeight;
int playerIdleTimeout;
bool forceGameType;
private:
// 4J Added
//int m_lastSentDifficulty;
public:
// 4J Stu - This value should be incremented every time the list of players with friends-only UGC settings changes
// It is sent with PreLoginPacket and compared when it comes back in the LoginPacket
DWORD m_ugcPlayersVersion;
// This value is used to store the texture pack id for the currently loaded world
DWORD m_texturePackId;
public:
MinecraftServer();
~MinecraftServer();
private:
// 4J Added - LoadSaveDataThreadParam
bool initServer(int64_t seed, NetworkGameInitData *initData, DWORD initSettings, bool findSeed);
void postProcessTerminate(ProgressRenderer *mcprogress);
bool loadLevel(LevelStorageSource *storageSource, const wstring& name, int64_t levelSeed, LevelType *pLevelType, NetworkGameInitData *initData);
void setProgress(const wstring& status, int progress);
void endProgress();
void saveAllChunks();
void saveGameRules();
void stopServer(bool didInit);
#ifdef _LARGE_WORLDS
void overwriteBordersForNewWorldSize(ServerLevel* level);
void overwriteHellBordersForNewWorldSize(ServerLevel* level, int oldHellSize);
#endif
public:
void setMaxBuildHeight(int maxBuildHeight);
int getMaxBuildHeight();
PlayerList *getPlayers();
void setPlayers(PlayerList *players);
ServerConnection *getConnection();
bool isAnimals();
void setAnimals(bool animals);
bool isNpcsEnabled();
void setNpcsEnabled(bool npcs);
bool isPvpAllowed();
void setPvpAllowed(bool pvp);
bool isFlightAllowed();
void setFlightAllowed(bool allowFlight);
bool isCommandBlockEnabled();
bool isNetherEnabled();
bool isHardcore();
int getOperatorUserPermissionLevel();
CommandDispatcher *getCommandDispatcher();
Pos *getCommandSenderWorldPosition();
Level *getCommandSenderWorld();
int getSpawnProtectionRadius();
bool isUnderSpawnProtection(Level *level, int x, int y, int z, shared_ptr<Player> player);
void setForceGameType(bool forceGameType);
bool getForceGameType();
static int64_t getCurrentTimeMillis();
int getPlayerIdleTimeout();
void setPlayerIdleTimeout(int playerIdleTimeout);
public:
void halt();
void run(int64_t seed, void *lpParameter);
void broadcastStartSavingPacket();
void broadcastStopSavingPacket();
private:
void tick();
public:
void handleConsoleInput(const wstring& msg, ConsoleInputSource *source);
void handleConsoleInputs();
// void addTickable(Tickable tickable); // 4J removed
static void main(int64_t seed, void *lpParameter);
static void HaltServer(bool bPrimaryPlayerSignedOut=false);
File *getFile(const wstring& name);
void info(const wstring& string);
void warn(const wstring& string);
wstring getConsoleName();
ServerLevel *getLevel(int dimension);
void setLevel(int dimension, ServerLevel *level); // 4J added
static MinecraftServer *getInstance() { return server; } // 4J added
static bool serverHalted() { return s_bServerHalted; }
static bool saveOnExitAnswered() { return s_bSaveOnExitAnswered; }
static void resetFlags() { s_bServerHalted = false; s_bSaveOnExitAnswered = false; }
bool flagEntitiesToBeRemoved(unsigned int *flags); // 4J added
private:
//4J Added
static MinecraftServer *server;
static bool setTimeOfDayAtEndOfTick;
static int64_t setTimeOfDay;
static bool setTimeAtEndOfTick;
static int64_t setTime;
static bool m_bPrimaryPlayerSignedOut; // 4J-PB added to tell the stopserver not to save the game - another player may have signed in in their place, so ProfileManager.IsSignedIn isn't enough
static bool s_bServerHalted; // 4J Stu Added so that we can halt the server even before it's been created properly
static bool s_bSaveOnExitAnswered; // 4J Stu Added so that we only ask this question once when we exit
// 4J - added so that we can have a separate thread for post processing chunks on level creation
static int runPostUpdate(void* lpParam);
C4JThread* m_postUpdateThread;
bool m_postUpdateTerminate;
class postProcessRequest
{
public:
int x, z;
ChunkSource *chunkSource;
postProcessRequest(int x, int z, ChunkSource *chunkSource) : x(x), z(z), chunkSource(chunkSource) {}
};
vector<postProcessRequest> m_postProcessRequests;
CRITICAL_SECTION m_postProcessCS;
public:
void addPostProcessRequest(ChunkSource *chunkSource, int x, int z);
static PlayerList *getPlayerList() { if( server != nullptr) return server->players; else return nullptr; }
static void SetTimeOfDay(int64_t time) { setTimeOfDayAtEndOfTick = true; setTimeOfDay = time; }
static void SetTime(int64_t time) { setTimeAtEndOfTick = true; setTime = time; }
C4JThread::Event* m_serverPausedEvent;
private:
// 4J Added
bool m_isServerPaused;
// 4J Added - A static that stores the QNet index of the player that is next allowed to send a packet in the slow queue
#ifdef _ACK_CHUNK_SEND_THROTTLING
static bool s_hasSentEnoughPackets;
static int64_t s_tickStartTime;
static vector<INetworkPlayer *> s_sentTo;
static const int MAX_TICK_TIME_FOR_PACKET_SENDS = 35;
#else
static int s_slowQueuePlayerIndex;
static int s_slowQueueLastTime;
static bool s_slowQueuePacketSent;
#endif
bool IsServerPaused() { return m_isServerPaused; }
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:
static bool chunkPacketManagement_CanSendTo(INetworkPlayer *player);
static void chunkPacketManagement_DidSendTo(INetworkPlayer *player);
#ifndef _ACK_CHUNK_SEND_THROTTLING
static void cycleSlowQueueIndex();
#endif
void chunkPacketManagement_PreTick();
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();
// 4J Stu - A load of functions were all added in 1.0.1 in the ServerInterface, but I don't think we need any of them
};