82 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
ZorpLemon 43d520f692 Comment out FOV modification line, since the FOV is stored and doesn't need to be adjusted. (#1248)
I guess this line was just the games way of applying modifications the player made to the FOV. But since whenever ago someone changed the way the FOV stuff is saved, so this line just adds the difference *again* causing issues when the FOV is set above 70. Setting your FOV to 80 actually sets it to 90, and setting it to 110 actually sets it to 150.
2026-03-15 18:14:15 -05:00
Ananasloll3 0b0d74a638 Fix audio volume levels for cows and bats (#1225)
Cow volume 0.4f to 1.f
Bat volume 0.1f to 0.8f
2026-03-15 18:13:11 -05:00
iris b27cb536a5 Fix game crashing when crafting fireworks (#1230) (#1240) 2026-03-15 18:13:03 -05:00
Xenovyy 15ea3dc85c Adjusted Exit Game title (#1277)
* Adjusted Exit Game title

Replaced "Return to Xbox Dashboard" with "Exit Minecraft" on the exit game screen.

* Fixed Blending on Intro Sequence

Fixed Blocky, Crappy blending on the ESRB and Mojang Logos in the Intro Sequence.
2026-03-15 18:12:53 -05:00
retucio 9fde19eca0 organized keybinds a lil bit (#1279) 2026-03-15 18:11:55 -05:00
kuwa e9f5b4b6f0 Fix server DedicatedServer issues (#1266)
* add: Dedicated Server implementation

- Introduced `ServerMain.cpp` for the dedicated server logic, handling command-line arguments, server initialization, and network management.
- Created `postbuild_server.ps1` script for post-build tasks, including copying necessary resources and DLLs for the dedicated server.
- Added `CopyServerAssets.cmake` to manage the copying of server assets during the build process, ensuring required files are available for the dedicated server.
- Defined project filters in `Minecraft.Server.vcxproj.filters` for better organization of server-related files.

* add: refactor world loader & add server properties

- Introduced ServerLogger for logging startup steps and world I/O operations.
- Implemented ServerProperties for loading and saving server configuration from `server.properties`.
- Added WorldManager to handle world loading and creation based on server properties.
- Updated ServerMain to integrate server properties loading and world management.
- Enhanced project files to include new source and header files for the server components.

* update: implement enhanced logging functionality with configurable log levels

* update: update keyboard and mouse input initialization 1dc8a005ed

* fix: change virtual screen resolution to 1920x1080(HD)

Since 31881af56936aeef38ff322b975fd0 , `skinHud.swf` for 720 is not included in `MediaWindows64.arc`,
the app crashes unless the virtual screen is set to HD.

* fix: dedicated server build settings for miniaudio migration and missing sources

- remove stale Windows64 Miles (mss64) link/copy references from server build
- add Common/Filesystem/Filesystem.cpp to Minecraft.Server.vcxproj
- add Windows64/PostProcesser.cpp to Minecraft.Server.vcxproj
- fix unresolved externals (PostProcesser::*, FileExists) in dedicated server build

* update: changed the virtual screen to 720p

Since the crash caused by the 720p `skinHud.swf` not being included in `MediaWindows64.arc` has been resolved, switching back to 720p to reduce resource usage.

* add: add Docker support for Dedicated Server

add with entrypoint and build scripts

* fix: add initial save for newly created worlds in dedicated server

on the server side, I fixed the behavior introduced after commit aadb511, where newly created worlds are intentionally not saved to disk immediately.

* update: add basically all configuration options that are implemented in the classes to `server.properties`

* update: add LAN advertising configuration for server.properties

LAN-Discovery, which isn’t needed in server mode and could potentially be a security risk, has also been disabled(only server mode).

* add: add implementing interactive command line using linenoise

- Integrated linenoise library for line editing and completion in the server console.
- Updated ServerLogger to handle external writes safely during logging.
- Modified ServerMain to initialize and manage the ServerCli for command input.
- The implementation is separate from everything else, so it doesn't affect anything else.
- The command input section and execution section are separated into threads.

* update: enhance command line completion with predictive hints

Like most command line tools, it highlights predictions in gray.

* add: implement `StringUtils` for string manipulation and refactor usages

Unified the scattered utility functions.

* fix: send DisconnectPacket on shutdown and fix Win64 recv-thread teardown race

Before this change, server/host shutdown closed sockets directly in
ServerConnection::stop(), which bypassed the normal disconnect flow.
As a result, clients could be dropped without receiving a proper
DisconnectPacket during stop/kill/world-close paths.

Also, WinsockNetLayer::Shutdown() could destroy synchronization objects
while host-side recv threads were still exiting, causing a crash in
RecvThreadProc (access violation on world close in host mode).

* fix: return client to menus when Win64 host connection drops

- Add client-side host disconnect handling in CPlatformNetworkManagerStub::DoWork() for _WINDOWS64.
- When in QNET_STATE_GAME_PLAY as a non-host and WinsockNetLayer::IsConnected() becomes false, trigger g_NetworkManager.HandleDisconnect(false) to enter the normal disconnect/UI flow.
- Use m_bLeaveGameOnTick as a one-shot guard to prevent repeated disconnect handling while the link remains down.
- Reset m_bLeaveGameOnTick on LeaveGame(), HostGame(), and JoinGame() to avoid stale state across sessions.

* update: converted Japanese comments to English

* add: create `Minecraft.Server` developer guide in English and Japanese

* update: add note about issue

* add: add `nlohmann/json` json lib

* add: add FileUtils

Moved file operations to `utils`.

* add: Dedicated Server BAN access manager with persistent player and IP bans

- add Access frontend that publishes thread-safe ban manager snapshots for dedicated server use
- add BanManager storage for banned-players.json and banned-ips.json with load/save/update flows
- add persistent player and IP ban checks during dedicated server connection handling
- add UTF-8 BOM-safe JSON parsing and shared file helpers backed by nlohmann/json
- add Unicode-safe ban file read/write and safer atomic replacement behavior on Windows
- add active-ban snapshot APIs and expiry-aware filtering for expires metadata
- add RAII-based dedicated access shutdown handling during server startup and teardown

* update: changed file read/write operations to use `FileUtils`.

- As a side effect, saving has become faster!

* fix: Re-added the source that had somehow disappeared.

* add: significantly improved the dedicated server logging system

- add ServerLogManager to Minecraft.Server as the single entry point for dedicated-server log output
- forward CMinecraftApp logger output to the server logger when running with g_Win64DedicatedServer
- add named network logs for incoming, accepted, rejected, and disconnected connections
- cache connection metadata by smallId so player name and remote IP remain available for disconnect logs
- keep Minecraft.Client changes minimal by using lightweight hook points and handling log orchestration on the server side

* fix: added the updated library source

* add: add `ban` and `pardon` commands for Player and IP

* fix: fix stop command shutdown process

add dedicated server shutdown request handling

* fix: fixed the save logic during server shutdown

Removed redundant repeated saves and eliminated the risks of async writes.

* update: added new sever files to Docker entrypoint

* fix: replace shutdown flag with atomic variable for thread safety

* update: update Dedicated Server developer guide

English is machine translated.
Please forgive me.

* update: check for the existence of `GameHDD` and create

* add: add Whitelist to Dedicated Server

* refactor: clean up and refactor the code

- unify duplicated implementations that were copied repeatedly
- update outdated patterns to more modern ones

* fix: include UI header (new update fix)

* fix: fix the detection range for excessive logging

`getHighestNonEmptyY()` returning `-1` occurs normally when the chunk is entirely air.
The caller (`Minecraft.World/LevelChunk.cpp:2400`) normalizes `-1` to `0`.

* update: add world size config to dedicated server properties

* update: update README add explanation of  `server.properties` & launch arguments

* update: add nightly release workflow for dedicated server and client builds to Actions

* fix: update name for workflow

* add random seed generation

* add: add Docker nightly workflow for Dedicated Server publish to GitHub Container Registry

* fix: ghost player when clients disconnect out of order

#4

* fix: fix 7zip option

* fix: fix Docker workflow for Dedicated Server artifact handling

* add: add no build Dedicated Server startup scripts and Docker Compose

* update: add README for Docker Dedicated Server setup with no local build

* refactor: refactor command path structure

As the number of commands has increased and become harder to navigate, each command has been organized into separate folders.

* update: support stream(file stdin) input mode for server CLI

Support for the stream (file stdin) required when attaching a tty to a Docker container on Linux.

* add: add new CLI Console Commands for Dedicated Server

Most of these commands are executed using the command dispatcher implemented on the `Minecraft.World` side. When registering them with the dispatcher, the sender uses a permission-enabled configuration that treats the CLI as a player.

- default game.
- enchant
- experience.
- give
- kill(currently, getting a permission error for some reason)
- time
- weather.
- update tp & gamemode command

* fix: change player map icon to random select

* update: increase the player limit

* add: restore the basic anti-cheat implementation and add spawn protection

Added the following anti-cheat measures and add spawn protection to `server.properties`.
- instant break
- speed
- reach

* fix: fix Docker image tag

* make chunks delay less for dedi

* fix: prevent overwriting allow-flight value on server startup

* fix: mitigate entity id overflow and crash for max chunk updates

* remove autosave prompt for dedicated server

* fix: fix `Failed to create window instance.`

Wait for Xvfb to be fully ready before starting.

* Revert wrong readme order

---------

Co-authored-by: sylvessa <225480449+sylvessa@users.noreply.github.com>
Co-authored-by: Loki Rautio <lokirautio@gmail.com>
2026-03-15 15:50:12 -05:00
Matthew Toro 7b643cdd75 fix: removed headlesss server logic in favor of the new server exe. (#1265) 2026-03-15 15:43:27 -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
175 changed files with 12380 additions and 3167 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);
}
+6 -4
View File
@@ -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;
};
+2 -1
View File
@@ -879,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
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.
Binary file not shown.
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>
+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.
+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)
@@ -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
@@ -255,7 +255,7 @@ 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);
}
@@ -49,7 +49,7 @@ static wstring ReadLevelNameFromSaveFile(const wstring& filePath, bool *outHardc
if (len > 0)
{
wchar_t wbuf[128] = {};
mbstowcs(wbuf, buf, 127);
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, 127);
return wstring(wbuf);
}
}
@@ -210,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 );
@@ -234,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;
@@ -699,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))
@@ -720,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
@@ -762,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
@@ -774,19 +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");
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
{
@@ -1431,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] = {};
@@ -1449,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);
@@ -2517,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()
@@ -368,7 +368,7 @@ void UIScene_MainMenu::handlePress(F64 controlId, F64 childId)
UINT uiIDA[2];
uiIDA[0]=IDS_CANCEL;
uiIDA[1]=IDS_OK;
ui.RequestErrorMessage(IDS_WARNING_ARCADE_TITLE, IDS_WARNING_ARCADE_TEXT, uiIDA, 2, XUSER_INDEX_ANY,&UIScene_MainMenu::ExitGameReturned,this);
ui.RequestErrorMessage(IDS_WINDOWS_EXIT, IDS_WARNING_ARCADE_TEXT, uiIDA, 2, XUSER_INDEX_ANY,&UIScene_MainMenu::ExitGameReturned,this);
}
else
{
+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
};
+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
+7 -2
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,6 +393,8 @@ 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;
+10 -211
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,214 +853,11 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
// font.draw(str, x + 1, y, 0xffffff);
// }
#ifndef _FINAL_BUILD
MemSect(31);
// temporarily render overlay at all times so version is more obvious in bug reports
// we can turn this off once things stabilize
if (true)// minecraft->options->renderDebug && minecraft->player != nullptr && minecraft->level != nullptr)
{
const int debugLeft = 1;
const int debugTop = 1;
const float maxContentWidth = 1200.f;
const float maxContentHeight = 420.f;
float scale = static_cast<float>(screenWidth - debugLeft - 8) / maxContentWidth;
float scaleV = static_cast<float>(screenHeight - debugTop - 80) / maxContentHeight;
if (scaleV < scale) scale = scaleV;
if (scale > 1.f) scale = 1.f;
if (scale < 0.5f) scale = 0.5f;
glPushMatrix();
glTranslatef(static_cast<float>(debugLeft), static_cast<float>(debugTop), 0.f);
glScalef(scale, scale, 1.f);
glTranslatef(static_cast<float>(-debugLeft), static_cast<float>(-debugTop), 0.f);
vector<wstring> lines;
// Only add version/branch lines if the watermark toggle is enabled
if (ClientConstants::SHOW_VERSION_WATERMARK) {
lines.push_back(ClientConstants::VERSION_STRING);
lines.push_back(ClientConstants::BRANCH_STRING);
}
if (minecraft->options->renderDebug && minecraft->player != nullptr && minecraft->level != nullptr)
{
lines.push_back(minecraft->fpsString);
lines.push_back(L"E: " + std::to_wstring(minecraft->level->getAllEntities().size())); // Could maybe use entity::shouldRender to work out how many are rendered but thats like expensive
// TODO Add server information with packet counts - once multiplayer is more stable
int renderDistance = app.GetGameSettings(iPad, eGameSetting_RenderDistance);
// Calculate the chunk sections using 16 * (2n + 1)^2
lines.push_back(L"C: " + std::to_wstring(16 * (2 * renderDistance + 1) * (2 * renderDistance + 1)) + L" D: " + std::to_wstring(renderDistance));
lines.push_back(minecraft->gatherStats4()); // Chunk Cache
// Dimension
wstring dimension = L"unknown";
switch (minecraft->player->dimension)
{
case -1:
dimension = L"minecraft:the_nether";
break;
case 0:
dimension = L"minecraft:overworld";
break;
case 1:
dimension = L"minecraft:the_end";
break;
}
lines.push_back(dimension);
lines.push_back(L""); // Spacer
// Players block pos
int xBlockPos = Mth::floor(minecraft->player->x);
int yBlockPos = Mth::floor(minecraft->player->y);
int zBlockPos = Mth::floor(minecraft->player->z);
// Chunk player is in
int xChunkPos = xBlockPos >> 4;
int yChunkPos = yBlockPos >> 4;
int zChunkPos = zBlockPos >> 4;
// Players offset within the chunk
int xChunkOffset = xBlockPos & 15;
int yChunkOffset = yBlockPos & 15;
int zChunkOffset = zBlockPos & 15;
// Format the position like java with limited decumal places
WCHAR posString[44]; // Allows upto 7 digit positions (+-9_999_999)
swprintf(posString, 44, L"%.3f / %.5f / %.3f", minecraft->player->x, minecraft->player->y, minecraft->player->z);
lines.push_back(L"XYZ: " + std::wstring(posString));
lines.push_back(L"Block: " + std::to_wstring(static_cast<int>(xBlockPos)) + L" " + std::to_wstring(static_cast<int>(yBlockPos)) + L" " + std::to_wstring(static_cast<int>(zBlockPos)));
lines.push_back(L"Chunk: " + std::to_wstring(xChunkOffset) + L" " + std::to_wstring(yChunkOffset) + L" " + std::to_wstring(zChunkOffset) + L" in " + std::to_wstring(xChunkPos) + L" " + std::to_wstring(yChunkPos) + L" " + std::to_wstring(zChunkPos));
// Wrap the yRot to 360 then adjust to (-180 to 180) range to match java
float yRotDisplay = fmod(minecraft->player->yRot, 360.0f);
if (yRotDisplay > 180.0f)
{
yRotDisplay -= 360.0f;
}
if (yRotDisplay < -180.0f)
{
yRotDisplay += 360.0f;
}
// Generate the angle string in the format "yRot / xRot" with one decimal place, similar to java edition
WCHAR angleString[16];
swprintf(angleString, 16, L"%.1f / %.1f", yRotDisplay, minecraft->player->xRot);
// Work out the named direction
int direction = Mth::floor(minecraft->player->yRot * 4.0f / 360.0f + 0.5) & 0x3;
wstring cardinalDirection;
switch (direction)
{
case 0:
cardinalDirection = L"south";
break;
case 1:
cardinalDirection = L"west";
break;
case 2:
cardinalDirection = L"north";
break;
case 3:
cardinalDirection = L"east";
break;
}
lines.push_back(L"Facing: " + cardinalDirection + L" (" + angleString + L")");
// We have to limit y to 256 as we don't get any information past that
if (minecraft->level != NULL && minecraft->level->hasChunkAt(xBlockPos, fmod(yBlockPos, 256), zBlockPos))
{
LevelChunk *chunkAt = minecraft->level->getChunkAt(xBlockPos, zBlockPos);
if (chunkAt != NULL)
{
int skyLight = chunkAt->getBrightness(LightLayer::Sky, xChunkOffset, yChunkOffset, zChunkOffset);
int blockLight = chunkAt->getBrightness(LightLayer::Block, xChunkOffset, yChunkOffset, zChunkOffset);
int maxLight = fmax(skyLight, blockLight);
lines.push_back(L"Light: " + std::to_wstring(maxLight) + L" (" + std::to_wstring(skyLight) + L" sky, " + std::to_wstring(blockLight) + L" block)");
lines.push_back(L"CH S: " + std::to_wstring(chunkAt->getHeightmap(xChunkOffset, zChunkOffset)));
Biome *biome = chunkAt->getBiome(xChunkOffset, zChunkOffset, minecraft->level->getBiomeSource());
lines.push_back(L"Biome: " + biome->m_name + L" (" + std::to_wstring(biome->id) + L")");
lines.push_back(L"Difficulty: " + std::to_wstring(minecraft->level->difficulty) + L" (Day " + std::to_wstring(minecraft->level->getGameTime() / Level::TICKS_PER_DAY) + L")");
}
}
// This is all LCE only stuff, it was never on java
lines.push_back(L""); // Spacer
lines.push_back(L"Seed: " + std::to_wstring(minecraft->level->getLevelData()->getSeed()));
lines.push_back(minecraft->gatherStats1()); // Time to autosave
lines.push_back(minecraft->gatherStats2()); // Empty currently - CPlatformNetworkManagerStub::GatherStats()
lines.push_back(minecraft->gatherStats3()); // RTT
}
#ifdef _DEBUG // Only show terrain features in debug builds not release
// TERRAIN FEATURES
if (minecraft->level->dimension->id == 0)
{
wstring wfeature[eTerrainFeature_Count];
wfeature[eTerrainFeature_Stronghold] = L"Stronghold: ";
wfeature[eTerrainFeature_Mineshaft] = L"Mineshaft: ";
wfeature[eTerrainFeature_Village] = L"Village: ";
wfeature[eTerrainFeature_Ravine] = L"Ravine: ";
float maxW = static_cast<float>(screenWidth - debugLeft - 8) / scale;
float maxWForContent = maxW - static_cast<float>(font->width(L"..."));
bool truncated[eTerrainFeature_Count] = {};
for (size_t i = 0; i < app.m_vTerrainFeatures.size(); i++)
{
FEATURE_DATA *pFeatureData = app.m_vTerrainFeatures[i];
int type = pFeatureData->eTerrainFeature;
if (type < eTerrainFeature_Stronghold || type > eTerrainFeature_Ravine)
{
continue;
}
if (truncated[type])
{
continue;
}
wstring itemInfo = L"[" + std::to_wstring(pFeatureData->x * 16) + L", " + std::to_wstring(pFeatureData->z * 16) + L"] ";
if (font->width(wfeature[type] + itemInfo) <= maxWForContent)
{
wfeature[type] += itemInfo;
}
else
{
wfeature[type] += L"...";
truncated[type] = true;
}
}
lines.push_back(L""); // Add a spacer line
for (int i = eTerrainFeature_Stronghold; i <= static_cast<int>(eTerrainFeature_Ravine); i++)
{
lines.push_back(wfeature[i]);
}
lines.push_back(L"");
}
#endif
// Loop through the lines and draw them all on screen
int yPos = debugTop;
for (const auto &line : lines)
{
drawString(font, line, debugLeft, yPos, 0xffffff);
yPos += 10;
}
glPopMatrix();
}
MemSect(0);
#endif
lastTickA = a;
// 4J Stu - This is now displayed in a xui scene
#if 0
// Jukebox CD message
// Jukebox CD message
if (overlayMessageTime > 0)
{
float t = overlayMessageTime - a;
@@ -1269,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);
@@ -1590,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;
@@ -1811,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);
+272 -250
View File
@@ -658,7 +658,7 @@ bool MinecraftServer::initServer(int64_t seed, NetworkGameInitData *initData, DW
setFlightAllowed(GetDedicatedServerBool(settings, L"allow-flight", true));
// 4J Stu - Enabling flight to stop it kicking us when we use it
#ifdef _DEBUG_MENUS_ENABLED
#if (defined _DEBUG_MENUS_ENABLED && defined _DEBUG)
setFlightAllowed(true);
#endif
@@ -1007,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();
@@ -1725,330 +1728,345 @@ void MinecraftServer::setPlayerIdleTimeout(int playerIdleTimeout)
extern int c0a, c0b, c1a, c1b, c1c, c2a, c2b;
void MinecraftServer::run(int64_t seed, void *lpParameter)
{
NetworkGameInitData *initData = nullptr;
DWORD initSettings = 0;
bool findSeed = false;
NetworkGameInitData *initData = nullptr;
DWORD initSettings = 0;
bool findSeed = false;
if(lpParameter != nullptr)
{
initData = static_cast<NetworkGameInitData *>(lpParameter);
initSettings = app.GetGameHostOption(eGameHostOption_All);
findSeed = initData->findSeed;
m_texturePackId = initData->texturePackId;
}
// try { // 4J - removed try/catch/finally
bool didInit = false;
{
initData = static_cast<NetworkGameInitData *>(lpParameter);
initSettings = app.GetGameHostOption(eGameHostOption_All);
findSeed = initData->findSeed;
m_texturePackId = initData->texturePackId;
}
// try { // 4J - removed try/catch/finally
bool didInit = false;
if (initServer(seed, initData, initSettings,findSeed))
{
didInit = true;
ServerLevel *levelNormalDimension = levels[0];
// 4J-PB - Set the Stronghold position in the leveldata if there isn't one in there
Minecraft *pMinecraft = Minecraft::GetInstance();
{
didInit = true;
ServerLevel *levelNormalDimension = levels[0];
// 4J-PB - Set the Stronghold position in the leveldata if there isn't one in there
Minecraft *pMinecraft = Minecraft::GetInstance();
LevelData *pLevelData=levelNormalDimension->getLevelData();
if(pLevelData && pLevelData->getHasStronghold()==false)
{
{
int x,z;
if(app.GetTerrainFeaturePosition(eTerrainFeature_Stronghold,&x,&z))
{
pLevelData->setXStronghold(x);
pLevelData->setZStronghold(z);
pLevelData->setHasStronghold();
}
}
{
pLevelData->setXStronghold(x);
pLevelData->setZStronghold(z);
pLevelData->setHasStronghold();
}
}
int64_t lastTime = getCurrentTimeMillis();
int64_t unprocessedTime = 0;
while (running && !s_bServerHalted)
{
int64_t now = getCurrentTimeMillis();
int64_t lastTime = getCurrentTimeMillis();
int64_t unprocessedTime = 0;
while (running && !s_bServerHalted)
{
int64_t now = getCurrentTimeMillis();
// 4J Stu - When we pause the server, we don't want to count that as time passed
// 4J Stu - TU-1 hotifx - Remove this line. We want to make sure that we tick connections at the proper rate when paused
// 4J Stu - When we pause the server, we don't want to count that as time passed
// 4J Stu - TU-1 hotifx - Remove this line. We want to make sure that we tick connections at the proper rate when paused
//Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost
//if(m_isServerPaused) lastTime = now;
int64_t passedTime = now - lastTime;
if (passedTime > MS_PER_TICK * 40)
{
// logger.warning("Can't keep up! Did the system time change, or is the server overloaded?");
passedTime = MS_PER_TICK * 40;
}
if (passedTime < 0)
{
// logger.warning("Time ran backwards! Did the system time change?");
passedTime = 0;
}
unprocessedTime += passedTime;
lastTime = now;
int64_t passedTime = now - lastTime;
if (passedTime > MS_PER_TICK * 40)
{
// logger.warning("Can't keep up! Did the system time change, or is the server overloaded?");
passedTime = MS_PER_TICK * 40;
}
if (passedTime < 0)
{
// logger.warning("Time ran backwards! Did the system time change?");
passedTime = 0;
}
unprocessedTime += passedTime;
lastTime = now;
// 4J Added ability to pause the server
// 4J Added ability to pause the server
if( !m_isServerPaused )
{
bool didTick = false;
if (levels[0]->allPlayersAreSleeping())
{
tick();
unprocessedTime = 0;
}
else
{
// int tickcount = 0;
// int64_t beforeall = System::currentTimeMillis();
while (unprocessedTime > MS_PER_TICK)
{
unprocessedTime -= MS_PER_TICK;
chunkPacketManagement_PreTick();
// int64_t before = System::currentTimeMillis();
tick();
// int64_t after = System::currentTimeMillis();
// PIXReportCounter(L"Server time",(float)(after-before));
{
bool didTick = false;
if (levels[0]->allPlayersAreSleeping())
{
tick();
unprocessedTime = 0;
}
else
{
// int tickcount = 0;
// int64_t beforeall = System::currentTimeMillis();
while (unprocessedTime > MS_PER_TICK)
{
unprocessedTime -= MS_PER_TICK;
chunkPacketManagement_PreTick();
// int64_t before = System::currentTimeMillis();
tick();
// int64_t after = System::currentTimeMillis();
// PIXReportCounter(L"Server time",(float)(after-before));
chunkPacketManagement_PostTick();
}
// int64_t afterall = System::currentTimeMillis();
// PIXReportCounter(L"Server time all",(float)(afterall-beforeall));
// PIXReportCounter(L"Server ticks",(float)tickcount);
}
}
else
{
// 4J Stu - TU1-hotfix
chunkPacketManagement_PostTick();
}
// int64_t afterall = System::currentTimeMillis();
// PIXReportCounter(L"Server time all",(float)(afterall-beforeall));
// PIXReportCounter(L"Server ticks",(float)tickcount);
}
}
else
{
// 4J Stu - TU1-hotfix
//Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost
// The connections should tick at the same frequency even when paused
while (unprocessedTime > MS_PER_TICK)
{
unprocessedTime -= MS_PER_TICK;
// Keep ticking the connections to stop them timing out
connection->tick();
}
}
// The connections should tick at the same frequency even when paused
while (unprocessedTime > MS_PER_TICK)
{
unprocessedTime -= MS_PER_TICK;
// Keep ticking the connections to stop them timing out
connection->tick();
}
}
if(MinecraftServer::setTimeAtEndOfTick)
{
MinecraftServer::setTimeAtEndOfTick = false;
for (unsigned int i = 0; i < levels.length; i++)
{
// if (i == 0 || settings->getBoolean(L"allow-nether", true)) // 4J removed - we always have nether
{
ServerLevel *level = levels[i];
{
MinecraftServer::setTimeAtEndOfTick = false;
for (unsigned int i = 0; i < levels.length; i++)
{
// if (i == 0 || settings->getBoolean(L"allow-nether", true)) // 4J removed - we always have nether
{
ServerLevel *level = levels[i];
level->setGameTime( MinecraftServer::setTime );
}
}
}
}
}
}
if(MinecraftServer::setTimeOfDayAtEndOfTick)
{
MinecraftServer::setTimeOfDayAtEndOfTick = false;
for (unsigned int i = 0; i < levels.length; i++)
{
if (i == 0 || GetDedicatedServerBool(settings, L"allow-nether", true))
{
ServerLevel *level = levels[i];
{
MinecraftServer::setTimeOfDayAtEndOfTick = false;
for (unsigned int i = 0; i < levels.length; i++)
{
if (i == 0 || GetDedicatedServerBool(settings, L"allow-nether", true))
{
ServerLevel *level = levels[i];
level->setDayTime( MinecraftServer::setTimeOfDay );
}
}
}
}
}
}
// Process delayed actions
eXuiServerAction eAction;
LPVOID param;
// Process delayed actions
eXuiServerAction eAction;
LPVOID param;
for(int i=0;i<XUSER_MAX_COUNT;i++)
{
eAction = app.GetXuiServerAction(i);
param = app.GetXuiServerActionParam(i);
{
eAction = app.GetXuiServerAction(i);
param = app.GetXuiServerActionParam(i);
switch(eAction)
{
case eXuiServerAction_AutoSaveGame:
{
case eXuiServerAction_AutoSaveGame:
#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(MINECRAFT_SERVER_BUILD)
{
#if defined(_XBOX_ONE) || defined(__ORBIS__)
{
PIXBeginNamedEvent(0,"Autosave");
PIXBeginNamedEvent(0, "Autosave");
// Get the frequency of the timer
LARGE_INTEGER qwTicksPerSec, qwTime, qwNewTime, qwDeltaTime;
float fElapsedTime = 0.0f;
QueryPerformanceFrequency( &qwTicksPerSec );
float fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart;
// Get the frequency of the timer
LARGE_INTEGER qwTicksPerSec, qwTime, qwNewTime, qwDeltaTime;
float fElapsedTime = 0.0f;
QueryPerformanceFrequency(&qwTicksPerSec);
float fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart;
// Save the start time
QueryPerformanceCounter( &qwTime );
if (players != nullptr)
{
players->saveAll(nullptr);
}
for (unsigned int j = 0; j < levels.length; j++)
{
if( s_bServerHalted ) break;
// 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
// with the data from the nethers leveldata.
// Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
ServerLevel *level = levels[levels.length - 1 - j];
PIXBeginNamedEvent(0, "Saving level %d",levels.length - 1 - j);
level->save(false, nullptr, true);
PIXEndNamedEvent();
}
if( !s_bServerHalted )
{
PIXBeginNamedEvent(0,"Saving game rules");
saveGameRules();
PIXEndNamedEvent();
PIXBeginNamedEvent(0,"Save to disc");
levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, true);
PIXEndNamedEvent();
}
PIXEndNamedEvent();
QueryPerformanceCounter( &qwNewTime );
qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart;
fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart));
app.DebugPrintf("Autosave: Elapsed time %f\n", fElapsedTime);
}
break;
// Save the start time
QueryPerformanceCounter(&qwTime);
#endif
case eXuiServerAction_SaveGame:
app.EnterSaveNotificationSection();
if (players != nullptr)
{
players->saveAll(Minecraft::GetInstance()->progressRenderer);
}
players->broadcastAll(std::make_shared<UpdateProgressPacket>(20));
if (players != nullptr)
{
players->saveAll(nullptr);
}
for (unsigned int j = 0; j < levels.length; j++)
{
for (unsigned int j = 0; j < levels.length; j++)
{
if( s_bServerHalted ) break;
// 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
// with the data from the nethers leveldata.
// Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
ServerLevel *level = levels[levels.length - 1 - j];
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXBeginNamedEvent(0, "Saving level %d", levels.length - 1 - j);
#endif
level->save(false, nullptr, true);
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXEndNamedEvent();
#endif
}
if (!s_bServerHalted)
{
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXBeginNamedEvent(0, "Saving game rules");
#endif
saveGameRules();
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXEndNamedEvent();
PIXBeginNamedEvent(0, "Save to disc");
#endif
levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, true);
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXEndNamedEvent();
#endif
}
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXEndNamedEvent();
QueryPerformanceCounter(&qwNewTime);
qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart;
fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart));
app.DebugPrintf("Autosave: Elapsed time %f\n", fElapsedTime);
#endif
}
break;
#endif
case eXuiServerAction_SaveGame:
app.EnterSaveNotificationSection();
if (players != nullptr)
{
players->saveAll(Minecraft::GetInstance()->progressRenderer);
}
players->broadcastAll(std::make_shared<UpdateProgressPacket>(20));
for (unsigned int j = 0; j < levels.length; j++)
{
if( s_bServerHalted ) break;
// 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
// with the data from the nethers leveldata.
// Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
ServerLevel *level = levels[levels.length - 1 - j];
// 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
// with the data from the nethers leveldata.
// Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
ServerLevel *level = levels[levels.length - 1 - j];
level->save(true, Minecraft::GetInstance()->progressRenderer, (eAction==eXuiServerAction_AutoSaveGame));
players->broadcastAll(std::make_shared<UpdateProgressPacket>(33 + (j * 33)));
}
players->broadcastAll(std::make_shared<UpdateProgressPacket>(33 + (j * 33)));
}
if( !s_bServerHalted )
{
saveGameRules();
{
saveGameRules();
levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, (eAction==eXuiServerAction_AutoSaveGame));
}
app.LeaveSaveNotificationSection();
break;
case eXuiServerAction_DropItem:
// Find the player, and drop the id at their feet
{
shared_ptr<ServerPlayer> player = players->players.at(0);
}
app.LeaveSaveNotificationSection();
break;
case eXuiServerAction_DropItem:
// Find the player, and drop the id at their feet
{
shared_ptr<ServerPlayer> player = players->players.at(0);
size_t id = (size_t) param;
player->drop(std::make_shared<ItemInstance>(id, 1, 0));
}
break;
case eXuiServerAction_SpawnMob:
{
shared_ptr<ServerPlayer> player = players->players.at(0);
eINSTANCEOF factory = static_cast<eINSTANCEOF>((size_t)param);
player->drop(std::make_shared<ItemInstance>(id, 1, 0));
}
break;
case eXuiServerAction_SpawnMob:
{
shared_ptr<ServerPlayer> player = players->players.at(0);
eINSTANCEOF factory = static_cast<eINSTANCEOF>((size_t)param);
shared_ptr<Mob> mob = dynamic_pointer_cast<Mob>(EntityIO::newByEnumType(factory,player->level ));
mob->moveTo(player->x+1, player->y, player->z+1, player->level->random->nextFloat() * 360, 0);
mob->setDespawnProtected(); // 4J added, default to being protected against despawning (has to be done after initial position is set)
player->level->addEntity(mob);
}
break;
case eXuiServerAction_PauseServer:
player->level->addEntity(mob);
}
break;
case eXuiServerAction_PauseServer:
m_isServerPaused = ( (size_t) param == TRUE );
if( m_isServerPaused )
{
m_serverPausedEvent->Set();
}
break;
case eXuiServerAction_ToggleRain:
{
bool isRaining = levels[0]->getLevelData()->isRaining();
levels[0]->getLevelData()->setRaining(!isRaining);
levels[0]->getLevelData()->setRainTime(levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + Level::TICKS_PER_DAY / 2);
}
break;
case eXuiServerAction_ToggleThunder:
{
bool isThundering = levels[0]->getLevelData()->isThundering();
levels[0]->getLevelData()->setThundering(!isThundering);
levels[0]->getLevelData()->setThunderTime(levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + Level::TICKS_PER_DAY / 2);
}
break;
case eXuiServerAction_ServerSettingChanged_Gamertags:
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_OPTIONS, app.GetGameHostOption(eGameHostOption_Gamertags)));
break;
case eXuiServerAction_ServerSettingChanged_BedrockFog:
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS, app.GetGameHostOption(eGameHostOption_All)));
break;
{
m_serverPausedEvent->Set();
}
break;
case eXuiServerAction_ToggleRain:
{
bool isRaining = levels[0]->getLevelData()->isRaining();
levels[0]->getLevelData()->setRaining(!isRaining);
levels[0]->getLevelData()->setRainTime(levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + Level::TICKS_PER_DAY / 2);
}
break;
case eXuiServerAction_ToggleThunder:
{
bool isThundering = levels[0]->getLevelData()->isThundering();
levels[0]->getLevelData()->setThundering(!isThundering);
levels[0]->getLevelData()->setThunderTime(levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + Level::TICKS_PER_DAY / 2);
}
break;
case eXuiServerAction_ServerSettingChanged_Gamertags:
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_OPTIONS, app.GetGameHostOption(eGameHostOption_Gamertags)));
break;
case eXuiServerAction_ServerSettingChanged_BedrockFog:
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS, app.GetGameHostOption(eGameHostOption_All)));
break;
case eXuiServerAction_ServerSettingChanged_Difficulty:
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_DIFFICULTY, Minecraft::GetInstance()->options->difficulty));
break;
case eXuiServerAction_ExportSchematic:
case eXuiServerAction_ServerSettingChanged_Difficulty:
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_DIFFICULTY, Minecraft::GetInstance()->options->difficulty));
break;
case eXuiServerAction_ExportSchematic:
#ifndef _CONTENT_PACKAGE
app.EnterSaveNotificationSection();
app.EnterSaveNotificationSection();
//players->broadcastAll( shared_ptr<UpdateProgressPacket>( new UpdateProgressPacket(20) ) );
if( !s_bServerHalted )
{
ConsoleSchematicFile::XboxSchematicInitParam *initData = static_cast<ConsoleSchematicFile::XboxSchematicInitParam *>(param);
{
ConsoleSchematicFile::XboxSchematicInitParam *initData = static_cast<ConsoleSchematicFile::XboxSchematicInitParam *>(param);
#ifdef _XBOX
File targetFileDir(File::pathRoot + File::pathSeparator + L"Schematics");
File targetFileDir(File::pathRoot + File::pathSeparator + L"Schematics");
#else
File targetFileDir(L"Schematics");
File targetFileDir(L"Schematics");
#endif
if(!targetFileDir.exists()) targetFileDir.mkdir();
wchar_t filename[128];
wchar_t filename[128];
swprintf(filename,128,L"%ls%dx%dx%d.sch",initData->name,(initData->endX - initData->startX + 1), (initData->endY - initData->startY + 1), (initData->endZ - initData->startZ + 1));
File dataFile = File( targetFileDir, wstring(filename) );
if(dataFile.exists()) dataFile._delete();
FileOutputStream fos = FileOutputStream(dataFile);
DataOutputStream dos = DataOutputStream(&fos);
ConsoleSchematicFile::generateSchematicFile(&dos, levels[0], initData->startX, initData->startY, initData->startZ, initData->endX, initData->endY, initData->endZ, initData->bSaveMobs, initData->compressionType);
dos.close();
FileOutputStream fos = FileOutputStream(dataFile);
DataOutputStream dos = DataOutputStream(&fos);
ConsoleSchematicFile::generateSchematicFile(&dos, levels[0], initData->startX, initData->startY, initData->startZ, initData->endX, initData->endY, initData->endZ, initData->bSaveMobs, initData->compressionType);
dos.close();
delete initData;
}
app.LeaveSaveNotificationSection();
delete initData;
}
app.LeaveSaveNotificationSection();
#endif
break;
case eXuiServerAction_SetCameraLocation:
break;
case eXuiServerAction_SetCameraLocation:
#ifndef _CONTENT_PACKAGE
{
DebugSetCameraPosition *pos = static_cast<DebugSetCameraPosition *>(param);
{
DebugSetCameraPosition *pos = static_cast<DebugSetCameraPosition *>(param);
app.DebugPrintf( "DEBUG: Player=%i\n", pos->player );
app.DebugPrintf( "DEBUG: Teleporting to pos=(%f.2, %f.2, %f.2), looking at=(%f.2,%f.2)\n",
pos->m_camX, pos->m_camY, pos->m_camZ,
pos->m_camX, pos->m_camY, pos->m_camZ,
pos->m_yRot, pos->m_elev
);
shared_ptr<ServerPlayer> player = players->players.at(pos->player);
shared_ptr<ServerPlayer> player = players->players.at(pos->player);
player->debug_setPosition( pos->m_camX, pos->m_camY, pos->m_camZ,
pos->m_yRot, pos->m_elev );
// Doesn't work
// Doesn't work
//player->setYHeadRot(pos->m_yRot);
//player->absMoveTo(pos->m_camX, pos->m_camY, pos->m_camZ, pos->m_yRot, pos->m_elev);
}
}
#endif
break;
}
break;
}
app.SetXuiServerAction(i,eXuiServerAction_Idle);
}
}
Sleep(1);
}
}
Sleep(1);
}
}
//else
//{
//{
// while (running)
// {
// {
// handleConsoleInputs();
// Sleep(10);
// Sleep(10);
// }
//}
#if 0
@@ -2075,9 +2093,9 @@ void MinecraftServer::run(int64_t seed, void *lpParameter)
}
#endif
// 4J Stu - Stop the server when the loops complete, as the finally would do
stopServer(didInit);
stopped = true;
// 4J Stu - Stop the server when the loops complete, as the finally would do
stopServer(didInit);
stopped = true;
}
void MinecraftServer::broadcastStartSavingPacket()
@@ -2385,6 +2403,9 @@ bool MinecraftServer::chunkPacketManagement_CanSendTo(INetworkPlayer *player)
{
if( player == nullptr ) return false;
#ifdef MINECRAFT_SERVER_BUILD
return true;
#else
int time = GetTickCount();
DWORD currentPlayerCount = g_NetworkManager.GetPlayerCount();
if( currentPlayerCount == 0 ) return false;
@@ -2396,6 +2417,7 @@ bool MinecraftServer::chunkPacketManagement_CanSendTo(INetworkPlayer *player)
}
return false;
#endif
}
void MinecraftServer::chunkPacketManagement_DidSendTo(INetworkPlayer *player)
@@ -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;
+77
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
@@ -1729,6 +1733,79 @@ void PlayerList::banXuid(PlayerUID xuid)
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)
{
+1
View File
@@ -137,6 +137,7 @@ public:
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);
};
+1 -1
View File
@@ -204,7 +204,7 @@ void Screen::updateEvents()
// Map to Screen::keyPressed
int mappedKey = -1;
wchar_t ch = 0;
if (vk == VK_ESCAPE) mappedKey = Keyboard::KEY_ESCAPE;
if (vk == VK_ESCAPE) mappedKey = Keyboard::KEY_ESCAPE;
else if (vk == VK_RETURN) mappedKey = Keyboard::KEY_RETURN;
else if (vk == VK_BACK) mappedKey = Keyboard::KEY_BACK;
else if (vk == VK_UP) mappedKey = Keyboard::KEY_UP;
+24 -19
View File
@@ -146,7 +146,8 @@ void ServerPlayer::flagEntitiesToBeRemoved(unsigned int *flags, bool *removedFou
if( ( *removedFound ) == false )
{
*removedFound = true;
memset(flags, 0, 2048/32);
// before this left 192 bytes uninitialized!!!!!
memset(flags, 0, (2048 / 32) * sizeof(unsigned int));
}
for(int index : entitiesToRemove)
@@ -376,6 +377,9 @@ void ServerPlayer::doChunkSendingTick(bool dontDelayChunks)
// connection->done);
// }
#ifdef MINECRAFT_SERVER_BUILD
if (dontDelayChunks || (canSendToPlayer && !connection->done))
#else
if( dontDelayChunks ||
(canSendToPlayer &&
#ifdef _XBOX_ONE
@@ -390,21 +394,22 @@ void ServerPlayer::doChunkSendingTick(bool dontDelayChunks)
#endif
//(tickCount - lastBrupSendTickCount) > (connection->getNetworkPlayer()->GetCurrentRtt()>>4) &&
!connection->done) )
{
lastBrupSendTickCount = tickCount;
okToSend = true;
MinecraftServer::chunkPacketManagement_DidSendTo(connection->getNetworkPlayer());
#endif
{
lastBrupSendTickCount = tickCount;
okToSend = true;
MinecraftServer::chunkPacketManagement_DidSendTo(connection->getNetworkPlayer());
// static unordered_map<wstring,int64_t> mapLastTime;
// int64_t thisTime = System::currentTimeMillis();
// int64_t lastTime = mapLastTime[connection->getNetworkPlayer()->GetUID().toString()];
// app.DebugPrintf(" - OK to send (%d ms since last)\n", thisTime - lastTime);
// mapLastTime[connection->getNetworkPlayer()->GetUID().toString()] = thisTime;
}
else
{
// app.DebugPrintf(" - <NOT OK>\n");
}
// static unordered_map<wstring,int64_t> mapLastTime;
// int64_t thisTime = System::currentTimeMillis();
// int64_t lastTime = mapLastTime[connection->getNetworkPlayer()->GetUID().toString()];
// app.DebugPrintf(" - OK to send (%d ms since last)\n", thisTime - lastTime);
// mapLastTime[connection->getNetworkPlayer()->GetUID().toString()] = thisTime;
}
else
{
// app.DebugPrintf(" - <NOT OK>\n");
}
}
if (okToSend)
@@ -569,10 +574,10 @@ void ServerPlayer::die(DamageSource *source)
{
setGameMode(GameType::ADVENTURE);
// Ban this player's XUID and force-save so the host
// cannot circumvent the death by quitting without saving.
server->getPlayers()->banXuid(getOnlineXuid());
app.SetXuiServerAction(ProfileManager.GetPrimaryPad(), eXuiServerAction_SaveGame);
// 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))
@@ -25,12 +25,18 @@ public:
static const int KEY_DROP = 'Q';
static const int KEY_CRAFTING = 'C';
static const int KEY_CRAFTING_ALT = 'R';
static const int KEY_CHAT = 'T';
static const int KEY_CONFIRM = VK_RETURN;
static const int KEY_CANCEL = VK_ESCAPE;
static const int KEY_PAUSE = VK_ESCAPE;
static const int KEY_THIRD_PERSON = VK_F5;
static const int KEY_TOGGLE_HUD = VK_F1;
static const int KEY_DEBUG_INFO = VK_F3;
static const int KEY_DEBUG_MENU = VK_F4;
static const int KEY_THIRD_PERSON = VK_F5;
static const int KEY_DEBUG_CONSOLE = VK_F6;
static const int KEY_HOST_SETTINGS = VK_TAB;
static const int KEY_FULLSCREEN = VK_F11;
static const int KEY_SCREENSHOT = VK_F2;
void Init();
void Tick();
@@ -121,7 +121,6 @@ static WINDOWPLACEMENT g_wpPrev = { sizeof(g_wpPrev) };
struct Win64LaunchOptions
{
int screenMode;
bool serverMode;
bool fullscreen;
};
@@ -207,13 +206,9 @@ static Win64LaunchOptions ParseLaunchOptions()
{
Win64LaunchOptions options = {};
options.screenMode = 0;
options.serverMode = false;
g_Win64MultiplayerJoin = false;
g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT;
g_Win64DedicatedServer = false;
g_Win64DedicatedServerPort = WIN64_NET_DEFAULT_PORT;
g_Win64DedicatedServerBindIP[0] = 0;
int argc = 0;
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
@@ -226,17 +221,6 @@ static Win64LaunchOptions ParseLaunchOptions()
options.screenMode = argv[1][0] - L'0';
}
for (int i = 1; i < argc; ++i)
{
if (_wcsicmp(argv[i], L"-server") == 0)
{
options.serverMode = true;
break;
}
}
g_Win64DedicatedServer = options.serverMode;
for (int i = 1; i < argc; ++i)
{
if (_wcsicmp(argv[i], L"-name") == 0 && (i + 1) < argc)
@@ -247,15 +231,8 @@ static Win64LaunchOptions ParseLaunchOptions()
{
char ipBuf[256];
CopyWideArgToAnsi(argv[++i], ipBuf, sizeof(ipBuf));
if (options.serverMode)
{
strncpy_s(g_Win64DedicatedServerBindIP, sizeof(g_Win64DedicatedServerBindIP), ipBuf, _TRUNCATE);
}
else
{
strncpy_s(g_Win64MultiplayerIP, sizeof(g_Win64MultiplayerIP), ipBuf, _TRUNCATE);
g_Win64MultiplayerJoin = true;
}
strncpy_s(g_Win64MultiplayerIP, sizeof(g_Win64MultiplayerIP), ipBuf, _TRUNCATE);
g_Win64MultiplayerJoin = true;
}
else if (_wcsicmp(argv[i], L"-port") == 0 && (i + 1) < argc)
{
@@ -263,10 +240,7 @@ static Win64LaunchOptions ParseLaunchOptions()
const long port = wcstol(argv[++i], &endPtr, 10);
if (endPtr != argv[i] && *endPtr == 0 && port > 0 && port <= 65535)
{
if (options.serverMode)
g_Win64DedicatedServerPort = static_cast<int>(port);
else
g_Win64MultiplayerPort = static_cast<int>(port);
g_Win64MultiplayerPort = static_cast<int>(port);
}
}
else if (_wcsicmp(argv[i], L"-fullscreen") == 0)
@@ -277,36 +251,6 @@ static Win64LaunchOptions ParseLaunchOptions()
return options;
}
static BOOL WINAPI HeadlessServerCtrlHandler(DWORD ctrlType)
{
switch (ctrlType)
{
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
case CTRL_CLOSE_EVENT:
case CTRL_SHUTDOWN_EVENT:
app.m_bShutdown = true;
MinecraftServer::HaltServer();
return TRUE;
default:
return FALSE;
}
}
static void SetupHeadlessServerConsole()
{
if (AllocConsole())
{
FILE* stream = nullptr;
freopen_s(&stream, "CONIN$", "r", stdin);
freopen_s(&stream, "CONOUT$", "w", stdout);
freopen_s(&stream, "CONOUT$", "w", stderr);
SetConsoleTitleA("Minecraft Server");
}
SetConsoleCtrlHandler(HeadlessServerCtrlHandler, TRUE);
}
void DefineActions(void)
{
// The app needs to define the actions required, and the possible mappings for these
@@ -562,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:
@@ -619,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:
@@ -717,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;
}
@@ -729,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);
}
//
@@ -765,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,
@@ -1350,161 +1294,6 @@ static Minecraft* InitialiseMinecraftRuntime()
return pMinecraft;
}
static int HeadlessServerConsoleThreadProc(void* lpParameter)
{
UNREFERENCED_PARAMETER(lpParameter);
std::string line;
while (!app.m_bShutdown)
{
if (!std::getline(std::cin, line))
{
if (std::cin.eof())
{
break;
}
std::cin.clear();
Sleep(50);
continue;
}
wstring command = trimString(convStringToWstring(line));
if (command.empty())
continue;
MinecraftServer* server = MinecraftServer::getInstance();
if (server != nullptr)
{
server->handleConsoleInput(command, server);
}
}
return 0;
}
static int RunHeadlessServer()
{
SetupHeadlessServerConsole();
Settings serverSettings(new File(L"server.properties"));
const wstring configuredBindIp = serverSettings.getString(L"server-ip", L"");
const char* bindIp = "*";
if (g_Win64DedicatedServerBindIP[0] != 0)
{
bindIp = g_Win64DedicatedServerBindIP;
}
else if (!configuredBindIp.empty())
{
bindIp = wstringtochararray(configuredBindIp);
}
const int port = g_Win64DedicatedServerPort > 0 ? g_Win64DedicatedServerPort : serverSettings.getInt(L"server-port", WIN64_NET_DEFAULT_PORT);
printf("Starting headless server on %s:%d\n", bindIp, port);
fflush(stdout);
const Minecraft* pMinecraft = InitialiseMinecraftRuntime();
if (pMinecraft == nullptr)
{
fprintf(stderr, "Failed to initialise the Minecraft runtime.\n");
return 1;
}
app.SetGameHostOption(eGameHostOption_Difficulty, serverSettings.getInt(L"difficulty", 1));
app.SetGameHostOption(eGameHostOption_Gamertags, 1);
app.SetGameHostOption(eGameHostOption_GameType, serverSettings.getInt(L"gamemode", 0));
app.SetGameHostOption(eGameHostOption_LevelType, 0);
app.SetGameHostOption(eGameHostOption_Structures, serverSettings.getBoolean(L"generate-structures", true) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_BonusChest, serverSettings.getBoolean(L"bonus-chest", false) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_PvP, serverSettings.getBoolean(L"pvp", true) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_TrustPlayers, serverSettings.getBoolean(L"trust-players", true) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_FireSpreads, serverSettings.getBoolean(L"fire-spreads", true) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_TNT, serverSettings.getBoolean(L"tnt", true) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_HostCanFly, 1);
app.SetGameHostOption(eGameHostOption_HostCanChangeHunger, 1);
app.SetGameHostOption(eGameHostOption_HostCanBeInvisible, 1);
app.SetGameHostOption(eGameHostOption_MobGriefing, 1);
app.SetGameHostOption(eGameHostOption_KeepInventory, 0);
app.SetGameHostOption(eGameHostOption_DoMobSpawning, 1);
app.SetGameHostOption(eGameHostOption_DoMobLoot, 1);
app.SetGameHostOption(eGameHostOption_DoTileDrops, 1);
app.SetGameHostOption(eGameHostOption_NaturalRegeneration, 1);
app.SetGameHostOption(eGameHostOption_DoDaylightCycle, 1);
MinecraftServer::resetFlags();
g_NetworkManager.HostGame(0, false, true, MINECRAFT_NET_MAX_PLAYERS, 0);
if (!WinsockNetLayer::IsActive())
{
fprintf(stderr, "Failed to bind the server socket on %s:%d.\n", bindIp, port);
return 1;
}
g_NetworkManager.FakeLocalPlayerJoined();
NetworkGameInitData* param = new NetworkGameInitData();
param->seed = 0;
param->settings = app.GetGameHostOption(eGameHostOption_All);
g_NetworkManager.ServerStoppedCreate(true);
g_NetworkManager.ServerReadyCreate(true);
C4JThread* thread = new C4JThread(&CGameNetworkManager::ServerThreadProc, param, "Server", 256 * 1024);
thread->SetProcessor(CPU_CORE_SERVER);
thread->Run();
g_NetworkManager.ServerReadyWait();
g_NetworkManager.ServerReadyDestroy();
if (MinecraftServer::serverHalted())
{
fprintf(stderr, "The server halted during startup.\n");
g_NetworkManager.LeaveGame(false);
return 1;
}
app.SetGameStarted(true);
g_NetworkManager.DoWork();
printf("Server ready on %s:%d\n", bindIp, port);
printf("Type 'help' for server commands.\n");
fflush(stdout);
C4JThread* consoleThread = new C4JThread(&HeadlessServerConsoleThreadProc, nullptr, "Server console", 128 * 1024);
consoleThread->Run();
MSG msg = { 0 };
while (WM_QUIT != msg.message && !app.m_bShutdown && !MinecraftServer::serverHalted())
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
continue;
}
app.UpdateTime();
ProfileManager.Tick();
StorageManager.Tick();
RenderManager.Tick();
ui.tick();
g_NetworkManager.DoWork();
app.HandleXuiActions();
Sleep(10);
}
printf("Stopping server...\n");
fflush(stdout);
app.m_bShutdown = true;
MinecraftServer::HaltServer();
g_NetworkManager.LeaveGame(false);
return 0;
}
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
@@ -1566,11 +1355,8 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
const Win64LaunchOptions launchOptions = ParseLaunchOptions();
ApplyScreenMode(launchOptions.screenMode);
// Ensure uid.dat exists from startup in client mode (before any multiplayer/login path).
if (!launchOptions.serverMode)
{
Win64Xuid::ResolvePersistentXuid();
}
// Ensure uid.dat exists from startup (before any multiplayer/login path).
Win64Xuid::ResolvePersistentXuid();
// If no username, let's fall back
if (g_Win64Username[0] == 0)
@@ -1651,7 +1437,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, launchOptions.serverMode ? SW_HIDE : nCmdShow))
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
@@ -1670,13 +1456,6 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
ToggleFullscreen();
}
if (launchOptions.serverMode)
{
const int serverResult = RunHeadlessServer();
CleanupDevice();
return serverResult;
}
#if 0
// Main message loop
MSG msg = {0};
@@ -1986,7 +1765,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
}
// F1 toggles the HUD
if (g_KBMInput.IsKeyPressed(VK_F1))
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_TOGGLE_HUD))
{
const int primaryPad = ProfileManager.GetPrimaryPad();
const unsigned char displayHud = app.GetGameSettings(primaryPad, eGameSetting_DisplayHUD);
@@ -1995,7 +1774,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
}
// F3 toggles onscreen debug info
if (g_KBMInput.IsKeyPressed(VK_F3))
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_DEBUG_INFO))
{
if (const Minecraft* pMinecraft = Minecraft::GetInstance())
{
@@ -2008,7 +1787,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
#ifdef _DEBUG_MENUS_ENABLED
// F6 Open debug console
if (g_KBMInput.IsKeyPressed(VK_F6))
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_DEBUG_CONSOLE))
{
static bool s_debugConsole = false;
s_debugConsole = !s_debugConsole;
@@ -2016,14 +1795,14 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
}
#endif
// F11 Toggle fullscreen
if (g_KBMInput.IsKeyPressed(VK_F11))
// toggle fullscreen
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_FULLSCREEN))
{
ToggleFullscreen();
}
// TAB opens game info menu. - Vvis :3 - Updated by detectiveren
if (g_KBMInput.IsKeyPressed(VK_TAB) && !ui.GetMenuDisplayed(0))
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_HOST_SETTINGS) && !ui.GetMenuDisplayed(0))
{
if (Minecraft* pMinecraft = Minecraft::GetInstance())
{
@@ -2035,7 +1814,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
}
// Open chat
if (g_KBMInput.IsKeyPressed('T') && app.GetGameStarted() && !ui.GetMenuDisplayed(0) && pMinecraft->screen == NULL)
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_CHAT) && app.GetGameStarted() && !ui.GetMenuDisplayed(0) && pMinecraft->screen == NULL)
{
g_KBMInput.ClearCharBuffer();
pMinecraft->setScreen(new ChatScreen());
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

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