Merge branch 'smartcmd:main' into main

This commit is contained in:
Cath0deGayTube 2026-03-15 00:38:35 -07:00 committed by GitHub
commit 04f93f5fc0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
148 changed files with 39364 additions and 432 deletions

160
.github/workflows/docker-nightly.yml vendored Normal file
View file

@ -0,0 +1,160 @@
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

View file

@ -1,15 +1,19 @@
name: Nightly Release name: Nightly Releases
on: on:
workflow_dispatch: workflow_dispatch:
push: push:
branches: branches:
- 'main' - 'main'
- 'feature/dedicated-server'
paths-ignore: paths-ignore:
- '.gitignore' - '.gitignore'
- '*.md' - '*.md'
- '.github/*.md' - '.github/*.md'
permissions:
contents: write
jobs: jobs:
build: build:
name: Build Windows64 name: Build Windows64
@ -28,13 +32,16 @@ jobs:
- name: Zip Build - name: Zip Build
run: 7z a -r LCEWindows64.zip ./x64/Release/* run: 7z a -r LCEWindows64.zip ./x64/Release/*
- name: Update release - name: Zip Dedicated Server Build
run: 7z a -r LCEServerWindows64.zip ./x64/Minecraft.Server/Release/*
- name: Update Client release
uses: andelf/nightly-release@main uses: andelf/nightly-release@main
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
tag_name: nightly tag_name: nightly
name: Nightly Release name: Nightly Client Release
body: | body: |
Requires at least Windows 7 and DirectX 11 compatible GPU to run. Compiled with MSVC v14.44.35207 in Release mode with Whole Program Optimization, as well as `/O2 /Ot /Oi /Ob3 /GF /fp:precise`. Requires at least Windows 7 and DirectX 11 compatible GPU to run. Compiled with MSVC v14.44.35207 in Release mode with Whole Program Optimization, as well as `/O2 /Ot /Oi /Ob3 /GF /fp:precise`.
@ -44,3 +51,19 @@ jobs:
LCEWindows64.zip LCEWindows64.zip
./x64/Release/Minecraft.Client.exe ./x64/Release/Minecraft.Client.exe
./x64/Release/Minecraft.Client.pdb ./x64/Release/Minecraft.Client.pdb
- name: Update Dedicated Server 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: |
LCEServerWindows64.zip
./x64/Minecraft.Server/Release/Minecraft.Server.exe
./x64/Minecraft.Server/Release/Minecraft.Server.pdb

9
.gitignore vendored
View file

@ -423,6 +423,12 @@ Minecraft.World/x64_Debug/
Minecraft.World/Release/ Minecraft.World/Release/
Minecraft.World/x64_Release/ Minecraft.World/x64_Release/
Minecraft.Server/x64/
Minecraft.Server/Debug/
Minecraft.Server/x64_Debug/
Minecraft.Server/Release/
Minecraft.Server/x64_Release/
build/* build/*
# Existing build output files # Existing build output files
@ -434,6 +440,9 @@ build/*
# Local saves # Local saves
Minecraft.Client/Saves/ Minecraft.Client/Saves/
tmp*/
_server_asset_probe/
server-data/
# Visual Studio Per-User Config # Visual Studio Per-User Config
*.user *.user
/out /out

View file

@ -92,6 +92,91 @@ target_link_libraries(MinecraftClient PRIVATE
> >
) )
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) if(CMAKE_HOST_WIN32)
message(STATUS "Starting redist copy...") message(STATUS "Starting redist copy...")
execute_process( execute_process(
@ -147,4 +232,13 @@ else()
message(FATAL_ERROR "Redist and asset copying is only supported on Windows (Robocopy) and Unix systems (rsync).") message(FATAL_ERROR "Redist and asset copying is only supported on Windows (Robocopy) and Unix systems (rsync).")
endif() endif()
set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT MinecraftClient) 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"
VERBATIM
)
set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT MinecraftServer)

View file

@ -3,7 +3,9 @@
## Visual Studio (`.sln`) ## Visual Studio (`.sln`)
1. Open `MinecraftConsoles.sln` in Visual Studio 2022. 1. Open `MinecraftConsoles.sln` in Visual Studio 2022.
2. Set `Minecraft.Client` as the Startup Project. 2. Set Startup Project:
- Client: `Minecraft.Client`
- Dedicated server: `Minecraft.Server`
3. Select configuration: 3. Select configuration:
- `Debug` (recommended), or - `Debug` (recommended), or
- `Release` - `Release`
@ -12,6 +14,17 @@
- `Build > Build Solution` (or `Ctrl+Shift+B`) - `Build > Build Solution` (or `Ctrl+Shift+B`)
- Start debugging with `F5`. - Start debugging with `F5`.
### Dedicated server debug arguments
- Default debugger arguments for `Minecraft.Server`:
- `-port 25565 -bind 0.0.0.0 -name DedicatedServer`
- You can override arguments in:
- `Project Properties > Debugging > Command Arguments`
- `Minecraft.Server` post-build copies only the dedicated-server asset set:
- `Common/Media/MediaWindows64.arc`
- `Common/res`
- `Windows64/GameHDD`
## CMake (Windows x64) ## CMake (Windows x64)
Configure (use your VS Community instance explicitly): Configure (use your VS Community instance explicitly):
@ -32,6 +45,18 @@ Build Release:
cmake --build build --config Release --target MinecraftClient cmake --build build --config Release --target MinecraftClient
``` ```
Build Dedicated Server (Debug):
```powershell
cmake --build build --config Debug --target MinecraftServer
```
Build Dedicated Server (Release):
```powershell
cmake --build build --config Release --target MinecraftServer
```
Run executable: Run executable:
```powershell ```powershell
@ -39,6 +64,13 @@ cd .\build\Debug
.\MinecraftClient.exe .\MinecraftClient.exe
``` ```
Run dedicated server:
```powershell
cd .\build\Debug
.\Minecraft.Server.exe -port 25565 -bind 0.0.0.0 -name DedicatedServer
```
Notes: Notes:
- The CMake build is Windows-only and x64-only. - 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. - 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.

View file

