75 Commits

Author SHA1 Message Date
Revela e773e64840 Restore Visual Studio project files and build scripts
Preserve vcxproj, sln, and PowerShell build scripts that were
removed during the upstream CMake migration merge, so this branch
retains a working Visual Studio build configuration.
2026-03-17 17:52:56 -05:00
Revela bf2b488f2d Add x64_* build output directories to .gitignore 2026-03-17 17:29:02 -05:00
Revela 7c88569e8b Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp
#	Minecraft.Client/Minecraft.Client.vcxproj
#	Minecraft.Client/Minecraft.Client.vcxproj.filters
#	Minecraft.Server/Minecraft.Server.vcxproj
#	Minecraft.World/Minecraft.World.vcxproj
#	Minecraft.World/Minecraft.World.vcxproj.filters
#	README.md
2026-03-17 17:26:40 -05:00
Revela a02538daa0 Add Arabic text shaping support for chat functionality
This commit introduces Arabic text shaping in the chat application by adding `ArabicShaping.cpp` and `ArabicShaping.h` for handling contextual forms and visual reordering.

The rendering logic in `ChatScreen.cpp` is updated to utilize this new functionality, adjusting cursor positions accordingly. Other UI components, including `UIControl_Base.cpp`, `UIControl_Label.cpp`, and `UIControl_SaveList.cpp`, are modified to ensure proper display of Arabic text.