@ -149,8 +149,56 @@ bool ClientConnection::isPrimaryConnection() const
return g_NetworkManager.IsHost() || m_userIndex == ProfileManager.GetPrimaryPad(); return g_NetworkManager.IsHost() || m_userIndex == ProfileManager.GetPrimaryPad();
} }
ClientConnection* ClientConnection::findPrimaryConnection() const
{
if (level == nullptr) return nullptr;
int primaryPad = ProfileManager.GetPrimaryPad();
MultiPlayerLevel* mpLevel = (MultiPlayerLevel*)level;
for (ClientConnection* conn : mpLevel->connections)
{
if (conn != this && conn->m_userIndex == primaryPad)
return conn;
}
return nullptr;
}
bool ClientConnection::shouldProcessForEntity(int entityId) const
{
if (g_NetworkManager.IsHost()) return true;
if (m_userIndex == ProfileManager.GetPrimaryPad()) return true;
ClientConnection* primary = findPrimaryConnection();
if (primary == nullptr) return true;
return !primary->isTrackingEntity(entityId);
}
bool ClientConnection::shouldProcessForPosition(int blockX, int blockZ) const
{
if (g_NetworkManager.IsHost()) return true;
if (m_userIndex == ProfileManager.GetPrimaryPad()) return true;
ClientConnection* primary = findPrimaryConnection();
if (primary == nullptr) return true;
return !primary->m_visibleChunks.count(chunkKey(blockX >> 4, blockZ >> 4));
}
bool ClientConnection::anyOtherConnectionHasChunk(int x, int z) const
{
if (level == nullptr) return false;
MultiPlayerLevel* mpLevel = (MultiPlayerLevel*)level;
int64_t key = chunkKey(x, z);
for (ClientConnection* conn : mpLevel->connections)
{
if (conn != this && conn->m_visibleChunks.count(key))
return true;
}
return false;
}
ClientConnection::~ClientConnection() ClientConnection::~ClientConnection()
{ {
m_trackedEntityIds.clear();
m_visibleChunks.clear();
delete connection; delete connection;
delete random; delete random;
delete savedDataStorage; delete savedDataStorage;
@ -664,6 +712,7 @@ void ClientConnection::handleAddEntity(shared_ptr<AddEntityPacket> packet)
} }
e->entityId = packet->id; e->entityId = packet->id;
level->putEntity(packet->id, e); level->putEntity(packet->id, e);
m_trackedEntityIds.insert(packet->id);
if (packet->data > -1) // 4J - changed "no data" value to be -1, we can have a valid entity id of 0 if (packet->data > -1) // 4J - changed "no data" value to be -1, we can have a valid entity id of 0
{ {
@ -712,6 +761,7 @@ void ClientConnection::handleAddExperienceOrb(shared_ptr<AddExperienceOrbPacket>
e->xRot = 0; e->xRot = 0;
e->entityId = packet->id; e->entityId = packet->id;
level->putEntity(packet->id, e); level->putEntity(packet->id, e);
m_trackedEntityIds.insert(packet->id);
} }
void ClientConnection::handleAddGlobalEntity(shared_ptr<AddGlobalEntityPacket> packet) void ClientConnection::handleAddGlobalEntity(shared_ptr<AddGlobalEntityPacket> packet)
@ -738,13 +788,13 @@ void ClientConnection::handleAddPainting(shared_ptr<AddPaintingPacket> packet)
{ {
shared_ptr<Painting> painting = std::make_shared<Painting>(level, packet->x, packet->y, packet->z, packet->dir, packet->motive); shared_ptr<Painting> painting = std::make_shared<Painting>(level, packet->x, packet->y, packet->z, packet->dir, packet->motive);
level->putEntity(packet->id, painting); level->putEntity(packet->id, painting);
m_trackedEntityIds.insert(packet->id);
} }
void ClientConnection::handleSetEntityMotion(shared_ptr<SetEntityMotionPacket> packet) void ClientConnection::handleSetEntityMotion(shared_ptr<SetEntityMotionPacket> packet)
{ {
if (!isPrimaryConnection()) if (!shouldProcessForEntity(packet->id))
{ {
// Secondary connection: only accept motion for our own local player (knockback)
if (minecraft->localplayers[m_userIndex] == NULL || if (minecraft->localplayers[m_userIndex] == NULL ||
packet->id != minecraft->localplayers[m_userIndex]->entityId) packet->id != minecraft->localplayers[m_userIndex]->entityId)
return; return;
@ -939,6 +989,7 @@ void ClientConnection::handleAddPlayer(shared_ptr<AddPlayerPacket> packet)
app.DebugPrintf("Custom cape for player %ls is %ls\n",player->name.c_str(),player->customTextureUrl2.c_str()); app.DebugPrintf("Custom cape for player %ls is %ls\n",player->name.c_str(),player->customTextureUrl2.c_str());
level->putEntity(packet->id, player); level->putEntity(packet->id, player);
m_trackedEntityIds.insert(packet->id);
vector<shared_ptr<SynchedEntityData::DataItem> > *unpackedData = packet->getUnpackedData(); vector<shared_ptr<SynchedEntityData::DataItem> > *unpackedData = packet->getUnpackedData();
if (unpackedData != nullptr) if (unpackedData != nullptr)
@ -979,7 +1030,7 @@ void ClientConnection::handleSetCarriedItem(shared_ptr<SetCarriedItemPacket> pac
void ClientConnection::handleMoveEntity(shared_ptr<MoveEntityPacket> packet) void ClientConnection::handleMoveEntity(shared_ptr<MoveEntityPacket> packet)
{ {
if (!isPrimaryConnection()) return; if (!shouldProcessForEntity(packet->id)) return;
shared_ptr<Entity> e = getEntity(packet->id); shared_ptr<Entity> e = getEntity(packet->id);
if (e == nullptr) return; if (e == nullptr) return;
e->xp += packet->xa; e->xp += packet->xa;
@ -1009,7 +1060,7 @@ void ClientConnection::handleRotateMob(shared_ptr<RotateHeadPacket> packet)
void ClientConnection::handleMoveEntitySmall(shared_ptr<MoveEntityPacketSmall> packet) void ClientConnection::handleMoveEntitySmall(shared_ptr<MoveEntityPacketSmall> packet)
{ {
if (!isPrimaryConnection()) return; if (!shouldProcessForEntity(packet->id)) return;
shared_ptr<Entity> e = getEntity(packet->id); shared_ptr<Entity> e = getEntity(packet->id);
if (e == nullptr) return; if (e == nullptr) return;
e->xp += packet->xa; e->xp += packet->xa;
@ -1068,6 +1119,7 @@ void ClientConnection::handleRemoveEntity(shared_ptr<RemoveEntitiesPacket> packe
#endif #endif
for (int i = 0; i < packet->ids.length; i++) for (int i = 0; i < packet->ids.length; i++)
{ {
m_trackedEntityIds.erase(packet->ids[i]);
level->removeEntity(packet->ids[i]); level->removeEntity(packet->ids[i]);
} }
} }
@ -1136,19 +1188,35 @@ void ClientConnection::handleChunkVisibilityArea(shared_ptr<ChunkVisibilityAreaP
{ {
if (level == NULL) return; if (level == NULL) return;
for(int z = packet->m_minZ; z <= packet->m_maxZ; ++z) for(int z = packet->m_minZ; z <= packet->m_maxZ; ++z)
{
for(int x = packet->m_minX; x <= packet->m_maxX; ++x) for(int x = packet->m_minX; x <= packet->m_maxX; ++x)
{
m_visibleChunks.insert(chunkKey(x, z));
level->setChunkVisible(x, z, true); level->setChunkVisible(x, z, true);
}
}
} }
void ClientConnection::handleChunkVisibility(shared_ptr<ChunkVisibilityPacket> packet) void ClientConnection::handleChunkVisibility(shared_ptr<ChunkVisibilityPacket> packet)
{ {
if (level == NULL) return; if (level == NULL) return;
level->setChunkVisible(packet->x, packet->z, packet->visible); if (packet->visible)
{
m_visibleChunks.insert(chunkKey(packet->x, packet->z));
level->setChunkVisible(packet->x, packet->z, true);
}
else
{
m_visibleChunks.erase(chunkKey(packet->x, packet->z));
if (!anyOtherConnectionHasChunk(packet->x, packet->z))
{
level->setChunkVisible(packet->x, packet->z, false);
}
}
} }
void ClientConnection::handleChunkTilesUpdate(shared_ptr<ChunkTilesUpdatePacket> packet) void ClientConnection::handleChunkTilesUpdate(shared_ptr<ChunkTilesUpdatePacket> packet)
{ {
if (!isPrimaryConnection()) return;
// 4J - changed to encode level in packet // 4J - changed to encode level in packet
MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->levels[packet->levelIdx]; MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->levels[packet->levelIdx];
if( dimensionLevel ) if( dimensionLevel )
@ -1218,7 +1286,6 @@ void ClientConnection::handleChunkTilesUpdate(shared_ptr<ChunkTilesUpdatePacket>
void ClientConnection::handleBlockRegionUpdate(shared_ptr<BlockRegionUpdatePacket> packet) void ClientConnection::handleBlockRegionUpdate(shared_ptr<BlockRegionUpdatePacket> packet)
{ {
if (!isPrimaryConnection()) return;
// 4J - changed to encode level in packet // 4J - changed to encode level in packet
MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->levels[packet->levelIdx]; MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->levels[packet->levelIdx];
if( dimensionLevel ) if( dimensionLevel )
@ -1279,7 +1346,6 @@ void ClientConnection::handleBlockRegionUpdate(shared_ptr<BlockRegionUpdatePacke
void ClientConnection::handleTileUpdate(shared_ptr<TileUpdatePacket> packet) void ClientConnection::handleTileUpdate(shared_ptr<TileUpdatePacket> packet)
{ {
if (!isPrimaryConnection()) return;
// 4J added - using a block of 255 to signify that this is a packet for destroying a tile, where we need to inform the level renderer that we are about to do so. // 4J added - using a block of 255 to signify that this is a packet for destroying a tile, where we need to inform the level renderer that we are about to do so.
// This is used in creative mode as the point where a tile is first destroyed at the client end of things. Packets formed like this are potentially sent from // This is used in creative mode as the point where a tile is first destroyed at the client end of things. Packets formed like this are potentially sent from
// ServerPlayerGameMode::destroyBlock // ServerPlayerGameMode::destroyBlock
@ -1394,7 +1460,7 @@ void ClientConnection::send(shared_ptr<Packet> packet)
void ClientConnection::handleTakeItemEntity(shared_ptr<TakeItemEntityPacket> packet) void ClientConnection::handleTakeItemEntity(shared_ptr<TakeItemEntityPacket> packet)
{ {
if (!isPrimaryConnection()) return; if (!shouldProcessForEntity(packet->itemId)) return;
shared_ptr<Entity> from = getEntity(packet->itemId); shared_ptr<Entity> from = getEntity(packet->itemId);
shared_ptr<LivingEntity> to = dynamic_pointer_cast<LivingEntity>(getEntity(packet->playerId)); shared_ptr<LivingEntity> to = dynamic_pointer_cast<LivingEntity>(getEntity(packet->playerId));
@ -2414,6 +2480,8 @@ void ClientConnection::close()
// If it's already done, then we don't need to do anything here. And in fact trying to do something could cause a crash // If it's already done, then we don't need to do anything here. And in fact trying to do something could cause a crash
if(done) return; if(done) return;
done = true; done = true;
m_trackedEntityIds.clear();
m_visibleChunks.clear();
connection->flush(); connection->flush();
connection->close(DisconnectPacket::eDisconnect_Closed); connection->close(DisconnectPacket::eDisconnect_Closed);
} }
@ -2453,6 +2521,7 @@ void ClientConnection::handleAddMob(shared_ptr<AddMobPacket> packet)
mob->yd = packet->yd / 8000.0f; mob->yd = packet->yd / 8000.0f;
mob->zd = packet->zd / 8000.0f; mob->zd = packet->zd / 8000.0f;
level->putEntity(packet->id, mob); level->putEntity(packet->id, mob);
m_trackedEntityIds.insert(packet->id);
vector<shared_ptr<SynchedEntityData::DataItem> > *unpackedData = packet->getUnpackedData(); vector<shared_ptr<SynchedEntityData::DataItem> > *unpackedData = packet->getUnpackedData();
if (unpackedData != nullptr) if (unpackedData != nullptr)
@ -2792,6 +2861,9 @@ void ClientConnection::handleRespawn(shared_ptr<RespawnPacket> packet)
// so it doesn't leak into the new dimension // so it doesn't leak into the new dimension
level->playStreamingMusic(L"", 0, 0, 0); level->playStreamingMusic(L"", 0, 0, 0);
m_trackedEntityIds.clear();
m_visibleChunks.clear();
// Remove client connection from this level // Remove client connection from this level
level->removeClientConnection(this, false); level->removeClientConnection(this, false);
@ -2899,8 +2971,7 @@ void ClientConnection::handleRespawn(shared_ptr<RespawnPacket> packet)
void ClientConnection::handleExplosion(shared_ptr<ExplodePacket> packet) void ClientConnection::handleExplosion(shared_ptr<ExplodePacket> packet)
{ {
// World modification (block destruction) must only happen once if (shouldProcessForPosition((int)packet->x, (int)packet->z))
if (isPrimaryConnection())
{ {
if(!packet->m_bKnockbackOnly) if(!packet->m_bKnockbackOnly)
{ {
@ -3244,7 +3315,6 @@ void ClientConnection::handleTileEditorOpen(shared_ptr<TileEditorOpenPacket> pac
void ClientConnection::handleSignUpdate(shared_ptr<SignUpdatePacket> packet) void ClientConnection::handleSignUpdate(shared_ptr<SignUpdatePacket> packet)
{ {
if (!isPrimaryConnection()) return;
app.DebugPrintf("ClientConnection::handleSignUpdate - "); app.DebugPrintf("ClientConnection::handleSignUpdate - ");
if (minecraft->level->hasChunkAt(packet->x, packet->y, packet->z)) if (minecraft->level->hasChunkAt(packet->x, packet->y, packet->z))
{ {
@ -3278,7 +3348,6 @@ void ClientConnection::handleSignUpdate(shared_ptr<SignUpdatePacket> packet)
void ClientConnection::handleTileEntityData(shared_ptr<TileEntityDataPacket> packet) void ClientConnection::handleTileEntityData(shared_ptr<TileEntityDataPacket> packet)
{ {
if (!isPrimaryConnection()) return;
if (minecraft->level->hasChunkAt(packet->x, packet->y, packet->z)) if (minecraft->level->hasChunkAt(packet->x, packet->y, packet->z))
{ {
shared_ptr<TileEntity> te = minecraft->level->getTileEntity(packet->x, packet->y, packet->z); shared_ptr<TileEntity> te = minecraft->level->getTileEntity(packet->x, packet->y, packet->z);
@ -3331,7 +3400,6 @@ void ClientConnection::handleContainerClose(shared_ptr<ContainerClosePacket> pac
void ClientConnection::handleTileEvent(shared_ptr<TileEventPacket> packet) void ClientConnection::handleTileEvent(shared_ptr<TileEventPacket> packet)
{ {
if (!isPrimaryConnection()) return;
PIXBeginNamedEvent(0,"Handle tile event\n"); PIXBeginNamedEvent(0,"Handle tile event\n");
minecraft->level->tileEvent(packet->x, packet->y, packet->z, packet->tile, packet->b0, packet->b1); minecraft->level->tileEvent(packet->x, packet->y, packet->z, packet->tile, packet->b0, packet->b1);
PIXEndNamedEvent(); PIXEndNamedEvent();
@ -3339,7 +3407,6 @@ void ClientConnection::handleTileEvent(shared_ptr<TileEventPacket> packet)
void ClientConnection::handleTileDestruction(shared_ptr<TileDestructionPacket> packet) void ClientConnection::handleTileDestruction(shared_ptr<TileDestructionPacket> packet)
{ {
if (!isPrimaryConnection()) return;
minecraft->level->destroyTileProgress(packet->getEntityId(), packet->getX(), packet->getY(), packet->getZ(), packet->getState()); minecraft->level->destroyTileProgress(packet->getEntityId(), packet->getX(), packet->getY(), packet->getZ(), packet->getState());
} }
@ -3421,7 +3488,6 @@ void ClientConnection::handleGameEvent(shared_ptr<GameEventPacket> gameEventPack
void ClientConnection::handleComplexItemData(shared_ptr<ComplexItemDataPacket> packet) void ClientConnection::handleComplexItemData(shared_ptr<ComplexItemDataPacket> packet)
{ {
if (!isPrimaryConnection()) return;
if (packet->itemType == Item::map->id) if (packet->itemType == Item::map->id)
{ {
MapItem::getSavedData(packet->itemId, minecraft->level)->handleComplexItemData(packet->data); MapItem::getSavedData(packet->itemId, minecraft->level)->handleComplexItemData(packet->data);
@ -3436,7 +3502,7 @@ void ClientConnection::handleComplexItemData(shared_ptr<ComplexItemDataPacket> p
void ClientConnection::handleLevelEvent(shared_ptr<LevelEventPacket> packet) void ClientConnection::handleLevelEvent(shared_ptr<LevelEventPacket> packet)
{ {
if (!isPrimaryConnection()) return; if (!shouldProcessForPosition(packet->x, packet->z)) return;
if (packet->type == LevelEvent::SOUND_DRAGON_DEATH) if (packet->type == LevelEvent::SOUND_DRAGON_DEATH)
{ {
for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i)
@ -3456,8 +3522,6 @@ void ClientConnection::handleLevelEvent(shared_ptr<LevelEventPacket> packet)
{ {
minecraft->level->levelEvent(packet->type, packet->x, packet->y, packet->z, packet->data); minecraft->level->levelEvent(packet->type, packet->x, packet->y, packet->z, packet->data);
} }
minecraft->level->levelEvent(packet->type, packet->x, packet->y, packet->z, packet->data);
} }
void ClientConnection::handleAwardStat(shared_ptr<AwardStatPacket> packet) void ClientConnection::handleAwardStat(shared_ptr<AwardStatPacket> packet)
@ -3660,7 +3724,6 @@ void ClientConnection::handlePlayerAbilities(shared_ptr<PlayerAbilitiesPacket> p
void ClientConnection::handleSoundEvent(shared_ptr<LevelSoundPacket> packet) void ClientConnection::handleSoundEvent(shared_ptr<LevelSoundPacket> packet)
{ {
if (!isPrimaryConnection()) return;
minecraft->level->playLocalSound(packet->getX(), packet->getY(), packet->getZ(), packet->getSound(), packet->getVolume(), packet->getPitch(), false); minecraft->level->playLocalSound(packet->getX(), packet->getY(), packet->getZ(), packet->getSound(), packet->getVolume(), packet->getPitch(), false);
} }
@ -3973,7 +4036,6 @@ void ClientConnection::handleSetPlayerTeamPacket(shared_ptr<SetPlayerTeamPacket>
void ClientConnection::handleParticleEvent(shared_ptr<LevelParticlesPacket> packet) void ClientConnection::handleParticleEvent(shared_ptr<LevelParticlesPacket> packet)
{ {
if (!isPrimaryConnection()) return;
for (int i = 0; i < packet->getCount(); i++) for (int i = 0; i < packet->getCount(); i++)
{ {
double xVarience = random->nextGaussian() * packet->getXDist(); double xVarience = random->nextGaussian() * packet->getXDist();

View file

@ -1,4 +1,5 @@
#pragma once #pragma once
#include <unordered_set>
#include "..\Minecraft.World\net.minecraft.network.h" #include "..\Minecraft.World\net.minecraft.network.h"
class Minecraft; class Minecraft;
class MultiPlayerLevel; class MultiPlayerLevel;
@ -44,6 +45,20 @@ public:
private: private:
DWORD m_userIndex; // 4J Added DWORD m_userIndex; // 4J Added
bool isPrimaryConnection() const; bool isPrimaryConnection() const;
std::unordered_set<int> m_trackedEntityIds;
std::unordered_set<int64_t> m_visibleChunks;
static int64_t chunkKey(int x, int z) { return ((int64_t)x << 32) | ((int64_t)z & 0xFFFFFFFF); }
ClientConnection* findPrimaryConnection() const;
bool shouldProcessForEntity(int entityId) const;
bool shouldProcessForPosition(int blockX, int blockZ) const;
bool anyOtherConnectionHasChunk(int x, int z) const;
public:
bool isTrackingEntity(int entityId) const { return m_trackedEntityIds.count(entityId) > 0; }
public: public:
SavedDataStorage *savedDataStorage; SavedDataStorage *savedDataStorage;
ClientConnection(Minecraft *minecraft, const wstring& ip, int port); ClientConnection(Minecraft *minecraft, const wstring& ip, int port);

View file

@ -1,15 +1,26 @@
#include "stdafx.h" #include "stdafx.h"
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
#include "..\..\Minecraft.Server\ServerLogManager.h"
#endif
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
// Name: DebugSpewV() // Name: DebugSpewV()
// Desc: Internal helper function // Desc: Internal helper function
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
#ifndef _CONTENT_PACKAGE #ifndef _CONTENT_PACKAGE
static VOID DebugSpewV( const CHAR* strFormat, const va_list pArgList ) static VOID DebugSpewV( const CHAR* strFormat, va_list pArgList )
{ {
#if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ #if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__
assert(0); assert(0);
#else #else
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
// Dedicated server routes legacy debug spew through ServerLogger to preserve CLI prompt handling.
if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs())
{
ServerRuntime::ServerLogManager::ForwardClientDebugSpewLogV(strFormat, pArgList);
return;
}
#endif
CHAR str[2048]; CHAR str[2048];
// Use the secure CRT to avoid buffer overruns. Specify a count of // Use the secure CRT to avoid buffer overruns. Specify a count of
// _TRUNCATE so that too long strings will be silently truncated // _TRUNCATE so that too long strings will be silently truncated
@ -37,4 +48,3 @@ VOID CDECL DebugPrintf( const CHAR* strFormat, ... )
va_end( pArgList ); va_end( pArgList );
#endif #endif
} }

View file

@ -38,6 +38,9 @@
#include "GameRules\ConsoleSchematicFile.h" #include "GameRules\ConsoleSchematicFile.h"
#include "..\User.h" #include "..\User.h"
#include "..\..\Minecraft.World\LevelData.h" #include "..\..\Minecraft.World\LevelData.h"
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
#include "..\..\Minecraft.Server\ServerLogManager.h"
#endif
#include "..\..\Minecraft.World\net.minecraft.world.entity.player.h" #include "..\..\Minecraft.World\net.minecraft.world.entity.player.h"
#include "..\EntityRenderDispatcher.h" #include "..\EntityRenderDispatcher.h"
#include "..\..\Minecraft.World\compression.h" #include "..\..\Minecraft.World\compression.h"
@ -240,9 +243,18 @@ void CMinecraftApp::DebugPrintf(const char *szFormat, ...)
{ {
#ifndef _FINAL_BUILD #ifndef _FINAL_BUILD
char buf[1024];
va_list ap; va_list ap;
va_start(ap, szFormat); va_start(ap, szFormat);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
// Dedicated server routes client debug spew through ServerLogger so CLI output stays prompt-safe.
if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs())
{
ServerRuntime::ServerLogManager::ForwardClientAppDebugLogV(szFormat, ap);
va_end(ap);
return;
}
#endif
char buf[1024];
vsnprintf(buf, sizeof(buf), szFormat, ap); vsnprintf(buf, sizeof(buf), szFormat, ap);
va_end(ap); va_end(ap);
OutputDebugStringA(buf); OutputDebugStringA(buf);
@ -255,9 +267,18 @@ void CMinecraftApp::DebugPrintf(int user, const char *szFormat, ...)
#ifndef _FINAL_BUILD #ifndef _FINAL_BUILD
if(user == USER_NONE) if(user == USER_NONE)
return; return;
char buf[1024];
va_list ap; va_list ap;
va_start(ap, szFormat); va_start(ap, szFormat);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
// Dedicated server routes client debug spew through ServerLogger so CLI output stays prompt-safe.
if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs())
{
ServerRuntime::ServerLogManager::ForwardClientUserDebugLogV(user, szFormat, ap);
va_end(ap);
return;
}
#endif
char buf[1024];
vsnprintf(buf, sizeof(buf), szFormat, ap); vsnprintf(buf, sizeof(buf), szFormat, ap);
va_end(ap); va_end(ap);
#ifdef __PS3__ #ifdef __PS3__

View file

@ -200,10 +200,12 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
#endif #endif
int64_t seed = 0; int64_t seed = 0;
bool dedicatedNoLocalHostPlayer = false;
if (lpParameter != nullptr) if (lpParameter != nullptr)
{ {
NetworkGameInitData *param = static_cast<NetworkGameInitData *>(lpParameter); NetworkGameInitData *param = static_cast<NetworkGameInitData *>(lpParameter);
seed = param->seed; seed = param->seed;
dedicatedNoLocalHostPlayer = param->dedicatedNoLocalHostPlayer;
app.setLevelGenerationOptions(param->levelGen); app.setLevelGenerationOptions(param->levelGen);
if(param->levelGen != nullptr) if(param->levelGen != nullptr)
@ -359,9 +361,19 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
// PRIMARY PLAYER // PRIMARY PLAYER
vector<ClientConnection *> createdConnections; vector<ClientConnection *> createdConnections;
ClientConnection *connection; ClientConnection *connection = nullptr;
if( g_NetworkManager.IsHost() ) if( g_NetworkManager.IsHost() && dedicatedNoLocalHostPlayer )
{
app.DebugPrintf("Dedicated server mode: skipping local host client connection\n");
// Keep telemetry behavior consistent with the host path.
INT multiplayerInstanceId = TelemetryManager->GenerateMultiplayerInstanceId();
TelemetryManager->SetMultiplayerInstanceId(multiplayerInstanceId);
app.SetGameMode( eMode_Multiplayer );
}
else if( g_NetworkManager.IsHost() )
{ {
connection = new ClientConnection(minecraft, nullptr); connection = new ClientConnection(minecraft, nullptr);
} }
@ -390,6 +402,8 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
connection = new ClientConnection(minecraft, socket); connection = new ClientConnection(minecraft, socket);
} }
if (connection != nullptr)
{
if( !connection->createdOk ) if( !connection->createdOk )
{ {
assert(false); assert(false);
@ -541,6 +555,7 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
MinecraftServer::HaltServer(); MinecraftServer::HaltServer();
return false; return false;
} }
}
if(g_NetworkManager.IsLeavingGame() || !IsInSession() ) if(g_NetworkManager.IsLeavingGame() || !IsInSession() )

View file

@ -240,7 +240,13 @@ void CPlatformNetworkManagerStub::DoWork()
qnetPlayer->m_resolvedXuid = INVALID_XUID; qnetPlayer->m_resolvedXuid = INVALID_XUID;
qnetPlayer->m_gamertag[0] = 0; qnetPlayer->m_gamertag[0] = 0;
qnetPlayer->SetCustomDataValue(0); qnetPlayer->SetCustomDataValue(0);
if (IQNet::s_playerCount > 1) // Recalculate s_playerCount as the highest active slot + 1.
// A blind decrement would hide players at higher-indexed slots when a
// lower-indexed player disconnects first: GetPlayerBySmallId scans
// [0, s_playerCount) so any slot at or above the decremented count
// becomes invisible, causing its disconnect to be missed (ghost player).
while (IQNet::s_playerCount > 1 &&
IQNet::m_player[IQNet::s_playerCount - 1].GetCustomDataValue() == 0)
IQNet::s_playerCount--; IQNet::s_playerCount--;
} }
// NOTE: Do NOT call PushFreeSmallId here. The old PlayerConnection's // NOTE: Do NOT call PushFreeSmallId here. The old PlayerConnection's
@ -257,6 +263,25 @@ void CPlatformNetworkManagerStub::DoWork()
SystemFlagRemoveBySmallId(disconnectedSmallId); SystemFlagRemoveBySmallId(disconnectedSmallId);
} }
} }
// Client-side host disconnect detection:
// if TCP is gone, propagate through normal network-disconnect flow so UI returns to menus.
// The processing from the Xbox version will be reused.
if (_iQNetStubState == QNET_STATE_GAME_PLAY && !m_pIQNet->IsHost() && !m_bLeavingGame)
{
if (!WinsockNetLayer::IsConnected())
{
if (!m_bLeaveGameOnTick)
{
m_bLeaveGameOnTick = true;
g_NetworkManager.HandleDisconnect(false);
}
}
else
{
m_bLeaveGameOnTick = false;
}
}
#endif #endif
} }
@ -356,6 +381,7 @@ bool CPlatformNetworkManagerStub::LeaveGame(bool bMigrateHost)
if( m_bLeavingGame ) return true; if( m_bLeavingGame ) return true;
m_bLeavingGame = true; m_bLeavingGame = true;
m_bLeaveGameOnTick = false;
#ifdef _WINDOWS64 #ifdef _WINDOWS64
WinsockNetLayer::StopAdvertising(); WinsockNetLayer::StopAdvertising();
@ -404,6 +430,7 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame,
localUsersMask |= GetLocalPlayerMask( g_NetworkManager.GetPrimaryPad() ); localUsersMask |= GetLocalPlayerMask( g_NetworkManager.GetPrimaryPad() );
m_bLeavingGame = false; m_bLeavingGame = false;
m_bLeaveGameOnTick = false;
m_pIQNet->HostGame(); m_pIQNet->HostGame();
@ -432,11 +459,25 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame,
WinsockNetLayer::HostGame(port, bindIp); WinsockNetLayer::HostGame(port, bindIp);
if (WinsockNetLayer::IsActive()) if (WinsockNetLayer::IsActive())
{
// For Dedicated Server, refer to `lan-advertise` in `server.properties`
bool enableLanAdvertising = true;
if (g_Win64DedicatedServer)
{
enableLanAdvertising = g_Win64DedicatedServerLanAdvertise;
}
if (enableLanAdvertising)
{ {
const wchar_t* hostName = IQNet::m_player[0].m_gamertag; const wchar_t* hostName = IQNet::m_player[0].m_gamertag;
unsigned int settings = app.GetGameHostOption(eGameHostOption_All); unsigned int settings = app.GetGameHostOption(eGameHostOption_All);
WinsockNetLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION); WinsockNetLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION);
} }
else
{
WinsockNetLayer::StopAdvertising();
}
}
#endif #endif
//#endif //#endif
} }
@ -463,6 +504,7 @@ int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo* searchResult, int l
return CGameNetworkManager::JOINGAME_FAIL_GENERAL; return CGameNetworkManager::JOINGAME_FAIL_GENERAL;
m_bLeavingGame = false; m_bLeavingGame = false;
m_bLeaveGameOnTick = false;
IQNet::s_isHosting = false; IQNet::s_isHosting = false;
m_pIQNet->ClientJoinGame(); m_pIQNet->ClientJoinGame();

View file

@ -93,18 +93,22 @@ void UIComponent_Tooltips::updateSafeZone()
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
safeTop = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeLeft = getSafeZoneHalfWidth(); safeLeft = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
safeBottom = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeLeft = getSafeZoneHalfWidth(); safeLeft = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
safeLeft = getSafeZoneHalfWidth(); safeTop = getSafeZoneHalfHeight();
safeBottom = getSafeZoneHalfHeight(); safeBottom = getSafeZoneHalfHeight();
safeLeft = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT:
safeRight = getSafeZoneHalfWidth(); safeTop = getSafeZoneHalfHeight();
safeBottom = getSafeZoneHalfHeight(); safeBottom = getSafeZoneHalfHeight();
break; break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
safeTop = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
@ -112,22 +116,22 @@ void UIComponent_Tooltips::updateSafeZone()
break; break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
safeTop = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeRight = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
safeBottom = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeLeft = getSafeZoneHalfWidth(); safeLeft = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
safeBottom = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeRight = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_FULLSCREEN: case C4JRender::VIEWPORT_TYPE_FULLSCREEN:
default: default:
safeTop = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeBottom = getSafeZoneHalfHeight(); safeBottom = getSafeZoneHalfHeight();
safeLeft = getSafeZoneHalfWidth(); safeLeft = getSafeZoneHalfWidth();
safeRight = getSafeZoneHalfWidth();
break; break;
} }
setSafeZone(safeTop, safeBottom, safeLeft, safeRight); setSafeZone(safeTop, safeBottom, safeLeft, safeRight);

View file

@ -1,6 +1,7 @@
#include "stdafx.h" #include "stdafx.h"
#include "UI.h" #include "UI.h"
#include "UIComponent_TutorialPopup.h" #include "UIComponent_TutorialPopup.h"
#include "UISplitScreenHelpers.h"
#include "..\..\Common\Tutorial\Tutorial.h" #include "..\..\Common\Tutorial\Tutorial.h"
#include "..\..\..\Minecraft.World\StringHelpers.h" #include "..\..\..\Minecraft.World\StringHelpers.h"
#include "..\..\MultiplayerLocalPlayer.h" #include "..\..\MultiplayerLocalPlayer.h"
@ -474,27 +475,17 @@ void UIComponent_TutorialPopup::render(S32 width, S32 height, C4JRender::eViewpo
{ {
if(viewport != C4JRender::VIEWPORT_TYPE_FULLSCREEN) if(viewport != C4JRender::VIEWPORT_TYPE_FULLSCREEN)
{ {
S32 xPos = 0; // Derive the viewport origin and fit a 16:9 box inside it (same as UIScene::render),
S32 yPos = 0; // then apply safezone nudges so the popup stays clear of screen edges.
switch( viewport ) F32 originX, originY, viewW, viewH;
{ GetViewportRect(ui.getScreenWidth(), ui.getScreenHeight(), viewport, originX, originY, viewW, viewH);
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
xPos = static_cast<S32>(ui.getScreenWidth() / 2); S32 fitW, fitH, offsetX, offsetY;
yPos = static_cast<S32>(ui.getScreenHeight() / 2); Fit16x9(viewW, viewH, fitW, fitH, offsetX, offsetY);
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: S32 xPos = static_cast<S32>(originX) + offsetX;
yPos = static_cast<S32>(ui.getScreenHeight() / 2); S32 yPos = static_cast<S32>(originY) + offsetY;
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
xPos = static_cast<S32>(ui.getScreenWidth() / 2);
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
xPos = static_cast<S32>(ui.getScreenWidth() / 2);
yPos = static_cast<S32>(ui.getScreenHeight() / 2);
break;
}
//Adjust for safezone //Adjust for safezone
switch( viewport ) switch( viewport )
{ {
@ -505,6 +496,7 @@ void UIComponent_TutorialPopup::render(S32 width, S32 height, C4JRender::eViewpo
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
yPos += getSafeZoneHalfHeight(); yPos += getSafeZoneHalfHeight();
break; break;
default: break;
} }
switch( viewport ) switch( viewport )
{ {
@ -515,10 +507,11 @@ void UIComponent_TutorialPopup::render(S32 width, S32 height, C4JRender::eViewpo
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
xPos -= getSafeZoneHalfWidth(); xPos -= getSafeZoneHalfWidth();
break; break;
default: break;
} }
ui.setupRenderPosition(xPos, yPos); ui.setupRenderPosition(xPos, yPos);
IggyPlayerSetDisplaySize( getMovie(), width, height ); IggyPlayerSetDisplaySize( getMovie(), fitW, fitH );
IggyPlayerDraw( getMovie() ); IggyPlayerDraw( getMovie() );
} }
else else

View file

@ -172,15 +172,22 @@ void UIScene::updateSafeZone()
{ {
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
safeTop = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeLeft = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
safeBottom = getSafeZoneHalfHeight(); // safeTop mirrors SPLIT_TOP for visual symmetry. safeBottom omitted.
safeTop = getSafeZoneHalfHeight();
safeLeft = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
safeTop = getSafeZoneHalfHeight();
safeLeft = getSafeZoneHalfWidth(); safeLeft = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT:
safeRight = getSafeZoneHalfWidth(); safeTop = getSafeZoneHalfHeight();
break; break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
safeTop = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
@ -188,22 +195,22 @@ void UIScene::updateSafeZone()
break; break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
safeTop = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeRight = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
safeBottom = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeLeft = getSafeZoneHalfWidth(); safeLeft = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
safeBottom = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeRight = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_FULLSCREEN: case C4JRender::VIEWPORT_TYPE_FULLSCREEN:
default: default:
safeTop = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeBottom = getSafeZoneHalfHeight(); safeBottom = getSafeZoneHalfHeight();
safeLeft = getSafeZoneHalfWidth(); safeLeft = getSafeZoneHalfWidth();
safeRight = getSafeZoneHalfWidth();
break; break;
} }
setSafeZone(safeTop, safeBottom, safeLeft, safeRight); setSafeZone(safeTop, safeBottom, safeLeft, safeRight);

View file

@ -278,7 +278,7 @@ void UIScene_FullscreenProgress::handleInput(int iPad, int key, bool repeat, boo
#ifdef __ORBIS__ #ifdef __ORBIS__
case ACTION_MENU_TOUCHPAD_PRESS: case ACTION_MENU_TOUCHPAD_PRESS:
#endif #endif
if(pressed) if(pressed && m_threadCompleted)
{ {
sendInputToMovie(key, repeat, pressed, released); sendInputToMovie(key, repeat, pressed, released);
} }
@ -292,6 +292,7 @@ void UIScene_FullscreenProgress::handleInput(int iPad, int key, bool repeat, boo
} }
break; break;
} }
handled = true;
} }
} }

View file

@ -65,22 +65,26 @@ void UIScene_HUD::updateSafeZone()
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
safeTop = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeLeft = getSafeZoneHalfWidth(); safeLeft = getSafeZoneHalfWidth();
safeRight = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
safeBottom = getSafeZoneHalfHeight(); // safeTop mirrors SPLIT_TOP so both players have the same vertical inset
// from their viewport's top edge (split divider), keeping GUI symmetrical.
// safeBottom is intentionally omitted: it would shift m_Hud.y upward in
// ActionScript, placing the hotbar too high relative to SPLIT_TOP.
safeTop = getSafeZoneHalfHeight();
safeLeft = getSafeZoneHalfWidth(); safeLeft = getSafeZoneHalfWidth();
safeRight = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
safeLeft = getSafeZoneHalfWidth();
safeTop = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeBottom = getSafeZoneHalfHeight(); safeBottom = getSafeZoneHalfHeight();
safeLeft = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT:
safeRight = getSafeZoneHalfWidth();
safeTop = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeBottom = getSafeZoneHalfHeight(); safeBottom = getSafeZoneHalfHeight();
break; break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
safeTop = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
@ -88,22 +92,22 @@ void UIScene_HUD::updateSafeZone()
break; break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
safeTop = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeRight = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
safeBottom = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeLeft = getSafeZoneHalfWidth(); safeLeft = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
safeBottom = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeRight = getSafeZoneHalfWidth();
break; break;
case C4JRender::VIEWPORT_TYPE_FULLSCREEN: case C4JRender::VIEWPORT_TYPE_FULLSCREEN:
default: default:
safeTop = getSafeZoneHalfHeight(); safeTop = getSafeZoneHalfHeight();
safeBottom = getSafeZoneHalfHeight(); safeBottom = getSafeZoneHalfHeight();
safeLeft = getSafeZoneHalfWidth(); safeLeft = getSafeZoneHalfWidth();
safeRight = getSafeZoneHalfWidth();
break; break;
} }
setSafeZone(safeTop, safeBottom, safeLeft, safeRight); setSafeZone(safeTop, safeBottom, safeLeft, safeRight);
@ -734,7 +738,7 @@ void UIScene_HUD::render(S32 width, S32 height, C4JRender::eViewportType viewpor
IggyPlayerSetDisplaySize( getMovie(), (S32)(m_movieWidth * scale), (S32)(m_movieHeight * scale) ); IggyPlayerSetDisplaySize( getMovie(), (S32)(m_movieWidth * scale), (S32)(m_movieHeight * scale) );
repositionHud(tileWidth, tileHeight, scale); repositionHud(tileWidth, tileHeight, scale, needsYTile);
m_renderWidth = tileWidth; m_renderWidth = tileWidth;
m_renderHeight = tileHeight; m_renderHeight = tileHeight;
@ -805,7 +809,7 @@ void UIScene_HUD::handleTimerComplete(int id)
//setVisible(anyVisible); //setVisible(anyVisible);
} }
void UIScene_HUD::repositionHud(S32 tileWidth, S32 tileHeight, F32 scale) void UIScene_HUD::repositionHud(S32 tileWidth, S32 tileHeight, F32 scale, bool needsYTile)
{ {
if(!m_bSplitscreen) return; if(!m_bSplitscreen) return;

View file

@ -176,5 +176,5 @@ protected:
#endif #endif
private: private:
void repositionHud(S32 tileWidth, S32 tileHeight, F32 scale); void repositionHud(S32 tileWidth, S32 tileHeight, F32 scale, bool needsYTile);
}; };

View file

@ -443,7 +443,8 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
double maxHealth = minecraft->localplayers[iPad]->getAttribute(SharedMonsterAttributes.MAX_HEALTH); double maxHealth = minecraft->localplayers[iPad]->getAttribute(SharedMonsterAttributes.MAX_HEALTH);
double totalAbsorption = minecraft->localplayers[iPad]->getAbsorptionAmount(); double totalAbsorption = minecraft->localplayers[iPad]->getAbsorptionAmount();
int numHealthRows = Mth.ceil((maxHealth + totalAbsorption) / 2 / (float) NUM_HEARTS_PER_ROW); const double healthHalves = (maxHealth + totalAbsorption) / 2.0;
int numHealthRows = Mth.ceil(healthHalves / (float) NUM_HEARTS_PER_ROW);
int healthRowHeight = Math.max(10 - (numHealthRows - 2), 3); int healthRowHeight = Math.max(10 - (numHealthRows - 2), 3);
int yLine2 = yLine1 - (numHealthRows - 1) * healthRowHeight - 10; int yLine2 = yLine1 - (numHealthRows - 1) * healthRowHeight - 10;
absorption = totalAbsorption; absorption = totalAbsorption;
@ -469,7 +470,7 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
} }
//minecraft.profiler.popPush("health"); //minecraft.profiler.popPush("health");
for (int i = Mth.ceil((maxHealth + totalAbsorption) / 2) - 1; i >= 0; i--) for (int i = (int)Mth.ceil(healthHalves) - 1; i >= 0; i--)
{ {
int healthTexBaseX = 16; int healthTexBaseX = 16;
if (minecraft.player.hasEffect(MobEffect.poison)) if (minecraft.player.hasEffect(MobEffect.poison))
@ -607,8 +608,11 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
// render air bubbles // render air bubbles
if (minecraft->player->isUnderLiquid(Material::water)) if (minecraft->player->isUnderLiquid(Material::water))
{ {
int count = (int) ceil((minecraft->player->getAirSupply() - 2) * 10.0f / Player::TOTAL_AIR_SUPPLY); const int airSupply = minecraft->player->getAirSupply();
int extra = (int) ceil((minecraft->player->getAirSupply()) * 10.0f / Player::TOTAL_AIR_SUPPLY) - count; const float airScale = 10.0f / Player::TOTAL_AIR_SUPPLY;
const float airSupplyScaled = airSupply * airScale;
int count = (int) ceil((airSupply - 2) * airScale);
int extra = (int) ceil(airSupplyScaled) - count;
for (int i = 0; i < count + extra; i++) for (int i = 0; i < count + extra; i++)
{ {
// Air bubbles // Air bubbles
@ -725,7 +729,8 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
Lighting::turnOn(); Lighting::turnOn();
glRotatef(-45 - 90, 0, 1, 0); glRotatef(-45 - 90, 0, 1, 0);
glRotatef(-(float) atan(yd / 40.0f ) * 20, 1, 0, 0); const float xRotAngle = -(float) atan(yd / 40.0f) * 20;
glRotatef(xRotAngle, 1, 0, 0);
float bodyRot = (minecraft->player->yBodyRotO + (minecraft->player->yBodyRot - minecraft->player->yBodyRotO)); float bodyRot = (minecraft->player->yBodyRotO + (minecraft->player->yBodyRot - minecraft->player->yBodyRotO));
// Fixed rotation angle of degrees, adjusted by bodyRot to negate the rotation that occurs in the renderer // Fixed rotation angle of degrees, adjusted by bodyRot to negate the rotation that occurs in the renderer
// bodyRot in the rotation below is a simplification of "180 - (180 - bodyRot)" where the first 180 is EntityRenderDispatcher::instance->playerRotY that we set below // bodyRot in the rotation below is a simplification of "180 - (180 - bodyRot)" where the first 180 is EntityRenderDispatcher::instance->playerRotY that we set below
@ -736,7 +741,7 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
// Set head rotation to body rotation to make head static // Set head rotation to body rotation to make head static
minecraft->player->yRot = bodyRot; minecraft->player->yRot = bodyRot;
minecraft->player->yRotO = minecraft->player->yRot; minecraft->player->yRotO = minecraft->player->yRot;
minecraft->player->xRot = -(float) atan(yd / 40.0f) * 20; minecraft->player->xRot = xRotAngle;
minecraft->player->onFire = 0; minecraft->player->onFire = 0;
minecraft->player->setSharedFlag(Entity::FLAG_ONFIRE, false); minecraft->player->setSharedFlag(Entity::FLAG_ONFIRE, false);
@ -849,207 +854,6 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
// font.draw(str, x + 1, y, 0xffffff); // 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;
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; lastTickA = a;
// 4J Stu - This is now displayed in a xui scene // 4J Stu - This is now displayed in a xui scene
#if 0 #if 0
@ -1203,6 +1007,190 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
glPopMatrix(); glPopMatrix();
} }
#ifndef _FINAL_BUILD
MemSect(31);
if (true)
{
// Real window dimensions updated on every WM_SIZE — always current
extern int g_rScreenWidth;
extern int g_rScreenHeight;
// Set up a fresh projection using physical pixel coordinates so the debug
// text is never distorted regardless of aspect ratio, splitscreen layout,
// or menu state. 1 coordinate unit = 1 physical pixel.
// Compute the actual viewport dimensions for this player's screen section.
// glOrtho must match the viewport exactly for 1 unit = 1 physical pixel.
int vpW = g_rScreenWidth;
int vpH = g_rScreenHeight;
switch (minecraft->player->m_iScreenSection)
{
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
vpH /= 2;
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT:
vpW /= 2;
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
vpW /= 2;
vpH /= 2;
break;
default: // VIEWPORT_TYPE_FULLSCREEN
break;
}
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, vpW, vpH, 0, 1000, 3000);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glTranslatef(0, 0, -2000);
// Font was designed for guiScale px/unit; scale up so characters appear
// at the same physical size as the rest of the HUD at 0.5x.
const float fontScale = static_cast<float>(guiScale) * 1.0f;
const int debugLeft = 1;
const int debugTop = 1;
glTranslatef(static_cast<float>(debugLeft), static_cast<float>(debugTop), 0.f);
glScalef(fontScale, fontScale, 1.f);
glTranslatef(static_cast<float>(-debugLeft), static_cast<float>(-debugTop), 0.f);
vector<wstring> lines;
// Only show version/branch for player 0 to avoid cluttering each splitscreen viewport
if (iPad == 0)
{
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()));
int renderDistance = app.GetGameSettings(iPad, eGameSetting_RenderDistance);
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());
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"");
int xBlockPos = Mth::floor(minecraft->player->x);
int yBlockPos = Mth::floor(minecraft->player->y);
int zBlockPos = Mth::floor(minecraft->player->z);
int xChunkPos = xBlockPos >> 4;
int yChunkPos = yBlockPos >> 4;
int zChunkPos = zBlockPos >> 4;
int xChunkOffset = xBlockPos & 15;
int yChunkOffset = yBlockPos & 15;
int zChunkOffset = zBlockPos & 15;
WCHAR posString[44];
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(xBlockPos) + L" " + std::to_wstring(yBlockPos) + L" " + std::to_wstring(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));
float yRotDisplay = fmod(minecraft->player->yRot, 360.0f);
if (yRotDisplay > 180.0f) yRotDisplay -= 360.0f;
if (yRotDisplay < -180.0f) yRotDisplay += 360.0f;
WCHAR angleString[16];
swprintf(angleString, 16, L"%.1f / %.1f", yRotDisplay, minecraft->player->xRot);
int direction = Mth::floor(minecraft->player->yRot * 4.0f / 360.0f + 0.5) & 0x3;
const wchar_t* cardinals[] = { L"south", L"west", L"north", L"east" };
lines.push_back(L"Facing: " + std::wstring(cardinals[direction]) + L" (" + angleString + L")");
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")");
}
}
lines.push_back(L"");
lines.push_back(L"Seed: " + std::to_wstring(minecraft->level->getLevelData()->getSeed()));
lines.push_back(minecraft->gatherStats1());
lines.push_back(minecraft->gatherStats2());
lines.push_back(minecraft->gatherStats3());
}
#ifdef _DEBUG
if (minecraft->options->renderDebug && minecraft->player != nullptr && minecraft->level != nullptr && 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: ";
// maxW in font units: physical width divided by font scale
float maxW = (static_cast<float>(g_rScreenWidth) - debugLeft - 8) / fontScale;
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"");
for (int i = eTerrainFeature_Stronghold; i <= static_cast<int>(eTerrainFeature_Ravine); i++)
lines.push_back(wfeature[i]);
lines.push_back(L"");
}
#endif
int yPos = debugTop;
for (const auto &line : lines)
{
drawString(font, line, debugLeft, yPos, 0xffffff);
yPos += 10;
}
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
}
MemSect(0);
#endif
glColor4f(1, 1, 1, 1); glColor4f(1, 1, 1, 1);
glDisable(GL_BLEND); glDisable(GL_BLEND);
glEnable(GL_ALPHA_TEST); glEnable(GL_ALPHA_TEST);

View file

@ -228,7 +228,7 @@ void ItemInHandRenderer::renderItem(shared_ptr<LivingEntity> mob, shared_ptr<Ite
// by texture lighting. This is for colourising things held in 3rd person view. // by texture lighting. This is for colourising things held in 3rd person view.
if ( (setColor) && (item != nullptr) ) if ( (setColor) && (item != nullptr) )
{ {
int col = Item::items[item->id]->getColor(item,0); int col = Item::items[item->id]->getColor(item, layer);
float red = ((col >> 16) & 0xff) / 255.0f; float red = ((col >> 16) & 0xff) / 255.0f;
float g = ((col >> 8) & 0xff) / 255.0f; float g = ((col >> 8) & 0xff) / 255.0f;
float b = ((col) & 0xff) / 255.0f; float b = ((col) & 0xff) / 255.0f;

View file

@ -1629,7 +1629,7 @@ void Minecraft::run_middle()
s_prevXButtons[i] = xCurButtons; s_prevXButtons[i] = xCurButtons;
} }
bool startJustPressed = s_startPressLatch[i] > 0; bool startJustPressed = s_startPressLatch[i] > 0;
bool tryJoin = !pause && !ui.IsIgnorePlayerJoinMenuDisplayed(ProfileManager.GetPrimaryPad()) && g_NetworkManager.SessionHasSpace() && xCurButtons != 0; bool tryJoin = !pause && !ui.IsIgnorePlayerJoinMenuDisplayed(ProfileManager.GetPrimaryPad()) && g_NetworkManager.SessionHasSpace() && xCurButtons != 0 && g_KBMInput.IsWindowFocused();
#else #else
bool tryJoin = !pause && !ui.IsIgnorePlayerJoinMenuDisplayed(ProfileManager.GetPrimaryPad()) && g_NetworkManager.SessionHasSpace() && RenderManager.IsHiDef() && InputManager.ButtonPressed(i); bool tryJoin = !pause && !ui.IsIgnorePlayerJoinMenuDisplayed(ProfileManager.GetPrimaryPad()) && g_NetworkManager.SessionHasSpace() && RenderManager.IsHiDef() && InputManager.ButtonPressed(i);
#endif #endif
@ -3706,7 +3706,9 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
app.EnableDebugOverlay(options->renderDebug,iPad); app.EnableDebugOverlay(options->renderDebug,iPad);
#else #else
// 4J Stu - The xbox uses a completely different way of navigating to this scene // 4J Stu - The xbox uses a completely different way of navigating to this scene
ui.NavigateToScene(0, eUIScene_DebugOverlay, nullptr, eUILayer_Debug); // Always open in the fullscreen group so the overlay spans the full window
// regardless of split-screen viewport configuration.
ui.NavigateToScene(0, eUIScene_DebugOverlay, nullptr, eUILayer_Debug, eUIGroup_Fullscreen);
#endif #endif
#endif #endif
} }

View file

@ -569,6 +569,7 @@ MinecraftServer::MinecraftServer()
playerIdleTimeout = 0; playerIdleTimeout = 0;
m_postUpdateThread = nullptr; m_postUpdateThread = nullptr;
forceGameType = false; forceGameType = false;
m_spawnProtectionRadius = 0;
commandDispatcher = new ServerCommandDispatcher(); commandDispatcher = new ServerCommandDispatcher();
InitializeCriticalSection(&m_consoleInputCS); InitializeCriticalSection(&m_consoleInputCS);
@ -615,6 +616,10 @@ bool MinecraftServer::initServer(int64_t seed, NetworkGameInitData *initData, DW
logger.info("Loading properties"); logger.info("Loading properties");
#endif #endif
settings = new Settings(new File(L"server.properties")); settings = new Settings(new File(L"server.properties"));
// Dedicated-only: spawn-protection radius in blocks; 0 disables protection.
m_spawnProtectionRadius = GetDedicatedServerInt(settings, L"spawn-protection", 0);
if (m_spawnProtectionRadius < 0) m_spawnProtectionRadius = 0;
if (m_spawnProtectionRadius > 256) m_spawnProtectionRadius = 256;
app.SetGameHostOption(eGameHostOption_Difficulty, GetDedicatedServerInt(settings, L"difficulty", app.GetGameHostOption(eGameHostOption_Difficulty))); app.SetGameHostOption(eGameHostOption_Difficulty, GetDedicatedServerInt(settings, L"difficulty", app.GetGameHostOption(eGameHostOption_Difficulty)));
app.SetGameHostOption(eGameHostOption_GameType, GetDedicatedServerInt(settings, L"gamemode", app.GetGameHostOption(eGameHostOption_GameType))); app.SetGameHostOption(eGameHostOption_GameType, GetDedicatedServerInt(settings, L"gamemode", app.GetGameHostOption(eGameHostOption_GameType)));
@ -631,6 +636,7 @@ bool MinecraftServer::initServer(int64_t seed, NetworkGameInitData *initData, DW
app.DebugPrintf("ServerSettings: pvp is %s\n",(app.GetGameHostOption(eGameHostOption_PvP)>0)?"on":"off"); app.DebugPrintf("ServerSettings: pvp is %s\n",(app.GetGameHostOption(eGameHostOption_PvP)>0)?"on":"off");
app.DebugPrintf("ServerSettings: fire spreads is %s\n",(app.GetGameHostOption(eGameHostOption_FireSpreads)>0)?"on":"off"); app.DebugPrintf("ServerSettings: fire spreads is %s\n",(app.GetGameHostOption(eGameHostOption_FireSpreads)>0)?"on":"off");
app.DebugPrintf("ServerSettings: tnt explodes is %s\n",(app.GetGameHostOption(eGameHostOption_TNT)>0)?"on":"off"); app.DebugPrintf("ServerSettings: tnt explodes is %s\n",(app.GetGameHostOption(eGameHostOption_TNT)>0)?"on":"off");
app.DebugPrintf("ServerSettings: spawn protection radius is %d\n", m_spawnProtectionRadius);
app.DebugPrintf("\n"); app.DebugPrintf("\n");
// TODO 4J Stu - Init a load of settings based on data passed as params // TODO 4J Stu - Init a load of settings based on data passed as params
@ -1661,7 +1667,9 @@ Level *MinecraftServer::getCommandSenderWorld()
int MinecraftServer::getSpawnProtectionRadius() int MinecraftServer::getSpawnProtectionRadius()
{ {
return 16; // Client-host mode must never apply dedicated-server spawn protection settings.
if (!ShouldUseDedicatedServerProperties()) return 0;
return m_spawnProtectionRadius;
} }
bool MinecraftServer::isUnderSpawnProtection(Level *level, int x, int y, int z, shared_ptr<Player> player) bool MinecraftServer::isUnderSpawnProtection(Level *level, int x, int y, int z, shared_ptr<Player> player)

View file

@ -44,6 +44,7 @@ typedef struct _NetworkGameInitData
LevelGenerationOptions *levelGen; LevelGenerationOptions *levelGen;
DWORD texturePackId; DWORD texturePackId;
bool findSeed; bool findSeed;
bool dedicatedNoLocalHostPlayer;
unsigned int xzSize; unsigned int xzSize;
unsigned char hellScale; unsigned char hellScale;
ESavePlatform savePlatform; ESavePlatform savePlatform;
@ -57,6 +58,7 @@ typedef struct _NetworkGameInitData
levelGen = nullptr; levelGen = nullptr;
texturePackId = 0; texturePackId = 0;
findSeed = false; findSeed = false;
dedicatedNoLocalHostPlayer = false;
xzSize = LEVEL_LEGACY_WIDTH; xzSize = LEVEL_LEGACY_WIDTH;
hellScale = HELL_LEVEL_LEGACY_SCALE; hellScale = HELL_LEVEL_LEGACY_SCALE;
savePlatform = SAVE_FILE_PLATFORM_LOCAL; savePlatform = SAVE_FILE_PLATFORM_LOCAL;
@ -119,6 +121,7 @@ public:
int maxBuildHeight; int maxBuildHeight;
int playerIdleTimeout; int playerIdleTimeout;
bool forceGameType; bool forceGameType;
int m_spawnProtectionRadius;
private: private:
// 4J Added // 4J Added

View file

@ -12,6 +12,7 @@ using namespace std;
class MultiPlayerLevel : public Level class MultiPlayerLevel : public Level
{ {
friend class ClientConnection;
private: private:
static const int TICKS_BEFORE_RESET = 20 * 4; static const int TICKS_BEFORE_RESET = 20 * 4;

View file

@ -14,6 +14,11 @@
#include "..\Minecraft.World\net.minecraft.world.item.h" #include "..\Minecraft.World\net.minecraft.world.item.h"
#include "..\Minecraft.World\SharedConstants.h" #include "..\Minecraft.World\SharedConstants.h"
#include "Settings.h" #include "Settings.h"
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
#include "..\Minecraft.Server\ServerLogManager.h"
#include "..\Minecraft.Server\Access\Access.h"
#include "..\Minecraft.World\Socket.h"
#endif
// #ifdef __PS3__ // #ifdef __PS3__
// #include "PS3\Network\NetworkPlayerSony.h" // #include "PS3\Network\NetworkPlayerSony.h"
// #endif // #endif
@ -24,6 +29,24 @@ Random *PendingConnection::random = new Random();
bool g_bRejectDuplicateNames = true; bool g_bRejectDuplicateNames = true;
#endif #endif
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
namespace
{
static unsigned char GetPendingConnectionSmallId(Connection *connection)
{
if (connection != nullptr)
{
Socket *socket = connection->getSocket();
if (socket != nullptr)
{
return socket->getSmallId();
}
}
return 0;
}
}
#endif
PendingConnection::PendingConnection(MinecraftServer *server, Socket *socket, const wstring& id) PendingConnection::PendingConnection(MinecraftServer *server, Socket *socket, const wstring& id)
{ {
// 4J - added initialisers // 4J - added initialisers
@ -180,16 +203,55 @@ void PendingConnection::handleLogin(shared_ptr<LoginPacket> packet)
duplicateXuid = true; duplicateXuid = true;
} }
bool bannedXuid = false;
if (loginXuid != INVALID_XUID)
{
bannedXuid = server->getPlayers()->isXuidBanned(loginXuid);
}
if (!bannedXuid && packet->m_onlineXuid != INVALID_XUID && packet->m_onlineXuid != loginXuid)
{
bannedXuid = server->getPlayers()->isXuidBanned(packet->m_onlineXuid);
}
bool whitelistSatisfied = true;
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (ServerRuntime::Access::IsWhitelistEnabled())
{
whitelistSatisfied = false;
if (loginXuid != INVALID_XUID)
{
whitelistSatisfied = ServerRuntime::Access::IsPlayerWhitelisted(loginXuid);
}
if (!whitelistSatisfied && packet->m_onlineXuid != INVALID_XUID && packet->m_onlineXuid != loginXuid)
{
whitelistSatisfied = ServerRuntime::Access::IsPlayerWhitelisted(packet->m_onlineXuid);
}
}
#endif
if( sentDisconnect ) if( sentDisconnect )
{ {
// Do nothing // Do nothing
} }
else if( server->getPlayers()->isXuidBanned( packet->m_onlineXuid ) ) else if (bannedXuid)
{ {
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_BannedXuid);
#endif
disconnect(DisconnectPacket::eDisconnect_Banned);
}
else if (!whitelistSatisfied)
{
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_NotWhitelisted);
#endif
disconnect(DisconnectPacket::eDisconnect_Banned); disconnect(DisconnectPacket::eDisconnect_Banned);
} }
else if (duplicateXuid) else if (duplicateXuid)
{ {
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_DuplicateXuid);
#endif
// Reject the incoming connection — a player with this UID is already // Reject the incoming connection — a player with this UID is already
// on the server. Allowing duplicates causes invisible players and // on the server. Allowing duplicates causes invisible players and
// other undefined behaviour. // other undefined behaviour.
@ -211,6 +273,9 @@ void PendingConnection::handleLogin(shared_ptr<LoginPacket> packet)
} }
if (nameTaken) if (nameTaken)
{ {
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_DuplicateName);
#endif
app.DebugPrintf("Rejecting duplicate name: %ls\n", name.c_str()); app.DebugPrintf("Rejecting duplicate name: %ls\n", name.c_str());
disconnect(DisconnectPacket::eDisconnect_Banned); disconnect(DisconnectPacket::eDisconnect_Banned);
} }
@ -268,6 +333,9 @@ void PendingConnection::handleAcceptedLogin(shared_ptr<LoginPacket> packet)
shared_ptr<ServerPlayer> playerEntity = server->getPlayers()->getPlayerForLogin(this, name, playerXuid,packet->m_onlineXuid); shared_ptr<ServerPlayer> playerEntity = server->getPlayers()->getPlayerForLogin(this, name, playerXuid,packet->m_onlineXuid);
if (playerEntity != nullptr) if (playerEntity != nullptr)
{ {
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
ServerRuntime::ServerLogManager::OnAcceptedPlayerLogin(GetPendingConnectionSmallId(connection), name);
#endif
server->getPlayers()->placeNewPlayer(connection, playerEntity, packet); server->getPlayers()->placeNewPlayer(connection, playerEntity, packet);
connection = nullptr; // We've moved responsibility for this over to the new PlayerConnection, nullptr so we don't delete our reference to it here in our dtor connection = nullptr; // We've moved responsibility for this over to the new PlayerConnection, nullptr so we don't delete our reference to it here in our dtor
} }

View file

@ -34,9 +34,26 @@
// 4J Added // 4J Added
#include "..\Minecraft.World\net.minecraft.world.item.crafting.h" #include "..\Minecraft.World\net.minecraft.world.item.crafting.h"
#include "Options.h" #include "Options.h"
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
#include "..\Minecraft.Server\ServerLogManager.h"
#endif
namespace
{
// Anti-cheat thresholds. Keep server-side checks authoritative even in host mode.
// Base max squared displacement allowed per move packet before speed flags trigger.
const double kMoveBaseAllowanceSq = 100.0;
// Extra squared displacement allowance derived from current server-side velocity.
const double kMoveVelocityAllowanceScale = 100.0;
// Max squared distance for interact/attack when the target is visible (normal reach).
const double kInteractReachSq = 6.0 * 6.0;
// Stricter max squared distance used when LOS is blocked to reduce wall-hit abuse.
const double kInteractBlockedReachSq = 3.0 * 3.0;
}
Random PlayerConnection::random; Random PlayerConnection::random;
PlayerConnection::PlayerConnection(MinecraftServer *server, Connection *connection, shared_ptr<ServerPlayer> player) PlayerConnection::PlayerConnection(MinecraftServer *server, Connection *connection, shared_ptr<ServerPlayer> player)
{ {
// 4J - added initialisers // 4J - added initialisers
@ -66,6 +83,13 @@ PlayerConnection::PlayerConnection(MinecraftServer *server, Connection *connecti
m_offlineXUID = INVALID_XUID; m_offlineXUID = INVALID_XUID;
m_onlineXUID = INVALID_XUID; m_onlineXUID = INVALID_XUID;
m_bHasClientTickedOnce = false; m_bHasClientTickedOnce = false;
m_logSmallId = 0;
// Cache the first valid transport smallId because disconnect teardown can clear it before the server logger runs.
if (this->connection != NULL && this->connection->getSocket() != NULL)
{
m_logSmallId = this->connection->getSocket()->getSmallId();
}
setShowOnMaps(app.GetGameHostOption(eGameHostOption_Gamertags)!=0?true:false); setShowOnMaps(app.GetGameHostOption(eGameHostOption_Gamertags)!=0?true:false);
} }
@ -76,6 +100,17 @@ PlayerConnection::~PlayerConnection()
DeleteCriticalSection(&done_cs); DeleteCriticalSection(&done_cs);
} }
unsigned char PlayerConnection::getLogSmallId()
{
// Fall back to the live socket only while the cached value is still empty.
if (m_logSmallId == 0 && connection != NULL && connection->getSocket() != NULL)
{
m_logSmallId = connection->getSocket()->getSmallId();
}
return m_logSmallId;
}
void PlayerConnection::tick() void PlayerConnection::tick()
{ {
if( done ) return; if( done ) return;
@ -118,6 +153,13 @@ void PlayerConnection::disconnect(DisconnectPacket::eDisconnectReason reason)
return; return;
} }
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
ServerRuntime::ServerLogManager::OnPlayerDisconnected(
getLogSmallId(),
(player != NULL) ? player->name : std::wstring(),
reason,
true);
#endif
app.DebugPrintf("PlayerConnection disconect reason: %d\n", reason ); app.DebugPrintf("PlayerConnection disconect reason: %d\n", reason );
player->disconnect(); player->disconnect();
@ -271,16 +313,19 @@ void PlayerConnection::handleMovePlayer(shared_ptr<MovePlayerPacket> packet)
double dist = xDist * xDist + yDist * yDist + zDist * zDist; double dist = xDist * xDist + yDist * yDist + zDist * zDist;
// 4J-PB - removing this one for now // Anti-cheat: reject movement packets that exceed server-authoritative bounds.
/*if (dist > 100.0f) double velocitySq = player->xd * player->xd + player->yd * player->yd + player->zd * player->zd;
double maxAllowedSq = kMoveBaseAllowanceSq + (velocitySq * kMoveVelocityAllowanceScale);
if (player->isAllowedToFly() || player->gameMode->isCreative())
{
// Creative / flight-allowed players can move farther legitimately per tick.
maxAllowedSq *= 1.5;
}
if (dist > maxAllowedSq)
{ {
// logger.warning(player->name + " moved too quickly!");
disconnect(DisconnectPacket::eDisconnect_MovedTooQuickly); disconnect(DisconnectPacket::eDisconnect_MovedTooQuickly);
// System.out.println("Moved too quickly at " + xt + ", " + yt + ", " + zt);
// teleport(player->x, player->y, player->z, player->yRot, player->xRot);
return; return;
} }
*/
float r = 1 / 16.0f; float r = 1 / 16.0f;
bool oldOk = level->getCubes(player, player->bb->copy()->shrink(r, r, r))->empty(); bool oldOk = level->getCubes(player, player->bb->copy()->shrink(r, r, r))->empty();
@ -308,8 +353,8 @@ void PlayerConnection::handleMovePlayer(shared_ptr<MovePlayerPacket> packet)
xDist = xt - player->x; xDist = xt - player->x;
yDist = yt - player->y; yDist = yt - player->y;
// 4J-PB - line below will always be true! // Clamp tiny Y drift noise to reduce false positives.
if (yDist > -0.5 || yDist < 0.5) if (yDist > -0.5 && yDist < 0.5)
{ {
yDist = 0; yDist = 0;
} }
@ -430,7 +475,8 @@ void PlayerConnection::handlePlayerAction(shared_ptr<PlayerActionPacket> packet)
if (packet->action == PlayerActionPacket::START_DESTROY_BLOCK) if (packet->action == PlayerActionPacket::START_DESTROY_BLOCK)
{ {
if (true) player->gameMode->startDestroyBlock(x, y, z, packet->face); // 4J - condition was !server->isUnderSpawnProtection(level, x, y, z, player) (from Java 1.6.4) but putting back to old behaviour // Anti-cheat: validate spawn protection on the server for mining start.
if (!server->isUnderSpawnProtection(level, x, y, z, player)) player->gameMode->startDestroyBlock(x, y, z, packet->face);
else player->connection->send(std::make_shared<TileUpdatePacket>(x, y, z, level)); else player->connection->send(std::make_shared<TileUpdatePacket>(x, y, z, level));
} }
@ -458,8 +504,6 @@ void PlayerConnection::handleUseItem(shared_ptr<UseItemPacket> packet)
int face = packet->getFace(); int face = packet->getFace();
player->resetLastActionTime(); player->resetLastActionTime();
// 4J Stu - We don't have ops, so just use the levels setting
bool canEditSpawn = level->canEditSpawn; // = level->dimension->id != 0 || server->players->isOp(player->name);
if (packet->getFace() == 255) if (packet->getFace() == 255)
{ {
if (item == nullptr) return; if (item == nullptr) return;
@ -469,7 +513,8 @@ void PlayerConnection::handleUseItem(shared_ptr<UseItemPacket> packet)
{ {
if (synched && player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) < 8 * 8) if (synched && player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) < 8 * 8)
{ {
if (true) // 4J - condition was !server->isUnderSpawnProtection(level, x, y, z, player) (from java 1.6.4) but putting back to old behaviour // Anti-cheat: block placement/use must pass server-side spawn protection.
if (!server->isUnderSpawnProtection(level, x, y, z, player))
{ {
player->gameMode->useItemOn(player, level, item, x, y, z, face, packet->getClickX(), packet->getClickY(), packet->getClickZ()); player->gameMode->useItemOn(player, level, item, x, y, z, face, packet->getClickX(), packet->getClickY(), packet->getClickZ());
} }
@ -538,7 +583,18 @@ void PlayerConnection::handleUseItem(shared_ptr<UseItemPacket> packet)
void PlayerConnection::onDisconnect(DisconnectPacket::eDisconnectReason reason, void *reasonObjects) void PlayerConnection::onDisconnect(DisconnectPacket::eDisconnectReason reason, void *reasonObjects)
{ {
EnterCriticalSection(&done_cs); EnterCriticalSection(&done_cs);
if( done ) return; if( done )
{
LeaveCriticalSection(&done_cs);
return;
}
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
ServerRuntime::ServerLogManager::OnPlayerDisconnected(
getLogSmallId(),
(player != NULL) ? player->name : std::wstring(),
reason,
false);
#endif
// logger.info(player.name + " lost connection: " + reason); // logger.info(player.name + " lost connection: " + reason);
// 4J-PB - removed, since it needs to be localised in the language the client is in // 4J-PB - removed, since it needs to be localised in the language the client is in
//server->players->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(L"<22>e" + player->name + L" left the game.") ) ); //server->players->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(L"<22>e" + player->name + L" left the game.") ) );
@ -742,17 +798,16 @@ void PlayerConnection::handleInteract(shared_ptr<InteractPacket> packet)
// 4J Stu - If the client says that we hit something, then agree with it. The canSee can fail here as it checks // 4J Stu - If the client says that we hit something, then agree with it. The canSee can fail here as it checks
// a ray from head->head, but we may actually be looking at a different part of the entity that can be seen // a ray from head->head, but we may actually be looking at a different part of the entity that can be seen
// even though the ray is blocked. // even though the ray is blocked.
if (target != nullptr) // && player->canSee(target) && player->distanceToSqr(target) < 6 * 6) if (target != nullptr)
{ {
//boole canSee = player->canSee(target); // Anti-cheat: enforce reach and LOS on the server to reject forged hits.
//double maxDist = 6 * 6; bool canSeeTarget = player->canSee(target);
//if (!canSee) double maxDistSq = canSeeTarget ? kInteractReachSq : kInteractBlockedReachSq;
//{ if (player->distanceToSqr(target) > maxDistSq)
// maxDist = 3 * 3; {
//} return;
}
//if (player->distanceToSqr(target) < maxDist)
//{
if (packet->action == InteractPacket::INTERACT) if (packet->action == InteractPacket::INTERACT)
{ {
player->interact(target); player->interact(target);
@ -767,7 +822,6 @@ void PlayerConnection::handleInteract(shared_ptr<InteractPacket> packet)
} }
player->attack(target); player->attack(target);
} }
//}
} }
} }

View file

@ -37,6 +37,7 @@ private:
int dropSpamTickCount; int dropSpamTickCount;
bool m_bHasClientTickedOnce; bool m_bHasClientTickedOnce;
unsigned char m_logSmallId;
public: public:
PlayerConnection(MinecraftServer *server, Connection *connection, shared_ptr<ServerPlayer> player); PlayerConnection(MinecraftServer *server, Connection *connection, shared_ptr<ServerPlayer> player);
@ -45,6 +46,10 @@ public:
void disconnect(DisconnectPacket::eDisconnectReason reason); void disconnect(DisconnectPacket::eDisconnectReason reason);
private: private:
/**
* Returns the stable network smallId used by dedicated-server logging and refreshes it from the live socket when possible
*/
unsigned char getLogSmallId();
double xLastOk, yLastOk, zLastOk; double xLastOk, yLastOk, zLastOk;
bool synched; bool synched;

View file

@ -37,6 +37,11 @@
#include "Common\Network\Sony\NetworkPlayerSony.h" #include "Common\Network\Sony\NetworkPlayerSony.h"
#endif #endif
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
#include "..\Minecraft.Server\Access\Access.h"
extern bool g_Win64DedicatedServer;
#endif
// 4J - this class is fairly substantially altered as there didn't seem any point in porting code for banning, whitelisting, ops etc. // 4J - this class is fairly substantially altered as there didn't seem any point in porting code for banning, whitelisting, ops etc.
PlayerList::PlayerList(MinecraftServer *server) PlayerList::PlayerList(MinecraftServer *server)
@ -1674,6 +1679,13 @@ bool PlayerList::isXuidBanned(PlayerUID xuid)
} }
} }
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (!banned && g_Win64DedicatedServer)
{
banned = ServerRuntime::Access::IsPlayerBanned(xuid);
}
#endif
return banned; return banned;
} }

View file

@ -176,31 +176,29 @@ void ServerPlayerGameMode::stopDestroyBlock(int x, int y, int z)
{ {
if (x == xDestroyBlock && y == yDestroyBlock && z == zDestroyBlock) if (x == xDestroyBlock && y == yDestroyBlock && z == zDestroyBlock)
{ {
// int ticksSpentDestroying = gameTicks - destroyProgressStart;
int t = level->getTile(x, y, z); int t = level->getTile(x, y, z);
if (t != 0) if (t != 0)
{ {
Tile *tile = Tile::tiles[t]; Tile *tile = Tile::tiles[t];
// Anti-cheat: re-check destroy progress on the server for STOP_DESTROY.
// MGH - removed checking for the destroy progress here, it has already been checked on the client before it sent the packet. int ticksSpentDestroying = gameTicks - destroyProgressStart;
// fixes issues with this failing to destroy because of packets bunching up float destroyProgress = tile->getDestroyProgress(player, player->level, x, y, z) * (ticksSpentDestroying + 1);
// float destroyProgress = tile->getDestroyProgress(player, player->level, x, y, z) * (ticksSpentDestroying + 1); if (destroyProgress >= 1.0f)
// if (destroyProgress >= .7f || bIgnoreDestroyProgress)
{ {
isDestroyingBlock = false; isDestroyingBlock = false;
level->destroyTileProgress(player->entityId, x, y, z, -1); level->destroyTileProgress(player->entityId, x, y, z, -1);
destroyBlock(x, y, z); destroyBlock(x, y, z);
} }
// else if (!hasDelayedDestroy) else if (!hasDelayedDestroy)
// { {
// isDestroyingBlock = false; // Keep server-authoritative mining while allowing legit latency to finish via delayed tick progression.
// hasDelayedDestroy = true; isDestroyingBlock = false;
// delayedDestroyX = x; hasDelayedDestroy = true;
// delayedDestroyY = y; delayedDestroyX = x;
// delayedDestroyZ = z; delayedDestroyY = y;
// delayedTickStart = destroyProgressStart; delayedDestroyZ = z;
// } delayedTickStart = destroyProgressStart;
}
} }
} }
} }

View file

@ -8,11 +8,20 @@
#include "WinsockNetLayer.h" #include "WinsockNetLayer.h"
#include "..\..\Common\Network\PlatformNetworkManagerStub.h" #include "..\..\Common\Network\PlatformNetworkManagerStub.h"
#include "..\..\..\Minecraft.World\Socket.h" #include "..\..\..\Minecraft.World\Socket.h"
#if defined(MINECRAFT_SERVER_BUILD)
#include "..\..\..\Minecraft.Server\Access\Access.h"
#include "..\..\..\Minecraft.Server\ServerLogManager.h"
#endif
#include "..\..\..\Minecraft.World\DisconnectPacket.h" #include "..\..\..\Minecraft.World\DisconnectPacket.h"
#include "..\..\Minecraft.h" #include "..\..\Minecraft.h"
#include "..\4JLibs\inc\4J_Profile.h" #include "..\4JLibs\inc\4J_Profile.h"
#include <string>
static bool RecvExact(SOCKET sock, BYTE* buf, int len); static bool RecvExact(SOCKET sock, BYTE* buf, int len);
#if defined(MINECRAFT_SERVER_BUILD)
static bool TryGetNumericRemoteIp(const sockaddr_in &remoteAddress, std::string *outIp);
#endif
SOCKET WinsockNetLayer::s_listenSocket = INVALID_SOCKET; SOCKET WinsockNetLayer::s_listenSocket = INVALID_SOCKET;
SOCKET WinsockNetLayer::s_hostConnectionSocket = INVALID_SOCKET; SOCKET WinsockNetLayer::s_hostConnectionSocket = INVALID_SOCKET;
@ -65,6 +74,7 @@ char g_Win64MultiplayerIP[256] = "127.0.0.1";
bool g_Win64DedicatedServer = false; bool g_Win64DedicatedServer = false;
int g_Win64DedicatedServerPort = WIN64_NET_DEFAULT_PORT; int g_Win64DedicatedServerPort = WIN64_NET_DEFAULT_PORT;
char g_Win64DedicatedServerBindIP[256] = ""; char g_Win64DedicatedServerBindIP[256] = "";
bool g_Win64DedicatedServerLanAdvertise = true;
bool WinsockNetLayer::Initialize() bool WinsockNetLayer::Initialize()
{ {
@ -90,7 +100,11 @@ bool WinsockNetLayer::Initialize()
s_initialized = true; s_initialized = true;
// Dedicated Server does not use LAN session discovery and therefore does not initiate discovery.
if (!g_Win64DedicatedServer)
{
StartDiscovery(); StartDiscovery();
}
return true; return true;
} }
@ -392,6 +406,11 @@ bool WinsockNetLayer::JoinGame(const char* ip, int port)
} }
s_localSmallId = assignedSmallId; s_localSmallId = assignedSmallId;
// Save the host IP and port so JoinSplitScreen can connect to the same host
// regardless of how the connection was initiated (UI vs command line).
strncpy_s(g_Win64MultiplayerIP, sizeof(g_Win64MultiplayerIP), ip, _TRUNCATE);
g_Win64MultiplayerPort = port;
app.DebugPrintf("Win64 LAN: Connected to %s:%d, assigned smallId=%d\n", ip, port, s_localSmallId); app.DebugPrintf("Win64 LAN: Connected to %s:%d, assigned smallId=%d\n", ip, port, s_localSmallId);
s_active = true; s_active = true;
@ -507,6 +526,27 @@ static bool RecvExact(SOCKET sock, BYTE* buf, int len)
return true; return true;
} }
#if defined(MINECRAFT_SERVER_BUILD)
static bool TryGetNumericRemoteIp(const sockaddr_in &remoteAddress, std::string *outIp)
{
if (outIp == nullptr)
{
return false;
}
outIp->clear();
char ipBuffer[64] = {};
const char *ip = inet_ntop(AF_INET, (void *)&remoteAddress.sin_addr, ipBuffer, sizeof(ipBuffer));
if (ip == nullptr || ip[0] == 0)
{
return false;
}
*outIp = ip;
return true;
}
#endif
void WinsockNetLayer::HandleDataReceived(BYTE fromSmallId, BYTE toSmallId, unsigned char* data, unsigned int dataSize) void WinsockNetLayer::HandleDataReceived(BYTE fromSmallId, BYTE toSmallId, unsigned char* data, unsigned int dataSize)
{ {
INetworkPlayer* pPlayerFrom = g_NetworkManager.GetPlayerBySmallId(fromSmallId); INetworkPlayer* pPlayerFrom = g_NetworkManager.GetPlayerBySmallId(fromSmallId);
@ -541,7 +581,10 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
{ {
while (s_active) while (s_active)
{ {
SOCKET clientSocket = accept(s_listenSocket, nullptr, nullptr); sockaddr_in remoteAddress;
ZeroMemory(&remoteAddress, sizeof(remoteAddress));
int remoteAddressLength = sizeof(remoteAddress);
SOCKET clientSocket = accept(s_listenSocket, (sockaddr*)&remoteAddress, &remoteAddressLength);
if (clientSocket == INVALID_SOCKET) if (clientSocket == INVALID_SOCKET)
{ {
if (s_active) if (s_active)
@ -552,10 +595,36 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
int noDelay = 1; int noDelay = 1;
setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&noDelay, sizeof(noDelay)); setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&noDelay, sizeof(noDelay));
#if defined(MINECRAFT_SERVER_BUILD)
std::string remoteIp;
const bool hasRemoteIp = TryGetNumericRemoteIp(remoteAddress, &remoteIp);
const char *remoteIpForLog = hasRemoteIp ? remoteIp.c_str() : "unknown";
if (g_Win64DedicatedServer)
{
ServerRuntime::ServerLogManager::OnIncomingTcpConnection(remoteIpForLog);
if (hasRemoteIp && ServerRuntime::Access::IsIpBanned(remoteIp))
{
ServerRuntime::ServerLogManager::OnRejectedTcpConnection(remoteIpForLog, ServerRuntime::ServerLogManager::eTcpRejectReason_BannedIp);
SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_Banned);
closesocket(clientSocket);
continue;
}
}
#endif
extern QNET_STATE _iQNetStubState; extern QNET_STATE _iQNetStubState;
if (_iQNetStubState != QNET_STATE_GAME_PLAY) if (_iQNetStubState != QNET_STATE_GAME_PLAY)
{ {
#if defined(MINECRAFT_SERVER_BUILD)
if (g_Win64DedicatedServer)
{
ServerRuntime::ServerLogManager::OnRejectedTcpConnection(remoteIpForLog, ServerRuntime::ServerLogManager::eTcpRejectReason_GameNotReady);
}
else
#endif
{
app.DebugPrintf("Win64 LAN: Rejecting connection, game not ready\n"); app.DebugPrintf("Win64 LAN: Rejecting connection, game not ready\n");
}
closesocket(clientSocket); closesocket(clientSocket);
continue; continue;
} }
@ -563,7 +632,16 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
extern CPlatformNetworkManagerStub* g_pPlatformNetworkManager; extern CPlatformNetworkManagerStub* g_pPlatformNetworkManager;
if (g_pPlatformNetworkManager != nullptr && !g_pPlatformNetworkManager->CanAcceptMoreConnections()) if (g_pPlatformNetworkManager != nullptr && !g_pPlatformNetworkManager->CanAcceptMoreConnections())
{ {
#if defined(MINECRAFT_SERVER_BUILD)
if (g_Win64DedicatedServer)
{
ServerRuntime::ServerLogManager::OnRejectedTcpConnection(remoteIpForLog, ServerRuntime::ServerLogManager::eTcpRejectReason_ServerFull);
}
else
#endif
{
app.DebugPrintf("Win64 LAN: Rejecting connection, server at max players\n"); app.DebugPrintf("Win64 LAN: Rejecting connection, server at max players\n");
}
SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_ServerFull); SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_ServerFull);
closesocket(clientSocket); closesocket(clientSocket);
continue; continue;
@ -583,7 +661,16 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
else else
{ {
LeaveCriticalSection(&s_freeSmallIdLock); LeaveCriticalSection(&s_freeSmallIdLock);
#if defined(MINECRAFT_SERVER_BUILD)
if (g_Win64DedicatedServer)
{
ServerRuntime::ServerLogManager::OnRejectedTcpConnection(remoteIpForLog, ServerRuntime::ServerLogManager::eTcpRejectReason_ServerFull);
}
else
#endif
{
app.DebugPrintf("Win64 LAN: Server full, rejecting connection\n"); app.DebugPrintf("Win64 LAN: Server full, rejecting connection\n");
}
SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_ServerFull); SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_ServerFull);
closesocket(clientSocket); closesocket(clientSocket);
continue; continue;
@ -611,7 +698,16 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
int connIdx = static_cast<int>(s_connections.size()) - 1; int connIdx = static_cast<int>(s_connections.size()) - 1;
LeaveCriticalSection(&s_connectionsLock); LeaveCriticalSection(&s_connectionsLock);
#if defined(MINECRAFT_SERVER_BUILD)
if (g_Win64DedicatedServer)
{
ServerRuntime::ServerLogManager::OnAcceptedTcpConnection(assignedSmallId, remoteIpForLog);
}
else
#endif
{
app.DebugPrintf("Win64 LAN: Client connected, assigned smallId=%d\n", assignedSmallId); app.DebugPrintf("Win64 LAN: Client connected, assigned smallId=%d\n", assignedSmallId);
}
EnterCriticalSection(&s_smallIdToSocketLock); EnterCriticalSection(&s_smallIdToSocketLock);
s_smallIdToSocket[assignedSmallId] = clientSocket; s_smallIdToSocket[assignedSmallId] = clientSocket;
@ -733,6 +829,11 @@ bool WinsockNetLayer::PopDisconnectedSmallId(BYTE* outSmallId)
void WinsockNetLayer::PushFreeSmallId(BYTE smallId) void WinsockNetLayer::PushFreeSmallId(BYTE smallId)
{ {
// SmallIds 0..(XUSER_MAX_COUNT-1) are permanently reserved for the host's
// local pads and must never be recycled to remote clients.
if (smallId < (BYTE)XUSER_MAX_COUNT)
return;
EnterCriticalSection(&s_freeSmallIdLock); EnterCriticalSection(&s_freeSmallIdLock);
// Guard against double-recycle: the reconnect path (queueSmallIdForRecycle) and // Guard against double-recycle: the reconnect path (queueSmallIdForRecycle) and
// the DoWork disconnect path can both push the same smallId. If we allow duplicates, // the DoWork disconnect path can both push the same smallId. If we allow duplicates,

View file

@ -170,5 +170,6 @@ extern char g_Win64MultiplayerIP[256];
extern bool g_Win64DedicatedServer; extern bool g_Win64DedicatedServer;
extern int g_Win64DedicatedServerPort; extern int g_Win64DedicatedServerPort;
extern char g_Win64DedicatedServerBindIP[256]; extern char g_Win64DedicatedServerBindIP[256];
extern bool g_Win64DedicatedServerLanAdvertise;
#endif #endif

View file

@ -0,0 +1,460 @@
#include "stdafx.h"
#include "Access.h"
#include "..\Common\StringUtils.h"
#include "..\ServerLogger.h"
#include <memory>
#include <mutex>
namespace ServerRuntime
{
namespace Access
{
namespace
{
/**
* **Access State**
*
* These features are used extensively from various parts of the code, so safe read/write handling is implemented
* Stores the published BAN manager snapshot plus a writer gate for clone-and-publish updates
* BanManagerスナップショットと更新直列化用ロックを保持する
*/
struct AccessState
{
std::mutex stateLock;
std::mutex writeLock;
std::shared_ptr<BanManager> banManager;
std::shared_ptr<WhitelistManager> whitelistManager;
bool whitelistEnabled = false;
};
AccessState g_accessState;
/**
* Copies the currently published manager pointer so readers can work without holding the publish mutex
* BanManager共有ポインタを複製取得する
*/
static std::shared_ptr<BanManager> GetBanManagerSnapshot()
{
std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
return g_accessState.banManager;
}
/**
* Replaces the shared manager pointer with a fully prepared snapshot in one short critical section
* BanManagerスナップショットを短いロックで公開する
*/
static void PublishBanManagerSnapshot(const std::shared_ptr<BanManager> &banManager)
{
std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
g_accessState.banManager = banManager;
}
static std::shared_ptr<WhitelistManager> GetWhitelistManagerSnapshot()
{
std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
return g_accessState.whitelistManager;
}
static void PublishWhitelistManagerSnapshot(const std::shared_ptr<WhitelistManager> &whitelistManager)
{
std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
g_accessState.whitelistManager = whitelistManager;
}
}
std::string FormatXuid(PlayerUID xuid)
{
if (xuid == INVALID_XUID)
{
return "";
}
char buffer[32] = {};
sprintf_s(buffer, sizeof(buffer), "0x%016llx", (unsigned long long)xuid);
return buffer;
}
bool TryParseXuid(const std::string &text, PlayerUID *outXuid)
{
if (outXuid == nullptr)
{
return false;
}
unsigned long long parsed = 0;
if (!StringUtils::TryParseUnsignedLongLong(text, &parsed) || parsed == 0ULL)
{
return false;
}
*outXuid = (PlayerUID)parsed;
return true;
}
bool Initialize(const std::string &baseDirectory, bool whitelistEnabled)
{
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
// Build the replacement manager privately so readers keep using the last published snapshot during disk I/O.
std::shared_ptr<BanManager> banManager = std::make_shared<BanManager>(baseDirectory);
std::shared_ptr<WhitelistManager> whitelistManager = std::make_shared<WhitelistManager>(baseDirectory);
if (!banManager->EnsureBanFilesExist())
{
LogError("access", "failed to ensure dedicated server ban files exist");
return false;
}
if (!whitelistManager->EnsureWhitelistFileExists())
{
LogError("access", "failed to ensure dedicated server whitelist file exists");
return false;
}
if (!banManager->Reload())
{
LogError("access", "failed to load dedicated server ban files");
return false;
}
if (!whitelistManager->Reload())
{
LogError("access", "failed to load dedicated server whitelist file");
return false;
}
std::vector<BannedPlayerEntry> playerEntries;
std::vector<BannedIpEntry> ipEntries;
std::vector<WhitelistedPlayerEntry> whitelistEntries;
banManager->SnapshotBannedPlayers(&playerEntries);
banManager->SnapshotBannedIps(&ipEntries);
whitelistManager->SnapshotWhitelistedPlayers(&whitelistEntries);
PublishBanManagerSnapshot(banManager);
PublishWhitelistManagerSnapshot(whitelistManager);
{
std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
g_accessState.whitelistEnabled = whitelistEnabled;
}
LogInfof(
"access",
"loaded %u player bans, %u ip bans, and %u whitelist entries (whitelist=%s)",
(unsigned)playerEntries.size(),
(unsigned)ipEntries.size(),
(unsigned)whitelistEntries.size(),
whitelistEnabled ? "enabled" : "disabled");
return true;
}
void Shutdown()
{
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
PublishBanManagerSnapshot(std::shared_ptr<BanManager>{});
PublishWhitelistManagerSnapshot(std::shared_ptr<WhitelistManager>{});
std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
g_accessState.whitelistEnabled = false;
}
bool Reload()
{
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
std::shared_ptr<BanManager> current = GetBanManagerSnapshot();
std::shared_ptr<WhitelistManager> currentWhitelist = GetWhitelistManagerSnapshot();
if (current == nullptr || currentWhitelist == nullptr)
{
return false;
}
std::shared_ptr<BanManager> banManager = std::make_shared<BanManager>(*current);
std::shared_ptr<WhitelistManager> whitelistManager = std::make_shared<WhitelistManager>(*currentWhitelist);
if (!banManager->EnsureBanFilesExist())
{
return false;
}
if (!whitelistManager->EnsureWhitelistFileExists())
{
return false;
}
if (!banManager->Reload())
{
return false;
}
if (!whitelistManager->Reload())
{
return false;
}
PublishBanManagerSnapshot(banManager);
PublishWhitelistManagerSnapshot(whitelistManager);
return true;
}
bool ReloadWhitelist()
{
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
const auto current = GetWhitelistManagerSnapshot();
if (current == nullptr)
{
return false;
}
auto whitelistManager = std::make_shared<WhitelistManager>(*current);
if (!whitelistManager->EnsureWhitelistFileExists())
{
return false;
}
if (!whitelistManager->Reload())
{
return false;
}
PublishWhitelistManagerSnapshot(whitelistManager);
return true;
}
bool IsInitialized()
{
return GetBanManagerSnapshot() != nullptr && GetWhitelistManagerSnapshot() != nullptr;
}
bool IsWhitelistEnabled()
{
std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
return g_accessState.whitelistEnabled;
}
void SetWhitelistEnabled(bool enabled)
{
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
g_accessState.whitelistEnabled = enabled;
}
bool IsPlayerBanned(PlayerUID xuid)
{
const std::string formatted = FormatXuid(xuid);
if (formatted.empty())
{
return false;
}
std::shared_ptr<BanManager> banManager = GetBanManagerSnapshot();
return (banManager != nullptr) ? banManager->IsPlayerBannedByXuid(formatted) : false;
}
bool IsIpBanned(const std::string &ip)
{
std::shared_ptr<BanManager> banManager = GetBanManagerSnapshot();
return (banManager != nullptr) ? banManager->IsIpBanned(ip) : false;
}
bool IsPlayerWhitelisted(PlayerUID xuid)
{
const std::string formatted = FormatXuid(xuid);
if (formatted.empty())
{
return false;
}
std::shared_ptr<WhitelistManager> whitelistManager = GetWhitelistManagerSnapshot();
return (whitelistManager != nullptr) ? whitelistManager->IsPlayerWhitelistedByXuid(formatted) : false;
}
bool AddPlayerBan(PlayerUID xuid, const std::string &name, const BanMetadata &metadata)
{
const std::string formatted = FormatXuid(xuid);
if (formatted.empty())
{
return false;
}
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
std::shared_ptr<BanManager> current = GetBanManagerSnapshot();
if (current == nullptr)
{
return false;
}
std::shared_ptr<BanManager> banManager = std::make_shared<BanManager>(*current);
BannedPlayerEntry entry;
entry.xuid = formatted;
entry.name = name;
entry.metadata = metadata;
if (!banManager->AddPlayerBan(entry))
{
return false;
}
PublishBanManagerSnapshot(banManager);
return true;
}
bool AddIpBan(const std::string &ip, const BanMetadata &metadata)
{
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
std::shared_ptr<BanManager> current = GetBanManagerSnapshot();
if (current == nullptr)
{
return false;
}
std::shared_ptr<BanManager> banManager = std::make_shared<BanManager>(*current);
BannedIpEntry entry;
entry.ip = ip;
entry.metadata = metadata;
if (!banManager->AddIpBan(entry))
{
return false;
}
PublishBanManagerSnapshot(banManager);
return true;
}
bool RemovePlayerBan(PlayerUID xuid)
{
const std::string formatted = FormatXuid(xuid);
if (formatted.empty())
{
return false;
}
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
std::shared_ptr<BanManager> current = GetBanManagerSnapshot();
if (current == nullptr)
{
return false;
}
std::shared_ptr<BanManager> banManager = std::make_shared<BanManager>(*current);
if (!banManager->RemovePlayerBanByXuid(formatted))
{
return false;
}
PublishBanManagerSnapshot(banManager);
return true;
}
bool RemoveIpBan(const std::string &ip)
{
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
std::shared_ptr<BanManager> current = GetBanManagerSnapshot();
if (current == nullptr)
{
return false;
}
std::shared_ptr<BanManager> banManager = std::make_shared<BanManager>(*current);
if (!banManager->RemoveIpBan(ip))
{
return false;
}
PublishBanManagerSnapshot(banManager);
return true;
}
bool AddWhitelistedPlayer(PlayerUID xuid, const std::string &name, const WhitelistMetadata &metadata)
{
const auto formatted = FormatXuid(xuid);
if (formatted.empty())
{
return false;
}
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
const auto current = GetWhitelistManagerSnapshot();
if (current == nullptr)
{
return false;
}
auto whitelistManager = std::make_shared<WhitelistManager>(*current);
const WhitelistedPlayerEntry entry = { formatted, name, metadata };
if (!whitelistManager->AddPlayer(entry))
{
return false;
}
PublishWhitelistManagerSnapshot(whitelistManager);
return true;
}
bool RemoveWhitelistedPlayer(PlayerUID xuid)
{
const auto formatted = FormatXuid(xuid);
if (formatted.empty())
{
return false;
}
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
const auto current = GetWhitelistManagerSnapshot();
if (current == nullptr)
{
return false;
}
auto whitelistManager = std::make_shared<WhitelistManager>(*current);
if (!whitelistManager->RemovePlayerByXuid(formatted))
{
return false;
}
PublishWhitelistManagerSnapshot(whitelistManager);
return true;
}
bool SnapshotBannedPlayers(std::vector<BannedPlayerEntry> *outEntries)
{
if (outEntries == nullptr)
{
return false;
}
std::shared_ptr<BanManager> banManager = GetBanManagerSnapshot();
if (banManager == nullptr)
{
outEntries->clear();
return false;
}
return banManager->SnapshotBannedPlayers(outEntries);
}
bool SnapshotBannedIps(std::vector<BannedIpEntry> *outEntries)
{
if (outEntries == nullptr)
{
return false;
}
std::shared_ptr<BanManager> banManager = GetBanManagerSnapshot();
if (banManager == nullptr)
{
outEntries->clear();
return false;
}
return banManager->SnapshotBannedIps(outEntries);
}
bool SnapshotWhitelistedPlayers(std::vector<WhitelistedPlayerEntry> *outEntries)
{
if (outEntries == nullptr)
{
return false;
}
const auto whitelistManager = GetWhitelistManagerSnapshot();
if (whitelistManager == nullptr)
{
outEntries->clear();
return false;
}
return whitelistManager->SnapshotWhitelistedPlayers(outEntries);
}
}
}

View file

@ -0,0 +1,47 @@
#pragma once
#include "BanManager.h"
#include "WhitelistManager.h"
namespace ServerRuntime
{
/**
* A frontend that will be general-purpose, assuming the implementation of whitelists and ops in the future.
*/
namespace Access
{
bool Initialize(const std::string &baseDirectory = ".", bool whitelistEnabled = false);
void Shutdown();
bool Reload();
bool ReloadWhitelist();
bool IsInitialized();
bool IsWhitelistEnabled();
void SetWhitelistEnabled(bool enabled);
bool IsPlayerBanned(PlayerUID xuid);
bool IsIpBanned(const std::string &ip);
bool IsPlayerWhitelisted(PlayerUID xuid);
bool AddPlayerBan(PlayerUID xuid, const std::string &name, const BanMetadata &metadata);
bool AddIpBan(const std::string &ip, const BanMetadata &metadata);
bool RemovePlayerBan(PlayerUID xuid);
bool RemoveIpBan(const std::string &ip);
bool AddWhitelistedPlayer(PlayerUID xuid, const std::string &name, const WhitelistMetadata &metadata);
bool RemoveWhitelistedPlayer(PlayerUID xuid);
/**
* Copies the current cached player bans for inspection or command output
* BAN一覧を複製取得
*/
bool SnapshotBannedPlayers(std::vector<BannedPlayerEntry> *outEntries);
/**
* Copies the current cached IP bans for inspection or command output
* IP BAN一覧を複製取得
*/
bool SnapshotBannedIps(std::vector<BannedIpEntry> *outEntries);
bool SnapshotWhitelistedPlayers(std::vector<WhitelistedPlayerEntry> *outEntries);
std::string FormatXuid(PlayerUID xuid);
bool TryParseXuid(const std::string &text, PlayerUID *outXuid);
}
}

View file

@ -0,0 +1,631 @@
#include "stdafx.h"
#include "BanManager.h"
#include "..\Common\AccessStorageUtils.h"
#include "..\Common\FileUtils.h"
#include "..\Common\NetworkUtils.h"
#include "..\Common\StringUtils.h"
#include "..\ServerLogger.h"
#include "..\vendor\nlohmann\json.hpp"
#include <algorithm>
#include <stdio.h>
namespace ServerRuntime
{
namespace Access
{
using OrderedJson = nlohmann::ordered_json;
namespace
{
static const char *kBannedPlayersFileName = "banned-players.json";
static const char *kBannedIpsFileName = "banned-ips.json";
static bool TryParseUtcTimestamp(const std::string &text, unsigned long long *outFileTime)
{
if (outFileTime == nullptr)
{
return false;
}
std::string trimmed = StringUtils::TrimAscii(text);
if (trimmed.empty())
{
return false;
}
unsigned year = 0;
unsigned month = 0;
unsigned day = 0;
unsigned hour = 0;
unsigned minute = 0;
unsigned second = 0;
if (sscanf_s(trimmed.c_str(), "%4u-%2u-%2uT%2u:%2u:%2uZ", &year, &month, &day, &hour, &minute, &second) != 6)
{
return false;
}
SYSTEMTIME utc = {};
utc.wYear = (WORD)year;
utc.wMonth = (WORD)month;
utc.wDay = (WORD)day;
utc.wHour = (WORD)hour;
utc.wMinute = (WORD)minute;
utc.wSecond = (WORD)second;
FILETIME fileTime = {};
if (!SystemTimeToFileTime(&utc, &fileTime))
{
return false;
}
ULARGE_INTEGER value = {};
value.LowPart = fileTime.dwLowDateTime;
value.HighPart = fileTime.dwHighDateTime;
*outFileTime = value.QuadPart;
return true;
}
static bool IsMetadataExpired(const BanMetadata &metadata, unsigned long long nowFileTime)
{
if (metadata.expires.empty())
{
return false;
}
unsigned long long expiresFileTime = 0;
if (!TryParseUtcTimestamp(metadata.expires, &expiresFileTime))
{
// Keep malformed metadata active instead of silently unbanning a player or address.
return false;
}
return expiresFileTime <= nowFileTime;
}
}
BanManager::BanManager(const std::string &baseDirectory)
: m_baseDirectory(baseDirectory.empty() ? "." : baseDirectory)
{
}
bool BanManager::EnsureBanFilesExist() const
{
const std::string playersPath = GetBannedPlayersFilePath();
const std::string ipsPath = GetBannedIpsFilePath();
const bool playersOk = AccessStorageUtils::EnsureJsonListFileExists(playersPath);
const bool ipsOk = AccessStorageUtils::EnsureJsonListFileExists(ipsPath);
if (!playersOk)
{
LogErrorf("access", "failed to create %s", playersPath.c_str());
}
if (!ipsOk)
{
LogErrorf("access", "failed to create %s", ipsPath.c_str());
}
return playersOk && ipsOk;
}
bool BanManager::Reload()
{
std::vector<BannedPlayerEntry> players;
std::vector<BannedIpEntry> ips;
if (!LoadPlayers(&players))
{
return false;
}
if (!LoadIps(&ips))
{
return false;
}
m_bannedPlayers.swap(players);
m_bannedIps.swap(ips);
return true;
}
bool BanManager::Save() const
{
std::vector<BannedPlayerEntry> players;
std::vector<BannedIpEntry> ips;
return SnapshotBannedPlayers(&players) &&
SnapshotBannedIps(&ips) &&
SavePlayers(players) &&
SaveIps(ips);
}
bool BanManager::LoadPlayers(std::vector<BannedPlayerEntry> *outEntries) const
{
if (outEntries == nullptr)
{
return false;
}
outEntries->clear();
std::string text;
const std::string path = GetBannedPlayersFilePath();
if (!FileUtils::ReadTextFile(path, &text))
{
LogErrorf("access", "failed to read %s", path.c_str());
return false;
}
if (text.empty())
{
text = "[]";
}
OrderedJson root;
try
{
// Strip an optional UTF-8 BOM because some editors prepend it when rewriting JSON files.
root = OrderedJson::parse(StringUtils::StripUtf8Bom(text));
}
catch (const nlohmann::json::exception &e)
{
LogErrorf("access", "failed to parse %s: %s", path.c_str(), e.what());
return false;
}
if (!root.is_array())
{
LogErrorf("access", "failed to parse %s: root json value is not an array", path.c_str());
return false;
}
const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime();
for (const auto &object : root)
{
if (!object.is_object())
{
LogWarnf("access", "skipping banned player entry that is not an object in %s", path.c_str());
continue;
}
std::string rawXuid;
if (!AccessStorageUtils::TryGetStringField(object, "xuid", &rawXuid))
{
LogWarnf("access", "skipping banned player entry without xuid in %s", path.c_str());
continue;
}
BannedPlayerEntry entry;
entry.xuid = AccessStorageUtils::NormalizeXuid(rawXuid);
if (entry.xuid.empty())
{
LogWarnf("access", "skipping banned player entry with empty xuid in %s", path.c_str());
continue;
}
AccessStorageUtils::TryGetStringField(object, "name", &entry.name);
AccessStorageUtils::TryGetStringField(object, "created", &entry.metadata.created);
AccessStorageUtils::TryGetStringField(object, "source", &entry.metadata.source);
AccessStorageUtils::TryGetStringField(object, "expires", &entry.metadata.expires);
AccessStorageUtils::TryGetStringField(object, "reason", &entry.metadata.reason);
NormalizeMetadata(&entry.metadata);
// Ignore entries that already expired before reload so the in-memory cache starts from the active set.
if (IsMetadataExpired(entry.metadata, nowFileTime))
{
continue;
}
outEntries->push_back(entry);
}
return true;
}
bool BanManager::LoadIps(std::vector<BannedIpEntry> *outEntries) const
{
if (outEntries == nullptr)
{
return false;
}
outEntries->clear();
std::string text;
const std::string path = GetBannedIpsFilePath();
if (!FileUtils::ReadTextFile(path, &text))
{
LogErrorf("access", "failed to read %s", path.c_str());
return false;
}
if (text.empty())
{
text = "[]";
}
OrderedJson root;
try
{
// Strip an optional UTF-8 BOM because some editors prepend it when rewriting JSON files.
root = OrderedJson::parse(StringUtils::StripUtf8Bom(text));
}
catch (const nlohmann::json::exception &e)
{
LogErrorf("access", "failed to parse %s: %s", path.c_str(), e.what());
return false;
}
if (!root.is_array())
{
LogErrorf("access", "failed to parse %s: root json value is not an array", path.c_str());
return false;
}
const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime();
for (const auto &object : root)
{
if (!object.is_object())
{
LogWarnf("access", "skipping banned ip entry that is not an object in %s", path.c_str());
continue;
}
std::string rawIp;
if (!AccessStorageUtils::TryGetStringField(object, "ip", &rawIp))
{
LogWarnf("access", "skipping banned ip entry without ip in %s", path.c_str());
continue;
}
BannedIpEntry entry;
entry.ip = NetworkUtils::NormalizeIpToken(rawIp);
if (entry.ip.empty())
{
LogWarnf("access", "skipping banned ip entry with empty ip in %s", path.c_str());
continue;
}
AccessStorageUtils::TryGetStringField(object, "created", &entry.metadata.created);
AccessStorageUtils::TryGetStringField(object, "source", &entry.metadata.source);
AccessStorageUtils::TryGetStringField(object, "expires", &entry.metadata.expires);
AccessStorageUtils::TryGetStringField(object, "reason", &entry.metadata.reason);
NormalizeMetadata(&entry.metadata);
// Ignore entries that already expired before reload so the in-memory cache starts from the active set.
if (IsMetadataExpired(entry.metadata, nowFileTime))
{
continue;
}
outEntries->push_back(entry);
}
return true;
}
bool BanManager::SavePlayers(const std::vector<BannedPlayerEntry> &entries) const
{
OrderedJson root = OrderedJson::array();
for (const auto &entry : entries)
{
OrderedJson object = OrderedJson::object();
object["xuid"] = AccessStorageUtils::NormalizeXuid(entry.xuid);
object["name"] = entry.name;
object["created"] = entry.metadata.created;
object["source"] = entry.metadata.source;
object["expires"] = entry.metadata.expires;
object["reason"] = entry.metadata.reason;
root.push_back(object);
}
const std::string path = GetBannedPlayersFilePath();
const std::string json = root.empty() ? std::string("[]\n") : (root.dump(2) + "\n");
if (!FileUtils::WriteTextFileAtomic(path, json))
{
LogErrorf("access", "failed to write %s", path.c_str());
return false;
}
return true;
}
bool BanManager::SaveIps(const std::vector<BannedIpEntry> &entries) const
{
OrderedJson root = OrderedJson::array();
for (const auto &entry : entries)
{
OrderedJson object = OrderedJson::object();
object["ip"] = NetworkUtils::NormalizeIpToken(entry.ip);
object["created"] = entry.metadata.created;
object["source"] = entry.metadata.source;
object["expires"] = entry.metadata.expires;
object["reason"] = entry.metadata.reason;
root.push_back(object);
}
const std::string path = GetBannedIpsFilePath();
const std::string json = root.empty() ? std::string("[]\n") : (root.dump(2) + "\n");
if (!FileUtils::WriteTextFileAtomic(path, json))
{
LogErrorf("access", "failed to write %s", path.c_str());
return false;
}
return true;
}
const std::vector<BannedPlayerEntry> &BanManager::GetBannedPlayers() const
{
return m_bannedPlayers;
}
const std::vector<BannedIpEntry> &BanManager::GetBannedIps() const
{
return m_bannedIps;
}
bool BanManager::SnapshotBannedPlayers(std::vector<BannedPlayerEntry> *outEntries) const
{
if (outEntries == nullptr)
{
return false;
}
outEntries->clear();
outEntries->reserve(m_bannedPlayers.size());
const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime();
for (const auto &entry : m_bannedPlayers)
{
if (!IsMetadataExpired(entry.metadata, nowFileTime))
{
outEntries->push_back(entry);
}
}
return true;
}
bool BanManager::SnapshotBannedIps(std::vector<BannedIpEntry> *outEntries) const
{
if (outEntries == nullptr)
{
return false;
}
outEntries->clear();
outEntries->reserve(m_bannedIps.size());
const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime();
for (const auto &entry : m_bannedIps)
{
if (!IsMetadataExpired(entry.metadata, nowFileTime))
{
outEntries->push_back(entry);
}
}
return true;
}
bool BanManager::IsPlayerBannedByXuid(const std::string &xuid) const
{
const std::string normalized = AccessStorageUtils::NormalizeXuid(xuid);
if (normalized.empty())
{
return false;
}
const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime();
return std::any_of(
m_bannedPlayers.begin(),
m_bannedPlayers.end(),
[&normalized, nowFileTime](const BannedPlayerEntry &entry)
{
return entry.xuid == normalized && !IsMetadataExpired(entry.metadata, nowFileTime);
});
}
bool BanManager::IsIpBanned(const std::string &ip) const
{
const std::string normalized = NetworkUtils::NormalizeIpToken(ip);
if (normalized.empty())
{
return false;
}
const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime();
return std::any_of(
m_bannedIps.begin(),
m_bannedIps.end(),
[&normalized, nowFileTime](const BannedIpEntry &entry)
{
return entry.ip == normalized && !IsMetadataExpired(entry.metadata, nowFileTime);
});
}
bool BanManager::AddPlayerBan(const BannedPlayerEntry &entry)
{
std::vector<BannedPlayerEntry> updatedEntries;
if (!SnapshotBannedPlayers(&updatedEntries))
{
return false;
}
BannedPlayerEntry normalized = entry;
normalized.xuid = AccessStorageUtils::NormalizeXuid(normalized.xuid);
NormalizeMetadata(&normalized.metadata);
if (normalized.xuid.empty())
{
return false;
}
const auto existing = std::find_if(
updatedEntries.begin(),
updatedEntries.end(),
[&normalized](const BannedPlayerEntry &candidate)
{
return candidate.xuid == normalized.xuid;
});
if (existing != updatedEntries.end())
{
// Update the existing entry in place so the stored list remains unique by canonical XUID.
*existing = normalized;
if (!SavePlayers(updatedEntries))
{
return false;
}
m_bannedPlayers.swap(updatedEntries);
return true;
}
updatedEntries.push_back(normalized);
if (!SavePlayers(updatedEntries))
{
return false;
}
m_bannedPlayers.swap(updatedEntries);
return true;
}
bool BanManager::AddIpBan(const BannedIpEntry &entry)
{
std::vector<BannedIpEntry> updatedEntries;
if (!SnapshotBannedIps(&updatedEntries))
{
return false;
}
BannedIpEntry normalized = entry;
normalized.ip = NetworkUtils::NormalizeIpToken(normalized.ip);
NormalizeMetadata(&normalized.metadata);
if (normalized.ip.empty())
{
return false;
}
const auto existing = std::find_if(
updatedEntries.begin(),
updatedEntries.end(),
[&normalized](const BannedIpEntry &candidate)
{
return candidate.ip == normalized.ip;
});
if (existing != updatedEntries.end())
{
// Update the existing entry in place so the stored list remains unique by normalized IP.
*existing = normalized;
if (!SaveIps(updatedEntries))
{
return false;
}
m_bannedIps.swap(updatedEntries);
return true;
}
updatedEntries.push_back(normalized);
if (!SaveIps(updatedEntries))
{
return false;
}
m_bannedIps.swap(updatedEntries);
return true;
}
bool BanManager::RemovePlayerBanByXuid(const std::string &xuid)
{
const std::string normalized = AccessStorageUtils::NormalizeXuid(xuid);
if (normalized.empty())
{
return false;
}
std::vector<BannedPlayerEntry> updatedEntries;
if (!SnapshotBannedPlayers(&updatedEntries))
{
return false;
}
size_t oldSize = updatedEntries.size();
updatedEntries.erase(
std::remove_if(
updatedEntries.begin(),
updatedEntries.end(),
[&normalized](const BannedPlayerEntry &entry) { return entry.xuid == normalized; }),
updatedEntries.end());
if (updatedEntries.size() == oldSize)
{
return false;
}
if (!SavePlayers(updatedEntries))
{
return false;
}
m_bannedPlayers.swap(updatedEntries);
return true;
}
bool BanManager::RemoveIpBan(const std::string &ip)
{
const std::string normalized = NetworkUtils::NormalizeIpToken(ip);
if (normalized.empty())
{
return false;
}
std::vector<BannedIpEntry> updatedEntries;
if (!SnapshotBannedIps(&updatedEntries))
{
return false;
}
size_t oldSize = updatedEntries.size();
updatedEntries.erase(
std::remove_if(
updatedEntries.begin(),
updatedEntries.end(),
[&normalized](const BannedIpEntry &entry) { return entry.ip == normalized; }),
updatedEntries.end());
if (updatedEntries.size() == oldSize)
{
return false;
}
if (!SaveIps(updatedEntries))
{
return false;
}
m_bannedIps.swap(updatedEntries);
return true;
}
std::string BanManager::GetBannedPlayersFilePath() const
{
return BuildPath(kBannedPlayersFileName);
}
std::string BanManager::GetBannedIpsFilePath() const
{
return BuildPath(kBannedIpsFileName);
}
BanMetadata BanManager::BuildDefaultMetadata(const char *source)
{
BanMetadata metadata;
metadata.created = StringUtils::GetCurrentUtcTimestampIso8601();
metadata.source = (source != nullptr) ? source : "Server";
metadata.expires = "";
metadata.reason = "";
return metadata;
}
void BanManager::NormalizeMetadata(BanMetadata *metadata)
{
if (metadata == nullptr)
{
return;
}
metadata->created = StringUtils::TrimAscii(metadata->created);
metadata->source = StringUtils::TrimAscii(metadata->source);
metadata->expires = StringUtils::TrimAscii(metadata->expires);
metadata->reason = StringUtils::TrimAscii(metadata->reason);
}
std::string BanManager::BuildPath(const char *fileName) const
{
return AccessStorageUtils::BuildPathFromBaseDirectory(m_baseDirectory, fileName);
}
}
}

View file

@ -0,0 +1,106 @@
#pragma once
#include <string>
#include <vector>
namespace ServerRuntime
{
namespace Access
{
/**
* Information shared with player bans and IP bans
* BANとIP BANで共有する情報
*/
struct BanMetadata
{
std::string created;
std::string source;
std::string expires;
std::string reason;
};
struct BannedPlayerEntry
{
std::string xuid;
std::string name;
BanMetadata metadata;
};
struct BannedIpEntry
{
std::string ip;
BanMetadata metadata;
};
/**
* Dedicated server BAN file manager.
*
* Files:
* - banned-players.json
* - banned-ips.json
*
* This class only handles storage/caching.
* Connection-time hooks are wired separately.
*/
class BanManager
{
public:
/**
* **Create Ban Manager**
*
* Binds the manager to the directory that stores the dedicated-server access files
* Dedicated Server
*/
explicit BanManager(const std::string &baseDirectory = ".");
/**
* Creates empty JSON array files when the dedicated server starts without persisted access data
* BANファイルが無い初回起動時に空JSONを用意する
*/
bool EnsureBanFilesExist() const;
bool Reload();
bool Save() const;
bool LoadPlayers(std::vector<BannedPlayerEntry> *outEntries) const;
bool LoadIps(std::vector<BannedIpEntry> *outEntries) const;
bool SavePlayers(const std::vector<BannedPlayerEntry> &entries) const;
bool SaveIps(const std::vector<BannedIpEntry> &entries) const;
const std::vector<BannedPlayerEntry> &GetBannedPlayers() const;
const std::vector<BannedIpEntry> &GetBannedIps() const;
/**
* Copies only currently active player BAN entries so expired metadata does not leak into command output
* BAN一覧を複製取得する
*/
bool SnapshotBannedPlayers(std::vector<BannedPlayerEntry> *outEntries) const;
/**
* Copies only currently active IP BAN entries so expired metadata does not leak into command output
* IP BAN一覧を複製取得する
*/
bool SnapshotBannedIps(std::vector<BannedIpEntry> *outEntries) const;
bool IsPlayerBannedByXuid(const std::string &xuid) const;
bool IsIpBanned(const std::string &ip) const;
bool AddPlayerBan(const BannedPlayerEntry &entry);
bool AddIpBan(const BannedIpEntry &entry);
bool RemovePlayerBanByXuid(const std::string &xuid);
bool RemoveIpBan(const std::string &ip);
std::string GetBannedPlayersFilePath() const;
std::string GetBannedIpsFilePath() const;
static BanMetadata BuildDefaultMetadata(const char *source = "Server");
private:
static void NormalizeMetadata(BanMetadata *metadata);
std::string BuildPath(const char *fileName) const;
private:
std::string m_baseDirectory;
std::vector<BannedPlayerEntry> m_bannedPlayers;
std::vector<BannedIpEntry> m_bannedIps;
};
}
}

View file

@ -0,0 +1,297 @@
#include "stdafx.h"
#include "WhitelistManager.h"
#include "..\Common\AccessStorageUtils.h"
#include "..\Common\FileUtils.h"
#include "..\Common\StringUtils.h"
#include "..\ServerLogger.h"
#include "..\vendor\nlohmann\json.hpp"
#include <algorithm>
namespace ServerRuntime
{
namespace Access
{
using OrderedJson = nlohmann::ordered_json;
namespace
{
static const char *kWhitelistFileName = "whitelist.json";
}
WhitelistManager::WhitelistManager(const std::string &baseDirectory)
: m_baseDirectory(baseDirectory.empty() ? "." : baseDirectory)
{
}
bool WhitelistManager::EnsureWhitelistFileExists() const
{
const std::string path = GetWhitelistFilePath();
if (!AccessStorageUtils::EnsureJsonListFileExists(path))
{
LogErrorf("access", "failed to create %s", path.c_str());
return false;
}
return true;
}
bool WhitelistManager::Reload()
{
std::vector<WhitelistedPlayerEntry> players;
if (!LoadPlayers(&players))
{
return false;
}
m_whitelistedPlayers.swap(players);
return true;
}
bool WhitelistManager::Save() const
{
std::vector<WhitelistedPlayerEntry> players;
return SnapshotWhitelistedPlayers(&players) && SavePlayers(players);
}
bool WhitelistManager::LoadPlayers(std::vector<WhitelistedPlayerEntry> *outEntries) const
{
if (outEntries == nullptr)
{
return false;
}
outEntries->clear();
std::string text;
const std::string path = GetWhitelistFilePath();
if (!FileUtils::ReadTextFile(path, &text))
{
LogErrorf("access", "failed to read %s", path.c_str());
return false;
}
if (text.empty())
{
text = "[]";
}
OrderedJson root;
try
{
root = OrderedJson::parse(StringUtils::StripUtf8Bom(text));
}
catch (const nlohmann::json::exception &e)
{
LogErrorf("access", "failed to parse %s: %s", path.c_str(), e.what());
return false;
}
if (!root.is_array())
{
LogErrorf("access", "failed to parse %s: root json value is not an array", path.c_str());
return false;
}
for (const auto &object : root)
{
if (!object.is_object())
{
LogWarnf("access", "skipping whitelist entry that is not an object in %s", path.c_str());
continue;
}
std::string rawXuid;
if (!AccessStorageUtils::TryGetStringField(object, "xuid", &rawXuid))
{
LogWarnf("access", "skipping whitelist entry without xuid in %s", path.c_str());
continue;
}
WhitelistedPlayerEntry entry;
entry.xuid = AccessStorageUtils::NormalizeXuid(rawXuid);
if (entry.xuid.empty())
{
LogWarnf("access", "skipping whitelist entry with empty xuid in %s", path.c_str());
continue;
}
AccessStorageUtils::TryGetStringField(object, "name", &entry.name);
AccessStorageUtils::TryGetStringField(object, "created", &entry.metadata.created);
AccessStorageUtils::TryGetStringField(object, "source", &entry.metadata.source);
NormalizeMetadata(&entry.metadata);
outEntries->push_back(entry);
}
return true;
}
bool WhitelistManager::SavePlayers(const std::vector<WhitelistedPlayerEntry> &entries) const
{
OrderedJson root = OrderedJson::array();
for (const auto &entry : entries)
{
OrderedJson object = OrderedJson::object();
object["xuid"] = AccessStorageUtils::NormalizeXuid(entry.xuid);
object["name"] = entry.name;
object["created"] = entry.metadata.created;
object["source"] = entry.metadata.source;
root.push_back(object);
}
const std::string path = GetWhitelistFilePath();
const std::string json = root.empty() ? std::string("[]\n") : (root.dump(2) + "\n");
if (!FileUtils::WriteTextFileAtomic(path, json))
{
LogErrorf("access", "failed to write %s", path.c_str());
return false;
}
return true;
}
const std::vector<WhitelistedPlayerEntry> &WhitelistManager::GetWhitelistedPlayers() const
{
return m_whitelistedPlayers;
}
bool WhitelistManager::SnapshotWhitelistedPlayers(std::vector<WhitelistedPlayerEntry> *outEntries) const
{
if (outEntries == nullptr)
{
return false;
}
*outEntries = m_whitelistedPlayers;
return true;
}
bool WhitelistManager::IsPlayerWhitelistedByXuid(const std::string &xuid) const
{
const auto normalized = AccessStorageUtils::NormalizeXuid(xuid);
if (normalized.empty())
{
return false;
}
return std::any_of(
m_whitelistedPlayers.begin(),
m_whitelistedPlayers.end(),
[&normalized](const WhitelistedPlayerEntry &entry)
{
return entry.xuid == normalized;
});
}
bool WhitelistManager::AddPlayer(const WhitelistedPlayerEntry &entry)
{
std::vector<WhitelistedPlayerEntry> updatedEntries;
if (!SnapshotWhitelistedPlayers(&updatedEntries))
{
return false;
}
auto normalized = entry;
normalized.xuid = AccessStorageUtils::NormalizeXuid(normalized.xuid);
NormalizeMetadata(&normalized.metadata);
if (normalized.xuid.empty())
{
return false;
}
const auto existing = std::find_if(
updatedEntries.begin(),
updatedEntries.end(),
[&normalized](const WhitelistedPlayerEntry &candidate)
{
return candidate.xuid == normalized.xuid;
});
if (existing != updatedEntries.end())
{
*existing = normalized;
if (!SavePlayers(updatedEntries))
{
return false;
}
m_whitelistedPlayers.swap(updatedEntries);
return true;
}
updatedEntries.push_back(normalized);
if (!SavePlayers(updatedEntries))
{
return false;
}
m_whitelistedPlayers.swap(updatedEntries);
return true;
}
bool WhitelistManager::RemovePlayerByXuid(const std::string &xuid)
{
const auto normalized = AccessStorageUtils::NormalizeXuid(xuid);
if (normalized.empty())
{
return false;
}
std::vector<WhitelistedPlayerEntry> updatedEntries;
if (!SnapshotWhitelistedPlayers(&updatedEntries))
{
return false;
}
const auto oldSize = updatedEntries.size();
updatedEntries.erase(
std::remove_if(
updatedEntries.begin(),
updatedEntries.end(),
[&normalized](const WhitelistedPlayerEntry &entry) { return entry.xuid == normalized; }),
updatedEntries.end());
if (updatedEntries.size() == oldSize)
{
return false;
}
if (!SavePlayers(updatedEntries))
{
return false;
}
m_whitelistedPlayers.swap(updatedEntries);
return true;
}
std::string WhitelistManager::GetWhitelistFilePath() const
{
return BuildPath(kWhitelistFileName);
}
WhitelistMetadata WhitelistManager::BuildDefaultMetadata(const char *source)
{
WhitelistMetadata metadata;
metadata.created = StringUtils::GetCurrentUtcTimestampIso8601();
metadata.source = (source != nullptr) ? source : "Server";
return metadata;
}
void WhitelistManager::NormalizeMetadata(WhitelistMetadata *metadata)
{
if (metadata == nullptr)
{
return;
}
metadata->created = StringUtils::TrimAscii(metadata->created);
metadata->source = StringUtils::TrimAscii(metadata->source);
}
std::string WhitelistManager::BuildPath(const char *fileName) const
{
return AccessStorageUtils::BuildPathFromBaseDirectory(m_baseDirectory, fileName);
}
}
}

View file

@ -0,0 +1,64 @@
#pragma once
#include <string>
#include <vector>
namespace ServerRuntime
{
namespace Access
{
struct WhitelistMetadata
{
std::string created;
std::string source;
};
struct WhitelistedPlayerEntry
{
std::string xuid;
std::string name;
WhitelistMetadata metadata;
};
/**
* whitelist manager
*
* Files:
* - whitelist.json
*
* Stores and normalizes XUID-based allow entries.
*/
class WhitelistManager
{
public:
explicit WhitelistManager(const std::string &baseDirectory = ".");
bool EnsureWhitelistFileExists() const;
bool Reload();
bool Save() const;
bool LoadPlayers(std::vector<WhitelistedPlayerEntry> *outEntries) const;
bool SavePlayers(const std::vector<WhitelistedPlayerEntry> &entries) const;
const std::vector<WhitelistedPlayerEntry> &GetWhitelistedPlayers() const;
bool SnapshotWhitelistedPlayers(std::vector<WhitelistedPlayerEntry> *outEntries) const;
bool IsPlayerWhitelistedByXuid(const std::string &xuid) const;
bool AddPlayer(const WhitelistedPlayerEntry &entry);
bool RemovePlayerByXuid(const std::string &xuid);
std::string GetWhitelistFilePath() const;
static WhitelistMetadata BuildDefaultMetadata(const char *source = "Server");
private:
static void NormalizeMetadata(WhitelistMetadata *metadata);
std::string BuildPath(const char *fileName) const;
private:
std::string m_baseDirectory;
std::vector<WhitelistedPlayerEntry> m_whitelistedPlayers;
};
}
}

View file

@ -0,0 +1,105 @@
#pragma once
#include "FileUtils.h"
#include "StringUtils.h"
#include "..\vendor\nlohmann\json.hpp"
#include <stdio.h>
namespace ServerRuntime
{
namespace AccessStorageUtils
{
inline bool IsRegularFile(const std::string &path)
{
const std::wstring widePath = StringUtils::Utf8ToWide(path);
if (widePath.empty())
{
return false;
}
const DWORD attributes = GetFileAttributesW(widePath.c_str());
return (attributes != INVALID_FILE_ATTRIBUTES) && ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0);
}
inline bool EnsureJsonListFileExists(const std::string &path)
{
return IsRegularFile(path) || FileUtils::WriteTextFileAtomic(path, "[]\n");
}
inline bool TryGetStringField(const nlohmann::ordered_json &object, const char *key, std::string *outValue)
{
if (key == nullptr || outValue == nullptr || !object.is_object())
{
return false;
}
const auto it = object.find(key);
if (it == object.end() || !it->is_string())
{
return false;
}
*outValue = it->get<std::string>();
return true;
}
inline std::string NormalizeXuid(const std::string &xuid)
{
const std::string trimmed = StringUtils::TrimAscii(xuid);
if (trimmed.empty())
{
return "";
}
unsigned long long numericXuid = 0;
if (StringUtils::TryParseUnsignedLongLong(trimmed, &numericXuid))
{
if (numericXuid == 0ULL)
{
return "";
}
char buffer[32] = {};
sprintf_s(buffer, sizeof(buffer), "0x%016llx", numericXuid);
return buffer;
}
return StringUtils::ToLowerAscii(trimmed);
}
inline std::string BuildPathFromBaseDirectory(const std::string &baseDirectory, const char *fileName)
{
if (fileName == nullptr || fileName[0] == 0)
{
return "";
}
const std::wstring wideFileName = StringUtils::Utf8ToWide(fileName);
if (wideFileName.empty())
{
return "";
}
if (baseDirectory.empty() || baseDirectory == ".")
{
return StringUtils::WideToUtf8(wideFileName);
}
const std::wstring wideBaseDirectory = StringUtils::Utf8ToWide(baseDirectory);
if (wideBaseDirectory.empty())
{
return StringUtils::WideToUtf8(wideFileName);
}
const wchar_t last = wideBaseDirectory[wideBaseDirectory.size() - 1];
if (last == L'\\' || last == L'/')
{
return StringUtils::WideToUtf8(wideBaseDirectory + wideFileName);
}
return StringUtils::WideToUtf8(wideBaseDirectory + L"\\" + wideFileName);
}
}
}

View file

@ -0,0 +1,146 @@
#include "stdafx.h"
#include "FileUtils.h"
#include "StringUtils.h"
#include <io.h>
#include <stdio.h>
namespace ServerRuntime
{
namespace FileUtils
{
namespace
{
static std::wstring ToWidePath(const std::string &filePath)
{
return StringUtils::Utf8ToWide(filePath);
}
}
unsigned long long GetCurrentUtcFileTime()
{
FILETIME now = {};
GetSystemTimeAsFileTime(&now);
ULARGE_INTEGER value = {};
value.LowPart = now.dwLowDateTime;
value.HighPart = now.dwHighDateTime;
return value.QuadPart;
}
bool ReadTextFile(const std::string &filePath, std::string *outText)
{
if (outText == nullptr)
{
return false;
}
outText->clear();
const std::wstring widePath = ToWidePath(filePath);
if (widePath.empty())
{
return false;
}
FILE *inFile = nullptr;
if (_wfopen_s(&inFile, widePath.c_str(), L"rb") != 0 || inFile == nullptr)
{
return false;
}
if (fseek(inFile, 0, SEEK_END) != 0)
{
fclose(inFile);
return false;
}
long fileSize = ftell(inFile);
if (fileSize < 0)
{
fclose(inFile);
return false;
}
if (fseek(inFile, 0, SEEK_SET) != 0)
{
fclose(inFile);
return false;
}
if (fileSize == 0)
{
fclose(inFile);
return true;
}
outText->resize((size_t)fileSize);
size_t bytesRead = fread(&(*outText)[0], 1, (size_t)fileSize, inFile);
fclose(inFile);
if (bytesRead != (size_t)fileSize)
{
outText->clear();
return false;
}
return true;
}
bool WriteTextFileAtomic(const std::string &filePath, const std::string &text)
{
const std::wstring widePath = ToWidePath(filePath);
if (widePath.empty())
{
return false;
}
const std::wstring tmpPath = widePath + L".tmp";
FILE *outFile = nullptr;
if (_wfopen_s(&outFile, tmpPath.c_str(), L"wb") != 0 || outFile == nullptr)
{
return false;
}
if (!text.empty())
{
size_t bytesWritten = fwrite(text.data(), 1, text.size(), outFile);
if (bytesWritten != text.size())
{
fclose(outFile);
DeleteFileW(tmpPath.c_str());
return false;
}
}
if (fflush(outFile) != 0 || _commit(_fileno(outFile)) != 0)
{
fclose(outFile);
DeleteFileW(tmpPath.c_str());
return false;
}
fclose(outFile);
DWORD attrs = GetFileAttributesW(widePath.c_str());
if (attrs != INVALID_FILE_ATTRIBUTES && ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0))
{
// Replace the destination without deleting the last known-good file first.
if (ReplaceFileW(widePath.c_str(), tmpPath.c_str(), nullptr, REPLACEFILE_IGNORE_MERGE_ERRORS, nullptr, nullptr))
{
return true;
}
}
if (MoveFileExW(tmpPath.c_str(), widePath.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))
{
return true;
}
// Keep the temp file on failure so the original file remains recoverable and the caller can inspect the write result.
return false;
}
}
}

View file

@ -0,0 +1,25 @@
#pragma once
#include <string>
namespace ServerRuntime
{
namespace FileUtils
{
/**
* Reads the full UTF-8 path target into memory without interpreting JSON or line endings
* UTF-8
*/
bool ReadTextFile(const std::string &filePath, std::string *outText);
/**
* Writes text through a same-directory temporary file and publishes it with a single replacement step
*
*/
bool WriteTextFileAtomic(const std::string &filePath, const std::string &text);
/**
* Returns the current UTC timestamp encoded in Windows FILETIME units for expiry comparisons
* UTC時刻をWindows FILETIME単位で返す
*/
unsigned long long GetCurrentUtcFileTime();
}
}

View file

@ -0,0 +1,29 @@
#pragma once
#include "StringUtils.h"
#include <WS2tcpip.h>
namespace ServerRuntime
{
namespace NetworkUtils
{
inline std::string NormalizeIpToken(const std::string &ip)
{
return StringUtils::ToLowerAscii(StringUtils::TrimAscii(ip));
}
inline bool IsIpLiteral(const std::string &text)
{
const std::string trimmed = StringUtils::TrimAscii(text);
if (trimmed.empty())
{
return false;
}
IN_ADDR ipv4 = {};
IN6_ADDR ipv6 = {};
return InetPtonA(AF_INET, trimmed.c_str(), &ipv4) == 1 || InetPtonA(AF_INET6, trimmed.c_str(), &ipv6) == 1;
}
}
}

View file

@ -0,0 +1,212 @@
#include "stdafx.h"
#include "StringUtils.h"
#include <cctype>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
namespace ServerRuntime
{
namespace StringUtils
{
std::string WideToUtf8(const std::wstring &value)
{
if (value.empty())
{
return std::string();
}
int charCount = WideCharToMultiByte(CP_UTF8, 0, value.c_str(), (int)value.length(), NULL, 0, NULL, NULL);
if (charCount <= 0)
{
return std::string();
}
std::string utf8;
utf8.resize(charCount);
WideCharToMultiByte(CP_UTF8, 0, value.c_str(), (int)value.length(), &utf8[0], charCount, NULL, NULL);
return utf8;
}
std::wstring Utf8ToWide(const char *value)
{
if (value == NULL || value[0] == 0)
{
return std::wstring();
}
int wideCount = MultiByteToWideChar(CP_UTF8, 0, value, -1, NULL, 0);
if (wideCount <= 0)
{
// Fall back to the current ANSI code page so legacy non-UTF-8 inputs remain readable.
wideCount = MultiByteToWideChar(CP_ACP, 0, value, -1, NULL, 0);
if (wideCount <= 0)
{
return std::wstring();
}
std::wstring wide;
wide.resize(wideCount - 1);
MultiByteToWideChar(CP_ACP, 0, value, -1, &wide[0], wideCount);
return wide;
}
std::wstring wide;
wide.resize(wideCount - 1);
MultiByteToWideChar(CP_UTF8, 0, value, -1, &wide[0], wideCount);
return wide;
}
std::wstring Utf8ToWide(const std::string &value)
{
return Utf8ToWide(value.c_str());
}
std::string StripUtf8Bom(const std::string &value)
{
if (value.size() >= 3 &&
(unsigned char)value[0] == 0xEF &&
(unsigned char)value[1] == 0xBB &&
(unsigned char)value[2] == 0xBF)
{
return value.substr(3);
}
return value;
}
std::string TrimAscii(const std::string &value)
{
size_t start = 0;
while (start < value.length() && std::isspace((unsigned char)value[start]))
{
++start;
}
size_t end = value.length();
while (end > start && std::isspace((unsigned char)value[end - 1]))
{
--end;
}
return value.substr(start, end - start);
}
std::string ToLowerAscii(const std::string &value)
{
std::string lowered = value;
for (size_t i = 0; i < lowered.length(); ++i)
{
lowered[i] = (char)std::tolower((unsigned char)lowered[i]);
}
return lowered;
}
std::string JoinTokens(const std::vector<std::string> &tokens, size_t startIndex, const char *separator)
{
if (startIndex >= tokens.size())
{
return std::string();
}
const auto joinSeparator = std::string((separator != nullptr) ? separator : " ");
size_t totalLength = 0;
for (size_t i = startIndex; i < tokens.size(); ++i)
{
totalLength += tokens[i].size();
}
totalLength += (tokens.size() - startIndex - 1) * joinSeparator.size();
std::string joined;
joined.reserve(totalLength);
for (size_t i = startIndex; i < tokens.size(); ++i)
{
if (!joined.empty())
{
joined += joinSeparator;
}
joined += tokens[i];
}
return joined;
}
bool StartsWithIgnoreCase(const std::string &value, const std::string &prefix)
{
if (prefix.size() > value.size())
{
return false;
}
for (size_t i = 0; i < prefix.size(); ++i)
{
unsigned char a = (unsigned char)value[i];
unsigned char b = (unsigned char)prefix[i];
if (std::tolower(a) != std::tolower(b))
{
return false;
}
}
return true;
}
bool TryParseUnsignedLongLong(const std::string &value, unsigned long long *outValue)
{
if (outValue == nullptr)
{
return false;
}
const std::string trimmed = TrimAscii(value);
if (trimmed.empty())
{
return false;
}
errno = 0;
char *end = nullptr;
const unsigned long long parsed = _strtoui64(trimmed.c_str(), &end, 0);
if (end == trimmed.c_str() || errno != 0)
{
return false;
}
while (*end == ' ' || *end == '\t' || *end == '\r' || *end == '\n')
{
++end;
}
if (*end != 0)
{
return false;
}
*outValue = parsed;
return true;
}
std::string GetCurrentUtcTimestampIso8601()
{
SYSTEMTIME utc = {};
GetSystemTime(&utc);
char created[64] = {};
sprintf_s(
created,
sizeof(created),
"%04u-%02u-%02uT%02u:%02u:%02uZ",
(unsigned)utc.wYear,
(unsigned)utc.wMonth,
(unsigned)utc.wDay,
(unsigned)utc.wHour,
(unsigned)utc.wMinute,
(unsigned)utc.wSecond);
return created;
}
}
}

View file

@ -0,0 +1,23 @@
#pragma once
#include <string>
#include <vector>
namespace ServerRuntime
{
namespace StringUtils
{
std::string WideToUtf8(const std::wstring &value);
std::wstring Utf8ToWide(const char *value);
std::wstring Utf8ToWide(const std::string &value);
std::string StripUtf8Bom(const std::string &value);
std::string TrimAscii(const std::string &value);
std::string ToLowerAscii(const std::string &value);
std::string JoinTokens(const std::vector<std::string> &tokens, size_t startIndex = 0, const char *separator = " ");
bool StartsWithIgnoreCase(const std::string &value, const std::string &prefix);
bool TryParseUnsignedLongLong(const std::string &value, unsigned long long *outValue);
std::string GetCurrentUtcTimestampIso8601();
}
}

View file

@ -0,0 +1,44 @@
#include "stdafx.h"
#include "ServerCli.h"
#include "ServerCliEngine.h"
#include "ServerCliInput.h"
namespace ServerRuntime
{
ServerCli::ServerCli()
: m_engine(new ServerCliEngine())
, m_input(new ServerCliInput())
{
}
ServerCli::~ServerCli()
{
Stop();
}
void ServerCli::Start()
{
if (m_input && m_engine)
{
m_input->Start(m_engine.get());
}
}
void ServerCli::Stop()
{
if (m_input)
{
m_input->Stop();
}
}
void ServerCli::Poll()
{
if (m_engine)
{
m_engine->Poll();
}
}
}

View file

@ -0,0 +1,50 @@
#pragma once
#include <memory>
namespace ServerRuntime
{
class ServerCliEngine;
class ServerCliInput;
/**
* **Server CLI facade**
*
* Owns the command engine and input component, and exposes a small lifecycle API.
* CLI
*/
class ServerCli
{
public:
ServerCli();
~ServerCli();
/**
* **Start console input processing**
*
* Connects input to the engine and starts background reading.
*
*/
void Start();
/**
* **Stop console input processing**
*
* Stops background input safely and detaches from the engine.
*
*/
void Stop();
/**
* **Process queued command lines**
*
* Drains commands collected by input and executes them in the main loop.
*
*/
void Poll();
private:
std::unique_ptr<ServerCliEngine> m_engine;
std::unique_ptr<ServerCliInput> m_input;
};
}

View file

@ -0,0 +1,395 @@
#include "stdafx.h"
#include "ServerCliEngine.h"
#include "ServerCliParser.h"
#include "ServerCliRegistry.h"
#include "commands\IServerCliCommand.h"
#include "commands\ban\CliCommandBan.h"
#include "commands\ban-ip\CliCommandBanIp.h"
#include "commands\ban-list\CliCommandBanList.h"
#include "commands\defaultgamemode\CliCommandDefaultGamemode.h"
#include "commands\enchant\CliCommandEnchant.h"
#include "commands\experience\CliCommandExperience.h"
#include "commands\gamemode\CliCommandGamemode.h"
#include "commands\give\CliCommandGive.h"
#include "commands\help\CliCommandHelp.h"
#include "commands\kill\CliCommandKill.h"
#include "commands\list\CliCommandList.h"
#include "commands\pardon\CliCommandPardon.h"
#include "commands\pardon-ip\CliCommandPardonIp.h"
#include "commands\stop\CliCommandStop.h"
#include "commands\time\CliCommandTime.h"
#include "commands\tp\CliCommandTp.h"
#include "commands\weather\CliCommandWeather.h"
#include "commands\whitelist\CliCommandWhitelist.h"
#include "..\Common\StringUtils.h"
#include "..\ServerShutdown.h"
#include "..\ServerLogger.h"
#include "..\..\Minecraft.Client\MinecraftServer.h"
#include "..\..\Minecraft.Client\PlayerList.h"
#include "..\..\Minecraft.Client\ServerPlayer.h"
#include "..\..\Minecraft.World\CommandDispatcher.h"
#include "..\..\Minecraft.World\CommandSender.h"
#include "..\..\Minecraft.World\LevelSettings.h"
#include "..\..\Minecraft.World\StringHelpers.h"
#include <stdlib.h>
#include <unordered_set>
namespace ServerRuntime
{
/**
* Create an authorized Sender to make the CLI appear as a user.
* The return value can also be used to display logs.
*/
namespace
{
class ServerCliConsoleCommandSender : public CommandSender
{
public:
explicit ServerCliConsoleCommandSender(const ServerCliEngine *engine)
: m_engine(engine)
{
}
void sendMessage(const wstring &message, ChatPacket::EChatPacketMessage type, int customData, const wstring &additionalMessage) override
{
(void)type;
(void)customData;
(void)additionalMessage;
if (m_engine == nullptr)
{
return;
}
m_engine->LogInfo(StringUtils::WideToUtf8(message));
}
bool hasPermission(EGameCommand command) override
{
(void)command;
return true;
}
private:
const ServerCliEngine *m_engine;
};
}
ServerCliEngine::ServerCliEngine()
: m_registry(new ServerCliRegistry())
, m_consoleSender(std::make_shared<ServerCliConsoleCommandSender>(this))
{
RegisterDefaultCommands();
}
ServerCliEngine::~ServerCliEngine()
{
}
void ServerCliEngine::RegisterDefaultCommands()
{
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandHelp()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandStop()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandList()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandBan()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandBanIp()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandPardon()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandPardonIp()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandBanList()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandWhitelist()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandTp()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandTime()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandWeather()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandGive()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandEnchant()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandKill()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandGamemode()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandDefaultGamemode()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandExperience()));
}
void ServerCliEngine::EnqueueCommandLine(const std::string &line)
{
std::lock_guard<std::mutex> lock(m_queueMutex);
m_pendingLines.push(line);
}
void ServerCliEngine::Poll()
{
for (;;)
{
std::string line;
{
// Keep the lock scope minimal: dequeue only, execute outside.
std::lock_guard<std::mutex> lock(m_queueMutex);
if (m_pendingLines.empty())
{
break;
}
line = m_pendingLines.front();
m_pendingLines.pop();
}
ExecuteCommandLine(line);
}
}
bool ServerCliEngine::ExecuteCommandLine(const std::string &line)
{
// Normalize user input before parsing (trim + optional leading slash).
std::wstring wide = trimString(StringUtils::Utf8ToWide(line));
if (wide.empty())
{
return true;
}
std::string normalizedLine = StringUtils::WideToUtf8(wide);
if (!normalizedLine.empty() && normalizedLine[0] == '/')
{
normalizedLine = normalizedLine.substr(1);
}
ServerCliParsedLine parsed = ServerCliParser::Parse(normalizedLine);
if (parsed.tokens.empty())
{
return true;
}
IServerCliCommand *command = m_registry->FindMutable(parsed.tokens[0]);
if (command == NULL)
{
LogWarn("Unknown command: " + parsed.tokens[0]);
return false;
}
return command->Execute(parsed, this);
}
void ServerCliEngine::BuildCompletions(const std::string &line, std::vector<std::string> *out) const
{
if (out == NULL)
{
return;
}
out->clear();
ServerCliCompletionContext context = ServerCliParser::BuildCompletionContext(line);
bool slashPrefixedCommand = false;
std::string commandToken;
if (!context.parsed.tokens.empty())
{
// Completion accepts both "tp" and "/tp" style command heads.
commandToken = context.parsed.tokens[0];
if (!commandToken.empty() && commandToken[0] == '/')
{
commandToken = commandToken.substr(1);
slashPrefixedCommand = true;
}
}
if (context.currentTokenIndex == 0)
{
std::string prefix = context.prefix;
if (!prefix.empty() && prefix[0] == '/')
{
prefix = prefix.substr(1);
slashPrefixedCommand = true;
}
std::string linePrefix = context.linePrefix;
if (slashPrefixedCommand && linePrefix.empty())
{
// Preserve leading slash when user started with "/".
linePrefix = "/";
}
m_registry->SuggestCommandNames(prefix, linePrefix, out);
}
else
{
const IServerCliCommand *command = m_registry->Find(commandToken);
if (command != NULL)
{
command->Complete(context, this, out);
}
}
std::unordered_set<std::string> seen;
std::vector<std::string> unique;
for (size_t i = 0; i < out->size(); ++i)
{
// Remove duplicates while keeping first-seen ordering.
if (seen.insert((*out)[i]).second)
{
unique.push_back((*out)[i]);
}
}
out->swap(unique);
}
void ServerCliEngine::LogInfo(const std::string &message) const
{
LogInfof("console", "%s", message.c_str());
}
void ServerCliEngine::LogWarn(const std::string &message) const
{
LogWarnf("console", "%s", message.c_str());
}
void ServerCliEngine::LogError(const std::string &message) const
{
LogErrorf("console", "%s", message.c_str());
}
void ServerCliEngine::RequestShutdown() const
{
RequestDedicatedServerShutdown();
}
std::vector<std::string> ServerCliEngine::GetOnlinePlayerNamesUtf8() const
{
std::vector<std::string> result;
MinecraftServer *server = MinecraftServer::getInstance();
if (server == NULL)
{
return result;
}
PlayerList *players = server->getPlayers();
if (players == NULL)
{
return result;
}
for (size_t i = 0; i < players->players.size(); ++i)
{
std::shared_ptr<ServerPlayer> player = players->players[i];
if (player != NULL)
{
result.push_back(StringUtils::WideToUtf8(player->getName()));
}
}
return result;
}
std::shared_ptr<ServerPlayer> ServerCliEngine::FindPlayerByNameUtf8(const std::string &name) const
{
MinecraftServer *server = MinecraftServer::getInstance();
if (server == NULL)
{
return nullptr;
}
PlayerList *players = server->getPlayers();
if (players == NULL)
{
return nullptr;
}
std::wstring target = StringUtils::Utf8ToWide(name);
for (size_t i = 0; i < players->players.size(); ++i)
{
std::shared_ptr<ServerPlayer> player = players->players[i];
if (player != NULL && equalsIgnoreCase(player->getName(), target))
{
return player;
}
}
return nullptr;
}
void ServerCliEngine::SuggestPlayers(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const
{
std::vector<std::string> players = GetOnlinePlayerNamesUtf8();
std::string loweredPrefix = StringUtils::ToLowerAscii(prefix);
for (size_t i = 0; i < players.size(); ++i)
{
std::string loweredName = StringUtils::ToLowerAscii(players[i]);
if (loweredName.compare(0, loweredPrefix.size(), loweredPrefix) == 0)
{
out->push_back(linePrefix + players[i]);
}
}
}
void ServerCliEngine::SuggestGamemodes(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const
{
static const char *kModes[] = { "survival", "creative", "s", "c", "0", "1" };
std::string loweredPrefix = StringUtils::ToLowerAscii(prefix);
for (size_t i = 0; i < sizeof(kModes) / sizeof(kModes[0]); ++i)
{
std::string candidate = kModes[i];
std::string loweredCandidate = StringUtils::ToLowerAscii(candidate);
if (loweredCandidate.compare(0, loweredPrefix.size(), loweredPrefix) == 0)
{
out->push_back(linePrefix + candidate);
}
}
}
GameType *ServerCliEngine::ParseGamemode(const std::string &token) const
{
std::string lowered = StringUtils::ToLowerAscii(token);
if (lowered == "survival" || lowered == "s" || lowered == "0")
{
return GameType::SURVIVAL;
}
if (lowered == "creative" || lowered == "c" || lowered == "1")
{
return GameType::CREATIVE;
}
char *end = NULL;
long id = strtol(lowered.c_str(), &end, 10);
if (end != NULL && *end == 0)
{
// Numeric fallback supports extended ids handled by level settings.
return LevelSettings::validateGameType((int)id);
}
return NULL;
}
bool ServerCliEngine::DispatchWorldCommand(EGameCommand command, byteArray commandData, const std::shared_ptr<CommandSender> &sender) const
{
MinecraftServer *server = MinecraftServer::getInstance();
if (server == NULL)
{
LogWarn("MinecraftServer instance is not available.");
return false;
}
CommandDispatcher *dispatcher = server->getCommandDispatcher();
if (dispatcher == NULL)
{
LogWarn("Command dispatcher is not available.");
return false;
}
std::shared_ptr<CommandSender> commandSender = sender;
if (commandSender == nullptr)
{
// fall back to console sender if caller did not provide one
commandSender = m_consoleSender;
}
if (commandSender == nullptr)
{
LogWarn("No command sender is available.");
return false;
}
dispatcher->performCommand(commandSender, command, commandData);
return true;
}
const ServerCliRegistry &ServerCliEngine::Registry() const
{
return *m_registry;
}
}

View file

@ -0,0 +1,127 @@
#pragma once
#include <memory>
#include <mutex>
#include <queue>
#include <string>
#include <vector>
#include "..\..\Minecraft.World\ArrayWithLength.h"
#include "..\..\Minecraft.World\CommandsEnum.h"
class GameType;
class ServerPlayer;
class CommandSender;
namespace ServerRuntime
{
class ServerCliRegistry;
/**
* **CLI execution engine**
*
* Handles parsing, command dispatch, completion suggestions, and server-side helpers.
*
*/
class ServerCliEngine
{
public:
ServerCliEngine();
~ServerCliEngine();
/**
* **Queue one raw command line**
*
* Called by input thread; execution is deferred to `Poll()`.
*
*/
void EnqueueCommandLine(const std::string &line);
/**
* **Execute queued commands**
*
* Drains pending lines and dispatches them in order.
*
*/
void Poll();
/**
* **Execute one command line immediately**
*
* Parses and dispatches a normalized line to a registered command.
* 1
*/
bool ExecuteCommandLine(const std::string &line);
/**
* **Build completion candidates for current line**
*
* Produces command or argument suggestions based on parser context.
*
*/
void BuildCompletions(const std::string &line, std::vector<std::string> *out) const;
void LogInfo(const std::string &message) const;
void LogWarn(const std::string &message) const;
void LogError(const std::string &message) const;
void RequestShutdown() const;
/**
* **List connected players as UTF-8 names**
*
*
*/
std::vector<std::string> GetOnlinePlayerNamesUtf8() const;
/**
* **Find a player by UTF-8 name**
*/
std::shared_ptr<ServerPlayer> FindPlayerByNameUtf8(const std::string &name) const;
/**
* **Suggest player-name arguments**
*
* Appends matching player candidates using the given completion prefix.
*
*/
void SuggestPlayers(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const;
/**
* **Suggest gamemode arguments**
*
* Appends standard gamemode aliases (survival/creative/0/1).
*
*/
void SuggestGamemodes(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const;
/**
* **Parse gamemode token**
*
* Supports names, short aliases, and numeric ids.
*
*/
GameType *ParseGamemode(const std::string &token) const;
/**
* **Dispatch one Minecraft.World game command**
*
* Uses `Minecraft.World::CommandDispatcher` for actual execution.
* When `sender` is null, an internal console command sender is used.
*
* Minecraft.Worldのコマンドを実行するためのディスパッチャーのラッパー
* senderがnullの場合はコンソールコマンド送信者を使用
*/
bool DispatchWorldCommand(EGameCommand command, byteArray commandData, const std::shared_ptr<CommandSender> &sender = nullptr) const;
const ServerCliRegistry &Registry() const;
private:
void RegisterDefaultCommands();
private:
mutable std::mutex m_queueMutex;
std::queue<std::string> m_pendingLines;
std::unique_ptr<ServerCliRegistry> m_registry;
std::shared_ptr<CommandSender> m_consoleSender;
};
}

View file

@ -0,0 +1,285 @@
#include "stdafx.h"
#include "ServerCliInput.h"
#include "ServerCliEngine.h"
#include "..\ServerLogger.h"
#include "..\vendor\linenoise\linenoise.h"
#include <ctype.h>
#include <stdlib.h>
namespace
{
bool UseStreamInputMode()
{
const char *mode = getenv("SERVER_CLI_INPUT_MODE");
if (mode != NULL)
{
return _stricmp(mode, "stream") == 0
|| _stricmp(mode, "stdin") == 0;
}
return false;
}
int WaitForStdinReadable(HANDLE stdinHandle, DWORD waitMs)
{
if (stdinHandle == NULL || stdinHandle == INVALID_HANDLE_VALUE)
{
return -1;
}
DWORD fileType = GetFileType(stdinHandle);
if (fileType == FILE_TYPE_PIPE)
{
DWORD available = 0;
if (!PeekNamedPipe(stdinHandle, NULL, 0, NULL, &available, NULL))
{
return -1;
}
return available > 0 ? 1 : 0;
}
if (fileType == FILE_TYPE_CHAR)
{
// console/pty char handles are often not waitable across Wine+Docker.
return 1;
}
DWORD waitResult = WaitForSingleObject(stdinHandle, waitMs);
if (waitResult == WAIT_OBJECT_0)
{
return 1;
}
if (waitResult == WAIT_TIMEOUT)
{
return 0;
}
return -1;
}
}
namespace ServerRuntime
{
// C-style completion callback bridge requires a static instance pointer.
ServerCliInput *ServerCliInput::s_instance = NULL;
ServerCliInput::ServerCliInput()
: m_running(false)
, m_engine(NULL)
{
}
ServerCliInput::~ServerCliInput()
{
Stop();
}
void ServerCliInput::Start(ServerCliEngine *engine)
{
if (engine == NULL || m_running.exchange(true))
{
return;
}
m_engine = engine;
s_instance = this;
linenoiseResetStop();
linenoiseHistorySetMaxLen(128);
linenoiseSetCompletionCallback(&ServerCliInput::CompletionThunk);
m_inputThread = std::thread(&ServerCliInput::RunInputLoop, this);
LogInfo("console", "CLI input thread started.");
}
void ServerCliInput::Stop()
{
if (!m_running.exchange(false))
{
return;
}
// Ask linenoise to break out first, then join thread safely.
linenoiseRequestStop();
if (m_inputThread.joinable())
{
CancelSynchronousIo((HANDLE)m_inputThread.native_handle());
m_inputThread.join();
}
linenoiseSetCompletionCallback(NULL);
if (s_instance == this)
{
s_instance = NULL;
}
m_engine = NULL;
LogInfo("console", "CLI input thread stopped.");
}
bool ServerCliInput::IsRunning() const
{
return m_running.load();
}
void ServerCliInput::RunInputLoop()
{
if (UseStreamInputMode())
{
LogInfo("console", "CLI input mode: stream(file stdin)");
RunStreamInputLoop();
return;
}
RunLinenoiseLoop();
}
/**
* use linenoise for interactive console input, with line editing and history support
*/
void ServerCliInput::RunLinenoiseLoop()
{
while (m_running)
{
char *line = linenoise("server> ");
if (line == NULL)
{
// NULL is expected on stop request (or Ctrl+C inside linenoise).
if (!m_running)
{
break;
}
Sleep(10);
continue;
}
EnqueueLine(line);
linenoiseFree(line);
}
}
/**
* use file-based stdin reading instead of linenoise when requested or when stdin is not a console/pty (e.g. piped input or non-interactive docker)
*/
void ServerCliInput::RunStreamInputLoop()
{
HANDLE stdinHandle = GetStdHandle(STD_INPUT_HANDLE);
if (stdinHandle == NULL || stdinHandle == INVALID_HANDLE_VALUE)
{
LogWarn("console", "stream input mode requested but STDIN handle is unavailable; falling back to linenoise.");
RunLinenoiseLoop();
return;
}
std::string line;
bool skipNextLf = false;
printf("server> ");
fflush(stdout);
while (m_running)
{
int readable = WaitForStdinReadable(stdinHandle, 50);
if (readable <= 0)
{
Sleep(10);
continue;
}
char ch = 0;
DWORD bytesRead = 0;
if (!ReadFile(stdinHandle, &ch, 1, &bytesRead, NULL) || bytesRead == 0)
{
Sleep(10);
continue;
}
if (skipNextLf && ch == '\n')
{
skipNextLf = false;
continue;
}
if (ch == '\r' || ch == '\n')
{
if (ch == '\r')
{
skipNextLf = true;
}
else
{
skipNextLf = false;
}
if (!line.empty())
{
EnqueueLine(line.c_str());
line.clear();
}
printf("server> ");
fflush(stdout);
continue;
}
skipNextLf = false;
if ((unsigned char)ch == 3)
{
continue;
}
if ((unsigned char)ch == 8 || (unsigned char)ch == 127)
{
if (!line.empty())
{
line.resize(line.size() - 1);
}
continue;
}
if (isprint((unsigned char)ch) && line.size() < 4096)
{
line.push_back(ch);
}
}
}
void ServerCliInput::EnqueueLine(const char *line)
{
if (line == NULL || line[0] == 0 || m_engine == NULL)
{
return;
}
// Keep local history and forward command for main-thread execution.
linenoiseHistoryAdd(line);
m_engine->EnqueueCommandLine(line);
}
void ServerCliInput::CompletionThunk(const char *line, linenoiseCompletions *completions)
{
// Static thunk forwards callback into instance state.
if (s_instance != NULL)
{
s_instance->BuildCompletions(line, completions);
}
}
void ServerCliInput::BuildCompletions(const char *line, linenoiseCompletions *completions)
{
if (line == NULL || completions == NULL || m_engine == NULL)
{
return;
}
std::vector<std::string> suggestions;
m_engine->BuildCompletions(line, &suggestions);
for (size_t i = 0; i < suggestions.size(); ++i)
{
linenoiseAddCompletion(completions, suggestions[i].c_str());
}
}
}

View file

@ -0,0 +1,63 @@
#pragma once
#include <atomic>
#include <thread>
struct linenoiseCompletions;
namespace ServerRuntime
{
class ServerCliEngine;
/**
* **CLI input worker**
*
* Owns the interactive input thread and bridges linenoise callbacks to the engine.
*
*/
class ServerCliInput
{
public:
ServerCliInput();
~ServerCliInput();
/**
* **Start input loop**
*
* Binds to an engine and starts reading user input from the console.
*
*/
void Start(ServerCliEngine *engine);
/**
* **Stop input loop**
*
* Requests stop and joins the input thread.
*
*/
void Stop();
/**
* **Check running state**
*
* Returns true while the input thread is active.
*
*/
bool IsRunning() const;
private:
void RunInputLoop();
void RunLinenoiseLoop();
void RunStreamInputLoop();
void EnqueueLine(const char *line);
static void CompletionThunk(const char *line, linenoiseCompletions *completions);
void BuildCompletions(const char *line, linenoiseCompletions *completions);
private:
std::atomic<bool> m_running;
std::thread m_inputThread;
ServerCliEngine *m_engine;
static ServerCliInput *s_instance;
};
}

View file

@ -0,0 +1,116 @@
#include "stdafx.h"
#include "ServerCliParser.h"
namespace ServerRuntime
{
static void TokenizeLine(const std::string &line, std::vector<std::string> *tokens, bool *trailingSpace)
{
std::string current;
bool inQuotes = false;
bool escaped = false;
tokens->clear();
*trailingSpace = false;
for (size_t i = 0; i < line.size(); ++i)
{
char ch = line[i];
if (escaped)
{
// Keep escaped character literally (e.g. \" or \ ).
current.push_back(ch);
escaped = false;
continue;
}
if (ch == '\\')
{
escaped = true;
continue;
}
if (ch == '"')
{
// Double quotes group spaces into one token.
inQuotes = !inQuotes;
continue;
}
if (!inQuotes && (ch == ' ' || ch == '\t'))
{
if (!current.empty())
{
tokens->push_back(current);
current.clear();
}
continue;
}
current.push_back(ch);
}
if (!current.empty())
{
tokens->push_back(current);
}
if (!line.empty())
{
char tail = line[line.size() - 1];
// Trailing space means completion targets the next token slot.
*trailingSpace = (!inQuotes && (tail == ' ' || tail == '\t'));
}
}
ServerCliParsedLine ServerCliParser::Parse(const std::string &line)
{
ServerCliParsedLine parsed;
parsed.raw = line;
TokenizeLine(line, &parsed.tokens, &parsed.trailingSpace);
return parsed;
}
ServerCliCompletionContext ServerCliParser::BuildCompletionContext(const std::string &line)
{
ServerCliCompletionContext context;
context.parsed = Parse(line);
if (context.parsed.tokens.empty())
{
context.currentTokenIndex = 0;
context.prefix.clear();
context.linePrefix.clear();
return context;
}
if (context.parsed.trailingSpace)
{
// Cursor is after a separator, so complete a new token.
context.currentTokenIndex = context.parsed.tokens.size();
context.prefix.clear();
}
else
{
// Cursor is inside current token, so complete by its prefix.
context.currentTokenIndex = context.parsed.tokens.size() - 1;
context.prefix = context.parsed.tokens.back();
}
for (size_t i = 0; i < context.currentTokenIndex; ++i)
{
// linePrefix is the immutable left side reused by completion output.
if (!context.linePrefix.empty())
{
context.linePrefix.push_back(' ');
}
context.linePrefix += context.parsed.tokens[i];
}
if (!context.linePrefix.empty())
{
context.linePrefix.push_back(' ');
}
return context;
}
}

View file

@ -0,0 +1,63 @@
#pragma once
#include <string>
#include <vector>
namespace ServerRuntime
{
/**
* **Parsed command line**
*/
struct ServerCliParsedLine
{
std::string raw;
std::vector<std::string> tokens;
bool trailingSpace;
ServerCliParsedLine()
: trailingSpace(false)
{
}
};
/**
* **Completion context for one input line**
*
* Indicates current token index, token prefix, and the fixed line prefix.
*/
struct ServerCliCompletionContext
{
ServerCliParsedLine parsed;
size_t currentTokenIndex;
std::string prefix;
std::string linePrefix;
ServerCliCompletionContext()
: currentTokenIndex(0)
{
}
};
/**
* **CLI parser helpers**
*
* Converts raw input text into tokenized data used by execution and completion.
*/
class ServerCliParser
{
public:
/**
* **Tokenize one command line**
*
* Supports quoted segments and escaped characters.
*/
static ServerCliParsedLine Parse(const std::string &line);
/**
* **Build completion metadata**
*
* Determines active token position and reusable prefix parts.
*/
static ServerCliCompletionContext BuildCompletionContext(const std::string &line);
};
}

View file

@ -0,0 +1,99 @@
#include "stdafx.h"
#include "ServerCliRegistry.h"
#include "commands\IServerCliCommand.h"
#include "..\Common\StringUtils.h"
namespace ServerRuntime
{
bool ServerCliRegistry::Register(std::unique_ptr<IServerCliCommand> command)
{
if (!command)
{
return false;
}
IServerCliCommand *raw = command.get();
std::string baseName = StringUtils::ToLowerAscii(raw->Name());
// Reject empty/duplicate primary command names.
if (baseName.empty() || m_lookup.find(baseName) != m_lookup.end())
{
return false;
}
std::vector<std::string> aliases = raw->Aliases();
std::vector<std::string> normalizedAliases;
normalizedAliases.reserve(aliases.size());
for (size_t i = 0; i < aliases.size(); ++i)
{
std::string alias = StringUtils::ToLowerAscii(aliases[i]);
// Alias must also be unique across all names and aliases.
if (alias.empty() || m_lookup.find(alias) != m_lookup.end())
{
return false;
}
normalizedAliases.push_back(alias);
}
m_lookup[baseName] = raw;
for (size_t i = 0; i < normalizedAliases.size(); ++i)
{
m_lookup[normalizedAliases[i]] = raw;
}
// Command objects are owned here; lookup stores non-owning pointers.
m_commands.push_back(std::move(command));
return true;
}
const IServerCliCommand *ServerCliRegistry::Find(const std::string &name) const
{
std::string key = StringUtils::ToLowerAscii(name);
auto it = m_lookup.find(key);
if (it == m_lookup.end())
{
return NULL;
}
return it->second;
}
IServerCliCommand *ServerCliRegistry::FindMutable(const std::string &name)
{
std::string key = StringUtils::ToLowerAscii(name);
auto it = m_lookup.find(key);
if (it == m_lookup.end())
{
return NULL;
}
return it->second;
}
void ServerCliRegistry::SuggestCommandNames(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const
{
for (size_t i = 0; i < m_commands.size(); ++i)
{
const IServerCliCommand *command = m_commands[i].get();
std::string name = command->Name();
if (StringUtils::StartsWithIgnoreCase(name, prefix))
{
out->push_back(linePrefix + name);
}
// Include aliases so users can discover shorthand commands.
std::vector<std::string> aliases = command->Aliases();
for (size_t aliasIndex = 0; aliasIndex < aliases.size(); ++aliasIndex)
{
if (StringUtils::StartsWithIgnoreCase(aliases[aliasIndex], prefix))
{
out->push_back(linePrefix + aliases[aliasIndex]);
}
}
}
}
const std::vector<std::unique_ptr<IServerCliCommand>> &ServerCliRegistry::Commands() const
{
return m_commands;
}
}

View file

@ -0,0 +1,58 @@
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
namespace ServerRuntime
{
class IServerCliCommand;
/**
* **CLI command registry**
*/
class ServerCliRegistry
{
public:
/**
* **Register a command object**
*
* Validates name/aliases and adds lookup entries.
*
*/
bool Register(std::unique_ptr<IServerCliCommand> command);
/**
* **Find command by name or alias (const)**
*
* Returns null when no match exists.
*/
const IServerCliCommand *Find(const std::string &name) const;
/**
* **Find mutable command by name or alias**
*
* Used by runtime dispatch path.
*/
IServerCliCommand *FindMutable(const std::string &name);
/**
* **Suggest top-level command names**
*
* Adds matching command names and aliases to the output list.
*/
void SuggestCommandNames(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const;
/**
* **Get registered command list**
*
* Intended for help output and inspection.
*/
const std::vector<std::unique_ptr<IServerCliCommand>> &Commands() const;
private:
std::vector<std::unique_ptr<IServerCliCommand>> m_commands;
std::unordered_map<std::string, IServerCliCommand *> m_lookup;
};
}

View file

@ -0,0 +1,39 @@
#pragma once
#include <cerrno>
#include <cstdlib>
#include <limits>
#include <string>
namespace ServerRuntime
{
namespace CommandParsing
{
inline bool TryParseInt(const std::string &text, int *outValue)
{
if (outValue == nullptr || text.empty())
{
return false;
}
char *end = nullptr;
errno = 0;
const long parsedValue = std::strtol(text.c_str(), &end, 10);
if (end == text.c_str() || *end != '\0')
{
return false;
}
if (errno == ERANGE)
{
return false;
}
if (parsedValue < (std::numeric_limits<int>::min)() || parsedValue > (std::numeric_limits<int>::max)())
{
return false;
}
*outValue = static_cast<int>(parsedValue);
return true;
}
}
}

View file

@ -0,0 +1,50 @@
#pragma once
#include <string>
#include <vector>
namespace ServerRuntime
{
class ServerCliEngine;
struct ServerCliParsedLine;
struct ServerCliCompletionContext;
/**
* **Command interface for server CLI**
*
* Implement this contract to add new commands without changing engine internals.
*/
class IServerCliCommand
{
public:
virtual ~IServerCliCommand() = default;
/** Primary command name */
virtual const char *Name() const = 0;
/** Optional aliases */
virtual std::vector<std::string> Aliases() const { return {}; }
/** Usage text for help */
virtual const char *Usage() const = 0;
/** Short command description*/
virtual const char *Description() const = 0;
/**
* **Execute command logic**
*
* Called after tokenization and command lookup.
*/
virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) = 0;
/**
* **Provide argument completion candidates**
*
* Override when command-specific completion is needed.
*/
virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
(void)context;
(void)engine;
(void)out;
}
};
}

View file

@ -0,0 +1,171 @@
#include "stdafx.h"
#include "CliCommandBanIp.h"
#include "..\..\ServerCliEngine.h"
#include "..\..\ServerCliParser.h"
#include "..\..\..\Access\Access.h"
#include "..\..\..\Common\NetworkUtils.h"
#include "..\..\..\Common\StringUtils.h"
#include "..\..\..\ServerLogManager.h"
#include "..\..\..\..\Minecraft.Client\MinecraftServer.h"
#include "..\..\..\..\Minecraft.Client\PlayerConnection.h"
#include "..\..\..\..\Minecraft.Client\PlayerList.h"
#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
#include "..\..\..\..\Minecraft.World\Connection.h"
#include "..\..\..\..\Minecraft.World\DisconnectPacket.h"
namespace ServerRuntime
{
namespace
{
// The dedicated server keeps the accepted remote IP in ServerLogManager, keyed by connection smallId.
// It's a bit strange from a responsibility standpoint, so we'll need to implement it separately.
static bool TryGetPlayerRemoteIp(const std::shared_ptr<ServerPlayer> &player, std::string *outIp)
{
if (outIp == nullptr || player == nullptr || player->connection == nullptr || player->connection->connection == nullptr || player->connection->connection->getSocket() == nullptr)
{
return false;
}
const unsigned char smallId = player->connection->connection->getSocket()->getSmallId();
if (smallId == 0)
{
return false;
}
return ServerRuntime::ServerLogManager::TryGetConnectionRemoteIp(smallId, outIp);
}
// After persisting the ban, walk a snapshot of current players so every matching session is removed.
static int DisconnectPlayersByRemoteIp(const std::string &ip)
{
auto *server = MinecraftServer::getInstance();
if (server == nullptr || server->getPlayers() == nullptr)
{
return 0;
}
const std::string normalizedIp = NetworkUtils::NormalizeIpToken(ip);
const std::vector<std::shared_ptr<ServerPlayer>> playerSnapshot = server->getPlayers()->players;
int disconnectedCount = 0;
for (const auto &player : playerSnapshot)
{
std::string playerIp;
if (!TryGetPlayerRemoteIp(player, &playerIp))
{
continue;
}
if (NetworkUtils::NormalizeIpToken(playerIp) == normalizedIp)
{
if (player != nullptr && player->connection != nullptr)
{
player->connection->disconnect(DisconnectPacket::eDisconnect_Banned);
++disconnectedCount;
}
}
}
return disconnectedCount;
}
}
const char *CliCommandBanIp::Name() const
{
return "ban-ip";
}
const char *CliCommandBanIp::Usage() const
{
return "ban-ip <address|player> [reason ...]";
}
const char *CliCommandBanIp::Description() const
{
return "Ban an IP address or a player's current IP.";
}
/**
* Resolves either a literal IP or an online player's current IP, persists the ban, and disconnects every matching connection
* IPまたは接続中プレイヤーの現在IPをBANし一致する接続を切断する
*/
bool CliCommandBanIp::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
if (line.tokens.size() < 2)
{
engine->LogWarn("Usage: ban-ip <address|player> [reason ...]");
return false;
}
if (!ServerRuntime::Access::IsInitialized())
{
engine->LogWarn("Access manager is not initialized.");
return false;
}
const std::string targetToken = line.tokens[1];
std::string remoteIp;
// Match Java Edition behavior by accepting either a literal IP or an online player name.
const auto targetPlayer = engine->FindPlayerByNameUtf8(targetToken);
if (targetPlayer != nullptr)
{
if (!TryGetPlayerRemoteIp(targetPlayer, &remoteIp))
{
engine->LogWarn("Cannot ban that player's IP because no current remote IP is available.");
return false;
}
}
else if (NetworkUtils::IsIpLiteral(targetToken))
{
remoteIp = StringUtils::TrimAscii(targetToken);
}
else
{
engine->LogWarn("Unknown player or invalid IP address: " + targetToken);
return false;
}
// Refuse duplicate bans so operators get immediate feedback instead of rewriting the same entry.
if (ServerRuntime::Access::IsIpBanned(remoteIp))
{
engine->LogWarn("That IP address is already banned.");
return false;
}
ServerRuntime::Access::BanMetadata metadata = ServerRuntime::Access::BanManager::BuildDefaultMetadata("Console");
metadata.reason = StringUtils::JoinTokens(line.tokens, 2);
if (metadata.reason.empty())
{
metadata.reason = "Banned by an operator.";
}
// Publish the ban before disconnecting players so reconnect attempts are rejected immediately.
if (!ServerRuntime::Access::AddIpBan(remoteIp, metadata))
{
engine->LogError("Failed to write IP ban.");
return false;
}
const int disconnectedCount = DisconnectPlayersByRemoteIp(remoteIp);
// Report the resolved IP rather than the original token so player-name targets are explicit in the console.
engine->LogInfo("Banned IP address " + remoteIp + ".");
if (disconnectedCount > 0)
{
engine->LogInfo("Disconnected " + std::to_string(disconnectedCount) + " player(s) with that IP.");
}
return true;
}
/**
* Suggests online player names for the player-target form of the Java Edition command
*
*/
void CliCommandBanIp::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
if (context.currentTokenIndex == 1)
{
engine->SuggestPlayers(context.prefix, context.linePrefix, out);
}
}
}

View file

@ -0,0 +1,19 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
/**
* Applies a dedicated-server IP ban using Java Edition style syntax and Access-backed persistence
*/
class CliCommandBanIp : public IServerCliCommand
{
public:
virtual const char *Name() const;
virtual const char *Usage() const;
virtual const char *Description() const;
virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const;
};
}

View file

@ -0,0 +1,136 @@
#include "stdafx.h"
#include "CliCommandBanList.h"
#include "..\..\ServerCliEngine.h"
#include "..\..\ServerCliParser.h"
#include "..\..\..\Access\Access.h"
#include "..\..\..\Common\StringUtils.h"
#include <algorithm>
namespace ServerRuntime
{
namespace
{
static void AppendUniqueText(const std::string &text, std::vector<std::string> *out)
{
if (out == nullptr || text.empty())
{
return;
}
if (std::find(out->begin(), out->end(), text) == out->end())
{
out->push_back(text);
}
}
static bool CompareLowerAscii(const std::string &left, const std::string &right)
{
return StringUtils::ToLowerAscii(left) < StringUtils::ToLowerAscii(right);
}
static bool LogBannedPlayers(ServerCliEngine *engine)
{
std::vector<ServerRuntime::Access::BannedPlayerEntry> entries;
if (!ServerRuntime::Access::SnapshotBannedPlayers(&entries))
{
engine->LogError("Failed to read banned players.");
return false;
}
std::vector<std::string> names;
for (const auto &entry : entries)
{
AppendUniqueText(entry.name, &names);
}
std::sort(names.begin(), names.end(), CompareLowerAscii);
engine->LogInfo("There are " + std::to_string(names.size()) + " banned player(s).");
for (const auto &name : names)
{
engine->LogInfo(" " + name);
}
return true;
}
static bool LogBannedIps(ServerCliEngine *engine)
{
std::vector<ServerRuntime::Access::BannedIpEntry> entries;
if (!ServerRuntime::Access::SnapshotBannedIps(&entries))
{
engine->LogError("Failed to read banned IPs.");
return false;
}
std::vector<std::string> ips;
for (const auto &entry : entries)
{
AppendUniqueText(entry.ip, &ips);
}
std::sort(ips.begin(), ips.end(), CompareLowerAscii);
engine->LogInfo("There are " + std::to_string(ips.size()) + " banned IP(s).");
for (const auto &ip : ips)
{
engine->LogInfo(" " + ip);
}
return true;
}
static bool LogAllBans(ServerCliEngine *engine)
{
if (!LogBannedPlayers(engine))
{
return false;
}
// Always print the IP snapshot as well so ban-ip entries are visible from the same command output.
return LogBannedIps(engine);
}
}
const char *CliCommandBanList::Name() const
{
return "banlist";
}
const char *CliCommandBanList::Usage() const
{
return "banlist";
}
const char *CliCommandBanList::Description() const
{
return "List all banned players and IPs.";
}
/**
* Reads the current Access snapshots and always prints both banned players and banned IPs
* Access BANとIP BANをまとめて表示する
*/
bool CliCommandBanList::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
if (line.tokens.size() > 1)
{
engine->LogWarn("Usage: banlist");
return false;
}
if (!ServerRuntime::Access::IsInitialized())
{
engine->LogWarn("Access manager is not initialized.");
return false;
}
return LogAllBans(engine);
}
void CliCommandBanList::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
(void)context;
(void)engine;
(void)out;
}
}

View file

@ -0,0 +1,23 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
/**
* **Ban List Command**
*
* Lists dedicated-server player bans and IP bans in a single command output
* BANとIP BANをまとめて表示する
*/
class CliCommandBanList : public IServerCliCommand
{
public:
virtual const char *Name() const;
virtual const char *Usage() const;
virtual const char *Description() const;
virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const;
};
}

View file

@ -0,0 +1,145 @@
#include "stdafx.h"
#include "CliCommandBan.h"
#include "..\..\ServerCliEngine.h"
#include "..\..\ServerCliParser.h"
#include "..\..\..\Access\Access.h"
#include "..\..\..\Common\StringUtils.h"
#include "..\..\..\..\Minecraft.Client\PlayerConnection.h"
#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
#include "..\..\..\..\Minecraft.World\DisconnectPacket.h"
#include <algorithm>
namespace ServerRuntime
{
namespace
{
static void AppendUniqueXuid(PlayerUID xuid, std::vector<PlayerUID> *out)
{
if (out == nullptr || xuid == INVALID_XUID)
{
return;
}
if (std::find(out->begin(), out->end(), xuid) == out->end())
{
out->push_back(xuid);
}
}
static void CollectPlayerBanXuids(const std::shared_ptr<ServerPlayer> &player, std::vector<PlayerUID> *out)
{
if (player == nullptr || out == nullptr)
{
return;
}
// Keep both identity variants because the dedicated server checks login and online XUIDs separately.
AppendUniqueXuid(player->getXuid(), out);
AppendUniqueXuid(player->getOnlineXuid(), out);
}
}
const char *CliCommandBan::Name() const
{
return "ban";
}
const char *CliCommandBan::Usage() const
{
return "ban <player> [reason ...]";
}
const char *CliCommandBan::Description() const
{
return "Ban an online player.";
}
/**
* Resolves the live player, writes one or more Access ban entries, and disconnects the target with the banned reason
* BANを保存し切断する
*/
bool CliCommandBan::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
if (line.tokens.size() < 2)
{
engine->LogWarn("Usage: ban <player> [reason ...]");
return false;
}
if (!ServerRuntime::Access::IsInitialized())
{
engine->LogWarn("Access manager is not initialized.");
return false;
}
const auto target = engine->FindPlayerByNameUtf8(line.tokens[1]);
if (target == nullptr)
{
engine->LogWarn("Unknown player: " + line.tokens[1] + " (this server build can only ban players that are currently online).");
return false;
}
std::vector<PlayerUID> xuids;
CollectPlayerBanXuids(target, &xuids);
if (xuids.empty())
{
engine->LogWarn("Cannot ban that player because no valid XUID is available.");
return false;
}
const bool hasUnbannedIdentity = std::any_of(
xuids.begin(),
xuids.end(),
[](PlayerUID xuid) { return !ServerRuntime::Access::IsPlayerBanned(xuid); });
if (!hasUnbannedIdentity)
{
engine->LogWarn("That player is already banned.");
return false;
}
ServerRuntime::Access::BanMetadata metadata = ServerRuntime::Access::BanManager::BuildDefaultMetadata("Console");
metadata.reason = StringUtils::JoinTokens(line.tokens, 2);
if (metadata.reason.empty())
{
metadata.reason = "Banned by an operator.";
}
const std::string playerName = StringUtils::WideToUtf8(target->getName());
for (const auto xuid : xuids)
{
if (ServerRuntime::Access::IsPlayerBanned(xuid))
{
continue;
}
if (!ServerRuntime::Access::AddPlayerBan(xuid, playerName, metadata))
{
engine->LogError("Failed to write player ban.");
return false;
}
}
if (target->connection != nullptr)
{
target->connection->disconnect(DisconnectPacket::eDisconnect_Banned);
}
engine->LogInfo("Banned player " + playerName + ".");
return true;
}
/**
* Suggests currently connected player names for the Java-style player argument
*
*/
void CliCommandBan::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
if (context.currentTokenIndex == 1)
{
engine->SuggestPlayers(context.prefix, context.linePrefix, out);
}
}
}

View file

@ -0,0 +1,20 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
/**
* Applies a dedicated-server player ban using Java Edition style syntax and Access-backed persistence
* Java Edition ban BANを行う
*/
class CliCommandBan : public IServerCliCommand
{
public:
virtual const char *Name() const;
virtual const char *Usage() const;
virtual const char *Description() const;
virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const;
};
}

View file

@ -0,0 +1,117 @@
#include "stdafx.h"
#include "CliCommandDefaultGamemode.h"
#include "..\..\ServerCliEngine.h"
#include "..\..\ServerCliParser.h"
#include "..\..\..\..\Minecraft.Client\MinecraftServer.h"
#include "..\..\..\..\Minecraft.Client\PlayerList.h"
#include "..\..\..\..\Minecraft.Client\ServerLevel.h"
#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
#include "..\..\..\..\Minecraft.World\net.minecraft.world.level.storage.h"
namespace ServerRuntime
{
namespace
{
constexpr const char *kDefaultGamemodeUsage = "defaultgamemode <survival|creative|0|1>";
static std::string ModeLabel(GameType *mode)
{
if (mode == GameType::SURVIVAL)
{
return "survival";
}
if (mode == GameType::CREATIVE)
{
return "creative";
}
if (mode == GameType::ADVENTURE)
{
return "adventure";
}
return std::to_string(mode != nullptr ? mode->getId() : -1);
}
}
const char *CliCommandDefaultGamemode::Name() const
{
return "defaultgamemode";
}
const char *CliCommandDefaultGamemode::Usage() const
{
return kDefaultGamemodeUsage;
}
const char *CliCommandDefaultGamemode::Description() const
{
return "Set the default game mode (server-side implementation).";
}
bool CliCommandDefaultGamemode::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
if (line.tokens.size() != 2)
{
engine->LogWarn(std::string("Usage: ") + kDefaultGamemodeUsage);
return false;
}
GameType *mode = engine->ParseGamemode(line.tokens[1]);
if (mode == nullptr)
{
engine->LogWarn("Unknown gamemode: " + line.tokens[1]);
return false;
}
MinecraftServer *server = MinecraftServer::getInstance();
if (server == nullptr)
{
engine->LogWarn("MinecraftServer instance is not available.");
return false;
}
PlayerList *players = server->getPlayers();
if (players == nullptr)
{
engine->LogWarn("Player list is not available.");
return false;
}
players->setOverrideGameMode(mode);
for (unsigned int i = 0; i < server->levels.length; ++i)
{
ServerLevel *level = server->levels[i];
if (level != nullptr && level->getLevelData() != nullptr)
{
level->getLevelData()->setGameType(mode);
}
}
if (server->getForceGameType())
{
for (size_t i = 0; i < players->players.size(); ++i)
{
std::shared_ptr<ServerPlayer> player = players->players[i];
if (player != nullptr)
{
player->setGameMode(mode);
player->fallDistance = 0.0f;
}
}
}
engine->LogInfo("Default gamemode set to " + ModeLabel(mode) + ".");
return true;
}
void CliCommandDefaultGamemode::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
if (context.currentTokenIndex == 1)
{
engine->SuggestGamemodes(context.prefix, context.linePrefix, out);
}
}
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandDefaultGamemode : public IServerCliCommand
{
public:
const char *Name() const override;
const char *Usage() const override;
const char *Description() const override;
bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
};
}

View file

@ -0,0 +1,87 @@
#include "stdafx.h"
#include "CliCommandEnchant.h"
#include "..\..\ServerCliEngine.h"
#include "..\..\ServerCliParser.h"
#include "..\CommandParsing.h"
#include "..\..\..\..\Minecraft.World\GameCommandPacket.h"
#include "..\..\..\..\Minecraft.World\EnchantItemCommand.h"
#include "..\..\..\..\Minecraft.World\net.minecraft.world.entity.player.h"
#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
namespace ServerRuntime
{
namespace
{
constexpr const char *kEnchantUsage = "enchant <player> <enchantId> [level]";
}
const char *CliCommandEnchant::Name() const
{
return "enchant";
}
const char *CliCommandEnchant::Usage() const
{
return kEnchantUsage;
}
const char *CliCommandEnchant::Description() const
{
return "Enchant held item via Minecraft.World command dispatcher.";
}
bool CliCommandEnchant::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
if (line.tokens.size() < 3 || line.tokens.size() > 4)
{
engine->LogWarn(std::string("Usage: ") + kEnchantUsage);
return false;
}
std::shared_ptr<ServerPlayer> target = engine->FindPlayerByNameUtf8(line.tokens[1]);
if (target == nullptr)
{
engine->LogWarn("Unknown player: " + line.tokens[1]);
return false;
}
int enchantmentId = 0;
int enchantmentLevel = 1;
if (!CommandParsing::TryParseInt(line.tokens[2], &enchantmentId))
{
engine->LogWarn("Invalid enchantment id: " + line.tokens[2]);
return false;
}
if (line.tokens.size() >= 4 && !CommandParsing::TryParseInt(line.tokens[3], &enchantmentLevel))
{
engine->LogWarn("Invalid enchantment level: " + line.tokens[3]);
return false;
}
std::shared_ptr<Player> player = std::dynamic_pointer_cast<Player>(target);
if (player == nullptr)
{
engine->LogWarn("Cannot resolve target player entity.");
return false;
}
std::shared_ptr<GameCommandPacket> packet = EnchantItemCommand::preparePacket(player, enchantmentId, enchantmentLevel);
if (packet == nullptr)
{
engine->LogError("Failed to build enchant command packet.");
return false;
}
return engine->DispatchWorldCommand(packet->command, packet->data);
}
void CliCommandEnchant::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
if (context.currentTokenIndex == 1)
{
engine->SuggestPlayers(context.prefix, context.linePrefix, out);
}
}
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandEnchant : public IServerCliCommand
{
public:
const char *Name() const override;
const char *Usage() const override;
const char *Description() const override;
bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
};
}

View file

@ -0,0 +1,184 @@
#include "stdafx.h"
#include "CliCommandExperience.h"
#include "..\..\ServerCliEngine.h"
#include "..\..\ServerCliParser.h"
#include "..\CommandParsing.h"
#include "..\..\..\Common\StringUtils.h"
#include "..\..\..\..\Minecraft.Client\MinecraftServer.h"
#include "..\..\..\..\Minecraft.Client\PlayerList.h"
#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
#include <limits>
namespace ServerRuntime
{
namespace
{
constexpr const char *kExperienceUsage = "xp <amount>[L] [player]";
constexpr const char *kExperienceUsageWithPlayer = "xp <amount>[L] <player>";
struct ExperienceAmount
{
int amount = 0;
bool levels = false;
bool take = false;
};
static bool TryParseExperienceAmount(const std::string &token, ExperienceAmount *outValue)
{
if (outValue == nullptr || token.empty())
{
return false;
}
ExperienceAmount parsed;
std::string numericToken = token;
const char suffix = token[token.size() - 1];
if (suffix == 'l' || suffix == 'L')
{
parsed.levels = true;
numericToken = token.substr(0, token.size() - 1);
if (numericToken.empty())
{
return false;
}
}
int signedAmount = 0;
if (!CommandParsing::TryParseInt(numericToken, &signedAmount))
{
return false;
}
if (signedAmount == (std::numeric_limits<int>::min)())
{
return false;
}
parsed.take = signedAmount < 0;
parsed.amount = parsed.take ? -signedAmount : signedAmount;
*outValue = parsed;
return true;
}
static std::shared_ptr<ServerPlayer> ResolveTargetPlayer(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
if (line.tokens.size() >= 3)
{
return engine->FindPlayerByNameUtf8(line.tokens[2]);
}
MinecraftServer *server = MinecraftServer::getInstance();
if (server == nullptr || server->getPlayers() == nullptr)
{
return nullptr;
}
PlayerList *players = server->getPlayers();
if (players->players.size() == 1 && players->players[0] != nullptr)
{
return players->players[0];
}
return nullptr;
}
}
const char *CliCommandExperience::Name() const
{
return "xp";
}
std::vector<std::string> CliCommandExperience::Aliases() const
{
return { "experience" };
}
const char *CliCommandExperience::Usage() const
{
return kExperienceUsage;
}
const char *CliCommandExperience::Description() const
{
return "Grant or remove experience (server-side implementation).";
}
bool CliCommandExperience::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
if (line.tokens.size() < 2 || line.tokens.size() > 3)
{
engine->LogWarn(std::string("Usage: ") + kExperienceUsage);
return false;
}
ExperienceAmount amount;
if (!TryParseExperienceAmount(line.tokens[1], &amount))
{
engine->LogWarn(std::string("Usage: ") + kExperienceUsage);
return false;
}
std::shared_ptr<ServerPlayer> target = ResolveTargetPlayer(line, engine);
if (target == nullptr)
{
if (line.tokens.size() >= 3)
{
engine->LogWarn("Unknown player: " + line.tokens[2]);
}
else
{
engine->LogWarn(std::string("Usage: ") + kExperienceUsageWithPlayer);
}
return false;
}
if (amount.levels)
{
target->giveExperienceLevels(amount.take ? -amount.amount : amount.amount);
if (amount.take)
{
engine->LogInfo("Removed " + std::to_string(amount.amount) + " level(s) from " + StringUtils::WideToUtf8(target->getName()) + ".");
}
else
{
engine->LogInfo("Added " + std::to_string(amount.amount) + " level(s) to " + StringUtils::WideToUtf8(target->getName()) + ".");
}
return true;
}
if (amount.take)
{
engine->LogWarn("Removing raw experience points is not supported. Use negative levels (example: xp -5L <player>).");
return false;
}
target->increaseXp(amount.amount);
engine->LogInfo("Added " + std::to_string(amount.amount) + " experience points to " + StringUtils::WideToUtf8(target->getName()) + ".");
return true;
}
void CliCommandExperience::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
if (context.currentTokenIndex == 1)
{
if (StringUtils::StartsWithIgnoreCase("10", context.prefix))
{
out->push_back(context.linePrefix + "10");
}
if (StringUtils::StartsWithIgnoreCase("10L", context.prefix))
{
out->push_back(context.linePrefix + "10L");
}
if (StringUtils::StartsWithIgnoreCase("-5L", context.prefix))
{
out->push_back(context.linePrefix + "-5L");
}
}
else if (context.currentTokenIndex == 2)
{
engine->SuggestPlayers(context.prefix, context.linePrefix, out);
}
}
}

View file

@ -0,0 +1,17 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandExperience : public IServerCliCommand
{
public:
const char *Name() const override;
std::vector<std::string> Aliases() const override;
const char *Usage() const override;
const char *Description() const override;
bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
};
}

View file

@ -0,0 +1,109 @@
#include "stdafx.h"
#include "CliCommandGamemode.h"
#include "..\..\ServerCliEngine.h"
#include "..\..\ServerCliParser.h"
#include "..\..\..\..\Minecraft.Client\MinecraftServer.h"
#include "..\..\..\..\Minecraft.Client\PlayerList.h"
#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
namespace ServerRuntime
{
namespace
{
constexpr const char *kGamemodeUsage = "gamemode <survival|creative|0|1> [player]";
constexpr const char *kGamemodeUsageWithPlayer = "gamemode <survival|creative|0|1> <player>";
}
const char *CliCommandGamemode::Name() const
{
return "gamemode";
}
std::vector<std::string> CliCommandGamemode::Aliases() const
{
return { "gm" };
}
const char *CliCommandGamemode::Usage() const
{
return kGamemodeUsage;
}
const char *CliCommandGamemode::Description() const
{
return "Set a player's game mode.";
}
bool CliCommandGamemode::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
if (line.tokens.size() < 2 || line.tokens.size() > 3)
{
engine->LogWarn(std::string("Usage: ") + kGamemodeUsage);
return false;
}
GameType *mode = engine->ParseGamemode(line.tokens[1]);
if (mode == nullptr)
{
engine->LogWarn("Unknown gamemode: " + line.tokens[1]);
return false;
}
std::shared_ptr<ServerPlayer> target = nullptr;
if (line.tokens.size() >= 3)
{
target = engine->FindPlayerByNameUtf8(line.tokens[2]);
if (target == nullptr)
{
engine->LogWarn("Unknown player: " + line.tokens[2]);
return false;
}
}
else
{
MinecraftServer *server = MinecraftServer::getInstance();
if (server == nullptr || server->getPlayers() == nullptr)
{
engine->LogWarn("Player list is not available.");
return false;
}
PlayerList *players = server->getPlayers();
if (players->players.size() != 1 || players->players[0] == nullptr)
{
engine->LogWarn(std::string("Usage: ") + kGamemodeUsageWithPlayer);
return false;
}
target = players->players[0];
}
target->setGameMode(mode);
target->fallDistance = 0.0f;
if (line.tokens.size() >= 3)
{
engine->LogInfo("Set " + line.tokens[2] + " gamemode to " + line.tokens[1] + ".");
}
else
{
engine->LogInfo("Set gamemode to " + line.tokens[1] + ".");
}
return true;
}
void CliCommandGamemode::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
if (context.currentTokenIndex == 1)
{
engine->SuggestGamemodes(context.prefix, context.linePrefix, out);
}
else if (context.currentTokenIndex == 2)
{
engine->SuggestPlayers(context.prefix, context.linePrefix, out);
}
}
}

View file

@ -0,0 +1,18 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandGamemode : public IServerCliCommand
{
public:
const char *Name() const override;
std::vector<std::string> Aliases() const override;
const char *Usage() const override;
const char *Description() const override;
bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
};
}

View file

@ -0,0 +1,103 @@
#include "stdafx.h"
#include "CliCommandGive.h"
#include "..\..\ServerCliEngine.h"
#include "..\..\ServerCliParser.h"
#include "..\CommandParsing.h"
#include "..\..\..\..\Minecraft.World\GameCommandPacket.h"
#include "..\..\..\..\Minecraft.World\GiveItemCommand.h"
#include "..\..\..\..\Minecraft.World\net.minecraft.world.entity.player.h"
#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
namespace ServerRuntime
{
namespace
{
constexpr const char *kGiveUsage = "give <player> <itemId> [amount] [aux]";
}
const char *CliCommandGive::Name() const
{
return "give";
}
const char *CliCommandGive::Usage() const
{
return kGiveUsage;
}
const char *CliCommandGive::Description() const
{
return "Give an item via Minecraft.World command dispatcher.";
}
bool CliCommandGive::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
if (line.tokens.size() < 3 || line.tokens.size() > 5)
{
engine->LogWarn(std::string("Usage: ") + kGiveUsage);
return false;
}
std::shared_ptr<ServerPlayer> target = engine->FindPlayerByNameUtf8(line.tokens[1]);
if (target == nullptr)
{
engine->LogWarn("Unknown player: " + line.tokens[1]);
return false;
}
int itemId = 0;
int amount = 1;
int aux = 0;
if (!CommandParsing::TryParseInt(line.tokens[2], &itemId))
{
engine->LogWarn("Invalid item id: " + line.tokens[2]);
return false;
}
if (itemId <= 0)
{
engine->LogWarn("Item id must be greater than 0.");
return false;
}
if (line.tokens.size() >= 4 && !CommandParsing::TryParseInt(line.tokens[3], &amount))
{
engine->LogWarn("Invalid amount: " + line.tokens[3]);
return false;
}
if (line.tokens.size() >= 5 && !CommandParsing::TryParseInt(line.tokens[4], &aux))
{
engine->LogWarn("Invalid aux value: " + line.tokens[4]);
return false;
}
if (amount <= 0)
{
engine->LogWarn("Amount must be greater than 0.");
return false;
}
std::shared_ptr<Player> player = std::dynamic_pointer_cast<Player>(target);
if (player == nullptr)
{
engine->LogWarn("Cannot resolve target player entity.");
return false;
}
std::shared_ptr<GameCommandPacket> packet = GiveItemCommand::preparePacket(player, itemId, amount, aux);
if (packet == nullptr)
{
engine->LogError("Failed to build give command packet.");
return false;
}
return engine->DispatchWorldCommand(packet->command, packet->data);
}
void CliCommandGive::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
if (context.currentTokenIndex == 1)
{
engine->SuggestPlayers(context.prefix, context.linePrefix, out);
}
}
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandGive : public IServerCliCommand
{
public:
const char *Name() const override;
const char *Usage() const override;
const char *Description() const override;
bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
};
}