Additionally, `Font.cpp` is enhanced with methods for efficient rendering of pre-shaped text.
2026-03-17 17:08:58 -05:00
Matthew Toro 6df8089003 Merge pull request #669 from qwasdrizzel/main
Fix crash when FOV is equal to zero
2026-03-17 18:03:57 -04:00
rtm516 76f4832a6b Fix actions and sort docker nightly 2026-03-17 22:02:24 +00:00
rtm516 02a5961f39 Move project to CMake (#781)
* Move to cmake

* Move sources to source_groups and ditch more old VS files

* Add BuildVer.h generation

* Break out cmake source lists to platforms

* Don't copy swf files

* Revert audio changes from merge

* Add platform defines

* Match MSBuild flags

* Move BuildVer.h to common include and fix rebuild issue

* Seperate projects properly

* Exclude more files and make sure GameHDD exists

* Missing line

* Remove remaining VS project files

* Update readme and actions

* Use incremental LTCG

* Update workflows

* Update build workflows and output folder

* Disable vcpkg checks

* Force MSVC

* Use precompiled headers

* Only use PCH for cpp

* Exclude compat_shims from PCH

* Handle per-platform source includes

* Copy only current platform media

* Define Iggy libs per platform

* Fix EnsureGameHDD check

* Only set WIN32_EXECUTABLE on Windows

* Correct Iggy libs path

* Remove include of terrain_MipmapLevel

* Correct path to xsb/xwb

* Implement copilot suggestions

* Add clang flags (untested)

* Fix robocopy error checking

* Update documentation

* Drop CMakePresets.json version as we dont use v6 features

* Always cleanup artifacts in nightly even if some builds fail

* Re-work compiler target options

* Move newer iggy dll into redist and cleanup

* Fix typos

* Remove 'Source Files' from all source groups

* Remove old ps1 build scripts
2026-03-17 16:39:22 -05:00
Loki Rautio 1a3fcb5b20 Remove Miles Sound System from credits
we don't use it anymore!
2026-03-17 16:22:49 -05:00
Loki Rautio a3ca23fdf6 Cleanup project credits and README 2026-03-17 16:15:26 -05:00
Toru the Red Fox c98153bf07 Fix FOV modification so it respects applyEffects (#1297) 2026-03-17 19:47:34 +00:00
Matthew Toro a5f5595c63 Merge pull request #1315 from dognews/main
LoadJoinMenu UI Bug Fix: Properly display space indicator, remove label placeholder [BUG: #1004]
2026-03-17 15:45:22 -04:00
Matthew Toro dc8af13b45 Merge pull request #1321 from rtm516/fix/host-settings
Change KEY_HOST_SETTINGS back to to VK_TAB
2026-03-17 15:41:26 -04:00
rtm516 ab16f3bf45 Change KEY_HOST_SETTINGS back to to VK_TAB 2026-03-17 19:22:24 +00:00
Alex 994a23f96b Fix save space indicator display
Include space indicator UI element in windows build, logic to handle getting save file sizes on windows
2026-03-17 07:44:52 -07:00
Revela e4335cfc09 Merge branch 'main' of https://github.com/itsRevela/MinecraftConsoles 2026-03-17 00:32:54 -05:00
Revela 863998e19a Improve Nightly release management process
Enhanced the script to delete and recreate the Nightly-Dedicated-Server release, ensuring the client is always up-to-date. The process now fetches release information, deletes the old release, updates the tag to the latest commit, and creates a new release with an updated title and body. Streamlined asset uploads and clarified title update logic for better reliability and clarity.
2026-03-17 00:32:25 -05:00
Revela 9c6186f06e Added to README with multi-language support details
Added support for multi-language font rendering and Unicode text input, along with copy-paste functionality for various fields. Also included security enhancements and fixed a memory leak.
2026-03-16 23:16:46 -05:00
Revela d56a4bbe2a Merge branch 'smartcmd:main' into main 2026-03-16 23:12:41 -05:00
Revela 24f27462a6 Enable multi-language font rendering and Unicode text input
Goal:
Allow players to type and display text in any language supported by
Unicode, including Chinese, Japanese, Korean, Thai, Arabic, Korean, Hindi, and more. This
covers all text surfaces: chat editor, chat messages, signs (in-world
and editor), world name/seed, server address/port fields, and all
Iggy Flash UI text fields.

Multi-language support:
Two complementary rendering systems were added to handle Unicode text
across the entire client:

1. Iggy UI (Flash-based text fields): A new UIUnicodeBitmapFont class
   serves Java Minecraft's glyph page PNGs (glyph_00.png-glyph_FF.png)
   through Iggy's bitmap font provider API. Registered as the global
   fallback font with metrics matching the Mojangles bitmap font for
   correct baseline alignment. When the primary bitmap font lacks a
   glyph, it returns IGGY_GLYPH_INVALID and Iggy seamlessly falls back
   to the unicode bitmap font.

2. Legacy C++ Font renderer (chat editor, in-world signs): Revived the
   commented-out unicode glyph page system in Font.cpp. Characters not
   in the bitmap font texture are rendered from glyph page PNGs loaded
   on demand, with proper texture switching mid-string.

3. ChatScreen input: Removed the restrictive acceptableLetters filter
   so all printable Unicode characters are accepted in chat.

Languages now supported for text input and rendering:
- Japanese (Hiragana, Katakana, Kanji)
- Chinese (Simplified and Traditional)
- Korean (Hangul)
- Thai
- Arabic
- Hindi (Devanagari)
- Russian (Cyrillic) - already worked via bitmap font
- Greek - already worked via bitmap font
- Polish, Czech, Turkish (Extended Latin) - already worked via bitmap font
- Armenian, Georgian, and other scripts covered by glyph pages

Security fixes:
- Fixed memset under-initialization of Font::charWidths (zeroed 460
  bytes instead of 460*sizeof(int)=1840 bytes, leaving entries 115+
  uninitialized) - pre-existing bug
- Added bounds checks to all UIUnicodeBitmapFont callbacks to reject
  glyph IDs outside [0, 65535], preventing OOB array access
- Added bounds check in Font::width() section-sign fallback path to
  prevent OOB read on charWidths[] with high codepoints
- Blocked Unicode bidirectional override characters (U+202A-202E,
  U+2066-2069) in chat input to prevent message spoofing

Memory leak fix:
- Fixed SignTileEntity::load allocating wchar_t[256] with new[] on
  every sign load without freeing. Replaced with stack allocation.

Debug logging:
- Added [SIGN] prefixed logging for sign save/update operations
- Added [CHAT] prefixed logging for chat send/receive operations

Files changed:
- UIUnicodeBitmapFont.h/.cpp (new) - Iggy bitmap font for glyph pages
- UIBitmapFont.cpp - Return IGGY_GLYPH_INVALID for unknown chars
- UIFontData.h/.cpp - Added hasGlyph() method
- UIController.h/.cpp - Load and register unicode bitmap fallback font
- UITTFFont.h/.cpp - Added registerAsDefaultFonts parameter
- Font.h/.cpp - Revived unicode glyph page rendering system
- ChatScreen.cpp - Accept all Unicode input, block bidi overrides
- Gui.cpp - Chat display debug logging
- ClientConnection.cpp - Sign update debug logging
- SignTileEntity.cpp - Sign save logging, memory leak fix
2026-03-16 23:08:05 -05:00
qwasdrizzel d359957727 fix 2026-03-16 21:54:29 -05:00
qwasdrizzel ce739f6045 Merge branch 'smartcmd:main' into main 2026-03-16 21:44:26 -05:00
GabsPuNs 5a59f5d146 Update to more accurate logo (#1305)
* Updated Logo

* Updated two old files.
2026-03-16 19:00:51 -05:00
Revela 68537572e5 Update README with copy-paste support details
Added copy-paste support for various elements including IP/Port and world names.
2026-03-16 11:05:42 -05:00
Revela 43145a9366 Add Ctrl+V clipboard paste to UIControl_TextInput and UIScene_Keyboard
Previously paste only worked in the chat screen. Wire Screen::getClipboard() into the two remaining text input paths so Ctrl+V works for sign editing, seed entry, server IP/port, and world name fields.
2026-03-16 10:36:46 -05:00
Revela 72012da42f Merge branch 'main' of https://github.com/itsRevela/MinecraftConsoles 2026-03-16 08:10:47 -05:00
Revela e5a035795b Change default for hardcore-ban-ip to false
Updated the default value of the "hardcore-ban-ip" server property from "true" to "false" in the `kServerPropertyDefaults` array. Adjusted the `LoadServerPropertiesConfig` function to read the "hardcore-ban-ip" property as "false" to ensure consistency with the new default.
2026-03-16 08:10:40 -05:00
Revela 1a95b757ea Add hardcore-ban-ip property for IP banning on hardcore death
This commit introduces a new server property `hardcore-ban-ip` that controls whether players who die in hardcore mode are banned by their IP address. This setting should be set to `false` for playit.gg users!

Key changes include:
- Updated `banPlayerForHardcoreDeath` in `PlayerList.cpp` to check the `hardcoreBanIp` setting and handle IP bans accordingly.
- Added `hardcore-ban-ip` to the default server properties in `ServerProperties.cpp`.
- Declared the `hardcoreBanIp` boolean variable in `ServerProperties.h` to store the property value.

These changes enhance the server's ability to enforce IP bans based on configuration settings.
2026-03-16 08:00:48 -05:00
Revela faf32515f5 Update README with Hardcore Mode and server info
Added details about Hardcore Mode support and server downloads.
2026-03-16 04:11:04 -05:00
Revela 97bb759ecc Update workflows and Docker image references
- Modify `.gitattributes` to use "ours" merge strategy for specific workflow and Docker Compose files.
- Update `nightly.yml` to allow manual triggering and set permissions for content writing.
- Change Docker image reference for `minecraft-lce-dedicated-server` service to a new source.
2026-03-16 03:52:25 -05:00
Revela 1c4e5d5f95 Merge branch 'main' of https://github.com/itsRevela/MinecraftConsoles 2026-03-16 03:25:10 -05:00
Revela 635ffc6c54 Enhance nightly release script for client and server
Updated the script to build zips for both client and server, including new variables for server release management. Added functionality to create a server zip, fetch server release info, delete old assets, and upload the new server zip to GitHub. The script now updates the server release title with the latest commit hash and provides clearer output messages for both client and server releases.
2026-03-16 03:25:00 -05:00
Revela ba6a6393e8 Update Dedicated Server download link in README
Updated the link for Dedicated Server software to fork's build
2026-03-16 03:12:44 -05:00
Revela d2e9808a26 Implement persistent hardcore death bans (XUID + IP) for dedicated server
On the dedicated server, hardcore death now persists XUID and IP bans to
banned-players.json and banned-ips.json via the Access system, and
disconnects the player. Bans survive server restarts. Client-hosted games
retain the existing in-memory XUID ban with force-save behavior.

- Add hardcore property to server.properties (forces Hard difficulty)
- Add LevelData::setHardcore() so loaded worlds respect the server config
- Add PlayerList::banPlayerForHardcoreDeath() with persistent XUID + IP bans
- Reject respawn requests server-side in hardcore mode
- Ensure server-side player ticks run without move packets (fixes
  environmental damage not applying for some clients)
- Restore 0x8 hardcore bit on LoginPacket/RespawnPacket wire format so
  the client-side death screen detects hardcore mode correctly
2026-03-16 02:52:16 -05:00
Revela 110cec3dab Merge upstream/main into main 2026-03-15 23:38:41 -05:00
Revela 700fdf0c41 Add support for empty directory entries in ZIP archive
This commit introduces a new block in the `Update-NightlyRelease.ps1` script that creates empty directory entries in the ZIP archive. A `try` block is added to manage the `ZipArchive` object, and a loop iterates through all directories in the specified `$ReleaseDir`, ensuring that empty directories are included in the final archive.
2026-03-15 10:25:53 -05:00
Revela d7efcad139 Remove NDEBUG from preprocessor definitions
Incorrectly assumed that this was causing crashing on Steam deck. Reverting with this commit
2026-03-15 10:07:18 -05:00
Revela c60eabe32d Refactor ZIP creation using System.IO.Compression
Updated the ZIP file creation process to utilize the .NET `System.IO.Compression` library. This change eliminates the need for a temporary ZIP file and directly excludes `.pch` and `.zip` files from the archive. A top-level folder named "LCEWindows64" has been added to the ZIP structure, and resource management has been improved with proper disposal of file streams and ZIP archives.
2026-03-15 10:05:37 -05:00
Revela 7cb91529fb Include base directory in release script
Modified the CreateFromDirectory method to include the base directory in the zip file by changing the parameter from `$false` to `$true`.
2026-03-15 09:03:54 -05:00
Revela a4b0572362 Refactor zip creation using .NET Compression library
Updated the zipping process to utilize .NET's `System.IO.Compression` library for improved cross-platform compatibility. The new implementation creates a temporary zip file, removes unwanted entries (e.g., `.pch` and `.zip` files), and then finalizes the zip file, ensuring it only contains the desired files.
2026-03-15 08:50:39 -05:00
Revela c59010cb81 Prepare project for release build
Updated `<PreprocessorDefinitions>` to include `NDEBUG`,
indicating that debugging code will be excluded from the
build. This change is part of the preparation for a
release version of the project.
2026-03-15 07:37:07 -05:00
Revela a41c82539a Add F2 screenshot functionality and image writing support
- Updated `EControllerActions` to include `MINECRAFT_ACTION_SCREENSHOT`.
- Added conditional compilation for `stb_image_write.h` in `Minecraft.cpp`.
- Modified `run_middle()` to handle screenshot key press.
- Updated `tick()` to capture and save screenshots as PNG files.
- Introduced `KEY_SCREENSHOT` in `KeyboardMouseInput.h` mapped to F2.
- Added `stb_image_write.h` for image writing capabilities.
2026-03-15 07:36:49 -05:00
Revela 1e8c7a08b6 Update README with new features and functionality
Added details about in-game screenshot functionality
2026-03-15 05:46:51 -05:00
Revela f0c554a893 Merge branch 'main' of https://github.com/itsRevela/MinecraftConsoles 2026-03-15 04:00:31 -05:00
Revela e10ddcacd0 Merge upstream/main into main
Brings in dedicated server software, README updates, and .vscode cleanup
while preserving hardcore mode, screenshot, and watermark toggle features.
2026-03-15 03:54:37 -05:00
Revela 8a20e34b31 Add F2 screenshot functionality and image writing support
- Updated `EControllerActions` to include `MINECRAFT_ACTION_SCREENSHOT`.
- Added conditional compilation for `stb_image_write.h` in `Minecraft.cpp`.
- Modified `run_middle()` to handle screenshot key press.
- Updated `tick()` to capture and save screenshots as PNG files.
- Introduced `KEY_SCREENSHOT` in `KeyboardMouseInput.h` mapped to F2.
- Added `stb_image_write.h` for image writing capabilities.
2026-03-15 03:47:31 -05:00
Revela eaffb40316 Readme: Fix multiplayer exit loophole for non-host players
Added multiplayer fix to prevent host exit loophole.
2026-03-15 01:07:54 -05:00
Revela 50c4c42660 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 00:37:14 -05:00
Revela 31a6facf02 Powershell script that updates Nightly release & archives previous versions 2026-03-13 22:46:05 -05:00
Revela 022427cb93 Merge branch 'smartcmd:main' into main 2026-03-13 21:48:39 -05:00
Revela 8bd66905aa Merge branch 'main' of https://github.com/itsRevela/MinecraftConsoles 2026-03-13 11:11:43 -05:00
Revela 9397d404a6 Fix Gui.cpp merge and enhance hardcore mode visuals
This commit adds new `NamedFrame` entries and `KeyFrame` animations in `skin_Minecraft.xui` for various hardcore game modes, improving the UI representation (likely will get rid of this since it's dead code). A new `isHardcore` variable in `XUI_HUD.cpp` allows for conditional health icon animations based on the game mode. Additionally, commented-out code in `Gui.cpp` has been removed to streamline rendering logic. Several new PNG images for health states in hardcore mode have also been added to enhance the user experience.

TL;DR: This commit is basically just prep for adding hardcore heathbar hearts while also fixing some breaking that occurred in Gui.cpp after a merge.
2026-03-13 11:11:21 -05:00
Revela 54f70cd904 Update image in README for Hardcore Mode 2026-03-13 08:51:32 -05:00
Revela e2644b2c35 Merge branch 'main' of https://github.com/itsRevela/MinecraftConsoles 2026-03-13 08:50:14 -05:00
Revela 85c589f453 added hardcore preview image 2026-03-13 08:50:05 -05:00
Revela 4a284a289b Update README with Hardcore Mode features
Added details about the latest Hardcore Mode implementation including features for singleplayer and multiplayer.
2026-03-13 08:46:17 -05:00
Revela 18081f8fb9 Merge branch 'smartcmd:main' into main
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 07:17:11 -05:00
Revela 514c4cf102 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-13 07:12:26 -05:00
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
Revela 176a3db770 Merge branch 'smartcmd:main' into main 2026-03-12 18:42:37 -05:00
Revela c00375b72c Merge branch 'smartcmd:main' into main 2026-03-12 07:59:09 -05:00
Revela 0b3281d17f Enhance Fluxer server link presentation
Updated Fluxer server link with a centered image.
2026-03-12 06:13:02 -05:00
Revela 857155f3d6 Fix spelling and update Fluxer server link image 2026-03-12 06:09:38 -05:00
Revela e0e3a00a83 Fluxer link in readme
Updated Discord link and added information about Fluxer server.
2026-03-12 05:58:26 -05:00
Revela 812b3ea6af Merge branch 'main' of https://github.com/itsRevela/MinecraftConsoles 2026-03-12 05:20:44 -05:00
Revela b51fb18120 Merge branch 'smartcmd:main' into main 2026-03-12 05:20:21 -05:00
Revela b1c44c33bd set default values 2026-03-12 05:19:39 -05:00
Revela 330d764972 Update download link for Nightly Build 2026-03-12 00:10:36 -05:00
Revela 3caa5713e5 Gives option to remove the top-left watermark in ClientConstants.cpp:
Watermark on:
const bool ClientConstants::SHOW_VERSION_WATERMARK = true;

Watermark off:
const bool ClientConstants::SHOW_VERSION_WATERMARK = false;
2026-03-11 23:56:22 -05:00
Revela 0631aefec2 Fix portal cache key to use chunk coordinates 2026-03-11 22:12:03 -05:00
qwasdrizzel 255a18fe8e Fix crash by ensuring FOV is not less than 1 2026-03-05 22:20:39 -06:00
qwasdrizzel cffe636e35 Merge branch 'smartcmd:main' into main 2026-03-05 22:18:36 -06:00
qwasdrizzel d1eb09a4b9 Merge branch 'smartcmd:main' into main 2026-03-05 18:01:32 -06:00
qwasdrizzel 4b13b3345e Fix the angle problem with flying
Removed checks that limit flying to a 90 degree angle, which caused the problem.
2026-03-05 18:01:13 -06:00
qwasdrizzel 0666959d31 Merge branch 'smartcmd:main' into main 2026-03-05 17:17:45 -06:00
qwasdrizzel 9370cbc7d8 Modify dispense behavior to set outcome as LEFT_ITEM 2026-03-05 00:02:17 -06:00
179 changed files with 12256 additions and 2364 deletions
+3
View File
@@ -0,0 +1,3 @@
.github/workflows/docker-nightly.yml merge=ours
.github/workflows/nightly.yml merge=ours
docker-compose.dedicated-server.ghcr.yml merge=ours
Binary file not shown.

After

Width:  |  Height:  |  Size: 778 KiB

-31
View File
@@ -1,31 +0,0 @@
name: Build Minecraft Legacy Console Edition
on:
workflow_dispatch:
jobs:
build:
runs-on: windows-2022
strategy:
matrix:
configuration: [Release, Debug]
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
- name: Build Minecraft Legacy Console Edition
run: |
msbuild MinecraftConsoles.sln `
/p:Configuration=${{ matrix.configuration }} `
/p:Platform=Windows64 `
/m
- name: Upload Release + Debug Artifacts
uses: actions/upload-artifact@v4
with:
name: MinecraftClient-${{ matrix.configuration }}
path: x64/${{ matrix.configuration }}
-32
View File
@@ -1,32 +0,0 @@
name: MSBuild Debug Test
on:
workflow_dispatch:
pull_request:
types: [opened, reopened, synchronize]
paths-ignore:
- '.gitignore'
- '*.md'
- '.github/*.md'
push:
branches:
- 'main'
paths-ignore:
- '.gitignore'
- '*.md'
- '.github/*.md'
jobs:
build:
name: Build Windows64 (DEBUG)
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup msbuild
uses: microsoft/setup-msbuild@v2
- name: Build
run: MSBuild.exe MinecraftConsoles.sln /p:Configuration=Debug /p:Platform="Windows64"
-160
View File
@@ -1,160 +0,0 @@
name: Docker Nightly Dedicated Server
on:
workflow_dispatch:
push:
branches:
- "main"
- 'feature/dedicated-server'
paths-ignore:
- ".gitignore"
- "*.md"
- ".github/*.md"
permissions:
contents: read
packages: write
concurrency:
group: docker-nightly-dedicated-server
cancel-in-progress: true
jobs:
build-runtime:
name: Build Dedicated Server Runtime
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup msbuild
uses: microsoft/setup-msbuild@v2
- name: Build Dedicated Server Runtime Only
shell: pwsh
run: |
MSBuild.exe Minecraft.World\Minecraft.World.vcxproj /p:Configuration=Release /p:Platform=x64 /m
MSBuild.exe Minecraft.Server\Minecraft.Server.vcxproj /p:Configuration=Release /p:Platform=x64 /m
- name: Stage dedicated server runtime
shell: pwsh
run: |
$serverOut = "Minecraft.Server/x64/Minecraft.Server/Release"
$stage = ".artifacts/dedicated-server-runtime"
if (Test-Path $stage) {
Remove-Item -Path $stage -Recurse -Force
}
New-Item -ItemType Directory -Path (Join-Path $stage "Windows64") -Force | Out-Null
# Minimum required runtime files
$required = @(
"Minecraft.Server.exe",
"iggy_w64.dll",
"Common"
)
foreach ($entry in $required) {
$src = Join-Path $serverOut $entry
if (-not (Test-Path $src)) {
throw "Missing required runtime path: $src"
}
}
# Copy required files
Copy-Item -Path (Join-Path $serverOut "Minecraft.Server.exe") -Destination (Join-Path $stage "Minecraft.Server.exe") -Force
Copy-Item -Path (Join-Path $serverOut "iggy_w64.dll") -Destination (Join-Path $stage "iggy_w64.dll") -Force
Copy-Item -Path (Join-Path $serverOut "Common") -Destination (Join-Path $stage "Common") -Recurse -Force
if (Test-Path (Join-Path $serverOut "Windows64")) {
Copy-Item -Path (Join-Path $serverOut "Windows64/*") -Destination (Join-Path $stage "Windows64") -Recurse -Force
} else {
Write-Host "Windows64 directory is not present in build output; staging without it."
}
Get-ChildItem -Path $stage -Recurse -File | Select-Object -First 20 | ForEach-Object {
Write-Host "Staged: $($_.FullName)"
}
- name: Upload dedicated server runtime to artifacts
uses: actions/upload-artifact@v4
with:
name: dedicated-server-runtime
if-no-files-found: error
path: |
.artifacts/dedicated-server-runtime/**
docker-publish:
name: Build and Push Docker Image
runs-on: ubuntu-latest
needs: build-runtime
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Download dedicated server runtime from artifacts
uses: actions/download-artifact@v4
with:
name: dedicated-server-runtime
path: .artifacts/runtime
- name: Prepare Docker runtime directory
shell: bash
run: |
set -euo pipefail
rm -rf runtime
mkdir -p runtime
cp .artifacts/runtime/Minecraft.Server.exe runtime/Minecraft.Server.exe
cp .artifacts/runtime/iggy_w64.dll runtime/iggy_w64.dll
cp -R .artifacts/runtime/Common runtime/Common
mkdir -p runtime/Windows64
if [[ -d ".artifacts/runtime/Windows64" ]]; then
cp -R .artifacts/runtime/Windows64/. runtime/Windows64/
fi
test -f runtime/Minecraft.Server.exe
test -f runtime/iggy_w64.dll
test -d runtime/Common
test -d runtime/Windows64
- name: Compute image name
id: image
shell: bash
run: |
owner="$(echo "${{ vars.CONTAINER_REGISTRY_OWNER || github.repository_owner }}" | tr '[:upper:]' '[:lower:]')"
image_tag="nightly"
# if [[ "${{ github.ref }}" != "refs/heads/main" ]]; then
# image_tag="nightly-test"
# fi
echo "owner=$owner" >> "$GITHUB_OUTPUT"
echo "image=ghcr.io/$owner/minecraft-lce-dedicated-server" >> "$GITHUB_OUTPUT"
echo "image_tag=$image_tag" >> "$GITHUB_OUTPUT"
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ steps.image.outputs.image }}
tags: |
type=raw,value=${{ steps.image.outputs.image_tag }}
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ secrets.GHCR_USERNAME || github.actor }}
password: ${{ secrets.GHCR_TOKEN || secrets.GITHUB_TOKEN }}
- name: Build and push image
uses: docker/build-push-action@v6
with:
context: .
file: docker/dedicated-server/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
MC_RUNTIME_DIR=runtime
+167
View File
@@ -0,0 +1,167 @@
name: Nightly Server Release
on:
workflow_dispatch:
push:
branches:
- 'main'
paths-ignore:
- '.gitignore'
- '*.md'
- '.github/**'
- '!.github/workflows/nightly-server.yml'
permissions:
contents: write
packages: write
concurrency:
group: nightly-server
cancel-in-progress: true
jobs:
build:
runs-on: windows-latest
strategy:
matrix:
platform: [Windows64]
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set platform lowercase
run: echo "MATRIX_PLATFORM=$('${{ matrix.platform }}'.ToLower())" >> $env:GITHUB_ENV
- name: Setup MSVC
uses: ilammy/msvc-dev-cmd@v1
- name: Setup CMake
uses: lukka/get-cmake@latest
- name: Run CMake
uses: lukka/run-cmake@v10
env:
VCPKG_ROOT: "" # Disable vcpkg for CI builds
with:
configurePreset: ${{ env.MATRIX_PLATFORM }}
buildPreset: ${{ env.MATRIX_PLATFORM }}-release
buildPresetAdditionalArgs: "['--target', 'Minecraft.Server']"
- name: Zip Build
run: 7z a -r LCEServer${{ matrix.platform }}.zip ./build/${{ env.MATRIX_PLATFORM }}/Minecraft.Server/Release/* -x!*.ipdb -x!*.iobj
- name: Stage artifacts
run: |
New-Item -ItemType Directory -Force -Path staging
Copy-Item LCEServer${{ matrix.platform }}.zip staging/
- name: Stage exe and pdb
if: matrix.platform == 'Windows64'
run: |
Copy-Item ./build/${{ env.MATRIX_PLATFORM }}/Minecraft.Server/Release/Minecraft.Server.exe staging/
- name: Upload artifacts
uses: actions/upload-artifact@v6
with:
name: build-${{ matrix.platform }}
path: staging/*
release:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download all artifacts
uses: actions/download-artifact@v7
with:
path: artifacts
merge-multiple: true
- name: Update release
uses: andelf/nightly-release@main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: nightly-dedicated-server
name: Nightly Dedicated Server Release
body: |
Dedicated Server runtime for Windows64.
Download `LCEServerWindows64.zip` and extract it to a folder where you'd like to keep the server runtime.
files: |
artifacts/*
docker-publish:
name: Build and Push Docker Image
runs-on: ubuntu-latest
needs: build
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Download dedicated server runtime from artifacts
uses: actions/download-artifact@v4
with:
name: build-Windows64
path: .artifacts/
- name: Prepare Docker runtime directory
shell: bash
run: |
set -euo pipefail
rm -rf runtime
mkdir -p runtime
unzip .artifacts/LCEServerWindows64.zip -d runtime
- name: Compute image name
id: image
shell: bash
run: |
owner="$(echo "${{ vars.CONTAINER_REGISTRY_OWNER || github.repository_owner }}" | tr '[:upper:]' '[:lower:]')"
image_tag="nightly"
# if [[ "${{ github.ref }}" != "refs/heads/main" ]]; then
# image_tag="nightly-test"
# fi
echo "owner=$owner" >> "$GITHUB_OUTPUT"
echo "image=ghcr.io/$owner/minecraft-lce-dedicated-server" >> "$GITHUB_OUTPUT"
echo "image_tag=$image_tag" >> "$GITHUB_OUTPUT"
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ steps.image.outputs.image }}
tags: |
type=raw,value=${{ steps.image.outputs.image_tag }}
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ secrets.GHCR_USERNAME || github.actor }}
password: ${{ secrets.GHCR_TOKEN || secrets.GITHUB_TOKEN }}
- name: Build and push image
uses: docker/build-push-action@v6
with:
context: .
file: docker/dedicated-server/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
MC_RUNTIME_DIR=runtime
cleanup:
needs: [build, release]
if: always()
runs-on: ubuntu-latest
steps:
- name: Cleanup artifacts
uses: geekyeggo/delete-artifact@v5
with:
name: build-*
+1 -9
View File
@@ -1,15 +1,7 @@
name: Nightly Releases
on:
workflow_dispatch:
push:
branches:
- 'main'
- 'feature/dedicated-server'
paths-ignore:
- '.gitignore'
- '*.md'
- '.github/*.md'
workflow_dispatch:
permissions:
contents: write
+32
View File
@@ -0,0 +1,32 @@
name: Pull Request Build
on:
workflow_dispatch:
pull_request:
types: [opened, reopened, synchronize]
paths-ignore:
- '.gitignore'
- '*.md'
- '.github/*.md'
jobs:
build:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup MSVC
uses: ilammy/msvc-dev-cmd@v1
- name: Setup CMake
uses: lukka/get-cmake@latest
- name: Run CMake
uses: lukka/run-cmake@v10
env:
VCPKG_ROOT: "" # Disable vcpkg for CI builds
with:
configurePreset: windows64
buildPreset: windows64-debug
+4 -32
View File
@@ -26,6 +26,7 @@ mono_crash.*
[Rr]elease/
[Rr]eleases/
x64/
x64_*/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
@@ -407,39 +408,10 @@ enc_temp_folder/
Minecraft.Client/Schematics/
Minecraft.Client/Windows64/GameHDD/
# Intermediate build files (per-project)
Minecraft.Client/x64/
Minecraft.Client/Debug/
Minecraft.Client/x64_Debug/
Minecraft.Client/Release/
Minecraft.Client/x64_Release/
Minecraft.World/x64/
Minecraft.World/Debug/
Minecraft.World/x64_Debug/
Minecraft.World/Release/
Minecraft.World/x64_Release/
Minecraft.Server/x64/
Minecraft.Server/Debug/
Minecraft.Server/x64_Debug/
Minecraft.Server/Release/
Minecraft.Server/x64_Release/
build/*
# Existing build output files
!x64/**/Effects.msscmp
!x64/**/iggy_w64.dll
!x64/**/mss64.dll
!x64/**/redist64/
# Local saves
Minecraft.Client/Saves/
# CMake build output
build/
# Server data
tmp*/
_server_asset_probe/
server-data/
# Visual Studio Per-User Config
*.user
/out
+85 -216
View File
@@ -13,232 +13,101 @@ if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
message(FATAL_ERROR "Use a 64-bit generator/toolchain (x64).")
endif()
set(CMAKE_CONFIGURATION_TYPES
"Debug"
"Release"
CACHE STRING "" FORCE
)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
function(configure_msvc_target target)
target_compile_options(${target} PRIVATE
$<$<AND:$<NOT:$<CONFIG:Release>>,$<COMPILE_LANGUAGE:C,CXX>>:/W3>
$<$<AND:$<CONFIG:Release>,$<COMPILE_LANGUAGE:C,CXX>>:/W0>
$<$<COMPILE_LANGUAGE:C,CXX>:/MP>
$<$<COMPILE_LANGUAGE:C,CXX>:/FS>
$<$<COMPILE_LANGUAGE:CXX>:/EHsc>
$<$<AND:$<CONFIG:Release>,$<COMPILE_LANGUAGE:C,CXX>>:/GL /O2 /Oi /GT /GF>
)
function(configure_compiler_target target)
# MSVC and compatible compilers (like Clang-cl)
if (MSVC)
target_compile_options(${target} PRIVATE
$<$<AND:$<CONFIG:Debug>,$<COMPILE_LANGUAGE:C,CXX>>:/W3>
$<$<AND:$<CONFIG:Release>,$<COMPILE_LANGUAGE:C,CXX>>:/W0>
$<$<COMPILE_LANGUAGE:C,CXX>:/MP>
$<$<COMPILE_LANGUAGE:C,CXX>:/FS>
$<$<COMPILE_LANGUAGE:C,CXX>:/GS>
$<$<COMPILE_LANGUAGE:CXX>:/EHsc>
$<$<COMPILE_LANGUAGE:CXX>:/GR>
$<$<AND:$<CONFIG:Debug>,$<COMPILE_LANGUAGE:C,CXX>>:/Od>
$<$<AND:$<CONFIG:Release>,$<COMPILE_LANGUAGE:C,CXX>>:/O2 /Oi /GT /GF>
)
endif()
# MSVC
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(${target} PRIVATE
$<$<AND:$<CONFIG:Release>,$<COMPILE_LANGUAGE:C,CXX>>:/GL>
)
target_link_options(${target} PRIVATE
$<$<CONFIG:Release>:/LTCG:incremental>
)
endif()
# Clang
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(${target} PRIVATE
$<$<AND:$<CONFIG:Debug>,$<COMPILE_LANGUAGE:C,CXX>>:-O0 -Wall>
$<$<AND:$<CONFIG:Release>,$<COMPILE_LANGUAGE:C,CXX>>:-O2 -w -flto>
)
target_link_options(${target} PRIVATE
$<$<CONFIG:Release>:-flto>
)
endif()
endfunction()
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/WorldSources.cmake")
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/ClientSources.cmake")
list(TRANSFORM MINECRAFT_WORLD_SOURCES PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.World/")
list(TRANSFORM MINECRAFT_CLIENT_SOURCES PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/")
list(APPEND MINECRAFT_CLIENT_SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Xbox/MinecraftWindows.rc"
# ---
# Configuration
# ---
set(MINECRAFT_SHARED_DEFINES
_LARGE_WORLDS
_DEBUG_MENUS_ENABLED
$<$<CONFIG:Debug>:_DEBUG>
_CRT_NON_CONFORMING_SWPRINTFS
_CRT_SECURE_NO_WARNINGS
)
add_library(MinecraftWorld STATIC ${MINECRAFT_WORLD_SOURCES})
target_include_directories(MinecraftWorld PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.World"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.World/x64headers"
)
target_compile_definitions(MinecraftWorld PRIVATE
$<$<CONFIG:Debug>:_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_DEBUG;_LIB;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64>
$<$<NOT:$<CONFIG:Debug>>:_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_LIB;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64>
)
if(MSVC)
configure_msvc_target(MinecraftWorld)
# Add platform-specific defines
list(APPEND MINECRAFT_SHARED_DEFINES ${PLATFORM_DEFINES})
# ---
# Sources
# ---
add_subdirectory(Minecraft.World)
add_subdirectory(Minecraft.Client)
if(PLATFORM_NAME STREQUAL "Windows64") # Server is only supported on Windows for now
add_subdirectory(Minecraft.Server)
endif()
add_executable(MinecraftClient WIN32 ${MINECRAFT_CLIENT_SOURCES})
target_include_directories(MinecraftClient PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/Iggy/include"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Xbox/Sentient/Include"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.World/x64headers"
"${CMAKE_CURRENT_SOURCE_DIR}/include/"
)
target_compile_definitions(MinecraftClient PRIVATE
$<$<CONFIG:Debug>:_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_DEBUG;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64>
$<$<NOT:$<CONFIG:Debug>>:_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64>
)
if(MSVC)
configure_msvc_target(MinecraftClient)
target_link_options(MinecraftClient PRIVATE
$<$<CONFIG:Release>:/LTCG /INCREMENTAL:NO>
)
endif()
# ---
# Build versioning
# ---
set(BUILDVER_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateBuildVer.cmake")
set(BUILDVER_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/generated/Common/BuildVer.h")
set_target_properties(MinecraftClient PROPERTIES
VS_DEBUGGER_WORKING_DIRECTORY "$<TARGET_FILE_DIR:MinecraftClient>"
)
target_link_libraries(MinecraftClient PRIVATE
MinecraftWorld
d3d11
XInput9_1_0
wsock32
legacy_stdio_definitions
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/Iggy/lib/iggy_w64.lib"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/Iggy/lib/iggyperfmon_w64.lib"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/Iggy/lib/iggyexpruntime_w64.lib"
$<$<CONFIG:Debug>:
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Input_d.lib"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Storage_d.lib"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Render_PC_d.lib"
>
$<$<NOT:$<CONFIG:Debug>>:
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Input.lib"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Storage.lib"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Render_PC.lib"
>
)
set(MINECRAFT_SERVER_SOURCES ${MINECRAFT_CLIENT_SOURCES})
list(APPEND MINECRAFT_SERVER_SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Windows64/ServerMain.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Access/Access.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Access/BanManager.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Access/WhitelistManager.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCli.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCliInput.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCliParser.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCliEngine.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCliRegistry.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/ban/CliCommandBan.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/ban-list/CliCommandBanList.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/help/CliCommandHelp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/pardon/CliCommandPardon.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/stop/CliCommandStop.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/list/CliCommandList.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/tp/CliCommandTp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/time/CliCommandTime.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/weather/CliCommandWeather.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/give/CliCommandGive.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/enchant/CliCommandEnchant.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/kill/CliCommandKill.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/experience/CliCommandExperience.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Common/FileUtils.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Common/StringUtils.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/ServerLogger.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/ServerLogManager.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/ServerProperties.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/vendor/linenoise/linenoise.c"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/WorldManager.cpp"
)
add_executable(MinecraftServer ${MINECRAFT_SERVER_SOURCES})
target_include_directories(MinecraftServer PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/Iggy/include"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Xbox/Sentient/Include"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.World/x64headers"
"${CMAKE_CURRENT_SOURCE_DIR}/include/"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Windows64"
)
target_compile_definitions(MinecraftServer PRIVATE
$<$<CONFIG:Debug>:_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_DEBUG;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;MINECRAFT_SERVER_BUILD>
$<$<NOT:$<CONFIG:Debug>>:_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;MINECRAFT_SERVER_BUILD>
)
if(MSVC)
configure_msvc_target(MinecraftServer)
target_link_options(MinecraftServer PRIVATE
$<$<CONFIG:Release>:/LTCG /INCREMENTAL:NO>
)
endif()
set_target_properties(MinecraftServer PROPERTIES
OUTPUT_NAME "Minecraft.Server"
VS_DEBUGGER_WORKING_DIRECTORY "$<TARGET_FILE_DIR:MinecraftServer>"
VS_DEBUGGER_COMMAND_ARGUMENTS "-port 25565 -bind 0.0.0.0 -name DedicatedServer"
)
target_link_libraries(MinecraftServer PRIVATE
MinecraftWorld
d3d11
XInput9_1_0
wsock32
legacy_stdio_definitions
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/Iggy/lib/iggy_w64.lib"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/Iggy/lib/iggyperfmon_w64.lib"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/Iggy/lib/iggyexpruntime_w64.lib"
$<$<CONFIG:Debug>:
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Input_d.lib"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Storage_d.lib"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Render_PC_d.lib"
>
$<$<NOT:$<CONFIG:Debug>>:
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Input.lib"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Storage.lib"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Render_PC.lib"
>
)
if(CMAKE_HOST_WIN32)
message(STATUS "Starting redist copy...")
execute_process(
COMMAND robocopy.exe
"${CMAKE_CURRENT_SOURCE_DIR}/x64/Release"
"${CMAKE_CURRENT_BINARY_DIR}"
/S /MT /R:0 /W:0 /NP
)
message(STATUS "Starting asset copy...")
execute_process(
COMMAND robocopy.exe
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client"
"${CMAKE_CURRENT_BINARY_DIR}"
/S /MT /R:0 /W:0 /NP
/XF "*.cpp" "*.c" "*.h" "*.hpp" "*.asm"
"*.xml" "*.lang" "*.vcxproj" "*.vcxproj.*" "*.sln"
"*.docx" "*.xls"
"*.bat" "*.cmd" "*.ps1" "*.py"
"*Test*"
/XD "Durango*" "Orbis*" "PS*" "Xbox"
)
message(STATUS "Patching Windows64Media...")
execute_process(
COMMAND robocopy.exe
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/DurangoMedia"
"${CMAKE_CURRENT_BINARY_DIR}/Windows64Media"
/S /MT /R:0 /W:0 /NP
/XF "*.h" "*.xml" "*.lang" "*.bat"
)
elseif(CMAKE_HOST_UNIX)
message(STATUS "Starting redist copy...")
execute_process(
COMMAND rsync -av "${CMAKE_CURRENT_SOURCE_DIR}/x64/Release/" "${CMAKE_CURRENT_BINARY_DIR}/"
)
message(STATUS "Starting asset copy...")
execute_process(
COMMAND rsync -av
"--exclude=*.cpp" "--exclude=*.c" "--exclude=*.h" "--exclude=*.hpp" "--exclude=*.asm"
"--exclude=*.xml" "--exclude=*.lang" "--exclude=*.vcxproj" "--exclude=*.vcxproj.*" "--exclude=*.sln"
"--exclude=*.docx" "--exclude=*.xls"
"--exclude=*.bat" "--exclude=*.cmd" "--exclude=*.ps1" "--exclude=*.py"
"--exclude=*Test*"
"--exclude=Durango*" "--exclude=Orbis*" "--exclude=PS*" "--exclude=Xbox"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/" "${CMAKE_CURRENT_BINARY_DIR}/"
)
message(STATUS "Patching Windows64Media...")
execute_process(
COMMAND rsync -av
"--exclude=*.h" "--exclude=*.xml" "--exclude=*.lang" "--exclude=*.bat"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/DurangoMedia/" "${CMAKE_CURRENT_BINARY_DIR}/Windows64Media/"
)
else()
message(FATAL_ERROR "Redist and asset copying is only supported on Windows (Robocopy) and Unix systems (rsync).")
endif()
add_custom_command(TARGET MinecraftServer POST_BUILD
COMMAND "${CMAKE_COMMAND}"
-DPROJECT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}"
-DOUTPUT_DIR="$<TARGET_FILE_DIR:MinecraftServer>"
-DCONFIGURATION=$<CONFIG>
-P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/CopyServerAssets.cmake"
add_custom_target(GenerateBuildVer
COMMAND ${CMAKE_COMMAND}
"-DOUTPUT_FILE=${BUILDVER_OUTPUT}"
-P "${BUILDVER_SCRIPT}"
COMMENT "Generating BuildVer.h..."
VERBATIM
)
set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT MinecraftServer)
add_dependencies(Minecraft.World GenerateBuildVer)
add_dependencies(Minecraft.Client GenerateBuildVer)
if(PLATFORM_NAME STREQUAL "Windows64")
add_dependencies(Minecraft.Server GenerateBuildVer)
endif()
# ---
# Project organisation
# ---
# Set the startup project for Visual Studio
set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT Minecraft.Client)
# Setup folders for Visual Studio, just hides the build targets under a sub folder
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set_property(TARGET GenerateBuildVer PROPERTY FOLDER "Build")
+94
View File
@@ -0,0 +1,94 @@
{
"version": 5,
"configurePresets": [
{
"name": "base",
"generator": "Ninja Multi-Config",
"binaryDir": "${sourceDir}/build/${presetName}",
"hidden": true
},
{
"name": "windows64",
"displayName": "Windows64",
"inherits": "base",
"cacheVariables": {
"PLATFORM_DEFINES": "_WINDOWS64",
"PLATFORM_NAME": "Windows64",
"IGGY_LIBS": "iggy_w64.lib;iggyperfmon_w64.lib;iggyexpruntime_w64.lib"
}
},
{
"name": "durango",
"displayName": "Durango",
"inherits": "base",
"toolchainFile": "${sourceDir}/cmake/toolchains/durango.cmake",
"cacheVariables": {
"PLATFORM_DEFINES": "_DURANGO;_XBOX_ONE",
"PLATFORM_NAME": "Durango",
"IGGY_LIBS": "iggy_durango.lib;iggyperfmon_durango.lib"
}
},
{
"name": "orbis",
"displayName": "ORBIS",
"inherits": "base",
"toolchainFile": "${sourceDir}/cmake/toolchains/orbis.cmake",
"cacheVariables": {
"PLATFORM_DEFINES": "__ORBIS__",
"PLATFORM_NAME": "Orbis",
"IGGY_LIBS": "libiggy_orbis.a;libiggyperfmon_orbis.a"
}
},
{
"name": "ps3",
"displayName": "PS3",
"inherits": "base",
"toolchainFile": "${sourceDir}/cmake/toolchains/ps3.cmake",
"cacheVariables": {
"PLATFORM_DEFINES": "__PS3__",
"PLATFORM_NAME": "PS3",
"IGGY_LIBS": "libiggy_ps3.a;libiggyperfmon_ps3.a;libiggyexpruntime_ps3.a"
}
},
{
"name": "psvita",
"displayName": "PSVita",
"inherits": "base",
"toolchainFile": "${sourceDir}/cmake/toolchains/psvita.cmake",
"cacheVariables": {
"PLATFORM_DEFINES": "__PSVITA__",
"PLATFORM_NAME": "PSVita",
"IGGY_LIBS": "libiggy_psp2.a;libiggyperfmon_psp2.a"
}
},
{
"name": "xbox360",
"displayName": "Xbox 360",
"inherits": "base",
"toolchainFile": "${sourceDir}/cmake/toolchains/xbox360.cmake",
"cacheVariables": {
"PLATFORM_DEFINES": "_XBOX",
"PLATFORM_NAME": "Xbox"
}
}
],
"buildPresets": [
{ "name": "windows64-debug", "displayName": "Windows64 - Debug", "configurePreset": "windows64", "configuration": "Debug" },
{ "name": "windows64-release", "displayName": "Windows64 - Release", "configurePreset": "windows64", "configuration": "Release" },
{ "name": "durango-debug", "displayName": "Durango - Debug", "configurePreset": "durango", "configuration": "Debug" },
{ "name": "durango-release", "displayName": "Durango - Release", "configurePreset": "durango", "configuration": "Release" },
{ "name": "orbis-debug", "displayName": "ORBIS - Debug", "configurePreset": "orbis", "configuration": "Debug" },
{ "name": "orbis-release", "displayName": "ORBIS - Release", "configurePreset": "orbis", "configuration": "Release" },
{ "name": "ps3-debug", "displayName": "PS3 - Debug", "configurePreset": "ps3", "configuration": "Debug" },
{ "name": "ps3-release", "displayName": "PS3 - Release", "configurePreset": "ps3", "configuration": "Release" },
{ "name": "psvita-debug", "displayName": "PSVita - Debug", "configurePreset": "psvita", "configuration": "Debug" },
{ "name": "psvita-release", "displayName": "PSVita - Release", "configurePreset": "psvita", "configuration": "Release" },
{ "name": "xbox360-debug", "displayName": "Xbox 360 - Debug", "configurePreset": "xbox360", "configuration": "Debug" },
{ "name": "xbox360-release", "displayName": "Xbox 360 - Release", "configurePreset": "xbox360", "configuration": "Release" }
]
}
+19 -19
View File
@@ -1,16 +1,14 @@
# Compile Instructions
## Visual Studio (`.sln`)
## Visual Studio
1. Open `MinecraftConsoles.sln` in Visual Studio 2022.
2. Set Startup Project:
- Client: `Minecraft.Client`
- Dedicated server: `Minecraft.Server`
3. Select configuration:
- `Debug` (recommended), or
- `Release`
4. Select platform: `Windows64`.
5. Build and run:
1. Clone or download the repository
1. Open the repo folder in Visual Studio 2022+.
2. Wait for cmake to configure the project and load all assets (this may take a few minutes on the first run).
3. Right click a folder in the solution explorer and switch to the 'CMake Targets View'
4. Select platform and configuration from the dropdown. EG: `Windows64 - Debug` or `Windows64 - Release`
5. Pick the startup project `Minecraft.Client.exe` or `Minecraft.Server.exe` using the debug targets dropdown
6. Build and run the project:
- `Build > Build Solution` (or `Ctrl+Shift+B`)
- Start debugging with `F5`.
@@ -29,50 +27,52 @@
Configure (use your VS Community instance explicitly):
Open `Developer PowerShell for VS` and run:
```powershell
cmake -S . -B build -G "Visual Studio 17 2022" -A x64 -DCMAKE_GENERATOR_INSTANCE="C:/Program Files/Microsoft Visual Studio/2022/Community"
cmake --preset windows64
```
Build Debug:
```powershell
cmake --build build --config Debug --target MinecraftClient
cmake --build --preset windows64-debug --target Minecraft.Client
```
Build Release:
```powershell
cmake --build build --config Release --target MinecraftClient
cmake --build --preset windows64-release --target Minecraft.Client
```
Build Dedicated Server (Debug):
```powershell
cmake --build build --config Debug --target MinecraftServer
cmake --build --preset windows64-debug --target Minecraft.Server
```
Build Dedicated Server (Release):
```powershell
cmake --build build --config Release --target MinecraftServer
cmake --build --preset windows64-release --target Minecraft.Server
```
Run executable:
```powershell
cd .\build\Debug
.\MinecraftClient.exe
cd .\build\windows64\Minecraft.Client\Debug
.\Minecraft.Client.exe
```
Run dedicated server:
```powershell
cd .\build\Debug
cd .\build\windows64\Minecraft.Server\Debug
.\Minecraft.Server.exe -port 25565 -bind 0.0.0.0 -name DedicatedServer
```
Notes:
- The CMake build is Windows-only and x64-only.
- Contributors on macOS or Linux need a Windows machine or VM to build the project. Running the game via Wine is separate from having a supported build environment.
- Post-build asset copy is automatic for `MinecraftClient` in CMake (Debug and Release variants).
- Post-build asset copy is automatic for `Minecraft.Client` in CMake (Debug and Release variants).
- The game relies on relative paths (for example `Common\Media\...`), so launching from the output directory is required.
+96
View File
@@ -0,0 +1,96 @@
include("${CMAKE_CURRENT_LIST_DIR}/cmake/sources/Common.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/cmake/sources/Durango.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/cmake/sources/ORBIS.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/cmake/sources/PS3.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/cmake/sources/PSVita.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/cmake/sources/Windows.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/cmake/sources/Xbox360.cmake")
include("${CMAKE_SOURCE_DIR}/cmake/CommonSources.cmake")
include("${CMAKE_SOURCE_DIR}/cmake/Utils.cmake")
# Combine all source files into a single variable for the target
# We cant use CMAKE_CONFIGURE_PRESET here as VS doesn't set it, so just rely on the folder
set(MINECRAFT_CLIENT_SOURCES
${MINECRAFT_CLIENT_COMMON}
$<$<STREQUAL:${PLATFORM_NAME},Durango>:${MINECRAFT_CLIENT_DURANGO}>
$<$<STREQUAL:${PLATFORM_NAME},Orbis>:${MINECRAFT_CLIENT_ORBIS}>
$<$<STREQUAL:${PLATFORM_NAME},PS3>:${MINECRAFT_CLIENT_PS3}>
$<$<STREQUAL:${PLATFORM_NAME},PSVita>:${MINECRAFT_CLIENT_PSVITA}>
$<$<STREQUAL:${PLATFORM_NAME},Windows64>:${MINECRAFT_CLIENT_WINDOWS}>
$<$<STREQUAL:${PLATFORM_NAME},Xbox>:${MINECRAFT_CLIENT_XBOX360}>
${SOURCES_COMMON}
)
add_executable(Minecraft.Client ${MINECRAFT_CLIENT_SOURCES})
# Only define executable on windows
if(PLATFORM_NAME STREQUAL "Windows64")
set_target_properties(Minecraft.Client PROPERTIES WIN32_EXECUTABLE TRUE)
endif()
target_include_directories(Minecraft.Client PRIVATE
"${CMAKE_BINARY_DIR}/generated/" # This is for the generated BuildVer.h
"${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/${PLATFORM_NAME}/Iggy/include"
"${CMAKE_SOURCE_DIR}/include/"
)
target_compile_definitions(Minecraft.Client PRIVATE
${MINECRAFT_SHARED_DEFINES}
)
target_precompile_headers(Minecraft.Client PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:stdafx.h>")
set_source_files_properties(compat_shims.cpp PROPERTIES SKIP_PRECOMPILE_HEADERS ON) # This redefines internal MSVC CRT symbols which will cause an issue with PCH
configure_compiler_target(Minecraft.Client)
set_target_properties(Minecraft.Client PROPERTIES
OUTPUT_NAME "Minecraft.Client"
VS_DEBUGGER_WORKING_DIRECTORY "$<TARGET_FILE_DIR:Minecraft.Client>"
)
target_link_libraries(Minecraft.Client PRIVATE
Minecraft.World
d3d11
d3dcompiler
XInput9_1_0
wsock32
legacy_stdio_definitions
$<$<CONFIG:Debug>: # Debug 4J libraries
"${CMAKE_CURRENT_SOURCE_DIR}/${PLATFORM_NAME}/4JLibs/libs/4J_Input_d.lib"
"${CMAKE_CURRENT_SOURCE_DIR}/${PLATFORM_NAME}/4JLibs/libs/4J_Storage_d.lib"
"${CMAKE_CURRENT_SOURCE_DIR}/${PLATFORM_NAME}/4JLibs/libs/4J_Render_PC_d.lib"
>
$<$<NOT:$<CONFIG:Debug>>: # Release 4J libraries
"${CMAKE_CURRENT_SOURCE_DIR}/${PLATFORM_NAME}/4JLibs/libs/4J_Input.lib"
"${CMAKE_CURRENT_SOURCE_DIR}/${PLATFORM_NAME}/4JLibs/libs/4J_Storage.lib"
"${CMAKE_CURRENT_SOURCE_DIR}/${PLATFORM_NAME}/4JLibs/libs/4J_Render_PC.lib"
>
)
# Iggy libs
foreach(lib IN LISTS IGGY_LIBS)
target_link_libraries(Minecraft.Client PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/${PLATFORM_NAME}/Iggy/lib/${lib}")
endforeach()
# ---
# Asset / redist copy
# ---
include("${CMAKE_SOURCE_DIR}/cmake/CopyAssets.cmake")
set(ASSET_FOLDER_PAIRS
"${CMAKE_CURRENT_SOURCE_DIR}/music" "music"
"${CMAKE_CURRENT_SOURCE_DIR}/Common/Media" "Common/Media"
"${CMAKE_CURRENT_SOURCE_DIR}/Common/res" "Common/res"
"${CMAKE_CURRENT_SOURCE_DIR}/Common/Trial" "Common/Trial"
"${CMAKE_CURRENT_SOURCE_DIR}/Common/Tutorial" "Common/Tutorial"
"${CMAKE_CURRENT_SOURCE_DIR}/${PLATFORM_NAME}Media" "${PLATFORM_NAME}Media"
)
setup_asset_folder_copy(Minecraft.Client "${ASSET_FOLDER_PAIRS}")
# Copy redist files
add_copyredist_target(Minecraft.Client)
# Make sure GameHDD exists on Windows
if(PLATFORM_NAME STREQUAL "Windows64")
add_gamehdd_target(Minecraft.Client)
endif()
+25 -8
View File
@@ -6,6 +6,7 @@
#include "..\Minecraft.World\SharedConstants.h"
#include "..\Minecraft.World\StringHelpers.h"
#include "..\Minecraft.World\ChatPacket.h"
#include "..\Minecraft.World\ArabicShaping.h"
const wstring ChatScreen::allowedChars = SharedConstants::acceptableLetters;
vector<wstring> ChatScreen::s_chatHistory;
@@ -14,7 +15,12 @@ wstring ChatScreen::s_historyDraft;
bool ChatScreen::isAllowedChatChar(wchar_t c)
{
return c >= 0x20 && (c == L'\u00A7' || allowedChars.empty() || allowedChars.find(c) != wstring::npos);
if (c < 0x20) return false;
// Block Unicode bidirectional override characters that can be used to
// spoof chat messages or impersonate players.
if (c >= 0x202A && c <= 0x202E) return false; // LRE, RLE, PDF, LRO, RLO
if (c >= 0x2066 && c <= 0x2069) return false; // LRI, RLI, FSI, PDI
return true;
}
ChatScreen::ChatScreen()
@@ -93,6 +99,9 @@ void ChatScreen::keyPressed(wchar_t ch, int eventKey)
if (eventKey == Keyboard::KEY_RETURN)
{
wstring trim = trimString(message);
{ char buf[64]; sprintf_s(buf, "[CHAT] Sending (%d chars): ", (int)trim.length()); OutputDebugStringA(buf); }
OutputDebugStringW(trim.c_str());
OutputDebugStringA("\n");
if (trim.length() > 0)
{
if (!minecraft->handleClientSideCommand(trim))
@@ -135,6 +144,7 @@ void ChatScreen::keyPressed(wchar_t ch, int eventKey)
{
message.insert(cursorIndex, 1, ch);
cursorIndex++;
{ char buf[64]; sprintf_s(buf, "[CHAT] Char U+%04X accepted (%d chars)\n", (unsigned)ch, (int)message.length()); OutputDebugStringA(buf); }
}
}
@@ -145,14 +155,21 @@ void ChatScreen::render(int xm, int ym, float a)
int x = 4;
drawString(font, prefix, x, height - 12, 0xe0e0e0);
x += font->width(prefix);
wstring beforeCursor = message.substr(0, cursorIndex);
wstring afterCursor = message.substr(cursorIndex);
drawStringLiteral(font, beforeCursor, x, height - 12, 0xe0e0e0);
x += font->widthLiteral(beforeCursor);
// Shape the full message as one unit so letter connections and word order
// are correct. Track where the logical cursor maps in the visual string.
int visualCursorPos = 0;
wstring shaped = shapeArabicText(message, cursorIndex, &visualCursorPos);
// Render the full shaped message without re-shaping it
drawStringPreshaped(font, shaped, x, height - 12, 0xe0e0e0);
// Place the cursor at the correct visual position
wstring beforeCursorVisual = shaped.substr(0, visualCursorPos);
int cursorX = x + font->widthPreshaped(beforeCursorVisual);
if (frame / 6 % 2 == 0)
drawString(font, L"_", x, height - 12, 0xe0e0e0);
x += font->width(L"_");
drawStringLiteral(font, afterCursor, x, height - 12, 0xe0e0e0);
drawString(font, L"_", cursorX, height - 12, 0xe0e0e0);
Screen::render(xm, ym, a);
}
+9 -7
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 )
@@ -3315,7 +3315,9 @@ void ClientConnection::handleTileEditorOpen(shared_ptr<TileEditorOpenPacket> pac
void ClientConnection::handleSignUpdate(shared_ptr<SignUpdatePacket> packet)
{
app.DebugPrintf("ClientConnection::handleSignUpdate - ");
app.DebugPrintf("[SIGN] handleSignUpdate at (%d, %d, %d):\n", packet->x, packet->y, packet->z);
for (int i = 0; i < MAX_SIGN_LINES; i++)
app.DebugPrintf("[SIGN] Line%d: \"%ls\"\n", i+1, packet->lines[i].c_str());
if (minecraft->level->hasChunkAt(packet->x, packet->y, packet->z))
{
shared_ptr<TileEntity> te = minecraft->level->getTileEntity(packet->x, packet->y, packet->z);
@@ -3329,7 +3331,7 @@ void ClientConnection::handleSignUpdate(shared_ptr<SignUpdatePacket> packet)
ste->SetMessage(i,packet->lines[i]);
}
app.DebugPrintf("verified = %d\tCensored = %d\n",packet->m_bVerified,packet->m_bCensored);
app.DebugPrintf("[SIGN] verified=%d censored=%d\n", packet->m_bVerified, packet->m_bCensored);
ste->SetVerified(packet->m_bVerified);
ste->SetCensored(packet->m_bCensored);
@@ -3337,12 +3339,12 @@ void ClientConnection::handleSignUpdate(shared_ptr<SignUpdatePacket> packet)
}
else
{
app.DebugPrintf("dynamic_pointer_cast<SignTileEntity>(te) == nullptr\n");
app.DebugPrintf("[SIGN] ERROR: tile entity is not a SignTileEntity\n");
}
}
else
{
app.DebugPrintf("hasChunkAt failed\n");
app.DebugPrintf("[SIGN] ERROR: chunk not loaded at position\n");
}
}
+8
View File
@@ -1,5 +1,13 @@
#include "stdafx.h"
#include "ClientConstants.h"
#include "Common/BuildVer.h"
const wstring ClientConstants::VERSION_STRING = wstring(L"Minecraft LCE ") + VER_FILEVERSION_STR_W;//+ SharedConstants::VERSION_STRING;
const wstring ClientConstants::BRANCH_STRING = VER_BRANCHVERSION_STR_W;
// Default value for the toggle. If BuildVer defines VER_SHOW_VERSION_WATERMARK, use that.
#ifdef VER_SHOW_VERSION_WATERMARK
const bool ClientConstants::SHOW_VERSION_WATERMARK = (VER_SHOW_VERSION_WATERMARK != 0);
#else
const bool ClientConstants::SHOW_VERSION_WATERMARK = false;
#endif
+3
View File
@@ -15,5 +15,8 @@ public:
static const wstring VERSION_STRING;
static const wstring BRANCH_STRING;
// Toggle to show/hide the version/branch watermark in the debug overlay
static const bool SHOW_VERSION_WATERMARK;
static const bool DEADMAU5_CAMERA_CHEATS = false;
};
+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
+3 -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
@@ -878,7 +879,8 @@ enum EControllerActions
MINECRAFT_ACTION_SPAWN_CREEPER,
MINECRAFT_ACTION_CHANGE_SKIN,
MINECRAFT_ACTION_FLY_TOGGLE,
MINECRAFT_ACTION_RENDER_DEBUG
MINECRAFT_ACTION_RENDER_DEBUG,
MINECRAFT_ACTION_SCREENSHOT
};
enum eMCLang
-7
View File
@@ -1,7 +0,0 @@
#pragma once
#define VER_PRODUCTBUILD 560
#define VER_PRODUCTVERSION_STR_W L"DEV (unknown version)"
#define VER_FILEVERSION_STR_W VER_PRODUCTVERSION_STR_W
#define VER_BRANCHVERSION_STR_W L"UNKNOWN BRANCH"
#define VER_NETWORK VER_PRODUCTBUILD
+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.

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.
@@ -38220,6 +38220,56 @@
<Time>19</Time>
<Command>stop</Command>
</NamedFrame>
<NamedFrame>
<Name>NormalHardcore</Name>
<Time>20</Time>
<Command>stop</Command>
</NamedFrame>
<NamedFrame>
<Name>HalfHardcore</Name>
<Time>21</Time>
<Command>stop</Command>
</NamedFrame>
<NamedFrame>
<Name>FullHardcore</Name>
<Time>22</Time>
<Command>stop</Command>
</NamedFrame>
<NamedFrame>
<Name>HalfPoisonHardcore</Name>
<Time>23</Time>
<Command>stop</Command>
</NamedFrame>
<NamedFrame>
<Name>FullPoisonHardcore</Name>
<Time>24</Time>
<Command>stop</Command>
</NamedFrame>
<NamedFrame>
<Name>NormalFlashHardcore</Name>
<Time>25</Time>
<Command>stop</Command>
</NamedFrame>
<NamedFrame>
<Name>HalfFlashHardcore</Name>
<Time>26</Time>
<Command>stop</Command>
</NamedFrame>
<NamedFrame>
<Name>FullFlashHardcore</Name>
<Time>27</Time>
<Command>stop</Command>
</NamedFrame>
<NamedFrame>
<Name>HalfPoisonFlashHardcore</Name>
<Time>28</Time>
<Command>stop</Command>
</NamedFrame>
<NamedFrame>
<Name>FullPoisonFlashHardcore</Name>
<Time>29</Time>
<Command>stop</Command>
</NamedFrame>
</NamedFrames>
<Timeline>
<Id>Border</Id>
@@ -38274,6 +38324,16 @@
<Interpolation>0</Interpolation>
<Prop>Graphics\HUD\Health_Background_Flash.png</Prop>
</KeyFrame>
<KeyFrame>
<Time>20</Time>
<Interpolation>0</Interpolation>
<Prop>Graphics\HUD\Health_Background_Hardcore.png</Prop>
</KeyFrame>
<KeyFrame>
<Time>25</Time>
<Interpolation>0</Interpolation>
<Prop>Graphics\HUD\Health_Background_Hardcore_Flash.png</Prop>
</KeyFrame>
</Timeline>
<Timeline>
<Id>Heart</Id>
@@ -38399,6 +38459,66 @@
<Prop>true</Prop>
<Prop>Graphics\HUD\HorseHealth_Half_Flash.png</Prop>
</KeyFrame>
<KeyFrame>
<Time>20</Time>
<Interpolation>0</Interpolation>
<Prop>false</Prop>
<Prop></Prop>
</KeyFrame>
<KeyFrame>
<Time>21</Time>
<Interpolation>0</Interpolation>
<Prop>true</Prop>
<Prop>Graphics\HUD\Health_Half_Hardcore.png</Prop>
</KeyFrame>
<KeyFrame>
<Time>22</Time>
<Interpolation>0</Interpolation>
<Prop>true</Prop>
<Prop>Graphics\HUD\Health_Full_Hardcore.png</Prop>
</KeyFrame>
<KeyFrame>
<Time>23</Time>
<Interpolation>0</Interpolation>
<Prop>true</Prop>
<Prop>Graphics\HUD\Health_Half_Poison_Hardcore.png</Prop>
</KeyFrame>
<KeyFrame>
<Time>24</Time>
<Interpolation>0</Interpolation>
<Prop>true</Prop>
<Prop>Graphics\HUD\Health_Full_Poison_Hardcore.png</Prop>
</KeyFrame>
<KeyFrame>
<Time>25</Time>
<Interpolation>0</Interpolation>
<Prop>false</Prop>
<Prop></Prop>
</KeyFrame>
<KeyFrame>
<Time>26</Time>
<Interpolation>0</Interpolation>
<Prop>true</Prop>
<Prop>Graphics\HUD\Health_Half_Flash_Hardcore.png</Prop>
</KeyFrame>
<KeyFrame>
<Time>27</Time>
<Interpolation>0</Interpolation>
<Prop>true</Prop>
<Prop>Graphics\HUD\Health_Full_Flash_Hardcore.png</Prop>
</KeyFrame>
<KeyFrame>
<Time>28</Time>
<Interpolation>0</Interpolation>
<Prop>true</Prop>
<Prop>Graphics\HUD\Health_Half_Poison_Flash_Hardcore.png</Prop>
</KeyFrame>
<KeyFrame>
<Time>29</Time>
<Interpolation>0</Interpolation>
<Prop>true</Prop>
<Prop>Graphics\HUD\Health_Full_Poison_Flash_Hardcore.png</Prop>
</KeyFrame>
</Timeline>
</Timelines>
</XuiVisual>
@@ -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();
+3 -13
View File
@@ -141,6 +141,9 @@ S32 UIBitmapFont::GetCodepointGlyph(U32 codepoint)
// 4J-JEV: Change "right single quotation marks" to apostrophies.
if (codepoint == 0x2019) codepoint = 0x27;
if (!m_cFontData->hasGlyph(codepoint))
return IGGY_GLYPH_INVALID;
return m_cFontData->getGlyphId(codepoint);
}
@@ -253,19 +256,6 @@ rrbool UIBitmapFont::GetGlyphBitmap(S32 glyph,F32 pixel_scale,IggyBitmapCharacte
while ( (0.5f + glyphScale) * truePixelScale < pixel_scale)
glyphScale++;
// Debug: log each unique (font, pixel_scale) pair
{
static std::unordered_set<int> s_loggedScaleKeys;
// Encode font pointer + quantized scale into a key to log each combo once
int scaleKey = (int)(pixel_scale * 100.0f) ^ (int)(uintptr_t)m_cFontData;
if (s_loggedScaleKeys.find(scaleKey) == s_loggedScaleKeys.end() && s_loggedScaleKeys.size() < 50) {
s_loggedScaleKeys.insert(scaleKey);
float tps = truePixelScale;
app.DebugPrintf("[FONT-DBG] GetGlyphBitmap: font=%s glyph=%d pixel_scale=%.3f truePixelScale=%.1f glyphScale=%.0f\n",
m_cFontData->getFontName().c_str(), glyph, pixel_scale, tps, glyphScale);
}
}
// 4J-JEV: Debug code to check which font sizes are being used.
#if (!defined _CONTENT_PACKAGE) && (VERBOSE_FONT_OUTPUT > 0)
+11 -4
View File
@@ -3,6 +3,7 @@
#include "UIControl.h"
#include "..\..\..\Minecraft.World\StringHelpers.h"
#include "..\..\..\Minecraft.World\JavaMath.h"
#include "..\..\..\Minecraft.World\ArabicShaping.h"
UIControl_Base::UIControl_Base()
{
@@ -32,13 +33,16 @@ void UIControl_Base::tick()
//app.DebugPrintf("Calling SetLabel - '%ls'\n", m_label.c_str());
m_bLabelChanged = false;
// Shape the text before sending to Iggy; m_label stays unshaped for future updates
wstring shaped = shapeArabicText(m_label.getString());
IggyDataValue result;
IggyDataValue value[1];
value[0].type = IGGY_DATATYPE_string_UTF16;
IggyStringUTF16 stringVal;
stringVal.string = (IggyUTF16*) m_label.c_str();
stringVal.length = m_label.length();
stringVal.string = (IggyUTF16*) shaped.c_str();
stringVal.length = (int)shaped.length();
value[0].string16 = stringVal;
IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_setLabelFunc , 1 , value );
@@ -56,13 +60,16 @@ void UIControl_Base::setLabel(UIString label, bool instant, bool force)
{
m_bLabelChanged = false;
// Shape the text before sending to Iggy; m_label stays unshaped for future updates
wstring shaped = shapeArabicText(m_label.getString());
IggyDataValue result;
IggyDataValue value[1];
value[0].type = IGGY_DATATYPE_string_UTF16;
IggyStringUTF16 stringVal;
stringVal.string = (IggyUTF16*)m_label.c_str();
stringVal.length = m_label.length();
stringVal.string = (IggyUTF16*) shaped.c_str();
stringVal.length = (int)shaped.length();
value[0].string16 = stringVal;
IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_setLabelFunc , 1 , value );
@@ -2,6 +2,7 @@
#include "UI.h"
#include "UIControl_Label.h"
#include "..\..\..\Minecraft.World\StringHelpers.h"
#include "..\..\..\Minecraft.World\ArabicShaping.h"
UIControl_Label::UIControl_Label()
{
@@ -22,13 +23,15 @@ void UIControl_Label::init(UIString label)
{
m_label = label;
wstring shaped = shapeArabicText(m_label.getString());
IggyDataValue result;
IggyDataValue value[1];
value[0].type = IGGY_DATATYPE_string_UTF16;
IggyStringUTF16 stringVal;
stringVal.string = (IggyUTF16*)label.c_str();
stringVal.length = label.length();
stringVal.string = (IggyUTF16*)shaped.c_str();
stringVal.length = (int)shaped.length();
value[0].string16 = stringVal;
IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_initFunc , 1 , value );
}
@@ -1,6 +1,7 @@
#include "stdafx.h"
#include "UI.h"
#include "UIControl_SaveList.h"
#include "..\..\..\Minecraft.World\ArabicShaping.h"
bool UIControl_SaveList::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName)
{
@@ -69,12 +70,14 @@ void UIControl_SaveList::addItem(const string &label, const wstring &iconName, i
void UIControl_SaveList::addItem(const wstring &label, const wstring &iconName, int data)
{
wstring shaped = shapeArabicText(label);
IggyDataValue result;
IggyDataValue value[3];
IggyStringUTF16 stringVal;
stringVal.string = (IggyUTF16*)label.c_str();
stringVal.length = static_cast<S32>(label.length());
stringVal.string = (IggyUTF16*)shaped.c_str();
stringVal.length = static_cast<S32>(shaped.length());
value[0].type = IGGY_DATATYPE_string_UTF16;
value[0].string16 = stringVal;
@@ -1,6 +1,7 @@
#include "stdafx.h"
#include "UI.h"
#include "UIControl_TextInput.h"
#include "..\..\Screen.h"
UIControl_TextInput::UIControl_TextInput()
{
@@ -211,6 +212,21 @@ UIControl_TextInput::EDirectEditResult UIControl_TextInput::tickDirectEdit()
}
}
// Paste from clipboard
if (g_KBMInput.IsKeyPressed('V') && g_KBMInput.IsKeyDown(VK_CONTROL))
{
wstring pasted = Screen::getClipboard();
for (size_t i = 0; i < pasted.length(); i++)
{
wchar_t pc = pasted[i];
if (pc < 0x20) continue; // skip control characters
if (m_iCharLimit > 0 && (int)m_editBuffer.length() >= m_iCharLimit) break;
m_editBuffer.insert(m_iCursorPos, 1, pc);
m_iCursorPos++;
changed = true;
}
}
// Arrow keys, Home, End, Delete for cursor movement
if (g_KBMInput.IsKeyPressed(VK_LEFT) && m_iCursorPos > 0)
{
@@ -13,6 +13,7 @@
#include "..\..\EnderDragonRenderer.h"
#include "..\..\MultiPlayerLocalPlayer.h"
#include "UIFontData.h"
#include "UIUnicodeBitmapFont.h"
#include "UISplitScreenHelpers.h"
#ifdef _WINDOWS64
#include "..\..\Windows64\KeyboardMouseInput.h"
@@ -193,6 +194,7 @@ UIController::UIController()
m_mcTTFFont = nullptr;
m_moj7 = nullptr;
m_moj11 = nullptr;
m_unicodeBitmapFont = nullptr;
// 4J-JEV: It's important that these remain the same, unless updateCurrentLanguage is going to be called.
m_eCurrentFont = m_eTargetFont = eFont_NotLoaded;
@@ -307,6 +309,14 @@ void UIController::postInit()
IggySetAS3ExternalFunctionCallbackUTF16 ( &UIController::ExternalFunctionCallback, this );
IggySetTextureSubstitutionCallbacks ( &UIController::TextureSubstitutionCreateCallback , &UIController::TextureSubstitutionDestroyCallback, this );
// Load a unicode bitmap font as Iggy's global fallback for characters not
// covered by the Mojangles bitmap font (CJK, Thai, Arabic, Korean, etc.).
// Uses the same glyph page PNGs as the legacy Font class, with matching
// Mojangles metrics for correct vertical alignment.
m_unicodeBitmapFont = new UIUnicodeBitmapFont("Mojangles_Unicode_Bitmap", SFontData::Mojangles_7);
m_unicodeBitmapFont->registerFont();
IggyFontSetFallbackFontUTF8("Mojangles_Unicode_Bitmap", -1, IGGY_FONTFLAG_none);
SetupFont();
//
loadSkins();
@@ -7,6 +7,7 @@ using namespace std;
class UIAbstractBitmapFont;
class UIBitmapFont;
class UIUnicodeBitmapFont;
class UITTFFont;
class UIComponent_DebugUIConsole;
class UIComponent_DebugUIMarketingGuide;
@@ -63,6 +64,7 @@ private:
UIAbstractBitmapFont *m_mcBitmapFont;
UITTFFont *m_mcTTFFont;
UIBitmapFont *m_moj7, *m_moj11;
UIUnicodeBitmapFont *m_unicodeBitmapFont;
std::mt19937 m_randomGenerator;
std::uniform_real_distribution<float> m_randomDistribution;
@@ -335,6 +335,11 @@ bool CFontData::unicodeIsWhitespace(unsigned int unicode)
return false;
}
bool CFontData::hasGlyph(unsigned int unicodepoint)
{
return m_unicodeMap.find(unicodepoint) != m_unicodeMap.end();
}
void CFontData::moveCursor(unsigned char *&cursor, unsigned int dx, unsigned int dy)
{
cursor += (dy * m_sFontData->m_uiGlyphMapX) + dx;
+3
View File
@@ -125,6 +125,9 @@ public:
// Returns true if this unicodepoint is whitespace
bool unicodeIsWhitespace(unsigned int unicodepoint);
// Returns true if this unicodepoint exists in the font's glyph map.
bool hasGlyph(unsigned int unicodepoint);
private:
// Move a pointer in an image dx pixels right and dy pixels down, wrap around in either dimension leads to unknown behaviour.
@@ -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);
+18 -8
View File
@@ -480,14 +480,6 @@ SCreditTextItemDef UIScene_Credits::gs_aCreditDefs[MAX_CREDIT_STRINGS] =
{ L"Copyright (C) 2009-2013 by RAD Game Tools, Inc.", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line
#else
{ L"Copyright (C) 2009-2014 by RAD Game Tools, Inc.", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line
#endif
{ L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line
{ L"", CREDIT_ICON, eCreditIcon_Miles,eSmallText }, // extra blank line
{ L"Uses Miles Sound System.", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line
#ifdef __PS3__
{ L"Copyright (C) 1991-2013 by RAD Game Tools, Inc.", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line
#else
{ L"Copyright (C) 1991-2014 by RAD Game Tools, Inc.", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line
#endif
#ifdef __PS3__
{ L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line
@@ -496,6 +488,24 @@ SCreditTextItemDef UIScene_Credits::gs_aCreditDefs[MAX_CREDIT_STRINGS] =
{ L"are trademarks of Dolby Laboratories.", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line
#endif
#endif
{L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"MinecraftConsoles", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eExtraLargeText},
{L"Project Maintainers", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eLargeText},
{L"smartcmd", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"codeHusky", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"Patoke", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"rtm516", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"mattsumi", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"dxf", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"la", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"Thank you to our 100+ contributors on GitHub!", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eLargeText},
{L"github.com/smartcmd/MinecraftConsoles", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText},
{L"Additional Thanks", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eLargeText},
{L"notpies - Security Fixes", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING, eSmallText}
};
UIScene_Credits::UIScene_Credits(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer)
@@ -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,6 +1,7 @@
#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.
@@ -224,6 +225,28 @@ void UIScene_Keyboard::tick()
}
}
// Paste from clipboard
if (g_KBMInput.IsKeyPressed('V') && g_KBMInput.IsKeyDown(VK_CONTROL))
{
wstring pasted = Screen::getClipboard();
for (size_t i = 0; i < pasted.length(); i++)
{
wchar_t pc = pasted[i];
if (pc < 0x20) continue; // skip control characters
if (static_cast<int>(m_win64TextBuffer.length()) >= m_win64MaxChars) break;
if (m_bPCMode)
{
m_win64TextBuffer.insert(m_iCursorPos, 1, pc);
m_iCursorPos++;
}
else
{
m_win64TextBuffer += pc;
}
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.
@@ -253,10 +255,34 @@ UIScene_LoadMenu::UIScene_LoadMenu(int iPad, void *initData, UILayer *parentLaye
{
wchar_t wSaveName[128];
ZeroMemory(wSaveName, sizeof(wSaveName));
mbstowcs(wSaveName, params->saveDetails->UTF8SaveName, 127);
MultiByteToWideChar(CP_UTF8, 0, params->saveDetails->UTF8SaveName, -1, wSaveName, 127);
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'\\');
@@ -49,7 +49,7 @@ static wstring ReadLevelNameFromSaveFile(const wstring& filePath)
if (len > 0)
{
wchar_t wbuf[128] = {};
mbstowcs(wbuf, buf, 127);
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, 127);
return wstring(wbuf);
}
}
@@ -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;
}
}
@@ -206,6 +210,8 @@ int UIScene_LoadOrJoinMenu::LoadSaveCallback(LPVOID lpParam,bool bRes)
UIScene_LoadOrJoinMenu::UIScene_LoadOrJoinMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer)
{
constexpr uint64_t MAXIMUM_SAVE_STORAGE = 4LL * 1024LL * 1024LL * 1024LL;
// Setup all the Iggy references we need for this scene
initialiseMovie();
app.SetLiveLinkRequired( true );
@@ -230,8 +236,8 @@ UIScene_LoadOrJoinMenu::UIScene_LoadOrJoinMenu(int iPad, void *initData, UILayer
m_controlJoinTimer.setVisible( true );
#if defined(_XBOX_ONE) || defined(__ORBIS__)
m_spaceIndicatorSaves.init(L"",eControl_SpaceIndicator,0, (4LL *1024LL * 1024LL * 1024LL) );
#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(_WINDOWS64)
m_spaceIndicatorSaves.init(L"",eControl_SpaceIndicator,0, MAXIMUM_SAVE_STORAGE);
#endif
m_bUpdateSaveSize = false;
@@ -695,7 +701,7 @@ void UIScene_LoadOrJoinMenu::tick()
if(m_eSaveTransferState == eSaveTransfer_Idle)
m_bSaveTransferRunning = false;
#endif
#if defined(_XBOX_ONE) || defined(__ORBIS__)
#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(_WINDOWS64)
if(m_bUpdateSaveSize)
{
if((m_iDefaultButtonsC > 0) && (m_iSaveListIndex >= m_iDefaultButtonsC))
@@ -716,7 +722,7 @@ void UIScene_LoadOrJoinMenu::tick()
if(m_pSaveDetails!=nullptr)
{
//CD - Fix - Adding define for ORBIS/XBOXONE
#if defined(_XBOX_ONE) || defined(__ORBIS__)
#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(_WINDOWS64)
m_spaceIndicatorSaves.reset();
#endif
@@ -758,6 +764,22 @@ void UIScene_LoadOrJoinMenu::tick()
{
#if defined(_XBOX_ONE)
m_spaceIndicatorSaves.addSave(m_pSaveDetails->SaveInfoA[i].totalSize);
#elif defined(_WINDOWS64)
int origIdx = sortedIdx[i];
wchar_t wFilename[MAX_SAVEFILENAME_LENGTH];
ZeroMemory(wFilename, sizeof(wFilename));
mbstowcs(wFilename, m_pSaveDetails->SaveInfoA[origIdx].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH - 1);
wstring filePath = wstring(L"Windows64\\GameHDD\\") + wstring(wFilename) + wstring(L"\\saveData.ms");
HANDLE hFile = CreateFileW(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
DWORD fileSize = 0;
if (hFile != INVALID_HANDLE_VALUE) {
fileSize = GetFileSize(hFile, nullptr);
if (fileSize < 12 || fileSize == INVALID_FILE_SIZE) fileSize = 0;
CloseHandle(hFile);
}
m_spaceIndicatorSaves.addSave(fileSize);
#elif defined(__ORBIS__)
m_spaceIndicatorSaves.addSave(m_pSaveDetails->SaveInfoA[i].blocksUsed * (32 * 1024) );
#endif
@@ -770,17 +792,17 @@ void UIScene_LoadOrJoinMenu::tick()
#else
#ifdef _WINDOWS64
{
int origIdx = sortedIdx[i];
wchar_t wFilename[MAX_SAVEFILENAME_LENGTH];
ZeroMemory(wFilename, sizeof(wFilename));
mbstowcs(wFilename, m_pSaveDetails->SaveInfoA[origIdx].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH - 1);
wstring filePath = wstring(L"Windows64\\GameHDD\\") + wstring(wFilename) + wstring(L"\\saveData.ms");
wstring levelName = ReadLevelNameFromSaveFile(filePath);
bool saveHardcore = false;
wstring levelName = ReadLevelNameFromSaveFile(filePath, &saveHardcore);
m_saveDetails[i].isHardcore = saveHardcore;
if (!levelName.empty())
{
m_buttonListSaves.addItem(levelName, wstring(L""));
wcstombs(m_saveDetails[i].UTF8SaveName, levelName.c_str(), 127);
m_saveDetails[i].UTF8SaveName[127] = '\0';
{
int n = WideCharToMultiByte(CP_UTF8, 0, levelName.c_str(), -1, m_saveDetails[i].UTF8SaveName, 127, nullptr, nullptr);
if (n <= 0) m_saveDetails[i].UTF8SaveName[0] = '\0';
m_saveDetails[i].UTF8SaveName[127] = '\0';
}
}
else
{
@@ -1425,9 +1447,9 @@ int UIScene_LoadOrJoinMenu::KeyboardCompleteWorldNameCallback(LPVOID lpParam,boo
for (int k = 0; k < 127 && ui16Text[k]; k++)
wNewName[k] = static_cast<wchar_t>(ui16Text[k]);
// Convert to narrow for storage and in-memory update
char narrowName[128] = {};
wcstombs(narrowName, wNewName, 127);
// Convert to narrow for storage and in-memory update (UTF-8 to preserve Unicode)
char narrowName[256] = {};
WideCharToMultiByte(CP_UTF8, 0, wNewName, -1, narrowName, 255, nullptr, nullptr);
// Build the sidecar path: Windows64\GameHDD\{folder}\worldname.txt
wchar_t wFilename[MAX_SAVEFILENAME_LENGTH] = {};
@@ -1443,7 +1465,7 @@ int UIScene_LoadOrJoinMenu::KeyboardCompleteWorldNameCallback(LPVOID lpParam,boo
// Update the in-memory display name so the list reflects it immediately
strncpy_s(pClass->m_saveDetails[listPos].UTF8SaveName, narrowName, 127);
pClass->m_saveDetails[listPos].UTF8SaveName[127] = '\0';
pClass->m_saveDetails[listPos].UTF8SaveName[127] = '\0'; // UTF8SaveName is still 128 bytes; narrowName fits as Arabic is <=2 bytes/char in UTF-8
// Reuse the existing callback to trigger the list repopulate
UIScene_LoadOrJoinMenu::RenameSaveDataReturned(pClass, true);
@@ -2511,7 +2533,7 @@ int UIScene_LoadOrJoinMenu::SaveOptionsDialogReturned(void *pParam,int iPad,C4JS
{
wchar_t wSaveName[128];
ZeroMemory(wSaveName, 128 * sizeof(wchar_t));
mbstowcs_s(nullptr, wSaveName, 128, pClass->m_saveDetails[pClass->m_iSaveListIndex - pClass->m_iDefaultButtonsC].UTF8SaveName, _TRUNCATE);
MultiByteToWideChar(CP_UTF8, 0, pClass->m_saveDetails[pClass->m_iSaveListIndex - pClass->m_iDefaultButtonsC].UTF8SaveName, -1, wSaveName, 127);
UIKeyboardInitData kbData;
kbData.title = app.GetString(IDS_RENAME_WORLD_TITLE);
kbData.defaultText = wSaveName;
@@ -20,7 +20,7 @@ private:
{
eControl_SavesList,
eControl_GamesList,
#if defined(_XBOX_ONE) || defined(__ORBIS__)
#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(_WINDOWS64)
eControl_SpaceIndicator,
#endif
};
@@ -52,7 +52,7 @@ protected:
UIControl_SaveList m_buttonListGames;
UIControl_Label m_labelSavesListTitle, m_labelJoinListTitle, m_labelNoGames;
UIControl m_controlSavesTimer, m_controlJoinTimer;
#if defined(_XBOX_ONE) || defined(__ORBIS__)
#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(_WINDOWS64)
UIControl_SpaceIndicatorBar m_spaceIndicatorSaves;
#endif
@@ -68,7 +68,7 @@ private:
UI_MAP_ELEMENT( m_controlSavesTimer, "SavesTimer")
UI_MAP_ELEMENT( m_controlJoinTimer, "JoinTimer")
#if defined(_XBOX_ONE) || defined(__ORBIS__)
#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(_WINDOWS64)
UI_MAP_ELEMENT( m_spaceIndicatorSaves, "SaveSizeBar")
#endif
UI_END_MAP_ELEMENTS_AND_NAMES()
+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);
+7 -4
View File
@@ -4,7 +4,7 @@
#include "..\..\..\Minecraft.World\File.h"
#include "UITTFFont.h"
UITTFFont::UITTFFont(const string &name, const string &path, S32 fallbackCharacter)
UITTFFont::UITTFFont(const string &name, const string &path, S32 fallbackCharacter, bool registerAsDefaultFonts)
: m_strFontName(name)
{
app.DebugPrintf("UITTFFont opening %s\n",path.c_str());
@@ -41,9 +41,12 @@ UITTFFont::UITTFFont(const string &name, const string &path, S32 fallbackCharact
IggyFontInstallTruetypeFallbackCodepointUTF8( m_strFontName.c_str(), -1, IGGY_FONTFLAG_none, fallbackCharacter );
// 4J Stu - These are so we can use the default flash controls
IggyFontInstallTruetypeUTF8 ( (void *)pbData, IGGY_TTC_INDEX_none, "Times New Roman", -1, IGGY_FONTFLAG_none );
IggyFontInstallTruetypeUTF8 ( (void *)pbData, IGGY_TTC_INDEX_none, "Arial", -1, IGGY_FONTFLAG_none );
if (registerAsDefaultFonts)
{
// 4J Stu - These are so we can use the default flash controls
IggyFontInstallTruetypeUTF8 ( (void *)pbData, IGGY_TTC_INDEX_none, "Times New Roman", -1, IGGY_FONTFLAG_none );
IggyFontInstallTruetypeUTF8 ( (void *)pbData, IGGY_TTC_INDEX_none, "Arial", -1, IGGY_FONTFLAG_none );
}
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ private:
//DWORD dwDataSize;
public:
UITTFFont(const string &name, const string &path, S32 fallbackCharacter);
UITTFFont(const string &name, const string &path, S32 fallbackCharacter, bool registerAsDefaultFonts = true);
~UITTFFont();
string getFontName();
@@ -0,0 +1,164 @@
#include "stdafx.h"
#include "BufferedImage.h"
#include "UIFontData.h"
#include "UIUnicodeBitmapFont.h"
UIUnicodeBitmapFont::UIUnicodeBitmapFont(const string &fontname, SFontData &referenceFontData)
: UIAbstractBitmapFont(fontname)
{
m_numGlyphs = 65536;
m_referenceFontData = &referenceFontData;
memset(m_glyphPages, 0, sizeof(m_glyphPages));
memset(m_unicodeWidth, 0, sizeof(m_unicodeWidth));
FILE *f = nullptr;
fopen_s(&f, "Common/res/1_2_2/font/glyph_sizes.bin", "rb");
if (f)
{
fread(m_unicodeWidth, 1, 65536, f);
fclose(f);
}
}
UIUnicodeBitmapFont::~UIUnicodeBitmapFont()
{
for (int i = 0; i < 256; i++)
delete[] m_glyphPages[i];
}
void UIUnicodeBitmapFont::loadGlyphPage(int page)
{
wchar_t fileName[64];
swprintf(fileName, 64, L"/1_2_2/font/glyph_%02X.png", page);
BufferedImage bimg(fileName);
int *rawData = bimg.getData();
if (!rawData) return;
int size = 256 * 256;
m_glyphPages[page] = new unsigned char[size];
for (int i = 0; i < size; i++)
m_glyphPages[page][i] = (rawData[i] & 0xFF000000) >> 24;
}
IggyFontMetrics *UIUnicodeBitmapFont::GetFontMetrics(IggyFontMetrics *metrics)
{
metrics->ascent = m_referenceFontData->m_fAscent;
metrics->descent = m_referenceFontData->m_fDescent;
metrics->average_glyph_width_for_tab_stops = 8.0f;
metrics->largest_glyph_bbox_y1 = metrics->descent;
return metrics;
}
S32 UIUnicodeBitmapFont::GetCodepointGlyph(U32 codepoint)
{
if (codepoint < 65536 && m_unicodeWidth[codepoint] != 0)
return (S32)codepoint;
return IGGY_GLYPH_INVALID;
}
IggyGlyphMetrics *UIUnicodeBitmapFont::GetGlyphMetrics(S32 glyph, IggyGlyphMetrics *metrics)
{
if (glyph < 0 || glyph >= 65536) { metrics->x0 = metrics->x1 = metrics->advance = metrics->y0 = metrics->y1 = 0; return metrics; }
int left = m_unicodeWidth[glyph] >> 4;
int right = (m_unicodeWidth[glyph] & 0xF) + 1;
float pixelWidth = (right - left) / 2.0f + 1.0f;
float advance = pixelWidth * m_referenceFontData->m_fAdvPerPixel;
metrics->x0 = 0.0f;
metrics->x1 = advance;
metrics->advance = advance;
metrics->y0 = 0.0f;
metrics->y1 = 1.0f;
return metrics;
}
rrbool UIUnicodeBitmapFont::IsGlyphEmpty(S32 glyph)
{
if (glyph < 0 || glyph >= 65536) return true;
return m_unicodeWidth[glyph] == 0;
}
F32 UIUnicodeBitmapFont::GetKerningForGlyphPair(S32 first_glyph, S32 second_glyph)
{
return 0.0f;
}
rrbool UIUnicodeBitmapFont::CanProvideBitmap(S32 glyph, F32 pixel_scale)
{
return glyph >= 0 && glyph < 65536;
}
rrbool UIUnicodeBitmapFont::GetGlyphBitmap(S32 glyph, F32 pixel_scale, IggyBitmapCharacter *bitmap)
{
if (glyph < 0 || glyph >= 65536) return false;
int page = glyph / 256;
if (!m_glyphPages[page])
{
loadGlyphPage(page);
if (!m_glyphPages[page]) return false;
}
int cx = (glyph % 16) * 16;
int cy = ((glyph & 0xFF) / 16) * 16;
bitmap->pixels_one_per_byte = m_glyphPages[page] + (cy * 256) + cx;
bitmap->width_in_pixels = 16;
bitmap->height_in_pixels = 16;
bitmap->stride_in_bytes = 256;
bitmap->top_left_x = 0;
bitmap->top_left_y = -static_cast<S32>(16) * m_referenceFontData->m_fAscent;
bitmap->oversample = 0;
// Scale parameters: match UIBitmapFont's approach.
// truePixelScale = the pixel_scale at which 1 glyph pixel = 1 screen pixel.
// For 16px glyphs displayed at the same visual size as Mojangles_7 (8px glyphs with advPerPixel 1/10):
// The reference truePixelScale for Mojangles_7 is 1.0f/m_fAdvPerPixel = 10.0f
// Since our glyphs are 16px (2x the Mojangles 8px), our truePixelScale is 20.0f
float truePixelScale = 2.0f / m_referenceFontData->m_fAdvPerPixel;
#ifdef _WINDOWS64
bitmap->pixel_scale_correct = truePixelScale;
if (pixel_scale < truePixelScale)
{
bitmap->pixel_scale_min = 0.0f;
bitmap->pixel_scale_max = truePixelScale;
bitmap->point_sample = false;
}
else
{
bitmap->pixel_scale_min = truePixelScale;
bitmap->pixel_scale_max = 99.0f;
bitmap->point_sample = true;
}
#else
float glyphScale = 1.0f;
while ((0.5f + glyphScale) * truePixelScale < pixel_scale)
glyphScale++;
if (glyphScale <= 1 && pixel_scale < truePixelScale)
{
bitmap->pixel_scale_correct = truePixelScale;
bitmap->pixel_scale_min = 0.0f;
bitmap->pixel_scale_max = truePixelScale * 1.001f;
bitmap->point_sample = false;
}
else
{
float actualScale = pixel_scale / glyphScale;
bitmap->pixel_scale_correct = actualScale;
bitmap->pixel_scale_min = truePixelScale;
bitmap->pixel_scale_max = 99.0f;
bitmap->point_sample = true;
}
#endif
bitmap->user_context_for_free = nullptr;
return true;
}
void UIUnicodeBitmapFont::FreeGlyphBitmap(S32 glyph, F32 pixel_scale, IggyBitmapCharacter *bitmap)
{
// Pixel data lives in m_glyphPages -- nothing to free.
}
@@ -0,0 +1,27 @@
#pragma once
#include "UIBitmapFont.h"
struct SFontData;
class UIUnicodeBitmapFont : public UIAbstractBitmapFont
{
private:
unsigned char m_unicodeWidth[65536];
unsigned char* m_glyphPages[256];
SFontData* m_referenceFontData;
void loadGlyphPage(int page);
public:
UIUnicodeBitmapFont(const string &fontname, SFontData &referenceFontData);
~UIUnicodeBitmapFont();
virtual IggyFontMetrics *GetFontMetrics(IggyFontMetrics *metrics);
virtual S32 GetCodepointGlyph(U32 codepoint);
virtual IggyGlyphMetrics *GetGlyphMetrics(S32 glyph, IggyGlyphMetrics *metrics);
virtual rrbool IsGlyphEmpty(S32 glyph);
virtual F32 GetKerningForGlyphPair(S32 first_glyph, S32 second_glyph);
virtual rrbool CanProvideBitmap(S32 glyph, F32 pixel_scale);
virtual rrbool GetGlyphBitmap(S32 glyph, F32 pixel_scale, IggyBitmapCharacter *bitmap);
virtual void FreeGlyphBitmap(S32 glyph, F32 pixel_scale, IggyBitmapCharacter *bitmap);
};
+3 -1
View File
@@ -2,6 +2,7 @@
#include "XUI_Chat.h"
#include "..\..\Minecraft.h"
#include "..\..\Gui.h"
#include "..\..\..\Minecraft.World\ArabicShaping.h"
HRESULT CScene_Chat::OnInit( XUIMessageInit* pInitData, BOOL& bHandled )
{
@@ -29,7 +30,8 @@ HRESULT CScene_Chat::OnTimer( XUIMessageTimer *pXUIMessageTimer, BOOL &bHandled)
{
m_Backgrounds[i].SetOpacity(opacity);
m_Labels[i].SetOpacity(opacity);
m_Labels[i].SetText( pGui->getMessage(m_iPad,i).c_str() );
wstring shaped = shapeArabicText(pGui->getMessage(m_iPad, i));
m_Labels[i].SetText( shaped.c_str() );
}
else
{
@@ -1,5 +1,6 @@
#include "stdafx.h"
#include "XUI_Ctrl_4JList.h"
#include "..\..\..\Minecraft.World\ArabicShaping.h"
static bool TimeSortFn(const void *a, const void *b);
@@ -294,8 +295,16 @@ HRESULT CXuiCtrl4JList::OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceT
if( ( 0 == pGetSourceTextData->iData ) && ( ( pGetSourceTextData->bItemData ) ) )
{
EnterCriticalSection(&m_AccessListData);
pGetSourceTextData->szText =
GetData(pGetSourceTextData->iItem).pwszText;
LPCWSTR rawText = GetData(pGetSourceTextData->iItem).pwszText;
if (rawText)
{
m_shapedTextCache = shapeArabicText(rawText);
pGetSourceTextData->szText = m_shapedTextCache.c_str();
}
else
{
pGetSourceTextData->szText = rawText;
}
LeaveCriticalSection(&m_AccessListData);
bHandled = TRUE;
}
@@ -75,4 +75,5 @@ private:
static bool IndexSortFn(const void *a, const void *b);
HXUIOBJ m_hSelectionChangedHandlerObj;
std::wstring m_shapedTextCache; // temp buffer for Arabic-shaped text in OnGetSourceDataText
};
+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);
}
+32 -11
View File
@@ -186,7 +186,8 @@ HRESULT CXuiSceneHud::OnCustomMessage_TickScene()
if (pMinecraft->localplayers[m_iPad]->invulnerableTime < 10) blink = false;
int iHealth = pMinecraft->localplayers[m_iPad]->getHealth();
int iLastHealth = pMinecraft->localplayers[m_iPad]->lastHealth;
bool bHasPoison = pMinecraft->localplayers[m_iPad]->hasEffect(MobEffect::poison);
bool bHasPoison = pMinecraft->localplayers[m_iPad]->hasEffect(MobEffect::poison);
bool isHardcore = pMinecraft->level != nullptr && pMinecraft->level->getLevelData()->isHardcore();
for (int icon = 0; icon < Player::MAX_HEALTH / 2; icon++)
{
if(blink)
@@ -196,11 +197,15 @@ HRESULT CXuiSceneHud::OnCustomMessage_TickScene()
// Full
if(bHasPoison)
{
m_healthIcon[icon].PlayVisualRange(L"FullPoisonFlash",nullptr,L"FullPoisonFlash");
m_healthIcon[icon].PlayVisualRange(
isHardcore ? L"FullPoisonFlashHardcore" : L"FullPoisonFlash", nullptr,
isHardcore ? L"FullPoisonFlashHardcore" : L"FullPoisonFlash");
}
else
{
m_healthIcon[icon].PlayVisualRange(L"FullFlash",nullptr,L"FullFlash");
m_healthIcon[icon].PlayVisualRange(
isHardcore ? L"FullFlashHardcore" : L"FullFlash", nullptr,
isHardcore ? L"FullFlashHardcore" : L"FullFlash");
}
}
else if (icon * 2 + 1 == iLastHealth || icon * 2 + 1 == iHealth)
@@ -208,17 +213,23 @@ HRESULT CXuiSceneHud::OnCustomMessage_TickScene()
// Half
if(bHasPoison)
{
m_healthIcon[icon].PlayVisualRange(L"HalfPoisonFlash",nullptr,L"HalfPoisonFlash");
m_healthIcon[icon].PlayVisualRange(
isHardcore ? L"HalfPoisonFlashHardcore" : L"HalfPoisonFlash", nullptr,
isHardcore ? L"HalfPoisonFlashHardcore" : L"HalfPoisonFlash");
}
else
{
m_healthIcon[icon].PlayVisualRange(L"HalfFlash",nullptr,L"HalfFlash");
m_healthIcon[icon].PlayVisualRange(
isHardcore ? L"HalfFlashHardcore" : L"HalfFlash", nullptr,
isHardcore ? L"HalfFlashHardcore" : L"HalfFlash");
}
}
else
{
// Empty
m_healthIcon[icon].PlayVisualRange(L"NormalFlash",nullptr,L"NormalFlash");
m_healthIcon[icon].PlayVisualRange(
isHardcore ? L"NormalFlashHardcore" : L"NormalFlash", nullptr,
isHardcore ? L"NormalFlashHardcore" : L"NormalFlash");
}
}
else
@@ -228,11 +239,15 @@ HRESULT CXuiSceneHud::OnCustomMessage_TickScene()
// Full
if(bHasPoison)
{
m_healthIcon[icon].PlayVisualRange(L"FullPoison",nullptr,L"FullPoison");
m_healthIcon[icon].PlayVisualRange(
isHardcore ? L"FullPoisonHardcore" : L"FullPoison", nullptr,
isHardcore ? L"FullPoisonHardcore" : L"FullPoison");
}
else
{
m_healthIcon[icon].PlayVisualRange(L"Full",nullptr,L"Full");
m_healthIcon[icon].PlayVisualRange(
isHardcore ? L"FullHardcore" : L"Full", nullptr,
isHardcore ? L"FullHardcore" : L"Full");
}
}
else if (icon * 2 + 1 == iHealth)
@@ -240,17 +255,23 @@ HRESULT CXuiSceneHud::OnCustomMessage_TickScene()
// Half
if(bHasPoison)
{
m_healthIcon[icon].PlayVisualRange(L"HalfPoison",nullptr,L"HalfPoison");
m_healthIcon[icon].PlayVisualRange(
isHardcore ? L"HalfPoisonHardcore" : L"HalfPoison", nullptr,
isHardcore ? L"HalfPoisonHardcore" : L"HalfPoison");
}
else
{
m_healthIcon[icon].PlayVisualRange(L"Half",nullptr,L"Half");
m_healthIcon[icon].PlayVisualRange(
isHardcore ? L"HalfHardcore" : L"Half", nullptr,
isHardcore ? L"HalfHardcore" : L"Half");
}
}
else
{
// Empty
m_healthIcon[icon].PlayVisualRange(L"Normal",nullptr,L"Normal");
m_healthIcon[icon].PlayVisualRange(
isHardcore ? L"NormalHardcore" : L"Normal", nullptr,
isHardcore ? L"NormalHardcore" : L"Normal");
}
}
@@ -1,10 +0,0 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
@@ -1,10 +0,0 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
+156 -31
View File
@@ -8,6 +8,7 @@
#include "..\Minecraft.World\net.minecraft.h"
#include "..\Minecraft.World\StringHelpers.h"
#include "..\Minecraft.World\Random.h"
#include "..\Minecraft.World\ArabicShaping.h"
Font::Font(Options *options, const wstring& name, Textures* textures, bool enforceUnicode, ResourceLocation *textureLocation, int cols, int rows, int charWidth, int charHeight, unsigned short charMap[]/* = nullptr */) : textures(textures)
{
@@ -16,7 +17,7 @@ Font::Font(Options *options, const wstring& name, Textures* textures, bool enfor
charWidths = new int[charC];
// 4J - added initialisers
memset(charWidths, 0, charC);
memset(charWidths, 0, charC * sizeof(int));
enforceUnicodeSheet = false;
bidirectional = false;
@@ -26,6 +27,19 @@ Font::Font(Options *options, const wstring& name, Textures* textures, bool enfor
m_underline = false;
m_strikethrough = false;
memset(unicodeTexID, 0, sizeof(unicodeTexID));
memset(unicodeWidth, 0, sizeof(unicodeWidth));
lastBoundTexture = 0;
// Load unicode glyph sizes
FILE *glyphFile = nullptr;
fopen_s(&glyphFile, "Common/res/1_2_2/font/glyph_sizes.bin", "rb");
if (glyphFile)
{
fread(unicodeWidth, 1, 65536, glyphFile);
fclose(glyphFile);
}
// Set up member variables
m_cols = cols;
m_rows = rows;
@@ -261,7 +275,87 @@ void Font::drawLiteral(const wstring& str, int x, int y, int color)
yPos = static_cast<float>(y);
wstring cleanStr = sanitize(str);
for (size_t i = 0; i < cleanStr.length(); ++i)
renderCharacter(cleanStr.at(i));
{
wchar_t c = cleanStr.at(i);
if (isUnicodeGlyphChar(c))
{
renderUnicodeCharacter(c);
textures->bindTexture(m_textureLocation);
lastBoundTexture = fontTexture;
}
else
{
renderCharacter(c);
}
}
}
// Like sanitize() but skips the shapeArabicText() call - for pre-shaped strings.
wstring Font::sanitizePreshaped(const wstring& str)
{
wstring sb = str;
for (unsigned int i = 0; i < sb.length(); i++)
{
if (CharacterExists(sb[i]))
sb[i] = MapCharacter(sb[i]);
else if (unicodeWidth[sb[i]] != 0)
{
// Leave as-is: raw codepoint for glyph page rendering
}
else
{
sb[i] = 0;
}
}
return sb;
}
void Font::drawLiteralPreshaped(const wstring& str, int x, int y, int color)
{
if (str.empty()) return;
if ((color & 0xFC000000) == 0) color |= 0xFF000000;
textures->bindTexture(m_textureLocation);
glColor4f((color >> 16 & 255) / 255.0F, (color >> 8 & 255) / 255.0F, (color & 255) / 255.0F, (color >> 24 & 255) / 255.0F);
xPos = static_cast<float>(x);
yPos = static_cast<float>(y);
wstring cleanStr = sanitizePreshaped(str);
for (size_t i = 0; i < cleanStr.length(); ++i)
{
wchar_t c = cleanStr.at(i);
if (isUnicodeGlyphChar(c))
{
renderUnicodeCharacter(c);
textures->bindTexture(m_textureLocation);
lastBoundTexture = fontTexture;
}
else
{
renderCharacter(c);
}
}
}
void Font::drawShadowLiteralPreshaped(const wstring& str, int x, int y, int color)
{
int shadowColor = (color & 0xFCFCFC) >> 2 | (color & 0xFF000000);
drawLiteralPreshaped(str, x + 1, y + 1, shadowColor);
drawLiteralPreshaped(str, x, y, color);
}
int Font::widthPreshaped(const wstring& str)
{
wstring cleanStr = sanitizePreshaped(str);
if (cleanStr.empty()) return 0;
int len = 0;
for (size_t i = 0; i < cleanStr.length(); ++i)
{
wchar_t wc = cleanStr.at(i);
if (isUnicodeGlyphChar(wc))
len += (int)unicodeCharWidth(wc);
else
len += charWidths[static_cast<unsigned>(wc)];
}
return len;
}
void Font::drawShadowWordWrap(const wstring &str, int x, int y, int w, int color, int h)
@@ -345,7 +439,7 @@ void Font::draw(const wstring &str, bool dropShadow)
}
// "noise" for crazy splash screen message
if (noise)
if (noise && !isUnicodeGlyphChar(c))
{
int newc;
do
@@ -355,7 +449,18 @@ void Font::draw(const wstring &str, bool dropShadow)
c = newc;
}
addCharacterQuad(c);
if (isUnicodeGlyphChar(c))
{
t->end();
renderUnicodeCharacter(c);
textures->bindTexture(m_textureLocation);
lastBoundTexture = fontTexture;
t->begin();
}
else
{
addCharacterQuad(c);
}
}
t->end();
@@ -396,11 +501,22 @@ int Font::width(const wstring& str)
++i;
else
{
len += charWidths[167];
if (isUnicodeGlyphChar(167))
len += (int)unicodeCharWidth(167);
else
len += charWidths[167];
if (i + 1 < cleanStr.length())
len += charWidths[static_cast<unsigned>(cleanStr[++i])];
{
wchar_t nextC = cleanStr[++i];
if (isUnicodeGlyphChar(nextC))
len += (int)unicodeCharWidth(nextC);
else if (static_cast<unsigned>(nextC) < static_cast<unsigned>(m_cols * m_rows))
len += charWidths[static_cast<unsigned>(nextC)];
}
}
}
else if (isUnicodeGlyphChar(c))
len += (int)unicodeCharWidth(c);
else
len += charWidths[c];
}
@@ -414,13 +530,19 @@ int Font::widthLiteral(const wstring& str)
if (cleanStr == L"") return 0;
int len = 0;
for (size_t i = 0; i < cleanStr.length(); ++i)
len += charWidths[static_cast<unsigned>(cleanStr.at(i))];
{
wchar_t wc = cleanStr.at(i);
if (isUnicodeGlyphChar(wc))
len += (int)unicodeCharWidth(wc);
else
len += charWidths[static_cast<unsigned>(wc)];
}
return len;
}
wstring Font::sanitize(const wstring& str)
{
wstring sb = str;
wstring sb = shapeArabicText(str);
for (unsigned int i = 0; i < sb.length(); i++)
{
@@ -428,6 +550,10 @@ wstring Font::sanitize(const wstring& str)
{
sb[i] = MapCharacter(sb[i]);
}
else if (unicodeWidth[sb[i]] != 0)
{
// Leave as-is: raw codepoint for glyph page rendering
}
else
{
// If this character isn't supported, just show the first character (empty square box character)
@@ -671,33 +797,22 @@ void Font::renderFakeCB(IntBuffer *ib)
}
}
}
*/
void Font::loadUnicodePage(int page)
{
wchar_t fileName[25];
//String fileName = String.format("/1_2_2/font/glyph_%02X.png", page);
swprintf(fileName,25,L"/1_2_2/font/glyph_%02X.png",page);
wchar_t fileName[40];
swprintf(fileName, 40, L"/1_2_2/font/glyph_%02X.png", page);
BufferedImage *image = new BufferedImage(fileName);
//try
//{
// image = ImageIO.read(Textures.class.getResourceAsStream(fileName.toString()));
//}
//catch (IOException e)
//{
// throw new RuntimeException(e);
//}
unicodeTexID[page] = textures->getTexture(image);
lastBoundTexture = unicodeTexID[page];
delete image;
}
void Font::renderUnicodeCharacter(wchar_t c)
{
if (unicodeWidth[c] == 0)
{
// System.out.println("no-width char " + c);
return;
}
int page = c / 256;
@@ -709,19 +824,17 @@ void Font::renderUnicodeCharacter(wchar_t c)
lastBoundTexture = unicodeTexID[page];
}
// first column with non-trans pixels
int firstLeft = unicodeWidth[c] >> 4;
// last column with non-trans pixels
int firstRight = unicodeWidth[c] & 0xF;
float left = firstLeft;
float right = firstRight + 1;
float left = (float)firstLeft;
float right = (float)(firstRight + 1);
float xOff = c % 16 * 16 + left;
float yOff = (c & 0xFF) / 16 * 16;
float xOff = (c % 16) * 16 + left;
float yOff = ((c & 0xFF) / 16) * 16;
float width = right - left - .02f;
Tesselator *t = Tesselator::getInstance();
Tesselator *t = Tesselator::getInstance();
t->begin(GL_TRIANGLE_STRIP);
t->tex(xOff / 256.0F, yOff / 256.0F);
t->vertex(xPos, yPos, 0.0f);
@@ -735,5 +848,17 @@ void Font::renderUnicodeCharacter(wchar_t c)
xPos += (right - left) / 2 + 1;
}
*/
float Font::unicodeCharWidth(wchar_t c)
{
if (unicodeWidth[c] == 0) return 0;
int firstLeft = unicodeWidth[c] >> 4;
int firstRight = unicodeWidth[c] & 0xF;
return (firstRight + 1 - firstLeft) / 2.0f + 1;
}
bool Font::isUnicodeGlyphChar(wchar_t c)
{
return c >= m_cols * m_rows && unicodeWidth[c] != 0;
}
+12
View File
@@ -18,6 +18,10 @@ private:
Textures *textures;
int unicodeTexID[256];
unsigned char unicodeWidth[65536];
int lastBoundTexture;
float xPos;
float yPos;
@@ -70,10 +74,18 @@ private:
void drawLiteral(const wstring& str, int x, int y, int color); // no § parsing
int MapCharacter(wchar_t c); // 4J added
bool CharacterExists(wchar_t c); // 4J added
void loadUnicodePage(int page);
void renderUnicodeCharacter(wchar_t c);
float unicodeCharWidth(wchar_t c);
bool isUnicodeGlyphChar(wchar_t c);
wstring sanitizePreshaped(const wstring& str); // sanitize without re-shaping Arabic
void drawLiteralPreshaped(const wstring& str, int x, int y, int color);
public:
int width(const wstring& str);
int widthLiteral(const wstring& str); // width without skipping § codes (for chat input)
int widthPreshaped(const wstring& str); // width of already-shaped text, no re-shaping
void drawShadowLiteralPreshaped(const wstring& str, int x, int y, int color);
wstring sanitize(const wstring& str);
void drawWordWrap(const wstring &string, int x, int y, int w, int col, int h); // 4J Added h param
+8 -3
View File
@@ -359,14 +359,17 @@ void GameRenderer::pick(float a)
}
}
// Toru - wrapping these methods for backwards compatibility,
// no longer setting m_fov as its use doesn't respect applyEffects param in GameRenderer::getFov
void GameRenderer::SetFovVal(float fov)
{
m_fov=fov;
//m_fov=fov;
mc->options->set(Options::Option::FOV, (fov - 70) / 40);
}
float GameRenderer::GetFovVal()
{
return m_fov;
return 70 + mc->options->fov * 40;//m_fov;
}
void GameRenderer::tickFov()
@@ -390,9 +393,11 @@ float GameRenderer::getFov(float a, bool applyEffects)
shared_ptr<LocalPlayer> player = dynamic_pointer_cast<LocalPlayer>(mc->cameraTargetPlayer);
int playerIdx = player ? player->GetXboxPad() : 0;
float fov = m_fov;//70;
if (fov < 1) fov = 1; // Crash fix
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)
+11 -8
View File
@@ -499,11 +499,10 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
int y0 = 0;
// No hardcore on console
/*if (minecraft->level.getLevelData().isHardcore())
{
y0 = 5;
}*/
//if (minecraft->level->getLevelData()->isHardcore())
//{
// y0 = 5;
//}
blit(xo, yo, 16 + bg * 9, 9 * y0, 9, 9);
if (blink)
@@ -854,10 +853,11 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
// font.draw(str, x + 1, y, 0xffffff);
// }
lastTickA = a;
// 4J Stu - This is now displayed in a xui scene
#if 0
// Jukebox CD message
// Jukebox CD message
if (overlayMessageTime > 0)
{
float t = overlayMessageTime - a;
@@ -1065,7 +1065,7 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
vector<wstring> lines;
// Only show version/branch for player 0 to avoid cluttering each splitscreen viewport
if (iPad == 0)
if (iPad == 0 && ClientConstants::SHOW_VERSION_WATERMARK)
{
lines.push_back(ClientConstants::VERSION_STRING);
lines.push_back(ClientConstants::BRANCH_STRING);
@@ -1386,6 +1386,9 @@ void Gui::clearMessages(int iPad)
void Gui::addMessage(const wstring& _string,int iPad,bool bIsDeathMessage)
{
{ char buf[32]; sprintf_s(buf, "[CHAT] Display (pad=%d): ", iPad); OutputDebugStringA(buf); }
OutputDebugStringW(_string.c_str());
OutputDebugStringA("\n");
wstring string = _string; // 4J - Take copy of input as it is const
//int iScale=1;
@@ -1607,7 +1610,7 @@ void Gui::renderGraph(int dataLength, int dataPos, int64_t *dataA, float dataASc
t->vertex((float)(xScale*i + 0.5f), (float)( height - aVal + 0.5f), static_cast<float>(0));
t->vertex((float)(xScale*i + 0.5f), (float)( height + 0.5f), static_cast<float>(0));
}
}
if( dataB != NULL )
{
+5
View File
@@ -110,6 +110,11 @@ void GuiComponent::drawStringLiteral(Font *font, const wstring& str, int x, int
font->drawShadowLiteral(str, x, y, color);
}
void GuiComponent::drawStringPreshaped(Font *font, const wstring& str, int x, int y, int color)
{
font->drawShadowLiteralPreshaped(str, x, y, color);
}
void GuiComponent::blit(int x, int y, int sx, int sy, int w, int h)
{
float us = 1 / 256.0f;
+1
View File
@@ -16,5 +16,6 @@ public:
void drawCenteredString(Font *font, const wstring& str, int x, int y, int color);
void drawString(Font *font, const wstring& str, int x, int y, int color);
void drawStringLiteral(Font* font, const wstring& str, int x, int y, int color);
void drawStringPreshaped(Font* font, const wstring& str, int x, int y, int color);
void blit(int x, int y, int sx, int sy, int w, int h);
};
+32
View File
@@ -6894,6 +6894,22 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|PSVita'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='CONTENTPACKAGE_SYMBOLS|PSVita'">false</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="Common\UI\UIUnicodeBitmapFont.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='CONTENTPACKAGE_SYMBOLS|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseForArt|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_NO_TU|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|PSVita'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_NO_TU|PSVita'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseForArt|PSVita'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|PSVita'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|PSVita'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|PSVita'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='CONTENTPACKAGE_SYMBOLS|PSVita'">false</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="Common\UI\UIComponent_Chat.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|Xbox 360'">true</ExcludedFromBuild>
@@ -29623,6 +29639,22 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|PSVita'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='CONTENTPACKAGE_SYMBOLS|PSVita'">false</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="Common\UI\UIUnicodeBitmapFont.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='CONTENTPACKAGE_SYMBOLS|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseForArt|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_NO_TU|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|PSVita'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_NO_TU|PSVita'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseForArt|PSVita'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|PSVita'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|PSVita'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|PSVita'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='CONTENTPACKAGE_SYMBOLS|PSVita'">false</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="Common\UI\UIComponent_Chat.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|Xbox 360'">true</ExcludedFromBuild>
@@ -2953,6 +2953,9 @@
<ClInclude Include="Common\UI\UIBitmapFont.h">
<Filter>Common\Source Files\UI</Filter>
</ClInclude>
<ClInclude Include="Common\UI\UIUnicodeBitmapFont.h">
<Filter>Common\Source Files\UI</Filter>
</ClInclude>
<ClInclude Include="Common\UI\UITTFFont.h">
<Filter>Common\Source Files\UI</Filter>
</ClInclude>
@@ -5199,6 +5202,9 @@
<ClCompile Include="Common\UI\UIBitmapFont.cpp">
<Filter>Common\Source Files\UI</Filter>
</ClCompile>
<ClCompile Include="Common\UI\UIUnicodeBitmapFont.cpp">
<Filter>Common\Source Files\UI</Filter>
</ClCompile>
<ClCompile Include="Common\UI\UITTFFont.cpp">
<Filter>Common\Source Files\UI</Filter>
</ClCompile>
@@ -1,11 +0,0 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "1"
"EXCLUDED_FILE0" = "Durango\\Autogenerated.appxmanifest"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
+82
View File
@@ -73,6 +73,11 @@
#include "Common\UI\UIFontData.h"
#include "DLCTexturePack.h"
#ifdef _WINDOWS64
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "Windows64/stb_image_write.h"
#endif
#ifdef __ORBIS__
#include "Orbis\Network\PsPlusUpsellWrapper_Orbis.h"
#endif
@@ -1549,6 +1554,9 @@ void Minecraft::run_middle()
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_RENDER_DEBUG;
}
if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_SCREENSHOT))
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_SCREENSHOT;
// In flying mode, Shift held = sneak/descend
if(g_KBMInput.IsKBMActive() && g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_SNEAK))
{
@@ -3737,6 +3745,80 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
//options->thirdPersonView = !options->thirdPersonView;
}
#ifdef _WINDOWS64
if(player->ullButtonsPressed&(1LL<<MINECRAFT_ACTION_SCREENSHOT))
{
extern ID3D11Device* g_pd3dDevice;
extern ID3D11DeviceContext* g_pImmediateContext;
extern IDXGISwapChain* g_pSwapChain;
ID3D11Texture2D* pBackBuffer = nullptr;
HRESULT hr = g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer);
if (SUCCEEDED(hr))
{
D3D11_TEXTURE2D_DESC desc;
pBackBuffer->GetDesc(&desc);
desc.Usage = D3D11_USAGE_STAGING;
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = 0;
ID3D11Texture2D* pStaging = nullptr;
hr = g_pd3dDevice->CreateTexture2D(&desc, nullptr, &pStaging);
if (SUCCEEDED(hr))
{
g_pImmediateContext->CopyResource(pStaging, pBackBuffer);
// Build path next to the executable
wchar_t exePath[MAX_PATH];
GetModuleFileNameW(NULL, exePath, MAX_PATH);
wchar_t* lastSlash = wcsrchr(exePath, L'\\');
if (lastSlash) *(lastSlash + 1) = L'\0';
wstring screenshotDirPath = wstring(exePath) + L"screenshots";
CreateDirectoryW(screenshotDirPath.c_str(), NULL);
SYSTEMTIME st;
GetLocalTime(&st);
wchar_t filename[128];
swprintf_s(filename, L"%04d-%02d-%02d_%02d.%02d.%02d.png",
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
wstring screenshotPath = screenshotDirPath + L"\\" + filename;
D3D11_MAPPED_SUBRESOURCE mapped;
hr = g_pImmediateContext->Map(pStaging, 0, D3D11_MAP_READ, 0, &mapped);
if (SUCCEEDED(hr))
{
// Copy rows and force alpha to fully opaque
unsigned char* rgba = new unsigned char[desc.Width * desc.Height * 4];
for (UINT row = 0; row < desc.Height; row++)
{
unsigned char* src = (unsigned char*)mapped.pData + row * mapped.RowPitch;
unsigned char* dst = rgba + row * desc.Width * 4;
memcpy(dst, src, desc.Width * 4);
for (UINT x = 0; x < desc.Width; x++)
dst[x * 4 + 3] = 0xFF;
}
g_pImmediateContext->Unmap(pStaging, 0);
// Save PNG via stb_image_write
string narrowPath(screenshotPath.begin(), screenshotPath.end());
int writeResult = stbi_write_png(narrowPath.c_str(), desc.Width, desc.Height, 4, rgba, desc.Width * 4);
delete[] rgba;
// Send local-only chat message on success
if (writeResult)
{
wstring msg = L"Saved screenshot to " + wstring(filename);
gui->addMessage(msg, iPad);
}
}
pStaging->Release();
}
pBackBuffer->Release();
}
}
#endif
if((player->ullButtonsPressed&(1LL<<MINECRAFT_ACTION_GAME_INFO)) && gameMode->isInputAllowed(MINECRAFT_ACTION_GAME_INFO))
{
ui.NavigateToScene(iPad,eUIScene_InGameInfoMenu);
+14 -1
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;
@@ -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);
@@ -997,6 +1007,9 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring
#endif
levels[i]->getLevelData()->setGameType(gameType);
// Apply hardcore flag from host option to level data so loaded worlds respect server.properties
levels[i]->getLevelData()->setHardcore(isHardcore());
if(app.getLevelGenerationOptions() != nullptr)
{
LevelGenerationOptions *mapOptions = app.getLevelGenerationOptions();
@@ -1642,7 +1655,7 @@ bool MinecraftServer::isNetherEnabled()
bool MinecraftServer::isHardcore()
{
return false;
return app.GetGameHostOption(eGameHostOption_Hardcore) > 0;
}
int MinecraftServer::getOperatorUserPermissionLevel()
+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();
@@ -1,10 +0,0 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
@@ -1,10 +0,0 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
@@ -1,10 +0,0 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
@@ -1,10 +0,0 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
@@ -1,10 +0,0 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
@@ -1,10 +0,0 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
@@ -1,10 +0,0 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
@@ -1,10 +0,0 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
@@ -1,10 +0,0 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
@@ -1,10 +0,0 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT"
}
@@ -1,10 +0,0 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT"
}
@@ -1,10 +0,0 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
@@ -1,10 +0,0 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
+15 -16
View File
@@ -25,6 +25,7 @@
#include "..\Minecraft.World\StringHelpers.h"
#include "..\Minecraft.World\Socket.h"
#include "..\Minecraft.World\Achievements.h"
#include "..\Minecraft.World\LevelData.h"
#include "..\Minecraft.World\net.minecraft.h"
#include "EntityTracker.h"
#include "ServerConnection.h"
@@ -142,6 +143,14 @@ void PlayerConnection::tick()
{
dropSpamTickCount--;
}
// Ensure server-side player tick runs even when no move packet was received this tick.
// Without this, environmental damage (drowning, fire, lava) is never applied to clients
// that don't send frequent move packets.
if (!didTick && player != nullptr)
{
player->doTick(false);
}
}
void PlayerConnection::disconnect(DisconnectPacket::eDisconnectReason reason)
@@ -1109,22 +1118,12 @@ void PlayerConnection::handleClientCommand(shared_ptr<ClientCommandPacket> packe
{
player = server->getPlayers()->respawn(player, player->m_enteredEndExitPortal?0:player->dimension, true);
}
//else if (player.getLevel().getLevelData().isHardcore())
//{
// if (server.isSingleplayer() && player.name.equals(server.getSingleplayerName()))
// {
// player.connection.disconnect("You have died. Game over, man, it's game over!");
// server.selfDestruct();
// }
// else
// {
// BanEntry ban = new BanEntry(player.name);
// ban.setReason("Death in Hardcore");
// server.getPlayers().getBans().add(ban);
// player.connection.disconnect("You have died. Game over, man, it's game over!");
// }
//}
else if (player->level->getLevelData()->isHardcore())
{
// Hardcore mode — server rejects respawn. Ban and disconnect are already
// handled in ServerPlayer::die() via banPlayerForHardcoreDeath().
return;
}
else
{
if (player->getHealth() > 0) return;
+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};
};
+120 -3
View File
@@ -39,6 +39,10 @@
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
#include "..\Minecraft.Server\Access\Access.h"
#include "..\Minecraft.Server\Common\StringUtils.h"
#include "..\Minecraft.Server\ServerLogger.h"
#include "..\Minecraft.Server\ServerLogManager.h"
#include "..\Minecraft.Server\ServerProperties.h"
extern bool g_Win64DedicatedServer;
#endif
@@ -67,6 +71,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 +85,7 @@ PlayerList::~PlayerList()
player->gameMode = nullptr;
}
DeleteCriticalSection(&m_banCS);
DeleteCriticalSection(&m_kickPlayersCS);
DeleteCriticalSection(&m_closePlayersCS);
}
@@ -271,7 +277,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 +709,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 +753,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 +870,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 +1684,7 @@ bool PlayerList::isXuidBanned(PlayerUID xuid)
bool banned = false;
EnterCriticalSection(&m_banCS);
for(PlayerUID it : m_bannedXuids)
{
if( ProfileManager.AreXUIDSEqual( xuid, it ) )
@@ -1678,6 +1693,7 @@ bool PlayerList::isXuidBanned(PlayerUID xuid)
break;
}
}
LeaveCriticalSection(&m_banCS);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (!banned && g_Win64DedicatedServer)
@@ -1689,6 +1705,107 @@ 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);
}
void PlayerList::banPlayerForHardcoreDeath(ServerPlayer *player)
{
if (player == nullptr) return;
// Always apply the in-memory XUID ban (works for both client-hosted and dedicated)
banXuid(player->getOnlineXuid());
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (g_Win64DedicatedServer)
{
const std::string playerName = ServerRuntime::StringUtils::WideToUtf8(player->getName());
ServerRuntime::Access::BanMetadata metadata = ServerRuntime::Access::BanManager::BuildDefaultMetadata("Hardcore Death");
metadata.reason = "Died in hardcore mode";
// Ban online XUID
ServerRuntime::Access::AddPlayerBan(player->getOnlineXuid(), playerName, metadata);
// Also ban offline XUID if it differs (follows CliCommandBan pattern)
PlayerUID offlineXuid = player->getXuid();
if (offlineXuid != INVALID_XUID && offlineXuid != player->getOnlineXuid())
{
ServerRuntime::Access::AddPlayerBan(offlineXuid, playerName, metadata);
}
// Ban the player's IP address (uses same access path as CliCommandBanIp)
auto serverConfig = ServerRuntime::LoadServerPropertiesConfig();
if (serverConfig.hardcoreBanIp)
{
if (player->connection != nullptr && player->connection->connection != nullptr && player->connection->connection->getSocket() != nullptr)
{
const unsigned char smallId = player->connection->connection->getSocket()->getSmallId();
std::string ip;
if (smallId != 0 && ServerRuntime::ServerLogManager::TryGetConnectionRemoteIp(smallId, &ip))
{
ServerRuntime::Access::AddIpBan(ip, metadata);
ServerRuntime::LogInfof("Hardcore", "Player %s banned (XUID + IP %s) for dying in hardcore mode.", playerName.c_str(), ip.c_str());
}
else
{
ServerRuntime::LogInfof("Hardcore", "Player %s banned (XUID only, IP not available) for dying in hardcore mode.", playerName.c_str());
}
}
else
{
ServerRuntime::LogInfof("Hardcore", "Player %s banned (XUID only, no connection) for dying in hardcore mode.", playerName.c_str());
}
}
else
{
ServerRuntime::LogInfof("Hardcore", "Player %s banned (XUID only, IP ban disabled) for dying in hardcore mode.", playerName.c_str());
}
// Send ban reason then defer the actual close to the next tick, because this
// method runs mid-tick inside ServerPlayer::die(). A synchronous disconnect
// can invalidate the player/connection while the tick is still executing.
if (player->connection != nullptr)
{
player->connection->send(std::make_shared<DisconnectPacket>(DisconnectPacket::eDisconnect_Banned));
player->connection->closeOnTick();
}
}
else
#endif
{
// Client-hosted: force-save so the host cannot circumvent death by quitting without saving.
// On dedicated server the autosave handles persistence, so skip the forced save to avoid
// the client getting stuck on a "host is saving" screen during disconnect.
app.SetXuiServerAction(ProfileManager.GetPrimaryPad(), eXuiServerAction_SaveGame);
}
}
// AP added for Vita so the range can be increased once the level starts
void PlayerList::setViewDistance(int newViewDistance)
{
+3
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,8 @@ 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
void banPlayerForHardcoreDeath(ServerPlayer *player); // Persistent XUID + IP ban on hardcore death
// AP added for Vita so the range can be increased once the level starts
void setViewDistance(int newViewDistance);
};
+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);
+13 -2
View File
@@ -569,6 +569,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 queue disconnect.
// The force-save is triggered inside banPlayerForHardcoreDeath after the
// disconnect is queued, so the client doesn't get stuck on a save screen.
server->getPlayers()->banPlayerForHardcoreDeath(this);
}
if (!level->getGameRules()->getBoolean(GameRules::RULE_KEEPINVENTORY))
{
inventory->dropAll();
@@ -1609,9 +1620,9 @@ bool ServerPlayer::hasPermission(EGameCommand command)
//
// // 4J - Don't need
// //if (server.isSingleplayer() && server.getSingleplayerName().equals(name))
// //{
/// //{
// // server.setDifficulty(packet.getDifficulty());
// //}
/// //}
//}
int ServerPlayer::getViewDistance()
@@ -34,10 +34,8 @@ public:
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_HOST_SETTINGS = VK_TAB;
static const int KEY_FULLSCREEN = VK_F11;
// todo: implement and shi
static const int KEY_SCREENSHOT = VK_F2;
void Init();
@@ -506,7 +506,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
return DefWindowProcW(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
@@ -563,7 +563,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
else if (vk == VK_MENU)
vk = (lParam & (1 << 24)) ? VK_RMENU : VK_LMENU;
g_KBMInput.OnKeyDown(vk);
return DefWindowProc(hWnd, message, wParam, lParam);
return DefWindowProcW(hWnd, message, wParam, lParam);
}
case WM_KEYUP:
case WM_SYSKEYUP:
@@ -661,7 +661,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
return DefWindowProcW(hWnd, message, wParam, lParam);
}
return 0;
}
@@ -673,23 +673,23 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.cbSize = sizeof(WNDCLASSEXW);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, "Minecraft");
wcex.hIcon = LoadIconW(hInstance, L"Minecraft");
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = "Minecraft";
wcex.lpszClassName = "MinecraftClass";
wcex.lpszMenuName = L"Minecraft";
wcex.lpszClassName = L"MinecraftClass";
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_MINECRAFTWINDOWS));
return RegisterClassEx(&wcex);
return RegisterClassExW(&wcex);
}
//
@@ -709,8 +709,8 @@ BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
RECT wr = {0, 0, g_rScreenWidth, g_rScreenHeight}; // set the size, but not the position
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE); // adjust the size
g_hWnd = CreateWindow( "MinecraftClass",
"Minecraft",
g_hWnd = CreateWindowW( L"MinecraftClass",
L"Minecraft",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
0,
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More