View file

@ -0,0 +1,46 @@
#include "stdafx.h"
#include "CliCommandHelp.h"
#include "..\..\ServerCliEngine.h"
#include "..\..\ServerCliRegistry.h"
namespace ServerRuntime
{
const char *CliCommandHelp::Name() const
{
return "help";
}
std::vector<std::string> CliCommandHelp::Aliases() const
{
return { "?" };
}
const char *CliCommandHelp::Usage() const
{
return "help";
}
const char *CliCommandHelp::Description() const
{
return "Show available server console commands.";
}
bool CliCommandHelp::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
(void)line;
const std::vector<std::unique_ptr<IServerCliCommand>> &commands = engine->Registry().Commands();
engine->LogInfo("Available commands:");
for (size_t i = 0; i < commands.size(); ++i)
{
std::string row = " ";
row += commands[i]->Usage();
row += " - ";
row += commands[i]->Description();
engine->LogInfo(row);
}
return true;
}
}

View file

@ -0,0 +1,17 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandHelp : public IServerCliCommand
{
public:
virtual const char *Name() const;
virtual std::vector<std::string> Aliases() const;
virtual const char *Usage() const;
virtual const char *Description() const;
virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
};
}

View file

@ -0,0 +1,64 @@
#include "stdafx.h"
#include "CliCommandKill.h"
#include "..\..\ServerCliEngine.h"
#include "..\..\ServerCliParser.h"
#include "..\..\..\..\Minecraft.World\CommandSender.h"
#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
namespace ServerRuntime
{
namespace
{
constexpr const char *kKillUsage = "kill <player>";
}
const char *CliCommandKill::Name() const
{
return "kill";
}
const char *CliCommandKill::Usage() const
{
return kKillUsage;
}
const char *CliCommandKill::Description() const
{
return "Kill a player via Minecraft.World command dispatcher.";
}
bool CliCommandKill::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
if (line.tokens.size() != 2)
{
engine->LogWarn(std::string("Usage: ") + kKillUsage);
return false;
}
std::shared_ptr<ServerPlayer> target = engine->FindPlayerByNameUtf8(line.tokens[1]);
if (target == nullptr)
{
engine->LogWarn("Unknown player: " + line.tokens[1]);
return false;
}
std::shared_ptr<CommandSender> sender = std::dynamic_pointer_cast<CommandSender>(target);
if (sender == nullptr)
{
engine->LogWarn("Cannot resolve target command sender.");
return false;
}
return engine->DispatchWorldCommand(eGameCommand_Kill, byteArray(), sender);
}
void CliCommandKill::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
if (context.currentTokenIndex == 1)
{
engine->SuggestPlayers(context.prefix, context.linePrefix, out);
}
}
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandKill : public IServerCliCommand
{
public:
const char *Name() const override;
const char *Usage() const override;
const char *Description() const override;
bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
};
}

View file

@ -0,0 +1,49 @@
#include "stdafx.h"
#include "CliCommandList.h"
#include "..\..\ServerCliEngine.h"
#include "..\..\..\Common\StringUtils.h"
#include "..\..\..\..\Minecraft.Client\MinecraftServer.h"
#include "..\..\..\..\Minecraft.Client\PlayerList.h"
namespace ServerRuntime
{
const char *CliCommandList::Name() const
{
return "list";
}
const char *CliCommandList::Usage() const
{
return "list";
}
const char *CliCommandList::Description() const
{
return "List connected players.";
}
bool CliCommandList::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
(void)line;
MinecraftServer *server = MinecraftServer::getInstance();
if (server == NULL || server->getPlayers() == NULL)
{
engine->LogWarn("Player list is not available.");
return false;
}
PlayerList *players = server->getPlayers();
std::string names = StringUtils::WideToUtf8(players->getPlayerNames());
if (names.empty())
{
names = "(none)";
}
engine->LogInfo("Players (" + std::to_string(players->getPlayerCount()) + "): " + names);
return true;
}
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandList : public IServerCliCommand
{
public:
virtual const char *Name() const;
virtual const char *Usage() const;
virtual const char *Description() const;
virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
};
}

View file

@ -0,0 +1,98 @@
#include "stdafx.h"
#include "CliCommandPardonIp.h"
#include "..\..\ServerCliEngine.h"
#include "..\..\ServerCliParser.h"
#include "..\..\..\Access\Access.h"
#include "..\..\..\Common\NetworkUtils.h"
#include "..\..\..\Common\StringUtils.h"
namespace ServerRuntime
{
const char *CliCommandPardonIp::Name() const
{
return "pardon-ip";
}
const char *CliCommandPardonIp::Usage() const
{
return "pardon-ip <address>";
}
const char *CliCommandPardonIp::Description() const
{
return "Remove an IP ban.";
}
/**
* Validates the literal IP argument and removes the matching Access IP ban entry
* IPを検証して一致するIP BANを解除する
*/
bool CliCommandPardonIp::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
if (line.tokens.size() != 2)
{
engine->LogWarn("Usage: pardon-ip <address>");
return false;
}
if (!ServerRuntime::Access::IsInitialized())
{
engine->LogWarn("Access manager is not initialized.");
return false;
}
// Java Edition pardon-ip only operates on a literal address, so do not resolve player names here.
const std::string ip = StringUtils::TrimAscii(line.tokens[1]);
if (!NetworkUtils::IsIpLiteral(ip))
{
engine->LogWarn("Invalid IP address: " + line.tokens[1]);
return false;
}
// Distinguish invalid input from a valid but currently unbanned address for clearer operator feedback.
if (!ServerRuntime::Access::IsIpBanned(ip))
{
engine->LogWarn("That IP address is not banned.");
return false;
}
if (!ServerRuntime::Access::RemoveIpBan(ip))
{
engine->LogError("Failed to remove IP ban.");
return false;
}
engine->LogInfo("Unbanned IP address " + ip + ".");
return true;
}
/**
* Suggests currently banned IP addresses for the Java Edition literal-IP argument
* BAN済みIPの補完候補を返す
*/
void CliCommandPardonIp::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
(void)engine;
// Complete from the persisted IP-ban snapshot because this command only accepts already-banned literals.
if (context.currentTokenIndex != 1 || out == nullptr)
{
return;
}
std::vector<ServerRuntime::Access::BannedIpEntry> entries;
if (!ServerRuntime::Access::SnapshotBannedIps(&entries))
{
return;
}
// Reuse the normalized prefix match used by other commands so completion stays case-insensitive.
for (const auto &entry : entries)
{
const std::string &candidate = entry.ip;
if (StringUtils::StartsWithIgnoreCase(candidate, context.prefix))
{
out->push_back(context.linePrefix + candidate);
}
}
}
}

View file

@ -0,0 +1,19 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
/**
* Removes a dedicated-server IP ban using Java Edition style syntax and Access-backed persistence
*/
class CliCommandPardonIp : public IServerCliCommand
{
public:
virtual const char *Name() const;
virtual const char *Usage() const;
virtual const char *Description() const;
virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const;
};
}

View file

@ -0,0 +1,173 @@
#include "stdafx.h"
#include "CliCommandPardon.h"
#include "..\..\ServerCliEngine.h"
#include "..\..\ServerCliParser.h"
#include "..\..\..\Access\Access.h"
#include "..\..\..\Common\StringUtils.h"
#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
#include <algorithm>
namespace ServerRuntime
{
namespace
{
static void AppendUniqueText(const std::string &text, std::vector<std::string> *out)
{
if (out == nullptr || text.empty())
{
return;
}
if (std::find(out->begin(), out->end(), text) == out->end())
{
out->push_back(text);
}
}
static void AppendUniqueXuid(PlayerUID xuid, std::vector<PlayerUID> *out)
{
if (out == nullptr || xuid == INVALID_XUID)
{
return;
}
if (std::find(out->begin(), out->end(), xuid) == out->end())
{
out->push_back(xuid);
}
}
}
const char *CliCommandPardon::Name() const
{
return "pardon";
}
const char *CliCommandPardon::Usage() const
{
return "pardon <player>";
}
const char *CliCommandPardon::Description() const
{
return "Remove a player ban.";
}
/**
* Removes every Access ban entry that matches the requested player name so dual-XUID entries are cleared together
* BANをまとめて解除する
*/
bool CliCommandPardon::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
if (line.tokens.size() != 2)
{
engine->LogWarn("Usage: pardon <player>");
return false;
}
if (!ServerRuntime::Access::IsInitialized())
{
engine->LogWarn("Access manager is not initialized.");
return false;
}
std::vector<PlayerUID> xuidsToRemove;
std::vector<std::string> matchedNames;
std::shared_ptr<ServerPlayer> onlineTarget = engine->FindPlayerByNameUtf8(line.tokens[1]);
if (onlineTarget != nullptr)
{
if (ServerRuntime::Access::IsPlayerBanned(onlineTarget->getXuid()))
{
AppendUniqueXuid(onlineTarget->getXuid(), &xuidsToRemove);
}
if (ServerRuntime::Access::IsPlayerBanned(onlineTarget->getOnlineXuid()))
{
AppendUniqueXuid(onlineTarget->getOnlineXuid(), &xuidsToRemove);
}
}
std::vector<ServerRuntime::Access::BannedPlayerEntry> entries;
if (!ServerRuntime::Access::SnapshotBannedPlayers(&entries))
{
engine->LogError("Failed to read banned players.");
return false;
}
const std::string loweredTarget = StringUtils::ToLowerAscii(line.tokens[1]);
for (const auto &entry : entries)
{
if (StringUtils::ToLowerAscii(entry.name) == loweredTarget)
{
PlayerUID parsedXuid = INVALID_XUID;
if (ServerRuntime::Access::TryParseXuid(entry.xuid, &parsedXuid))
{
AppendUniqueXuid(parsedXuid, &xuidsToRemove);
}
AppendUniqueText(entry.name, &matchedNames);
}
}
if (xuidsToRemove.empty())
{
engine->LogWarn("That player is not banned.");
return false;
}
for (const auto xuid : xuidsToRemove)
{
if (!ServerRuntime::Access::RemovePlayerBan(xuid))
{
engine->LogError("Failed to remove player ban.");
return false;
}
}
std::string playerName = line.tokens[1];
if (!matchedNames.empty())
{
playerName = matchedNames[0];
}
else if (onlineTarget != nullptr)
{
playerName = StringUtils::WideToUtf8(onlineTarget->getName());
}
engine->LogInfo("Unbanned player " + playerName + ".");
return true;
}
/**
* Suggests currently banned player names first and then online names for convenience
* BAN済み名とオンライン名を補完候補に出す
*/
void CliCommandPardon::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
if (context.currentTokenIndex != 1 || out == nullptr)
{
return;
}
std::vector<ServerRuntime::Access::BannedPlayerEntry> entries;
if (ServerRuntime::Access::SnapshotBannedPlayers(&entries))
{
std::vector<std::string> names;
for (const auto &entry : entries)
{
AppendUniqueText(entry.name, &names);
}
for (const auto &name : names)
{
if (StringUtils::StartsWithIgnoreCase(name, context.prefix))
{
out->push_back(context.linePrefix + name);
}
}
}
engine->SuggestPlayers(context.prefix, context.linePrefix, out);
}
}

View file

@ -0,0 +1,19 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
/**
* Removes dedicated-server player bans using Java Edition style syntax and Access-backed persistence
*/
class CliCommandPardon : public IServerCliCommand
{
public:
virtual const char *Name() const;
virtual const char *Usage() const;
virtual const char *Description() const;
virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const;
};
}

View file

@ -0,0 +1,32 @@
#include "stdafx.h"
#include "CliCommandStop.h"
#include "..\..\ServerCliEngine.h"
namespace ServerRuntime
{
const char *CliCommandStop::Name() const
{
return "stop";
}
const char *CliCommandStop::Usage() const
{
return "stop";
}
const char *CliCommandStop::Description() const
{
return "Stop the dedicated server.";
}
bool CliCommandStop::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
(void)line;
engine->LogInfo("Stopping server...");
engine->RequestShutdown();
return true;
}
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandStop : public IServerCliCommand
{
public:
virtual const char *Name() const;
virtual const char *Usage() const;
virtual const char *Description() const;
virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
};
}

View file

@ -0,0 +1,118 @@
#include "stdafx.h"
#include "CliCommandTime.h"
#include "..\..\ServerCliEngine.h"
#include "..\..\ServerCliParser.h"
#include "..\..\..\Common\StringUtils.h"
#include "..\..\..\..\Minecraft.World\GameCommandPacket.h"
#include "..\..\..\..\Minecraft.World\TimeCommand.h"
namespace ServerRuntime
{
namespace
{
constexpr const char *kTimeUsage = "time <day|night|set day|set night>";
static bool TryResolveNightFlag(const std::vector<std::string> &tokens, bool *outNight)
{
if (outNight == nullptr)
{
return false;
}
std::string value;
if (tokens.size() == 2)
{
value = StringUtils::ToLowerAscii(tokens[1]);
}
else if (tokens.size() == 3 && StringUtils::ToLowerAscii(tokens[1]) == "set")
{
value = StringUtils::ToLowerAscii(tokens[2]);
}
else
{
return false;
}
if (value == "day")
{
*outNight = false;
return true;
}
if (value == "night")
{
*outNight = true;
return true;
}
return false;
}
static void SuggestLiteral(const char *candidate, const ServerCliCompletionContext &context, std::vector<std::string> *out)
{
if (candidate == nullptr || out == nullptr)
{
return;
}
const std::string text(candidate);
if (StringUtils::StartsWithIgnoreCase(text, context.prefix))
{
out->push_back(context.linePrefix + text);
}
}
}
const char *CliCommandTime::Name() const
{
return "time";
}
const char *CliCommandTime::Usage() const
{
return kTimeUsage;
}
const char *CliCommandTime::Description() const
{
return "Set day or night via Minecraft.World command dispatcher.";
}
bool CliCommandTime::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
bool night = false;
if (!TryResolveNightFlag(line.tokens, &night))
{
engine->LogWarn(std::string("Usage: ") + kTimeUsage);
return false;
}
std::shared_ptr<GameCommandPacket> packet = TimeCommand::preparePacket(night);
if (packet == nullptr)
{
engine->LogError("Failed to build time command packet.");
return false;
}
return engine->DispatchWorldCommand(packet->command, packet->data);
}
void CliCommandTime::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
(void)engine;
if (context.currentTokenIndex == 1)
{
SuggestLiteral("day", context, out);
SuggestLiteral("night", context, out);
SuggestLiteral("set", context, out);
}
else if (context.currentTokenIndex == 2 &&
context.parsed.tokens.size() >= 2 &&
StringUtils::ToLowerAscii(context.parsed.tokens[1]) == "set")
{
SuggestLiteral("day", context, out);
SuggestLiteral("night", context, out);
}
}
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandTime : public IServerCliCommand
{
public:
const char *Name() const override;
const char *Usage() const override;
const char *Description() const override;
bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
};
}

View file

@ -0,0 +1,82 @@
#include "stdafx.h"
#include "CliCommandTp.h"
#include "..\..\ServerCliEngine.h"
#include "..\..\ServerCliParser.h"
#include "..\..\..\..\Minecraft.Client\PlayerConnection.h"
#include "..\..\..\..\Minecraft.Client\TeleportCommand.h"
#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
#include "..\..\..\..\Minecraft.World\GameCommandPacket.h"
namespace ServerRuntime
{
namespace
{
constexpr const char *kTpUsage = "tp <player> <target>";
}
const char *CliCommandTp::Name() const
{
return "tp";
}
std::vector<std::string> CliCommandTp::Aliases() const
{
return { "teleport" };
}
const char *CliCommandTp::Usage() const
{
return kTpUsage;
}
const char *CliCommandTp::Description() const
{
return "Teleport one player to another via Minecraft.World command dispatcher.";
}
bool CliCommandTp::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
if (line.tokens.size() != 3)
{
engine->LogWarn(std::string("Usage: ") + kTpUsage);
return false;
}
std::shared_ptr<ServerPlayer> subject = engine->FindPlayerByNameUtf8(line.tokens[1]);
std::shared_ptr<ServerPlayer> destination = engine->FindPlayerByNameUtf8(line.tokens[2]);
if (subject == nullptr)
{
engine->LogWarn("Unknown player: " + line.tokens[1]);
return false;
}
if (destination == nullptr)
{
engine->LogWarn("Unknown player: " + line.tokens[2]);
return false;
}
if (subject->connection == nullptr)
{
engine->LogWarn("Cannot teleport because source player connection is inactive.");
return false;
}
std::shared_ptr<GameCommandPacket> packet = TeleportCommand::preparePacket(subject->getXuid(), destination->getXuid());
if (packet == nullptr)
{
engine->LogError("Failed to build teleport command packet.");
return false;
}
return engine->DispatchWorldCommand(packet->command, packet->data);
}
void CliCommandTp::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
if (context.currentTokenIndex == 1 || context.currentTokenIndex == 2)
{
engine->SuggestPlayers(context.prefix, context.linePrefix, out);
}
}
}

View file

@ -0,0 +1,18 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandTp : public IServerCliCommand
{
public:
const char *Name() const override;
std::vector<std::string> Aliases() const override;
const char *Usage() const override;
const char *Description() const override;
bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
};
}

View file

@ -0,0 +1,49 @@
#include "stdafx.h"
#include "CliCommandWeather.h"
#include "..\..\ServerCliEngine.h"
#include "..\..\ServerCliParser.h"
#include "..\..\..\..\Minecraft.World\GameCommandPacket.h"
#include "..\..\..\..\Minecraft.World\ToggleDownfallCommand.h"
namespace ServerRuntime
{
namespace
{
constexpr const char *kWeatherUsage = "weather";
}
const char *CliCommandWeather::Name() const
{
return "weather";
}
const char *CliCommandWeather::Usage() const
{
return kWeatherUsage;
}
const char *CliCommandWeather::Description() const
{
return "Toggle weather via Minecraft.World command dispatcher.";
}
bool CliCommandWeather::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
if (line.tokens.size() != 1)
{
engine->LogWarn(std::string("Usage: ") + kWeatherUsage);
return false;
}
std::shared_ptr<GameCommandPacket> packet = ToggleDownfallCommand::preparePacket();
if (packet == nullptr)
{
engine->LogError("Failed to build weather command packet.");
return false;
}
return engine->DispatchWorldCommand(packet->command, packet->data);
}
}

View file

@ -0,0 +1,15 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandWeather : public IServerCliCommand
{
public:
const char *Name() const override;
const char *Usage() const override;
const char *Description() const override;
bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
};
}

View file

@ -0,0 +1,285 @@
#include "stdafx.h"
#include "CliCommandWhitelist.h"
#include "..\..\ServerCliEngine.h"
#include "..\..\ServerCliParser.h"
#include "..\..\..\Access\Access.h"
#include "..\..\..\Common\StringUtils.h"
#include "..\..\..\ServerProperties.h"
#include <algorithm>
#include <array>
namespace ServerRuntime
{
namespace
{
static const char *kWhitelistUsage = "whitelist <on|off|list|add|remove|reload> [...]";
static bool CompareWhitelistEntries(const ServerRuntime::Access::WhitelistedPlayerEntry &left, const ServerRuntime::Access::WhitelistedPlayerEntry &right)
{
const auto leftName = StringUtils::ToLowerAscii(left.name);
const auto rightName = StringUtils::ToLowerAscii(right.name);
if (leftName != rightName)
{
return leftName < rightName;
}
return StringUtils::ToLowerAscii(left.xuid) < StringUtils::ToLowerAscii(right.xuid);
}
static bool PersistWhitelistToggle(bool enabled)
{
auto config = LoadServerPropertiesConfig();
config.whiteListEnabled = enabled;
return SaveServerPropertiesConfig(config);
}
static std::string BuildWhitelistEntryRow(const ServerRuntime::Access::WhitelistedPlayerEntry &entry)
{
std::string row = " ";
row += entry.xuid;
if (!entry.name.empty())
{
row += " - ";
row += entry.name;
}
return row;
}
static void LogWhitelistMode(ServerCliEngine *engine)
{
engine->LogInfo(std::string("Whitelist is ") + (ServerRuntime::Access::IsWhitelistEnabled() ? "enabled." : "disabled."));
}
static bool LogWhitelistEntries(ServerCliEngine *engine)
{
std::vector<ServerRuntime::Access::WhitelistedPlayerEntry> entries;
if (!ServerRuntime::Access::SnapshotWhitelistedPlayers(&entries))
{
engine->LogError("Failed to read whitelist entries.");
return false;
}
std::sort(entries.begin(), entries.end(), CompareWhitelistEntries);
LogWhitelistMode(engine);
engine->LogInfo("There are " + std::to_string(entries.size()) + " whitelisted player(s).");
for (const auto &entry : entries)
{
engine->LogInfo(BuildWhitelistEntryRow(entry));
}
return true;
}
static bool TryParseWhitelistXuid(const std::string &text, ServerCliEngine *engine, PlayerUID *outXuid)
{
if (ServerRuntime::Access::TryParseXuid(text, outXuid))
{
return true;
}
engine->LogWarn("Invalid XUID: " + text);
return false;
}
static void SuggestLiteral(const std::string &candidate, const ServerCliCompletionContext &context, std::vector<std::string> *out)
{
if (out == nullptr)
{
return;
}
if (StringUtils::StartsWithIgnoreCase(candidate, context.prefix))
{
out->push_back(context.linePrefix + candidate);
}
}
}
const char *CliCommandWhitelist::Name() const
{
return "whitelist";
}
const char *CliCommandWhitelist::Usage() const
{
return kWhitelistUsage;
}
const char *CliCommandWhitelist::Description() const
{
return "Manage the dedicated-server XUID whitelist.";
}
bool CliCommandWhitelist::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
if (line.tokens.size() < 2)
{
engine->LogWarn(std::string("Usage: ") + kWhitelistUsage);
return false;
}
if (!ServerRuntime::Access::IsInitialized())
{
engine->LogWarn("Access manager is not initialized.");
return false;
}
const auto subcommand = StringUtils::ToLowerAscii(line.tokens[1]);
if (subcommand == "on" || subcommand == "off")
{
if (line.tokens.size() != 2)
{
engine->LogWarn("Usage: whitelist <on|off>");
return false;
}
const bool enabled = (subcommand == "on");
if (!PersistWhitelistToggle(enabled))
{
engine->LogError("Failed to persist whitelist mode to server.properties.");
return false;
}
ServerRuntime::Access::SetWhitelistEnabled(enabled);
engine->LogInfo(std::string("Whitelist ") + (enabled ? "enabled." : "disabled."));
return true;
}
if (subcommand == "list")
{
if (line.tokens.size() != 2)
{
engine->LogWarn("Usage: whitelist list");
return false;
}
return LogWhitelistEntries(engine);
}
if (subcommand == "reload")
{
if (line.tokens.size() != 2)
{
engine->LogWarn("Usage: whitelist reload");
return false;
}
if (!ServerRuntime::Access::ReloadWhitelist())
{
engine->LogError("Failed to reload whitelist.");
return false;
}
const auto config = LoadServerPropertiesConfig();
ServerRuntime::Access::SetWhitelistEnabled(config.whiteListEnabled);
engine->LogInfo("Reloaded whitelist from disk.");
LogWhitelistMode(engine);
return true;
}
if (subcommand == "add")
{
if (line.tokens.size() < 3)
{
engine->LogWarn("Usage: whitelist add <xuid> [name ...]");
return false;
}
PlayerUID xuid = INVALID_XUID;
if (!TryParseWhitelistXuid(line.tokens[2], engine, &xuid))
{
return false;
}
if (ServerRuntime::Access::IsPlayerWhitelisted(xuid))
{
engine->LogWarn("That XUID is already whitelisted.");
return false;
}
const auto metadata = ServerRuntime::Access::WhitelistManager::BuildDefaultMetadata("Console");
const auto name = StringUtils::JoinTokens(line.tokens, 3);
if (!ServerRuntime::Access::AddWhitelistedPlayer(xuid, name, metadata))
{
engine->LogError("Failed to write whitelist entry.");
return false;
}
std::string message = "Whitelisted XUID " + ServerRuntime::Access::FormatXuid(xuid) + ".";
if (!name.empty())
{
message += " Name: " + name;
}
engine->LogInfo(message);
return true;
}
if (subcommand == "remove")
{
if (line.tokens.size() != 3)
{
engine->LogWarn("Usage: whitelist remove <xuid>");
return false;
}
PlayerUID xuid = INVALID_XUID;
if (!TryParseWhitelistXuid(line.tokens[2], engine, &xuid))
{
return false;
}
if (!ServerRuntime::Access::IsPlayerWhitelisted(xuid))
{
engine->LogWarn("That XUID is not whitelisted.");
return false;
}
if (!ServerRuntime::Access::RemoveWhitelistedPlayer(xuid))
{
engine->LogError("Failed to remove whitelist entry.");
return false;
}
engine->LogInfo("Removed XUID " + ServerRuntime::Access::FormatXuid(xuid) + " from the whitelist.");
return true;
}
engine->LogWarn(std::string("Usage: ") + kWhitelistUsage);
return false;
}
void CliCommandWhitelist::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
(void)engine;
if (out == nullptr)
{
return;
}
if (context.currentTokenIndex == 1)
{
SuggestLiteral("on", context, out);
SuggestLiteral("off", context, out);
SuggestLiteral("list", context, out);
SuggestLiteral("add", context, out);
SuggestLiteral("remove", context, out);
SuggestLiteral("reload", context, out);
return;
}
if (context.currentTokenIndex == 2 && context.parsed.tokens.size() >= 2 && StringUtils::ToLowerAscii(context.parsed.tokens[1]) == "remove")
{
std::vector<ServerRuntime::Access::WhitelistedPlayerEntry> entries;
if (!ServerRuntime::Access::SnapshotWhitelistedPlayers(&entries))
{
return;
}
for (const auto &entry : entries)
{
SuggestLiteral(entry.xuid, context, out);
}
}
}
}

View file

@ -0,0 +1,17 @@
#pragma once
#include "..\IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandWhitelist : public IServerCliCommand
{
public:
const char *Name() const override;
const char *Usage() const override;
const char *Description() const override;
bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
};
}

View file

@ -0,0 +1,749 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{7CB40BFC-C8E4-4293-A22E-D2041348D5AF}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>MinecraftServer</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
</ImportGroup>
<ImportGroup Label="Shared" />
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(SolutionDir)$(Platform)\Minecraft.Server\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\Minecraft.Server\$(Configuration)\obj\MinecraftServer\</IntDir>
<TargetName>Minecraft.Server</TargetName>
<LocalDebuggerWorkingDirectory>$(OutDir)</LocalDebuggerWorkingDirectory>
<LocalDebuggerCommandArguments>-port 25565 -bind 0.0.0.0 -name DedicatedServer</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(SolutionDir)$(Platform)\Minecraft.Server\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\Minecraft.Server\$(Configuration)\obj\MinecraftServer\</IntDir>
<TargetName>Minecraft.Server</TargetName>
<LocalDebuggerWorkingDirectory>$(OutDir)</LocalDebuggerWorkingDirectory>
<LocalDebuggerCommandArguments>-port 25565 -bind 0.0.0.0 -name DedicatedServer</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderOutputFile>$(OutDir)MinecraftServer.pch</PrecompiledHeaderOutputFile>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeTypeInfo>true</RuntimeTypeInfo>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<PreprocessorDefinitions>_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_DEBUG;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;MINECRAFT_SERVER_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\Minecraft.Client;..\Minecraft.Client\Windows64\Iggy\include;..\Minecraft.Client\Xbox\Sentient\Include;..\Minecraft.World\x64headers;..\include;$(ProjectDir)Windows64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<MASM>
<UseSafeExceptionHandlers>false</UseSafeExceptionHandlers>
</MASM>
<ResourceCompile>
<PreprocessorDefinitions>_WINDOWS64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\Minecraft.Client;..\Minecraft.Client\Xbox;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<SubSystem>Console</SubSystem>
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>d3d11.lib;XInput9_1_0.lib;wsock32.lib;legacy_stdio_definitions.lib;..\Minecraft.World\x64_Debug\Minecraft.World.lib;..\Minecraft.Client\Windows64\Iggy\lib\iggy_w64.lib;..\Minecraft.Client\Windows64\Iggy\lib\iggyperfmon_w64.lib;..\Minecraft.Client\Windows64\Iggy\lib\iggyexpruntime_w64.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Input_d.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Storage_d.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Render_PC_d.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>powershell -ExecutionPolicy Bypass -File "$(ProjectDir)Windows64\postbuild_server.ps1" -OutDir "$(OutDir)." -ProjectRoot "$(ProjectDir).." -Configuration "$(Configuration)"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderOutputFile>$(OutDir)MinecraftServer.pch</PrecompiledHeaderOutputFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeTypeInfo>true</RuntimeTypeInfo>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<PreprocessorDefinitions>_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;MINECRAFT_SERVER_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\Minecraft.Client;..\Minecraft.Client\Windows64\Iggy\include;..\Minecraft.Client\Xbox\Sentient\Include;..\Minecraft.World\x64headers;..\include;$(ProjectDir)Windows64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<MASM>
<UseSafeExceptionHandlers>false</UseSafeExceptionHandlers>
</MASM>
<ResourceCompile>
<PreprocessorDefinitions>_WINDOWS64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\Minecraft.Client;..\Minecraft.Client\Xbox;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<SubSystem>Console</SubSystem>
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>d3d11.lib;XInput9_1_0.lib;wsock32.lib;legacy_stdio_definitions.lib;..\Minecraft.World\x64_Release\Minecraft.World.lib;..\Minecraft.Client\Windows64\Iggy\lib\iggy_w64.lib;..\Minecraft.Client\Windows64\Iggy\lib\iggyperfmon_w64.lib;..\Minecraft.Client\Windows64\Iggy\lib\iggyexpruntime_w64.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Input.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Storage.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Render_PC.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>powershell -ExecutionPolicy Bypass -File "$(ProjectDir)Windows64\postbuild_server.ps1" -OutDir "$(OutDir)." -ProjectRoot "$(ProjectDir).." -Configuration "$(Configuration)"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="Access\Access.cpp" />
<ClCompile Include="Access\BanManager.cpp" />
<ClCompile Include="Access\WhitelistManager.cpp" />
<ClCompile Include="ServerLogManager.cpp" />
<ClCompile Include="..\Minecraft.Client\AbstractTexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\AchievementPopup.cpp" />
<ClCompile Include="..\Minecraft.Client\AchievementScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\AllowAllCuller.cpp" />
<ClCompile Include="..\Minecraft.Client\ArchiveFile.cpp" />
<ClCompile Include="..\Minecraft.Client\ArrowRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\BatModel.cpp" />
<ClCompile Include="..\Minecraft.Client\BatRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\BeaconRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\BlazeModel.cpp" />
<ClCompile Include="..\Minecraft.Client\BlazeRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\BoatModel.cpp" />
<ClCompile Include="..\Minecraft.Client\BoatRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\BookModel.cpp" />
<ClCompile Include="..\Minecraft.Client\BossMobGuiInfo.cpp" />
<ClCompile Include="..\Minecraft.Client\BreakingItemParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\BubbleParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\BufferedImage.cpp" />
<ClCompile Include="..\Minecraft.Client\Button.cpp" />
<ClCompile Include="..\Minecraft.Client\Camera.cpp" />
<ClCompile Include="..\Minecraft.Client\CaveSpiderRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ChatScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\ChestModel.cpp" />
<ClCompile Include="..\Minecraft.Client\ChestRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ChickenModel.cpp" />
<ClCompile Include="..\Minecraft.Client\ChickenRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Chunk.cpp" />
<ClCompile Include="..\Minecraft.Client\ClientConnection.cpp" />
<ClCompile Include="..\Minecraft.Client\ClientConstants.cpp" />
<ClCompile Include="..\Minecraft.Client\ClockTexture.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Audio\Consoles_SoundEngine.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Audio\SoundEngine.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Audio\SoundNames.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Colours\ColourTable.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\ConsoleGameMode.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Console_Utils.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Consoles_App.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCAudioFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCCapeFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCColourTableFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCGameRulesFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCGameRulesHeader.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCLocalisationFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCPack.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCSkinFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCTextureFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCUIDataFile.cpp" />
<ClCompile Include="..\include\lce_filesystem\lce_filesystem.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\AddEnchantmentRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\AddItemRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\ApplySchematicRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\BiomeOverride.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\CollectItemRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\CompleteAllRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\CompoundGameRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\ConsoleGenerateStructure.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\ConsoleSchematicFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\GameRule.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\GameRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\GameRuleManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelGenerationOptions.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelGenerators.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelRules.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelRuleset.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\NamedAreaRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\StartFeature.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\UpdatePlayerRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\UseTileRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionGenerateBox.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionPlaceBlock.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionPlaceContainer.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionPlaceSpawner.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Leaderboards\LeaderboardInterface.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Leaderboards\LeaderboardManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Network\GameNetworkManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Network\PlatformNetworkManagerStub.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Telemetry\TelemetryManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Trial\TrialMode.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\AreaConstraint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\AreaHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\AreaTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\ChangeStateConstraint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\ChoiceTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\CompleteUsingItemTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\ControllerTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\CraftTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\DiggerItemHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\EffectChangedTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\FullTutorial.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\FullTutorialActiveTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\FullTutorialMode.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\HorseChoiceTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\InfoTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\InputConstraint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\LookAtEntityHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\LookAtTileHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\PickupTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\ProcedureCompoundTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\ProgressFlagTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\RideEntityTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\StatTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\TakeItemHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\Tutorial.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialMessage.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialMode.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\UseItemTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\UseTileTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\XuiCraftingTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_AbstractContainerMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_AnvilMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_BeaconMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_BrewingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_CommandBlockMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_ContainerMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_CraftingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_CreativeMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_DispenserMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_EnchantingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_FireworksMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_FurnaceMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_HUD.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_HopperMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_HorseInventoryMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_InventoryMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_PauseMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_StartGame.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_TradingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIBitmapFont.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Chat.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_DebugUIConsole.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_DebugUIMarketingGuide.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Logo.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_MenuBackground.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Panorama.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_PressStartToPlay.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Tooltips.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_TutorialPopup.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Base.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_BeaconEffectButton.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_BitmapIcon.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Button.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_ButtonList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_CheckBox.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Cursor.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_DLCList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_DynamicLabel.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_EnchantmentBook.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_EnchantmentButton.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_HTMLLabel.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Label.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_LeaderboardList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_MinecraftHorse.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_MinecraftPlayer.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_PlayerList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_PlayerSkinPreview.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Progress.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_SaveList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Slider.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_SlotList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_SpaceIndicatorBar.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_TextInput.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_TexturePackList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIController.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIFontData.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIGroup.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UILayer.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_AbstractContainerMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_AnvilMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_BeaconMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_BrewingStandMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ConnectingProgress.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ContainerMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ControlsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_CraftingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_CreateWorldMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_CreativeMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Credits.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DLCMainMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DLCOffersMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DeathMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugCreateSchematic.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugOptions.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugOverlay.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugSetCamera.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DispenserMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_EULA.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_EnchantingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_EndPoem.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_FireworksMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_FullscreenProgress.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_FurnaceMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HUD.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HelpAndOptionsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HopperMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HorseInventoryMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HowToPlay.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HowToPlayMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InGameHostOptionsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InGameInfoMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InGamePlayerOptionsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Intro.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InventoryMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_JoinMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Keyboard.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LanguageSelector.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LaunchMoreOptionsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LeaderboardsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LoadMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LoadOrJoinMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_MainMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_MessageBox.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_NewUpdateMessage.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_PauseMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_QuadrantSignin.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ReinstallMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SaveMessage.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsAudioMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsControlMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsGraphicsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsOptionsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsUIMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SignEntryMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SkinSelectMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_TeleportMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Timer.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_TradingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_TrialExitUpsell.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIString.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UITTFFont.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\adler32.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\Common\zlib\compress.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\Common\zlib\crc32.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\Common\zlib\deflate.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\Common\zlib\gzclose.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\Common\zlib\gzlib.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\Common\zlib\gzread.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\Common\zlib\gzwrite.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\Common\zlib\infback.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\Common\zlib\inffast.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\Common\zlib\inflate.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\Common\zlib\inftrees.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\Common\zlib\trees.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\Common\zlib\uncompr.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\Common\zlib\zutil.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\CompassTexture.cpp" />
<ClCompile Include="..\Minecraft.Client\ConfirmScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\ConsoleInput.cpp" />
<ClCompile Include="..\Minecraft.Client\ControlsScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\CowModel.cpp" />
<ClCompile Include="..\Minecraft.Client\CowRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\CreateWorldScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\CreeperModel.cpp" />
<ClCompile Include="..\Minecraft.Client\CreeperRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\CritParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\CritParticle2.cpp" />
<ClCompile Include="..\Minecraft.Client\Cube.cpp" />
<ClCompile Include="..\Minecraft.Client\DLCTexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\DeathScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\DefaultRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\DefaultTexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\DemoUser.cpp" />
<ClCompile Include="..\Minecraft.Client\DerivedServerLevel.cpp" />
<ClCompile Include="..\Minecraft.Client\DirtyChunkSorter.cpp" />
<ClCompile Include="..\Minecraft.Client\DispenserBootstrap.cpp" />
<ClCompile Include="..\Minecraft.Client\DistanceChunkSorter.cpp" />
<ClCompile Include="..\Minecraft.Client\DragonBreathParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\DragonModel.cpp" />
<ClCompile Include="..\Minecraft.Client\DripParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\EchantmentTableParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\EditBox.cpp" />
<ClCompile Include="..\Minecraft.Client\EnchantTableRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EnderChestRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EnderCrystalModel.cpp" />
<ClCompile Include="..\Minecraft.Client\EnderCrystalRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EnderDragonRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EnderParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\EndermanModel.cpp" />
<ClCompile Include="..\Minecraft.Client\EndermanRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EntityRenderDispatcher.cpp" />
<ClCompile Include="..\Minecraft.Client\EntityRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EntityTileRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EntityTracker.cpp" />
<ClCompile Include="..\Minecraft.Client\ErrorScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\ExperienceOrbRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ExplodeParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\Extrax64Stubs.cpp" />
<ClCompile Include="..\Minecraft.Client\FallingTileRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\FileTexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\FireballRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\FireworksParticles.cpp" />
<ClCompile Include="..\Minecraft.Client\FishingHookRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\FlameParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\FolderTexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\Font.cpp" />
<ClCompile Include="..\Minecraft.Client\FootstepParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\Frustum.cpp" />
<ClCompile Include="..\Minecraft.Client\FrustumCuller.cpp" />
<ClCompile Include="..\Minecraft.Client\FrustumData.cpp" />
<ClCompile Include="..\Minecraft.Client\GameRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\GhastModel.cpp" />
<ClCompile Include="..\Minecraft.Client\GhastRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\GiantMobRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Gui.cpp" />
<ClCompile Include="..\Minecraft.Client\GuiComponent.cpp" />
<ClCompile Include="..\Minecraft.Client\GuiMessage.cpp" />
<ClCompile Include="..\Minecraft.Client\GuiParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\GuiParticles.cpp" />
<ClCompile Include="..\Minecraft.Client\HeartParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\HorseRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\HttpTexture.cpp" />
<ClCompile Include="..\Minecraft.Client\HugeExplosionParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\HugeExplosionSeedParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\HumanoidMobRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\HumanoidModel.cpp" />
<ClCompile Include="..\Minecraft.Client\InBedChatScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\Input.cpp" />
<ClCompile Include="..\Minecraft.Client\ItemFrameRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ItemInHandRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ItemRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ItemSpriteRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\JoinMultiplayerScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\KeyMapping.cpp" />
<ClCompile Include="..\Minecraft.Client\LargeChestModel.cpp" />
<ClCompile Include="..\Minecraft.Client\LavaParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\LavaSlimeModel.cpp" />
<ClCompile Include="..\Minecraft.Client\LavaSlimeRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\LeashKnotModel.cpp" />
<ClCompile Include="..\Minecraft.Client\LeashKnotRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\LevelRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Lighting.cpp" />
<ClCompile Include="..\Minecraft.Client\LightningBoltRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\LivingEntityRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\LocalPlayer.cpp" />
<ClCompile Include="..\Minecraft.Client\MemTexture.cpp" />
<ClCompile Include="..\Minecraft.Client\MemoryTracker.cpp" />
<ClCompile Include="..\Minecraft.Client\MinecartModel.cpp" />
<ClCompile Include="..\Minecraft.Client\MinecartRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\MinecartSpawnerRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Minecraft.cpp" />
<ClCompile Include="..\Minecraft.Client\MinecraftServer.cpp" />
<ClCompile Include="..\Minecraft.Client\Minimap.cpp" />
<ClCompile Include="..\Minecraft.Client\MobRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\MobSkinMemTextureProcessor.cpp" />
<ClCompile Include="..\Minecraft.Client\MobSkinTextureProcessor.cpp" />
<ClCompile Include="..\Minecraft.Client\MobSpawnerRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Model.cpp" />
<ClCompile Include="..\Minecraft.Client\ModelHorse.cpp" />
<ClCompile Include="..\Minecraft.Client\ModelPart.cpp" />
<ClCompile Include="..\Minecraft.Client\MultiPlayerChunkCache.cpp" />
<ClCompile Include="..\Minecraft.Client\MultiPlayerGameMode.cpp" />
<ClCompile Include="..\Minecraft.Client\MultiPlayerLevel.cpp" />
<ClCompile Include="..\Minecraft.Client\MultiPlayerLocalPlayer.cpp" />
<ClCompile Include="..\Minecraft.Client\MushroomCowRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\NameEntryScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\NetherPortalParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\NoteParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\OcelotModel.cpp" />
<ClCompile Include="..\Minecraft.Client\OcelotRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\OffsettedRenderList.cpp" />
<ClCompile Include="..\Minecraft.Client\Options.cpp" />
<ClCompile Include="..\Minecraft.Client\OptionsScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\PS3\PS3Extras\ShutdownManager.cpp" />
<ClCompile Include="..\Minecraft.Client\PaintingRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Particle.cpp" />
<ClCompile Include="..\Minecraft.Client\ParticleEngine.cpp" />
<ClCompile Include="..\Minecraft.Client\PauseScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\PendingConnection.cpp" />
<ClCompile Include="..\Minecraft.Client\PigModel.cpp" />
<ClCompile Include="..\Minecraft.Client\PigRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\PistonPieceRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\PlayerChunkMap.cpp" />
<ClCompile Include="..\Minecraft.Client\PlayerCloudParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\PlayerConnection.cpp" />
<ClCompile Include="..\Minecraft.Client\PlayerList.cpp" />
<ClCompile Include="..\Minecraft.Client\PlayerRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Polygon.cpp" />
<ClCompile Include="..\Minecraft.Client\PreStitchedTextureMap.cpp" />
<ClCompile Include="..\Minecraft.Client\ProgressRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\QuadrupedModel.cpp" />
<ClCompile Include="..\Minecraft.Client\Rect2i.cpp" />
<ClCompile Include="..\Minecraft.Client\RedDustParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\RemotePlayer.cpp" />
<ClCompile Include="..\Minecraft.Client\RenameWorldScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\Screen.cpp" />
<ClCompile Include="..\Minecraft.Client\ScreenSizeCalculator.cpp" />
<ClCompile Include="..\Minecraft.Client\ScrolledSelectionList.cpp" />
<ClCompile Include="..\Minecraft.Client\SelectWorldScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerChunkCache.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerCommandDispatcher.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerConnection.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerLevel.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerLevelListener.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerPlayer.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerPlayerGameMode.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerScoreboard.cpp" />
<ClCompile Include="..\Minecraft.Client\Settings.cpp" />
<ClCompile Include="..\Minecraft.Client\SheepFurModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SheepModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SheepRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SignModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SignRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SilverfishModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SilverfishRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SimpleIcon.cpp" />
<ClCompile Include="..\Minecraft.Client\SkeletonHeadModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SkeletonModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SkeletonRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SkiModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SkullTileRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SlideButton.cpp" />
<ClCompile Include="..\Minecraft.Client\SlimeModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SlimeRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SmallButton.cpp" />
<ClCompile Include="..\Minecraft.Client\SmokeParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\SnowManModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SnowManRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SnowShovelParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\SpellParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\SpiderModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SpiderRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SplashParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\SquidModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SquidRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\StatsCounter.cpp" />
<ClCompile Include="..\Minecraft.Client\StatsScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\StatsSyncher.cpp" />
<ClCompile Include="..\Minecraft.Client\StitchSlot.cpp" />
<ClCompile Include="..\Minecraft.Client\StitchedTexture.cpp" />
<ClCompile Include="..\Minecraft.Client\Stitcher.cpp" />
<ClCompile Include="..\Minecraft.Client\StringTable.cpp" />
<ClCompile Include="..\Minecraft.Client\SuspendedParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\SuspendedTownParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\TakeAnimationParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\TeleportCommand.cpp" />
<ClCompile Include="..\Minecraft.Client\TerrainParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\Tesselator.cpp" />
<ClCompile Include="..\Minecraft.Client\TexOffs.cpp" />
<ClCompile Include="..\Minecraft.Client\Texture.cpp" />
<ClCompile Include="..\Minecraft.Client\TextureAtlas.cpp" />
<ClCompile Include="..\Minecraft.Client\TextureHolder.cpp" />
<ClCompile Include="..\Minecraft.Client\TextureManager.cpp" />
<ClCompile Include="..\Minecraft.Client\TextureMap.cpp" />
<ClCompile Include="..\Minecraft.Client\TexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\TexturePackRepository.cpp" />
<ClCompile Include="..\Minecraft.Client\Textures.cpp" />
<ClCompile Include="..\Minecraft.Client\TheEndPortalRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\TileEntityRenderDispatcher.cpp" />
<ClCompile Include="..\Minecraft.Client\TileEntityRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\TileRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Timer.cpp" />
<ClCompile Include="..\Minecraft.Client\TitleScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\TntMinecartRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\TntRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\TrackedEntity.cpp" />
<ClCompile Include="..\Minecraft.Client\User.cpp" />
<ClCompile Include="..\Minecraft.Client\Vertex.cpp" />
<ClCompile Include="..\Minecraft.Client\VideoSettingsScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\ViewportCuller.cpp" />
<ClCompile Include="..\Minecraft.Client\VillagerGolemModel.cpp" />
<ClCompile Include="..\Minecraft.Client\VillagerGolemRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\VillagerModel.cpp" />
<ClCompile Include="..\Minecraft.Client\VillagerRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\VillagerZombieModel.cpp" />
<ClCompile Include="..\Minecraft.Client\WaterDropParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Iggy\gdraw\gdraw_d3d11.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\KeyboardMouseInput.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Leaderboards\WindowsLeaderboardManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\PostProcesser.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Windows64_App.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Windows64_Minecraft.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Windows64_UIController.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Network\WinsockNetLayer.cpp" />
<ClCompile Include="..\Minecraft.Client\WitchModel.cpp" />
<ClCompile Include="..\Minecraft.Client\WitchRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\WitherBossModel.cpp" />
<ClCompile Include="..\Minecraft.Client\WitherBossRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\WitherSkullRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\WolfModel.cpp" />
<ClCompile Include="..\Minecraft.Client\WolfRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\WstringLookup.cpp" />
<ClCompile Include="..\Minecraft.Client\Xbox\Network\NetworkPlayerXbox.cpp" />
<ClCompile Include="..\Minecraft.Client\ZombieModel.cpp" />
<ClCompile Include="..\Minecraft.Client\ZombieRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\compat_shims.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="Console\ServerCli.cpp" />
<ClCompile Include="Console\ServerCliInput.cpp" />
<ClCompile Include="Console\commands\ban\CliCommandBan.cpp" />
<ClCompile Include="Console\commands\ban-ip\CliCommandBanIp.cpp" />
<ClCompile Include="Console\commands\ban-list\CliCommandBanList.cpp" />
<ClCompile Include="Console\commands\defaultgamemode\CliCommandDefaultGamemode.cpp" />
<ClCompile Include="Console\commands\enchant\CliCommandEnchant.cpp" />
<ClCompile Include="Console\commands\experience\CliCommandExperience.cpp" />
<ClCompile Include="Console\commands\gamemode\CliCommandGamemode.cpp" />
<ClCompile Include="Console\commands\give\CliCommandGive.cpp" />
<ClCompile Include="Console\commands\help\CliCommandHelp.cpp" />
<ClCompile Include="Console\commands\kill\CliCommandKill.cpp" />
<ClCompile Include="Console\commands\list\CliCommandList.cpp" />
<ClCompile Include="Console\commands\pardon\CliCommandPardon.cpp" />
<ClCompile Include="Console\commands\pardon-ip\CliCommandPardonIp.cpp" />
<ClCompile Include="Console\commands\stop\CliCommandStop.cpp" />
<ClCompile Include="Console\commands\time\CliCommandTime.cpp" />
<ClCompile Include="Console\commands\tp\CliCommandTp.cpp" />
<ClCompile Include="Console\commands\weather\CliCommandWeather.cpp" />
<ClCompile Include="Console\commands\whitelist\CliCommandWhitelist.cpp" />
<ClCompile Include="Console\ServerCliEngine.cpp" />
<ClCompile Include="Console\ServerCliParser.cpp" />
<ClCompile Include="Console\ServerCliRegistry.cpp" />
<ClCompile Include="Common\FileUtils.cpp" />
<ClCompile Include="Common\StringUtils.cpp" />
<ClCompile Include="..\Minecraft.Client\glWrapper.cpp" />
<ClCompile Include="ServerLogger.cpp" />
<ClCompile Include="ServerProperties.cpp" />
<ClCompile Include="vendor\linenoise\linenoise.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="WorldManager.cpp" />
<ClCompile Include="..\Minecraft.Client\stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\stubs.cpp" />
<ClCompile Include="Windows64\ServerMain.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Access\Access.h" />
<ClInclude Include="Access\BanManager.h" />
<ClInclude Include="Access\WhitelistManager.h" />
<ClInclude Include="Console\ServerCli.h" />
<ClInclude Include="Console\ServerCliInput.h" />
<ClInclude Include="Console\commands\ban\CliCommandBan.h" />
<ClInclude Include="Console\commands\ban-ip\CliCommandBanIp.h" />
<ClInclude Include="Console\commands\ban-list\CliCommandBanList.h" />
<ClInclude Include="Console\commands\defaultgamemode\CliCommandDefaultGamemode.h" />
<ClInclude Include="Console\commands\enchant\CliCommandEnchant.h" />
<ClInclude Include="Console\commands\experience\CliCommandExperience.h" />
<ClInclude Include="Console\commands\gamemode\CliCommandGamemode.h" />
<ClInclude Include="Console\commands\give\CliCommandGive.h" />
<ClInclude Include="Console\commands\help\CliCommandHelp.h" />
<ClInclude Include="Console\commands\CommandParsing.h" />
<ClInclude Include="Console\commands\kill\CliCommandKill.h" />
<ClInclude Include="Console\commands\list\CliCommandList.h" />
<ClInclude Include="Console\commands\pardon\CliCommandPardon.h" />
<ClInclude Include="Console\commands\pardon-ip\CliCommandPardonIp.h" />
<ClInclude Include="Console\commands\stop\CliCommandStop.h" />
<ClInclude Include="Console\commands\time\CliCommandTime.h" />
<ClInclude Include="Console\commands\tp\CliCommandTp.h" />
<ClInclude Include="Console\commands\weather\CliCommandWeather.h" />
<ClInclude Include="Console\commands\whitelist\CliCommandWhitelist.h" />
<ClInclude Include="Console\commands\IServerCliCommand.h" />
<ClInclude Include="Console\ServerCliEngine.h" />
<ClInclude Include="Console\ServerCliParser.h" />
<ClInclude Include="Console\ServerCliRegistry.h" />
<ClInclude Include="Common\FileUtils.h" />
<ClInclude Include="Common\AccessStorageUtils.h" />
<ClInclude Include="Common\NetworkUtils.h" />
<ClInclude Include="Common\StringUtils.h" />
<ClInclude Include="ServerLogger.h" />
<ClInclude Include="ServerLogManager.h" />
<ClInclude Include="ServerProperties.h" />
<ClInclude Include="vendor\linenoise\linenoise.h" />
<ClInclude Include="WorldManager.h" />
</ItemGroup>
<ItemGroup>
<MASM Include="..\Minecraft.Client\iob_shim.asm" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\Minecraft.Client\Xbox\MinecraftWindows.rc" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Minecraft.World\Minecraft.World.vcxproj">
<Project>{F046C3CE-9749-4823-B32B-D9CC10B1A2C8}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup>
</Project>

View file

@ -0,0 +1,737 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Server">
<UniqueIdentifier>{A8A47C24-66C0-4912-9D34-2CBF87F1D707}</UniqueIdentifier>
</Filter>
<Filter Include="Server\Console">
<UniqueIdentifier>{39B037A0-9B57-454A-AF34-7D9164E22A0F}</UniqueIdentifier>
</Filter>
<Filter Include="Server\Console\Commands">
<UniqueIdentifier>{7C28D123-0DA3-4B17-84C0-E326F5A75740}</UniqueIdentifier>
</Filter>
<Filter Include="Server\Access">
<UniqueIdentifier>{29AB58D1-E8A9-465A-B3EA-BC5E9110A7A1}</UniqueIdentifier>
</Filter>
<Filter Include="Server\Common">
<UniqueIdentifier>{BC6FD58B-1A40-45FE-B8D9-1A087C25126D}</UniqueIdentifier>
</Filter>
<Filter Include="Server\Vendor">
<UniqueIdentifier>{3E4D5A41-CAB8-4A10-82B5-8B2AE2E25CB2}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ServerLogger.cpp">
<Filter>Server</Filter>
</ClCompile>
<ClCompile Include="ServerLogManager.cpp">
<Filter>Server</Filter>
</ClCompile>
<ClCompile Include="ServerProperties.cpp">
<Filter>Server</Filter>
</ClCompile>
<ClCompile Include="WorldManager.cpp">
<Filter>Server</Filter>
</ClCompile>
<ClCompile Include="Console\ServerCli.cpp">
<Filter>Server\Console</Filter>
</ClCompile>
<ClCompile Include="Console\ServerCliEngine.cpp">
<Filter>Server\Console</Filter>
</ClCompile>
<ClCompile Include="Console\ServerCliParser.cpp">
<Filter>Server\Console</Filter>
</ClCompile>
<ClCompile Include="Console\ServerCliRegistry.cpp">
<Filter>Server\Console</Filter>
</ClCompile>
<ClCompile Include="Common\StringUtils.cpp">
<Filter>Server\Common</Filter>
</ClCompile>
<ClCompile Include="vendor\linenoise\linenoise.c">
<Filter>Server\Vendor</Filter>
</ClCompile>
<ClCompile Include="Windows64\ServerMain.cpp">
<Filter>Server</Filter>
</ClCompile>
<ClCompile Include="Access\Access.cpp">
<Filter>Server\Access</Filter>
</ClCompile>
<ClCompile Include="Access\BanManager.cpp">
<Filter>Server\Access</Filter>
</ClCompile>
<ClCompile Include="Access\WhitelistManager.cpp">
<Filter>Server\Access</Filter>
</ClCompile>
<ClCompile Include="Common\FileUtils.cpp">
<Filter>Server\Common</Filter>
</ClCompile>
<ClCompile Include="..\Minecraft.Client\AbstractTexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\AchievementPopup.cpp" />
<ClCompile Include="..\Minecraft.Client\AchievementScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\AllowAllCuller.cpp" />
<ClCompile Include="..\Minecraft.Client\ArchiveFile.cpp" />
<ClCompile Include="..\Minecraft.Client\ArrowRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\BatModel.cpp" />
<ClCompile Include="..\Minecraft.Client\BatRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\BeaconRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\BlazeModel.cpp" />
<ClCompile Include="..\Minecraft.Client\BlazeRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\BoatModel.cpp" />
<ClCompile Include="..\Minecraft.Client\BoatRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\BookModel.cpp" />
<ClCompile Include="..\Minecraft.Client\BossMobGuiInfo.cpp" />
<ClCompile Include="..\Minecraft.Client\BreakingItemParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\BubbleParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\BufferedImage.cpp" />
<ClCompile Include="..\Minecraft.Client\Button.cpp" />
<ClCompile Include="..\Minecraft.Client\Camera.cpp" />
<ClCompile Include="..\Minecraft.Client\CaveSpiderRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ChatScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\ChestModel.cpp" />
<ClCompile Include="..\Minecraft.Client\ChestRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ChickenModel.cpp" />
<ClCompile Include="..\Minecraft.Client\ChickenRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Chunk.cpp" />
<ClCompile Include="..\Minecraft.Client\ClientConnection.cpp" />
<ClCompile Include="..\Minecraft.Client\ClientConstants.cpp" />
<ClCompile Include="..\Minecraft.Client\ClockTexture.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Audio\Consoles_SoundEngine.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Audio\SoundEngine.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Audio\SoundNames.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Colours\ColourTable.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\ConsoleGameMode.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Console_Utils.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Consoles_App.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCAudioFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCCapeFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCColourTableFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCGameRulesFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCGameRulesHeader.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCLocalisationFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCPack.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCSkinFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCTextureFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCUIDataFile.cpp" />
<ClCompile Include="..\include\lce_filesystem\lce_filesystem.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\AddEnchantmentRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\AddItemRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\ApplySchematicRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\BiomeOverride.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\CollectItemRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\CompleteAllRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\CompoundGameRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\ConsoleGenerateStructure.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\ConsoleSchematicFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\GameRule.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\GameRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\GameRuleManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelGenerationOptions.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelGenerators.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelRules.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelRuleset.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\NamedAreaRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\StartFeature.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\UpdatePlayerRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\UseTileRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionGenerateBox.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionPlaceBlock.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionPlaceContainer.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionPlaceSpawner.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Leaderboards\LeaderboardInterface.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Leaderboards\LeaderboardManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Network\GameNetworkManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Network\PlatformNetworkManagerStub.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Telemetry\TelemetryManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Trial\TrialMode.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\AreaConstraint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\AreaHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\AreaTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\ChangeStateConstraint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\ChoiceTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\CompleteUsingItemTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\ControllerTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\CraftTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\DiggerItemHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\EffectChangedTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\FullTutorial.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\FullTutorialActiveTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\FullTutorialMode.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\HorseChoiceTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\InfoTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\InputConstraint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\LookAtEntityHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\LookAtTileHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\PickupTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\ProcedureCompoundTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\ProgressFlagTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\RideEntityTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\StatTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\TakeItemHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\Tutorial.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialMessage.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialMode.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\UseItemTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\UseTileTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\XuiCraftingTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_AbstractContainerMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_AnvilMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_BeaconMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_BrewingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_CommandBlockMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_ContainerMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_CraftingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_CreativeMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_DispenserMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_EnchantingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_FireworksMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_FurnaceMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_HUD.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_HopperMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_HorseInventoryMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_InventoryMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_PauseMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_StartGame.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_TradingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIBitmapFont.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Chat.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_DebugUIConsole.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_DebugUIMarketingGuide.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Logo.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_MenuBackground.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Panorama.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_PressStartToPlay.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Tooltips.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_TutorialPopup.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Base.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_BeaconEffectButton.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_BitmapIcon.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Button.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_ButtonList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_CheckBox.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Cursor.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_DLCList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_DynamicLabel.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_EnchantmentBook.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_EnchantmentButton.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_HTMLLabel.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Label.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_LeaderboardList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_MinecraftHorse.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_MinecraftPlayer.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_PlayerList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_PlayerSkinPreview.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Progress.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_SaveList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Slider.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_SlotList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_SpaceIndicatorBar.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_TextInput.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_TexturePackList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIController.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIFontData.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIGroup.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UILayer.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_AbstractContainerMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_AnvilMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_BeaconMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_BrewingStandMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ConnectingProgress.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ContainerMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ControlsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_CraftingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_CreateWorldMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_CreativeMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Credits.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DLCMainMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DLCOffersMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DeathMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugCreateSchematic.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugOptions.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugOverlay.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugSetCamera.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DispenserMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_EULA.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_EnchantingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_EndPoem.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_FireworksMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_FullscreenProgress.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_FurnaceMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HUD.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HelpAndOptionsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HopperMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HorseInventoryMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HowToPlay.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HowToPlayMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InGameHostOptionsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InGameInfoMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InGamePlayerOptionsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Intro.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InventoryMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_JoinMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Keyboard.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LanguageSelector.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LaunchMoreOptionsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LeaderboardsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LoadMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LoadOrJoinMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_MainMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_MessageBox.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_NewUpdateMessage.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_PauseMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_QuadrantSignin.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ReinstallMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SaveMessage.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsAudioMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsControlMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsGraphicsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsOptionsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsUIMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SignEntryMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SkinSelectMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_TeleportMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Timer.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_TradingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_TrialExitUpsell.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIString.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UITTFFont.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\adler32.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\compress.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\crc32.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\deflate.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\gzclose.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\gzlib.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\gzread.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\gzwrite.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\infback.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\inffast.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\inflate.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\inftrees.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\trees.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\uncompr.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\zutil.c" />
<ClCompile Include="..\Minecraft.Client\CompassTexture.cpp" />
<ClCompile Include="..\Minecraft.Client\ConfirmScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\ConsoleInput.cpp" />
<ClCompile Include="..\Minecraft.Client\ControlsScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\CowModel.cpp" />
<ClCompile Include="..\Minecraft.Client\CowRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\CreateWorldScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\CreeperModel.cpp" />
<ClCompile Include="..\Minecraft.Client\CreeperRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\CritParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\CritParticle2.cpp" />
<ClCompile Include="..\Minecraft.Client\Cube.cpp" />
<ClCompile Include="..\Minecraft.Client\DLCTexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\DeathScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\DefaultRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\DefaultTexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\DemoUser.cpp" />
<ClCompile Include="..\Minecraft.Client\DerivedServerLevel.cpp" />
<ClCompile Include="..\Minecraft.Client\DirtyChunkSorter.cpp" />
<ClCompile Include="..\Minecraft.Client\DispenserBootstrap.cpp" />
<ClCompile Include="..\Minecraft.Client\DistanceChunkSorter.cpp" />
<ClCompile Include="..\Minecraft.Client\DragonBreathParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\DragonModel.cpp" />
<ClCompile Include="..\Minecraft.Client\DripParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\EchantmentTableParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\EditBox.cpp" />
<ClCompile Include="..\Minecraft.Client\EnchantTableRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EnderChestRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EnderCrystalModel.cpp" />
<ClCompile Include="..\Minecraft.Client\EnderCrystalRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EnderDragonRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EnderParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\EndermanModel.cpp" />
<ClCompile Include="..\Minecraft.Client\EndermanRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EntityRenderDispatcher.cpp" />
<ClCompile Include="..\Minecraft.Client\EntityRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EntityTileRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EntityTracker.cpp" />
<ClCompile Include="..\Minecraft.Client\ErrorScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\ExperienceOrbRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ExplodeParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\Extrax64Stubs.cpp" />
<ClCompile Include="..\Minecraft.Client\FallingTileRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\FileTexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\FireballRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\FireworksParticles.cpp" />
<ClCompile Include="..\Minecraft.Client\FishingHookRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\FlameParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\FolderTexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\Font.cpp" />
<ClCompile Include="..\Minecraft.Client\FootstepParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\Frustum.cpp" />
<ClCompile Include="..\Minecraft.Client\FrustumCuller.cpp" />
<ClCompile Include="..\Minecraft.Client\FrustumData.cpp" />
<ClCompile Include="..\Minecraft.Client\GameRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\GhastModel.cpp" />
<ClCompile Include="..\Minecraft.Client\GhastRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\GiantMobRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Gui.cpp" />
<ClCompile Include="..\Minecraft.Client\GuiComponent.cpp" />
<ClCompile Include="..\Minecraft.Client\GuiMessage.cpp" />
<ClCompile Include="..\Minecraft.Client\GuiParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\GuiParticles.cpp" />
<ClCompile Include="..\Minecraft.Client\HeartParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\HorseRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\HttpTexture.cpp" />
<ClCompile Include="..\Minecraft.Client\HugeExplosionParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\HugeExplosionSeedParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\HumanoidMobRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\HumanoidModel.cpp" />
<ClCompile Include="..\Minecraft.Client\InBedChatScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\Input.cpp" />
<ClCompile Include="..\Minecraft.Client\ItemFrameRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ItemInHandRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ItemRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ItemSpriteRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\JoinMultiplayerScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\KeyMapping.cpp" />
<ClCompile Include="..\Minecraft.Client\LargeChestModel.cpp" />
<ClCompile Include="..\Minecraft.Client\LavaParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\LavaSlimeModel.cpp" />
<ClCompile Include="..\Minecraft.Client\LavaSlimeRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\LeashKnotModel.cpp" />
<ClCompile Include="..\Minecraft.Client\LeashKnotRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\LevelRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Lighting.cpp" />
<ClCompile Include="..\Minecraft.Client\LightningBoltRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\LivingEntityRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\LocalPlayer.cpp" />
<ClCompile Include="..\Minecraft.Client\MemTexture.cpp" />
<ClCompile Include="..\Minecraft.Client\MemoryTracker.cpp" />
<ClCompile Include="..\Minecraft.Client\MinecartModel.cpp" />
<ClCompile Include="..\Minecraft.Client\MinecartRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\MinecartSpawnerRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Minecraft.cpp" />
<ClCompile Include="..\Minecraft.Client\MinecraftServer.cpp" />
<ClCompile Include="..\Minecraft.Client\Minimap.cpp" />
<ClCompile Include="..\Minecraft.Client\MobRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\MobSkinMemTextureProcessor.cpp" />
<ClCompile Include="..\Minecraft.Client\MobSkinTextureProcessor.cpp" />
<ClCompile Include="..\Minecraft.Client\MobSpawnerRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Model.cpp" />
<ClCompile Include="..\Minecraft.Client\ModelHorse.cpp" />
<ClCompile Include="..\Minecraft.Client\ModelPart.cpp" />
<ClCompile Include="..\Minecraft.Client\MultiPlayerChunkCache.cpp" />
<ClCompile Include="..\Minecraft.Client\MultiPlayerGameMode.cpp" />
<ClCompile Include="..\Minecraft.Client\MultiPlayerLevel.cpp" />
<ClCompile Include="..\Minecraft.Client\MultiPlayerLocalPlayer.cpp" />
<ClCompile Include="..\Minecraft.Client\MushroomCowRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\NameEntryScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\NetherPortalParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\NoteParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\OcelotModel.cpp" />
<ClCompile Include="..\Minecraft.Client\OcelotRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\OffsettedRenderList.cpp" />
<ClCompile Include="..\Minecraft.Client\Options.cpp" />
<ClCompile Include="..\Minecraft.Client\OptionsScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\PS3\PS3Extras\ShutdownManager.cpp" />
<ClCompile Include="..\Minecraft.Client\PaintingRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Particle.cpp" />
<ClCompile Include="..\Minecraft.Client\ParticleEngine.cpp" />
<ClCompile Include="..\Minecraft.Client\PauseScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\PendingConnection.cpp" />
<ClCompile Include="..\Minecraft.Client\PigModel.cpp" />
<ClCompile Include="..\Minecraft.Client\PigRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\PistonPieceRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\PlayerChunkMap.cpp" />
<ClCompile Include="..\Minecraft.Client\PlayerCloudParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\PlayerConnection.cpp" />
<ClCompile Include="..\Minecraft.Client\PlayerList.cpp" />
<ClCompile Include="..\Minecraft.Client\PlayerRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Polygon.cpp" />
<ClCompile Include="..\Minecraft.Client\PreStitchedTextureMap.cpp" />
<ClCompile Include="..\Minecraft.Client\ProgressRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\QuadrupedModel.cpp" />
<ClCompile Include="..\Minecraft.Client\Rect2i.cpp" />
<ClCompile Include="..\Minecraft.Client\RedDustParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\RemotePlayer.cpp" />
<ClCompile Include="..\Minecraft.Client\RenameWorldScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\Screen.cpp" />
<ClCompile Include="..\Minecraft.Client\ScreenSizeCalculator.cpp" />
<ClCompile Include="..\Minecraft.Client\ScrolledSelectionList.cpp" />
<ClCompile Include="..\Minecraft.Client\SelectWorldScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerChunkCache.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerCommandDispatcher.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerConnection.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerLevel.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerLevelListener.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerPlayer.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerPlayerGameMode.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerScoreboard.cpp" />
<ClCompile Include="..\Minecraft.Client\Settings.cpp" />
<ClCompile Include="..\Minecraft.Client\SheepFurModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SheepModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SheepRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SignModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SignRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SilverfishModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SilverfishRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SimpleIcon.cpp" />
<ClCompile Include="..\Minecraft.Client\SkeletonHeadModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SkeletonModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SkeletonRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SkiModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SkullTileRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SlideButton.cpp" />
<ClCompile Include="..\Minecraft.Client\SlimeModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SlimeRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SmallButton.cpp" />
<ClCompile Include="..\Minecraft.Client\SmokeParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\SnowManModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SnowManRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SnowShovelParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\SpellParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\SpiderModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SpiderRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SplashParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\SquidModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SquidRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\StatsCounter.cpp" />
<ClCompile Include="..\Minecraft.Client\StatsScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\StatsSyncher.cpp" />
<ClCompile Include="..\Minecraft.Client\StitchSlot.cpp" />
<ClCompile Include="..\Minecraft.Client\StitchedTexture.cpp" />
<ClCompile Include="..\Minecraft.Client\Stitcher.cpp" />
<ClCompile Include="..\Minecraft.Client\StringTable.cpp" />
<ClCompile Include="..\Minecraft.Client\SuspendedParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\SuspendedTownParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\TakeAnimationParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\TeleportCommand.cpp" />
<ClCompile Include="..\Minecraft.Client\TerrainParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\Tesselator.cpp" />
<ClCompile Include="..\Minecraft.Client\TexOffs.cpp" />
<ClCompile Include="..\Minecraft.Client\Texture.cpp" />
<ClCompile Include="..\Minecraft.Client\TextureAtlas.cpp" />
<ClCompile Include="..\Minecraft.Client\TextureHolder.cpp" />
<ClCompile Include="..\Minecraft.Client\TextureManager.cpp" />
<ClCompile Include="..\Minecraft.Client\TextureMap.cpp" />
<ClCompile Include="..\Minecraft.Client\TexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\TexturePackRepository.cpp" />
<ClCompile Include="..\Minecraft.Client\Textures.cpp" />
<ClCompile Include="..\Minecraft.Client\TheEndPortalRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\TileEntityRenderDispatcher.cpp" />
<ClCompile Include="..\Minecraft.Client\TileEntityRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\TileRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Timer.cpp" />
<ClCompile Include="..\Minecraft.Client\TitleScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\TntMinecartRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\TntRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\TrackedEntity.cpp" />
<ClCompile Include="..\Minecraft.Client\User.cpp" />
<ClCompile Include="..\Minecraft.Client\Vertex.cpp" />
<ClCompile Include="..\Minecraft.Client\VideoSettingsScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\ViewportCuller.cpp" />
<ClCompile Include="..\Minecraft.Client\VillagerGolemModel.cpp" />
<ClCompile Include="..\Minecraft.Client\VillagerGolemRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\VillagerModel.cpp" />
<ClCompile Include="..\Minecraft.Client\VillagerRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\VillagerZombieModel.cpp" />
<ClCompile Include="..\Minecraft.Client\WaterDropParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Iggy\gdraw\gdraw_d3d11.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\KeyboardMouseInput.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Leaderboards\WindowsLeaderboardManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\PostProcesser.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Windows64_App.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Windows64_Minecraft.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Windows64_UIController.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Network\WinsockNetLayer.cpp" />
<ClCompile Include="..\Minecraft.Client\WitchModel.cpp" />
<ClCompile Include="..\Minecraft.Client\WitchRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\WitherBossModel.cpp" />
<ClCompile Include="..\Minecraft.Client\WitherBossRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\WitherSkullRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\WolfModel.cpp" />
<ClCompile Include="..\Minecraft.Client\WolfRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\WstringLookup.cpp" />
<ClCompile Include="..\Minecraft.Client\Xbox\Network\NetworkPlayerXbox.cpp" />
<ClCompile Include="..\Minecraft.Client\ZombieModel.cpp" />
<ClCompile Include="..\Minecraft.Client\ZombieRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\compat_shims.cpp" />
<ClCompile Include="Console\ServerCliInput.cpp" />
<ClCompile Include="..\Minecraft.Client\glWrapper.cpp" />
<ClCompile Include="..\Minecraft.Client\stdafx.cpp" />
<ClCompile Include="..\Minecraft.Client\stubs.cpp" />
<ClCompile Include="Console\commands\ban\CliCommandBan.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\ban-ip\CliCommandBanIp.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\ban-list\CliCommandBanList.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\defaultgamemode\CliCommandDefaultGamemode.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\enchant\CliCommandEnchant.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\experience\CliCommandExperience.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\gamemode\CliCommandGamemode.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\give\CliCommandGive.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\help\CliCommandHelp.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\kill\CliCommandKill.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\list\CliCommandList.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\pardon\CliCommandPardon.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\pardon-ip\CliCommandPardonIp.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\stop\CliCommandStop.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\time\CliCommandTime.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\tp\CliCommandTp.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\weather\CliCommandWeather.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\whitelist\CliCommandWhitelist.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Console\ServerCli.h">
<Filter>Server\Console</Filter>
</ClInclude>
<ClInclude Include="Console\ServerCliEngine.h">
<Filter>Server\Console</Filter>
</ClInclude>
<ClInclude Include="Console\ServerCliParser.h">
<Filter>Server\Console</Filter>
</ClInclude>
<ClInclude Include="Console\ServerCliRegistry.h">
<Filter>Server\Console</Filter>
</ClInclude>
<ClInclude Include="Access\Access.h">
<Filter>Server\Access</Filter>
</ClInclude>
<ClInclude Include="Access\BanManager.h">
<Filter>Server\Access</Filter>
</ClInclude>
<ClInclude Include="Access\WhitelistManager.h">
<Filter>Server\Access</Filter>
</ClInclude>
<ClInclude Include="Common\FileUtils.h">
<Filter>Server\Common</Filter>
</ClInclude>
<ClInclude Include="Common\AccessStorageUtils.h">
<Filter>Server\Common</Filter>
</ClInclude>
<ClInclude Include="Common\NetworkUtils.h">
<Filter>Server\Common</Filter>
</ClInclude>
<ClInclude Include="Common\StringUtils.h">
<Filter>Server\Common</Filter>
</ClInclude>
<ClInclude Include="ServerLogger.h">
<Filter>Server</Filter>
</ClInclude>
<ClInclude Include="ServerLogManager.h">
<Filter>Server</Filter>
</ClInclude>
<ClInclude Include="ServerProperties.h">
<Filter>Server</Filter>
</ClInclude>
<ClInclude Include="vendor\linenoise\linenoise.h">
<Filter>Server\Vendor</Filter>
</ClInclude>
<ClInclude Include="WorldManager.h">
<Filter>Server</Filter>
</ClInclude>
<ClInclude Include="Console\ServerCliInput.h" />
<ClInclude Include="Console\commands\ban\CliCommandBan.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\ban-ip\CliCommandBanIp.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\ban-list\CliCommandBanList.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\defaultgamemode\CliCommandDefaultGamemode.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\enchant\CliCommandEnchant.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\experience\CliCommandExperience.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\gamemode\CliCommandGamemode.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\give\CliCommandGive.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\help\CliCommandHelp.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\CommandParsing.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\kill\CliCommandKill.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\list\CliCommandList.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\pardon\CliCommandPardon.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\pardon-ip\CliCommandPardonIp.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\stop\CliCommandStop.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\time\CliCommandTime.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\tp\CliCommandTp.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\weather\CliCommandWeather.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\whitelist\CliCommandWhitelist.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\IServerCliCommand.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\Minecraft.Client\Xbox\MinecraftWindows.rc" />
</ItemGroup>
<ItemGroup>
<MASM Include="..\Minecraft.Client\iob_shim.asm" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,402 @@
#include "stdafx.h"
#include "ServerLogManager.h"
#include "Common\StringUtils.h"
#include "ServerLogger.h"
#include <array>
#include <mutex>
extern bool g_Win64DedicatedServer;
namespace ServerRuntime
{
namespace ServerLogManager
{
namespace
{
/**
* **!! This information is managed solely for logging purposes, but it is questionable from a liability perspective, so it will eventually need to be separated !!**
*
* Tracks the remote IP and accepted player name associated with one `smallId`
* 1smallIdに紐づく接続IPとプレイヤー名を保持する
*/
struct ConnectionLogEntry
{
std::string remoteIp;
std::string playerName;
};
/**
* Owns the shared connection cache used by hook points running on different threads
* hookから共有される接続キャッシュを保持する
*/
struct ServerLogState
{
std::mutex stateLock;
std::array<ConnectionLogEntry, 256> entries;
};
ServerLogState g_serverLogState;
static bool IsDedicatedServerLoggingEnabled()
{
return g_Win64DedicatedServer;
}
static void ResetConnectionLogEntry(ConnectionLogEntry *entry)
{
if (entry == NULL)
{
return;
}
entry->remoteIp.clear();
entry->playerName.clear();
}
static std::string NormalizeRemoteIp(const char *ip)
{
if (ip == NULL || ip[0] == 0)
{
return std::string("unknown");
}
return std::string(ip);
}
static std::string NormalizePlayerName(const std::wstring &playerName)
{
std::string playerNameUtf8 = StringUtils::WideToUtf8(playerName);
if (playerNameUtf8.empty())
{
playerNameUtf8 = "<unknown>";
}
return playerNameUtf8;
}
// Default to the main app channel when the caller does not provide a source tag.
static const char *NormalizeClientLogSource(const char *source)
{
if (source == NULL || source[0] == 0)
{
return "app";
}
return source;
}
static void EmitClientDebugLogLine(const char *source, const std::string &line)
{
if (line.empty())
{
return;
}
LogDebugf("client", "[%s] %s", NormalizeClientLogSource(source), line.c_str());
}
// Split one debug payload into individual lines so each line becomes a prompt-safe server log entry.
static void ForwardClientDebugMessage(const char *source, const char *message)
{
if (message == NULL || message[0] == 0)
{
return;
}
const char *cursor = message;
while (*cursor != 0)
{
const char *lineStart = cursor;
while (*cursor != 0 && *cursor != '\r' && *cursor != '\n')
{
++cursor;
}
// Split multi-line client debug output into prompt-safe server log entries.
if (cursor > lineStart)
{
EmitClientDebugLogLine(source, std::string(lineStart, (size_t)(cursor - lineStart)));
}
while (*cursor == '\r' || *cursor == '\n')
{
++cursor;
}
}
}
// Share the same formatting path for app, user, and legacy debug-spew forwards.
static void ForwardFormattedClientDebugLogV(const char *source, const char *format, va_list args)
{
if (!IsDedicatedServerLoggingEnabled() || format == NULL || format[0] == 0)
{
return;
}
char messageBuffer[2048] = {};
vsnprintf_s(messageBuffer, sizeof(messageBuffer), _TRUNCATE, format, args);
ForwardClientDebugMessage(source, messageBuffer);
}
static const char *TcpRejectReasonToString(ETcpRejectReason reason)
{
switch (reason)
{
case eTcpRejectReason_BannedIp: return "banned-ip";
case eTcpRejectReason_GameNotReady: return "game-not-ready";
case eTcpRejectReason_ServerFull: return "server-full";
default: return "unknown";
}
}
static const char *LoginRejectReasonToString(ELoginRejectReason reason)
{
switch (reason)
{
case eLoginRejectReason_BannedXuid: return "banned-xuid";
case eLoginRejectReason_NotWhitelisted: return "not-whitelisted";
case eLoginRejectReason_DuplicateXuid: return "duplicate-xuid";
case eLoginRejectReason_DuplicateName: return "duplicate-name";
default: return "unknown";
}
}
static const char *DisconnectReasonToString(DisconnectPacket::eDisconnectReason reason)
{
switch (reason)
{
case DisconnectPacket::eDisconnect_None: return "none";
case DisconnectPacket::eDisconnect_Quitting: return "quitting";
case DisconnectPacket::eDisconnect_Closed: return "closed";
case DisconnectPacket::eDisconnect_LoginTooLong: return "login-too-long";
case DisconnectPacket::eDisconnect_IllegalStance: return "illegal-stance";
case DisconnectPacket::eDisconnect_IllegalPosition: return "illegal-position";
case DisconnectPacket::eDisconnect_MovedTooQuickly: return "moved-too-quickly";
case DisconnectPacket::eDisconnect_NoFlying: return "no-flying";
case DisconnectPacket::eDisconnect_Kicked: return "kicked";
case DisconnectPacket::eDisconnect_TimeOut: return "timeout";
case DisconnectPacket::eDisconnect_Overflow: return "overflow";
case DisconnectPacket::eDisconnect_EndOfStream: return "end-of-stream";
case DisconnectPacket::eDisconnect_ServerFull: return "server-full";
case DisconnectPacket::eDisconnect_OutdatedServer: return "outdated-server";
case DisconnectPacket::eDisconnect_OutdatedClient: return "outdated-client";
case DisconnectPacket::eDisconnect_UnexpectedPacket: return "unexpected-packet";
case DisconnectPacket::eDisconnect_ConnectionCreationFailed: return "connection-creation-failed";
case DisconnectPacket::eDisconnect_NoMultiplayerPrivilegesHost: return "no-multiplayer-privileges-host";
case DisconnectPacket::eDisconnect_NoMultiplayerPrivilegesJoin: return "no-multiplayer-privileges-join";
case DisconnectPacket::eDisconnect_NoUGC_AllLocal: return "no-ugc-all-local";
case DisconnectPacket::eDisconnect_NoUGC_Single_Local: return "no-ugc-single-local";
case DisconnectPacket::eDisconnect_ContentRestricted_AllLocal: return "content-restricted-all-local";
case DisconnectPacket::eDisconnect_ContentRestricted_Single_Local: return "content-restricted-single-local";
case DisconnectPacket::eDisconnect_NoUGC_Remote: return "no-ugc-remote";
case DisconnectPacket::eDisconnect_NoFriendsInGame: return "no-friends-in-game";
case DisconnectPacket::eDisconnect_Banned: return "banned";
case DisconnectPacket::eDisconnect_NotFriendsWithHost: return "not-friends-with-host";
case DisconnectPacket::eDisconnect_NATMismatch: return "nat-mismatch";
default: return "unknown";
}
}
}
// Only forward client-side debug output while the process is running as the dedicated server.
bool ShouldForwardClientDebugLogs()
{
return IsDedicatedServerLoggingEnabled();
}
void ForwardClientAppDebugLogV(const char *format, va_list args)
{
ForwardFormattedClientDebugLogV("app", format, args);
}
void ForwardClientUserDebugLogV(int user, const char *format, va_list args)
{
char source[32] = {};
_snprintf_s(source, sizeof(source), _TRUNCATE, "app:user=%d", user);
ForwardFormattedClientDebugLogV(source, format, args);
}
void ForwardClientDebugSpewLogV(const char *format, va_list args)
{
ForwardFormattedClientDebugLogV("debug-spew", format, args);
}
// Clear every cached connection slot during startup so stale metadata never leaks into future logs.
void Initialize()
{
std::lock_guard<std::mutex> stateLock(g_serverLogState.stateLock);
for (size_t index = 0; index < g_serverLogState.entries.size(); ++index)
{
ResetConnectionLogEntry(&g_serverLogState.entries[index]);
}
}
// Reuse Initialize as the shutdown cleanup path because both operations wipe the cache.
void Shutdown()
{
Initialize();
}
// Log the raw socket arrival before a smallId is assigned so early rejects still have an IP in the logs.
void OnIncomingTcpConnection(const char *ip)
{
if (!IsDedicatedServerLoggingEnabled())
{
return;
}
const std::string remoteIp = NormalizeRemoteIp(ip);
LogInfof("network", "incoming tcp connection from %s", remoteIp.c_str());
}
// TCP rejects happen before connection state is cached, so log directly from the supplied remote IP.
void OnRejectedTcpConnection(const char *ip, ETcpRejectReason reason)
{
if (!IsDedicatedServerLoggingEnabled())
{
return;
}
const std::string remoteIp = NormalizeRemoteIp(ip);
LogWarnf("network", "rejected tcp connection from %s: reason=%s", remoteIp.c_str(), TcpRejectReasonToString(reason));
}
// Cache the accepted remote IP immediately so later login and disconnect logs can reuse it.
void OnAcceptedTcpConnection(unsigned char smallId, const char *ip)
{
if (!IsDedicatedServerLoggingEnabled())
{
return;
}
const std::string remoteIp = NormalizeRemoteIp(ip);
{
std::lock_guard<std::mutex> stateLock(g_serverLogState.stateLock);
ConnectionLogEntry &entry = g_serverLogState.entries[smallId];
ResetConnectionLogEntry(&entry);
entry.remoteIp = remoteIp;
}
LogInfof("network", "accepted tcp connection from %s as smallId=%u", remoteIp.c_str(), (unsigned)smallId);
}
// Once login succeeds, bind the resolved player name onto the cached transport entry.
void OnAcceptedPlayerLogin(unsigned char smallId, const std::wstring &playerName)
{
if (!IsDedicatedServerLoggingEnabled())
{
return;
}
const std::string playerNameUtf8 = NormalizePlayerName(playerName);
std::string remoteIp("unknown");
{
std::lock_guard<std::mutex> stateLock(g_serverLogState.stateLock);
ConnectionLogEntry &entry = g_serverLogState.entries[smallId];
entry.playerName = playerNameUtf8;
if (!entry.remoteIp.empty())
{
remoteIp = entry.remoteIp;
}
}
LogInfof("network", "accepted player login: name=\"%s\" ip=%s smallId=%u", playerNameUtf8.c_str(), remoteIp.c_str(), (unsigned)smallId);
}
// Read the cached IP for the rejection log, then clear the slot because the player never fully joined.
void OnRejectedPlayerLogin(unsigned char smallId, const std::wstring &playerName, ELoginRejectReason reason)
{
if (!IsDedicatedServerLoggingEnabled())
{
return;
}
const std::string playerNameUtf8 = NormalizePlayerName(playerName);
std::string remoteIp("unknown");
{
std::lock_guard<std::mutex> stateLock(g_serverLogState.stateLock);
ConnectionLogEntry &entry = g_serverLogState.entries[smallId];
if (!entry.remoteIp.empty())
{
remoteIp = entry.remoteIp;
}
ResetConnectionLogEntry(&entry);
}
LogWarnf("network", "rejected login from %s: name=\"%s\" reason=%s", remoteIp.c_str(), playerNameUtf8.c_str(), LoginRejectReasonToString(reason));
}
// Disconnect logging is the final consumer of cached metadata, so it also clears the slot afterward.
void OnPlayerDisconnected(
unsigned char smallId,
const std::wstring &playerName,
DisconnectPacket::eDisconnectReason reason,
bool initiatedByServer)
{
if (!IsDedicatedServerLoggingEnabled())
{
return;
}
std::string playerNameUtf8 = NormalizePlayerName(playerName);
std::string remoteIp("unknown");
{
// Copy state under lock and emit the log after unlocking so CLI output never blocks connection bookkeeping.
std::lock_guard<std::mutex> stateLock(g_serverLogState.stateLock);
ConnectionLogEntry &entry = g_serverLogState.entries[smallId];
if (!entry.remoteIp.empty())
{
remoteIp = entry.remoteIp;
}
if (playerNameUtf8 == "<unknown>" && !entry.playerName.empty())
{
playerNameUtf8 = entry.playerName;
}
ResetConnectionLogEntry(&entry);
}
LogInfof(
"network",
"%s: name=\"%s\" ip=%s smallId=%u reason=%s",
initiatedByServer ? "disconnecting player" : "player disconnected",
playerNameUtf8.c_str(),
remoteIp.c_str(),
(unsigned)smallId,
DisconnectReasonToString(reason));
}
/**
* For logging purposes, the responsibility is technically misplaced, but the IP is cached in `LogManager`.
* Those cached values are then used to retrieve the player's IP.
*
* Eventually, this should be implemented in a separate class or on the `Minecraft.Client` side instead.
*/
bool TryGetConnectionRemoteIp(unsigned char smallId, std::string *outIp)
{
if (!IsDedicatedServerLoggingEnabled() || outIp == NULL)
{
return false;
}
std::lock_guard<std::mutex> stateLock(g_serverLogState.stateLock);
const ConnectionLogEntry &entry = g_serverLogState.entries[smallId];
if (entry.remoteIp.empty() || entry.remoteIp == "unknown")
{
return false;
}
*outIp = entry.remoteIp;
return true;
}
// Provide explicit cache cleanup for paths that terminate without going through disconnect logging.
void ClearConnection(unsigned char smallId)
{
std::lock_guard<std::mutex> stateLock(g_serverLogState.stateLock);
ResetConnectionLogEntry(&g_serverLogState.entries[smallId]);
}
}
}

View file

@ -0,0 +1,127 @@
#pragma once
#include <string>
#include <stdarg.h>
#include "..\Minecraft.World\DisconnectPacket.h"
namespace ServerRuntime
{
namespace ServerLogManager
{
/**
* Identifies why the dedicated server rejected a TCP connection before login completed
* TCP接続を拒否した理由
*/
enum ETcpRejectReason
{
eTcpRejectReason_BannedIp = 0,
eTcpRejectReason_GameNotReady,
eTcpRejectReason_ServerFull
};
/**
* Identifies why the dedicated server rejected a player during login validation
*
*/
enum ELoginRejectReason
{
eLoginRejectReason_BannedXuid = 0,
eLoginRejectReason_NotWhitelisted,
eLoginRejectReason_DuplicateXuid,
eLoginRejectReason_DuplicateName
};
/**
* Returns `true` when client-side debug logs should be redirected into the dedicated server logger
* dedicated server時にclient側デバッグログを転送すかどうか
*/
bool ShouldForwardClientDebugLogs();
/**
* Formats and forwards `CMinecraftApp::DebugPrintf` output through the dedicated server logger
* CMinecraftApp::DebugPrintf
*/
void ForwardClientAppDebugLogV(const char *format, va_list args);
/**
* Formats and forwards `CMinecraftApp::DebugPrintf(int user, ...)` output through the dedicated server logger
* CMinecraftApp::DebugPrintf(int user, ...)
*/
void ForwardClientUserDebugLogV(int user, const char *format, va_list args);
/**
* Formats and forwards legacy `DebugSpew` output through the dedicated server logger
* DebugSpew
*/
void ForwardClientDebugSpewLogV(const char *format, va_list args);
/**
* Clears cached connection metadata before the dedicated server starts accepting players
*
*/
void Initialize();
/**
* Releases cached connection metadata after the dedicated server stops
*
*/
void Shutdown();
/**
* **Log Incoming TCP Connection**
*
* Emits a named log for a raw TCP accept before smallId assignment finishes
* smallId割り当て前のTCP接続を記録
*/
void OnIncomingTcpConnection(const char *ip);
/**
* Emits a named log for a TCP connection rejected before login starts
* TCP接続を記録
*/
void OnRejectedTcpConnection(const char *ip, ETcpRejectReason reason);
/**
* Stores the remote IP for the assigned smallId and logs the accepted transport connection
* smallIdに対接続IPを保存して記録
*/
void OnAcceptedTcpConnection(unsigned char smallId, const char *ip);
/**
* Associates a player name with the connection and emits the accepted login log
*
*/
void OnAcceptedPlayerLogin(unsigned char smallId, const std::wstring &playerName);
/**
* Emits a named login rejection log and clears cached metadata for that smallId
* smallIdのキャッシュを破棄
*/
void OnRejectedPlayerLogin(unsigned char smallId, const std::wstring &playerName, ELoginRejectReason reason);
/**
* Emits a named disconnect log using cached connection metadata and then clears that entry
* 使
*/
void OnPlayerDisconnected(
unsigned char smallId,
const std::wstring &playerName,
DisconnectPacket::eDisconnectReason reason,
bool initiatedByServer);
/**
* Reads the cached remote IP for a live smallId without consuming the entry
* Eventually, this should be implemented in a separate class or on the `Minecraft.Client` side instead.
*
* smallIdの接続IPをキャッシュから参照する
*/
bool TryGetConnectionRemoteIp(unsigned char smallId, std::string *outIp);
/**
* Removes any remembered IP or player name for the specified smallId
* smallIdに紐づく接続キャッシュを消去
*/
void ClearConnection(unsigned char smallId);
}
}

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