mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-08-20 09:57:09 +00:00
Merge branch 'smartcmd:main' into main
This commit is contained in:
commit
999a9576e9
BIN
.github/banner.png
vendored
Normal file
BIN
.github/banner.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
160
.github/workflows/docker-nightly.yml
vendored
Normal file
160
.github/workflows/docker-nightly.yml
vendored
Normal 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
|
||||||
29
.github/workflows/nightly.yml
vendored
29
.github/workflows/nightly.yml
vendored
|
|
@ -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
|
||||||
|
|
|
||||||
16
.gitignore
vendored
16
.gitignore
vendored
|
|
@ -379,11 +379,8 @@ MigrationBackup/
|
||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
|
|
||||||
# VS Code files for those working on multiple tools
|
# VS Code files for those working on multiple tools
|
||||||
.vscode/*
|
.vscode/
|
||||||
!.vscode/settings.json
|
!.vscode/*.example.json
|
||||||
!.vscode/tasks.json
|
|
||||||
!.vscode/launch.json
|
|
||||||
!.vscode/extensions.json
|
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
|
|
||||||
# Local History for Visual Studio Code
|
# Local History for Visual Studio Code
|
||||||
|
|
@ -423,6 +420,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 +437,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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
34
COMPILE.md
34
COMPILE.md
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,32 @@
|
||||||
#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
|
||||||
CHAR str[2048];
|
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||||
// Use the secure CRT to avoid buffer overruns. Specify a count of
|
// Dedicated server routes legacy debug spew through ServerLogger to preserve CLI prompt handling.
|
||||||
// _TRUNCATE so that too long strings will be silently truncated
|
if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs())
|
||||||
// rather than triggering an error.
|
{
|
||||||
_vsnprintf_s( str, _TRUNCATE, strFormat, pArgList );
|
ServerRuntime::ServerLogManager::ForwardClientDebugSpewLogV(strFormat, pArgList);
|
||||||
OutputDebugStringA( str );
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
CHAR str[2048];
|
||||||
|
// Use the secure CRT to avoid buffer overruns. Specify a count of
|
||||||
|
// _TRUNCATE so that too long strings will be silently truncated
|
||||||
|
// rather than triggering an error.
|
||||||
|
_vsnprintf_s( str, _TRUNCATE, strFormat, pArgList );
|
||||||
|
OutputDebugStringA( str );
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -31,10 +42,9 @@ VOID CDECL DebugPrintf( const CHAR* strFormat, ... )
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
#ifndef _CONTENT_PACKAGE
|
#ifndef _CONTENT_PACKAGE
|
||||||
va_list pArgList;
|
va_list pArgList;
|
||||||
va_start( pArgList, strFormat );
|
va_start( pArgList, strFormat );
|
||||||
DebugSpewV( strFormat, pArgList );
|
DebugSpewV( strFormat, pArgList );
|
||||||
va_end( pArgList );
|
va_end( pArgList );
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,12 +243,21 @@ 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)
|
||||||
vsnprintf(buf, sizeof(buf), szFormat, ap);
|
// Dedicated server routes client debug spew through ServerLogger so CLI output stays prompt-safe.
|
||||||
va_end(ap);
|
if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs())
|
||||||
OutputDebugStringA(buf);
|
{
|
||||||
|
ServerRuntime::ServerLogManager::ForwardClientAppDebugLogV(szFormat, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
char buf[1024];
|
||||||
|
vsnprintf(buf, sizeof(buf), szFormat, ap);
|
||||||
|
va_end(ap);
|
||||||
|
OutputDebugStringA(buf);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -253,53 +265,62 @@ void CMinecraftApp::DebugPrintf(const char *szFormat, ...)
|
||||||
void CMinecraftApp::DebugPrintf(int user, const char *szFormat, ...)
|
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)
|
||||||
vsnprintf(buf, sizeof(buf), szFormat, ap);
|
// Dedicated server routes client debug spew through ServerLogger so CLI output stays prompt-safe.
|
||||||
va_end(ap);
|
if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs())
|
||||||
|
{
|
||||||
|
ServerRuntime::ServerLogManager::ForwardClientUserDebugLogV(user, szFormat, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
char buf[1024];
|
||||||
|
vsnprintf(buf, sizeof(buf), szFormat, ap);
|
||||||
|
va_end(ap);
|
||||||
#ifdef __PS3__
|
#ifdef __PS3__
|
||||||
unsigned int writelen;
|
unsigned int writelen;
|
||||||
sys_tty_write(SYS_TTYP_USER1 + ( user - 1 ), buf, strlen(buf), &writelen );
|
sys_tty_write(SYS_TTYP_USER1 + ( user - 1 ), buf, strlen(buf), &writelen );
|
||||||
#elif defined __PSVITA__
|
#elif defined __PSVITA__
|
||||||
switch(user)
|
switch(user)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
SceUID tty2 = sceIoOpen("tty2:", SCE_O_WRONLY, 0);
|
SceUID tty2 = sceIoOpen("tty2:", SCE_O_WRONLY, 0);
|
||||||
if(tty2>=0)
|
if(tty2>=0)
|
||||||
{
|
{
|
||||||
std::string string1(buf);
|
std::string string1(buf);
|
||||||
sceIoWrite(tty2, string1.c_str(), string1.length());
|
sceIoWrite(tty2, string1.c_str(), string1.length());
|
||||||
sceIoClose(tty2);
|
sceIoClose(tty2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
SceUID tty3 = sceIoOpen("tty3:", SCE_O_WRONLY, 0);
|
SceUID tty3 = sceIoOpen("tty3:", SCE_O_WRONLY, 0);
|
||||||
if(tty3>=0)
|
if(tty3>=0)
|
||||||
{
|
{
|
||||||
std::string string1(buf);
|
std::string string1(buf);
|
||||||
sceIoWrite(tty3, string1.c_str(), string1.length());
|
sceIoWrite(tty3, string1.c_str(), string1.length());
|
||||||
sceIoClose(tty3);
|
sceIoClose(tty3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
OutputDebugStringA(buf);
|
OutputDebugStringA(buf);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
OutputDebugStringA(buf);
|
OutputDebugStringA(buf);
|
||||||
#endif
|
#endif
|
||||||
#ifndef _XBOX
|
#ifndef _XBOX
|
||||||
if(user == USER_UI)
|
if(user == USER_UI)
|
||||||
{
|
{
|
||||||
ui.logDebugString(buf);
|
ui.logDebugString(buf);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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,16 +402,18 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
|
||||||
connection = new ClientConnection(minecraft, socket);
|
connection = new ClientConnection(minecraft, socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !connection->createdOk )
|
if (connection != nullptr)
|
||||||
{
|
{
|
||||||
assert(false);
|
if( !connection->createdOk )
|
||||||
delete connection;
|
{
|
||||||
connection = nullptr;
|
assert(false);
|
||||||
MinecraftServer::HaltServer();
|
delete connection;
|
||||||
return false;
|
connection = nullptr;
|
||||||
}
|
MinecraftServer::HaltServer();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
connection->send(std::make_shared<PreLoginPacket>(minecraft->user->name));
|
connection->send(std::make_shared<PreLoginPacket>(minecraft->user->name));
|
||||||
|
|
||||||
// Tick connection until we're ready to go. The stages involved in this are:
|
// Tick connection until we're ready to go. The stages involved in this are:
|
||||||
// (1) Creating the ClientConnection sends a prelogin packet to the server
|
// (1) Creating the ClientConnection sends a prelogin packet to the server
|
||||||
|
|
@ -434,9 +448,9 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
|
||||||
connection->close();
|
connection->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
if( connection->isStarted() && !connection->isClosed() )
|
if( connection->isStarted() && !connection->isClosed() )
|
||||||
{
|
{
|
||||||
createdConnections.push_back( connection );
|
createdConnections.push_back( connection );
|
||||||
|
|
||||||
int primaryPad = ProfileManager.GetPrimaryPad();
|
int primaryPad = ProfileManager.GetPrimaryPad();
|
||||||
app.SetRichPresenceContext(primaryPad,CONTEXT_GAME_STATE_BLANK);
|
app.SetRichPresenceContext(primaryPad,CONTEXT_GAME_STATE_BLANK);
|
||||||
|
|
@ -533,13 +547,14 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app.SetGameMode( eMode_Multiplayer );
|
app.SetGameMode( eMode_Multiplayer );
|
||||||
}
|
}
|
||||||
else if ( connection->isClosed() || !IsInSession())
|
else if ( connection->isClosed() || !IsInSession())
|
||||||
{
|
{
|
||||||
// assert(false);
|
// assert(false);
|
||||||
MinecraftServer::HaltServer();
|
MinecraftServer::HaltServer();
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
while (IQNet::s_playerCount > 1 && IQNet::m_player[IQNet::s_playerCount - 1].GetCustomDataValue() == 0)
|
// 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();
|
||||||
|
|
||||||
|
|
@ -433,9 +460,23 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame,
|
||||||
|
|
||||||
if (WinsockNetLayer::IsActive())
|
if (WinsockNetLayer::IsActive())
|
||||||
{
|
{
|
||||||
const wchar_t* hostName = IQNet::m_player[0].m_gamertag;
|
// For Dedicated Server, refer to `lan-advertise` in `server.properties`
|
||||||
unsigned int settings = app.GetGameHostOption(eGameHostOption_All);
|
bool enableLanAdvertising = true;
|
||||||
WinsockNetLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION);
|
if (g_Win64DedicatedServer)
|
||||||
|
{
|
||||||
|
enableLanAdvertising = g_Win64DedicatedServerLanAdvertise;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableLanAdvertising)
|
||||||
|
{
|
||||||
|
const wchar_t* hostName = IQNet::m_player[0].m_gamertag;
|
||||||
|
unsigned int settings = app.GetGameHostOption(eGameHostOption_All);
|
||||||
|
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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -368,7 +368,7 @@ void UIScene_MainMenu::handlePress(F64 controlId, F64 childId)
|
||||||
UINT uiIDA[2];
|
UINT uiIDA[2];
|
||||||
uiIDA[0]=IDS_CANCEL;
|
uiIDA[0]=IDS_CANCEL;
|
||||||
uiIDA[1]=IDS_OK;
|
uiIDA[1]=IDS_OK;
|
||||||
ui.RequestErrorMessage(IDS_WARNING_ARCADE_TITLE, IDS_WARNING_ARCADE_TEXT, uiIDA, 2, XUSER_INDEX_ANY,&UIScene_MainMenu::ExitGameReturned,this);
|
ui.RequestErrorMessage(IDS_WINDOWS_EXIT, IDS_WARNING_ARCADE_TEXT, uiIDA, 2, XUSER_INDEX_ANY,&UIScene_MainMenu::ExitGameReturned,this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -392,7 +392,7 @@ float GameRenderer::getFov(float a, bool applyEffects)
|
||||||
float fov = m_fov;//70;
|
float fov = m_fov;//70;
|
||||||
if (applyEffects)
|
if (applyEffects)
|
||||||
{
|
{
|
||||||
fov += mc->options->fov * 40;
|
//fov += mc->options->fov * 40;
|
||||||
fov *= oFov[playerIdx] + (this->fov[playerIdx] - oFov[playerIdx]) * a;
|
fov *= oFov[playerIdx] + (this->fov[playerIdx] - oFov[playerIdx]) * a;
|
||||||
}
|
}
|
||||||
if (player->getHealth() <= 0)
|
if (player->getHealth() <= 0)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -651,7 +657,7 @@ bool MinecraftServer::initServer(int64_t seed, NetworkGameInitData *initData, DW
|
||||||
setFlightAllowed(GetDedicatedServerBool(settings, L"allow-flight", true));
|
setFlightAllowed(GetDedicatedServerBool(settings, L"allow-flight", true));
|
||||||
|
|
||||||
// 4J Stu - Enabling flight to stop it kicking us when we use it
|
// 4J Stu - Enabling flight to stop it kicking us when we use it
|
||||||
#ifdef _DEBUG_MENUS_ENABLED
|
#if (defined _DEBUG_MENUS_ENABLED && defined _DEBUG)
|
||||||
setFlightAllowed(true);
|
setFlightAllowed(true);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -1707,330 +1715,345 @@ void MinecraftServer::setPlayerIdleTimeout(int playerIdleTimeout)
|
||||||
extern int c0a, c0b, c1a, c1b, c1c, c2a, c2b;
|
extern int c0a, c0b, c1a, c1b, c1c, c2a, c2b;
|
||||||
void MinecraftServer::run(int64_t seed, void *lpParameter)
|
void MinecraftServer::run(int64_t seed, void *lpParameter)
|
||||||
{
|
{
|
||||||
NetworkGameInitData *initData = nullptr;
|
NetworkGameInitData *initData = nullptr;
|
||||||
DWORD initSettings = 0;
|
DWORD initSettings = 0;
|
||||||
bool findSeed = false;
|
bool findSeed = false;
|
||||||
if(lpParameter != nullptr)
|
if(lpParameter != nullptr)
|
||||||
{
|
{
|
||||||
initData = static_cast<NetworkGameInitData *>(lpParameter);
|
initData = static_cast<NetworkGameInitData *>(lpParameter);
|
||||||
initSettings = app.GetGameHostOption(eGameHostOption_All);
|
initSettings = app.GetGameHostOption(eGameHostOption_All);
|
||||||
findSeed = initData->findSeed;
|
findSeed = initData->findSeed;
|
||||||
m_texturePackId = initData->texturePackId;
|
m_texturePackId = initData->texturePackId;
|
||||||
}
|
}
|
||||||
// try { // 4J - removed try/catch/finally
|
// try { // 4J - removed try/catch/finally
|
||||||
bool didInit = false;
|
bool didInit = false;
|
||||||
if (initServer(seed, initData, initSettings,findSeed))
|
if (initServer(seed, initData, initSettings,findSeed))
|
||||||
{
|
{
|
||||||
didInit = true;
|
didInit = true;
|
||||||
ServerLevel *levelNormalDimension = levels[0];
|
ServerLevel *levelNormalDimension = levels[0];
|
||||||
// 4J-PB - Set the Stronghold position in the leveldata if there isn't one in there
|
// 4J-PB - Set the Stronghold position in the leveldata if there isn't one in there
|
||||||
Minecraft *pMinecraft = Minecraft::GetInstance();
|
Minecraft *pMinecraft = Minecraft::GetInstance();
|
||||||
LevelData *pLevelData=levelNormalDimension->getLevelData();
|
LevelData *pLevelData=levelNormalDimension->getLevelData();
|
||||||
|
|
||||||
if(pLevelData && pLevelData->getHasStronghold()==false)
|
if(pLevelData && pLevelData->getHasStronghold()==false)
|
||||||
{
|
{
|
||||||
int x,z;
|
int x,z;
|
||||||
if(app.GetTerrainFeaturePosition(eTerrainFeature_Stronghold,&x,&z))
|
if(app.GetTerrainFeaturePosition(eTerrainFeature_Stronghold,&x,&z))
|
||||||
{
|
{
|
||||||
pLevelData->setXStronghold(x);
|
pLevelData->setXStronghold(x);
|
||||||
pLevelData->setZStronghold(z);
|
pLevelData->setZStronghold(z);
|
||||||
pLevelData->setHasStronghold();
|
pLevelData->setHasStronghold();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t lastTime = getCurrentTimeMillis();
|
int64_t lastTime = getCurrentTimeMillis();
|
||||||
int64_t unprocessedTime = 0;
|
int64_t unprocessedTime = 0;
|
||||||
while (running && !s_bServerHalted)
|
while (running && !s_bServerHalted)
|
||||||
{
|
{
|
||||||
int64_t now = getCurrentTimeMillis();
|
int64_t now = getCurrentTimeMillis();
|
||||||
|
|
||||||
// 4J Stu - When we pause the server, we don't want to count that as time passed
|
// 4J Stu - When we pause the server, we don't want to count that as time passed
|
||||||
// 4J Stu - TU-1 hotifx - Remove this line. We want to make sure that we tick connections at the proper rate when paused
|
// 4J Stu - TU-1 hotifx - Remove this line. We want to make sure that we tick connections at the proper rate when paused
|
||||||
//Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost
|
//Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost
|
||||||
//if(m_isServerPaused) lastTime = now;
|
//if(m_isServerPaused) lastTime = now;
|
||||||
|
|
||||||
int64_t passedTime = now - lastTime;
|
int64_t passedTime = now - lastTime;
|
||||||
if (passedTime > MS_PER_TICK * 40)
|
if (passedTime > MS_PER_TICK * 40)
|
||||||
{
|
{
|
||||||
// logger.warning("Can't keep up! Did the system time change, or is the server overloaded?");
|
// logger.warning("Can't keep up! Did the system time change, or is the server overloaded?");
|
||||||
passedTime = MS_PER_TICK * 40;
|
passedTime = MS_PER_TICK * 40;
|
||||||
}
|
}
|
||||||
if (passedTime < 0)
|
if (passedTime < 0)
|
||||||
{
|
{
|
||||||
// logger.warning("Time ran backwards! Did the system time change?");
|
// logger.warning("Time ran backwards! Did the system time change?");
|
||||||
passedTime = 0;
|
passedTime = 0;
|
||||||
}
|
}
|
||||||
unprocessedTime += passedTime;
|
unprocessedTime += passedTime;
|
||||||
lastTime = now;
|
lastTime = now;
|
||||||
|
|
||||||
// 4J Added ability to pause the server
|
// 4J Added ability to pause the server
|
||||||
if( !m_isServerPaused )
|
if( !m_isServerPaused )
|
||||||
{
|
{
|
||||||
bool didTick = false;
|
bool didTick = false;
|
||||||
if (levels[0]->allPlayersAreSleeping())
|
if (levels[0]->allPlayersAreSleeping())
|
||||||
{
|
{
|
||||||
tick();
|
tick();
|
||||||
unprocessedTime = 0;
|
unprocessedTime = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// int tickcount = 0;
|
// int tickcount = 0;
|
||||||
// int64_t beforeall = System::currentTimeMillis();
|
// int64_t beforeall = System::currentTimeMillis();
|
||||||
while (unprocessedTime > MS_PER_TICK)
|
while (unprocessedTime > MS_PER_TICK)
|
||||||
{
|
{
|
||||||
unprocessedTime -= MS_PER_TICK;
|
unprocessedTime -= MS_PER_TICK;
|
||||||
chunkPacketManagement_PreTick();
|
chunkPacketManagement_PreTick();
|
||||||
// int64_t before = System::currentTimeMillis();
|
// int64_t before = System::currentTimeMillis();
|
||||||
tick();
|
tick();
|
||||||
// int64_t after = System::currentTimeMillis();
|
// int64_t after = System::currentTimeMillis();
|
||||||
// PIXReportCounter(L"Server time",(float)(after-before));
|
// PIXReportCounter(L"Server time",(float)(after-before));
|
||||||
|
|
||||||
chunkPacketManagement_PostTick();
|
chunkPacketManagement_PostTick();
|
||||||
}
|
}
|
||||||
// int64_t afterall = System::currentTimeMillis();
|
// int64_t afterall = System::currentTimeMillis();
|
||||||
// PIXReportCounter(L"Server time all",(float)(afterall-beforeall));
|
// PIXReportCounter(L"Server time all",(float)(afterall-beforeall));
|
||||||
// PIXReportCounter(L"Server ticks",(float)tickcount);
|
// PIXReportCounter(L"Server ticks",(float)tickcount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 4J Stu - TU1-hotfix
|
// 4J Stu - TU1-hotfix
|
||||||
//Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost
|
//Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost
|
||||||
// The connections should tick at the same frequency even when paused
|
// The connections should tick at the same frequency even when paused
|
||||||
while (unprocessedTime > MS_PER_TICK)
|
while (unprocessedTime > MS_PER_TICK)
|
||||||
{
|
{
|
||||||
unprocessedTime -= MS_PER_TICK;
|
unprocessedTime -= MS_PER_TICK;
|
||||||
// Keep ticking the connections to stop them timing out
|
// Keep ticking the connections to stop them timing out
|
||||||
connection->tick();
|
connection->tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(MinecraftServer::setTimeAtEndOfTick)
|
if(MinecraftServer::setTimeAtEndOfTick)
|
||||||
{
|
{
|
||||||
MinecraftServer::setTimeAtEndOfTick = false;
|
MinecraftServer::setTimeAtEndOfTick = false;
|
||||||
for (unsigned int i = 0; i < levels.length; i++)
|
for (unsigned int i = 0; i < levels.length; i++)
|
||||||
{
|
{
|
||||||
// if (i == 0 || settings->getBoolean(L"allow-nether", true)) // 4J removed - we always have nether
|
// if (i == 0 || settings->getBoolean(L"allow-nether", true)) // 4J removed - we always have nether
|
||||||
{
|
{
|
||||||
ServerLevel *level = levels[i];
|
ServerLevel *level = levels[i];
|
||||||
level->setGameTime( MinecraftServer::setTime );
|
level->setGameTime( MinecraftServer::setTime );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(MinecraftServer::setTimeOfDayAtEndOfTick)
|
if(MinecraftServer::setTimeOfDayAtEndOfTick)
|
||||||
{
|
{
|
||||||
MinecraftServer::setTimeOfDayAtEndOfTick = false;
|
MinecraftServer::setTimeOfDayAtEndOfTick = false;
|
||||||
for (unsigned int i = 0; i < levels.length; i++)
|
for (unsigned int i = 0; i < levels.length; i++)
|
||||||
{
|
{
|
||||||
if (i == 0 || GetDedicatedServerBool(settings, L"allow-nether", true))
|
if (i == 0 || GetDedicatedServerBool(settings, L"allow-nether", true))
|
||||||
{
|
{
|
||||||
ServerLevel *level = levels[i];
|
ServerLevel *level = levels[i];
|
||||||
level->setDayTime( MinecraftServer::setTimeOfDay );
|
level->setDayTime( MinecraftServer::setTimeOfDay );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process delayed actions
|
// Process delayed actions
|
||||||
eXuiServerAction eAction;
|
eXuiServerAction eAction;
|
||||||
LPVOID param;
|
LPVOID param;
|
||||||
for(int i=0;i<XUSER_MAX_COUNT;i++)
|
for(int i=0;i<XUSER_MAX_COUNT;i++)
|
||||||
{
|
{
|
||||||
eAction = app.GetXuiServerAction(i);
|
eAction = app.GetXuiServerAction(i);
|
||||||
param = app.GetXuiServerActionParam(i);
|
param = app.GetXuiServerActionParam(i);
|
||||||
|
|
||||||
switch(eAction)
|
switch(eAction)
|
||||||
{
|
{
|
||||||
case eXuiServerAction_AutoSaveGame:
|
case eXuiServerAction_AutoSaveGame:
|
||||||
|
#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(MINECRAFT_SERVER_BUILD)
|
||||||
|
{
|
||||||
#if defined(_XBOX_ONE) || defined(__ORBIS__)
|
#if defined(_XBOX_ONE) || defined(__ORBIS__)
|
||||||
{
|
PIXBeginNamedEvent(0, "Autosave");
|
||||||
PIXBeginNamedEvent(0,"Autosave");
|
|
||||||
|
|
||||||
// Get the frequency of the timer
|
// Get the frequency of the timer
|
||||||
LARGE_INTEGER qwTicksPerSec, qwTime, qwNewTime, qwDeltaTime;
|
LARGE_INTEGER qwTicksPerSec, qwTime, qwNewTime, qwDeltaTime;
|
||||||
float fElapsedTime = 0.0f;
|
float fElapsedTime = 0.0f;
|
||||||
QueryPerformanceFrequency( &qwTicksPerSec );
|
QueryPerformanceFrequency(&qwTicksPerSec);
|
||||||
float fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart;
|
float fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart;
|
||||||
|
|
||||||
// Save the start time
|
// Save the start time
|
||||||
QueryPerformanceCounter( &qwTime );
|
QueryPerformanceCounter(&qwTime);
|
||||||
|
|
||||||
if (players != nullptr)
|
|
||||||
{
|
|
||||||
players->saveAll(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned int j = 0; j < levels.length; j++)
|
|
||||||
{
|
|
||||||
if( s_bServerHalted ) break;
|
|
||||||
// 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
|
|
||||||
// with the data from the nethers leveldata.
|
|
||||||
// Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
|
|
||||||
ServerLevel *level = levels[levels.length - 1 - j];
|
|
||||||
PIXBeginNamedEvent(0, "Saving level %d",levels.length - 1 - j);
|
|
||||||
level->save(false, nullptr, true);
|
|
||||||
PIXEndNamedEvent();
|
|
||||||
}
|
|
||||||
if( !s_bServerHalted )
|
|
||||||
{
|
|
||||||
PIXBeginNamedEvent(0,"Saving game rules");
|
|
||||||
saveGameRules();
|
|
||||||
PIXEndNamedEvent();
|
|
||||||
|
|
||||||
PIXBeginNamedEvent(0,"Save to disc");
|
|
||||||
levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, true);
|
|
||||||
PIXEndNamedEvent();
|
|
||||||
}
|
|
||||||
PIXEndNamedEvent();
|
|
||||||
|
|
||||||
QueryPerformanceCounter( &qwNewTime );
|
|
||||||
qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart;
|
|
||||||
fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart));
|
|
||||||
app.DebugPrintf("Autosave: Elapsed time %f\n", fElapsedTime);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
case eXuiServerAction_SaveGame:
|
|
||||||
app.EnterSaveNotificationSection();
|
|
||||||
if (players != nullptr)
|
|
||||||
{
|
|
||||||
players->saveAll(Minecraft::GetInstance()->progressRenderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
players->broadcastAll(std::make_shared<UpdateProgressPacket>(20));
|
if (players != nullptr)
|
||||||
|
{
|
||||||
|
players->saveAll(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
for (unsigned int j = 0; j < levels.length; j++)
|
for (unsigned int j = 0; j < levels.length; j++)
|
||||||
{
|
{
|
||||||
|
if( s_bServerHalted ) break;
|
||||||
|
// 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
|
||||||
|
// with the data from the nethers leveldata.
|
||||||
|
// Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
|
||||||
|
ServerLevel *level = levels[levels.length - 1 - j];
|
||||||
|
#if defined(_XBOX_ONE) || defined(__ORBIS__)
|
||||||
|
PIXBeginNamedEvent(0, "Saving level %d", levels.length - 1 - j);
|
||||||
|
#endif
|
||||||
|
level->save(false, nullptr, true);
|
||||||
|
#if defined(_XBOX_ONE) || defined(__ORBIS__)
|
||||||
|
PIXEndNamedEvent();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (!s_bServerHalted)
|
||||||
|
{
|
||||||
|
#if defined(_XBOX_ONE) || defined(__ORBIS__)
|
||||||
|
PIXBeginNamedEvent(0, "Saving game rules");
|
||||||
|
#endif
|
||||||
|
saveGameRules();
|
||||||
|
#if defined(_XBOX_ONE) || defined(__ORBIS__)
|
||||||
|
PIXEndNamedEvent();
|
||||||
|
|
||||||
|
PIXBeginNamedEvent(0, "Save to disc");
|
||||||
|
#endif
|
||||||
|
levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, true);
|
||||||
|
#if defined(_XBOX_ONE) || defined(__ORBIS__)
|
||||||
|
PIXEndNamedEvent();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_XBOX_ONE) || defined(__ORBIS__)
|
||||||
|
PIXEndNamedEvent();
|
||||||
|
|
||||||
|
QueryPerformanceCounter(&qwNewTime);
|
||||||
|
qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart;
|
||||||
|
fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart));
|
||||||
|
app.DebugPrintf("Autosave: Elapsed time %f\n", fElapsedTime);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case eXuiServerAction_SaveGame:
|
||||||
|
app.EnterSaveNotificationSection();
|
||||||
|
if (players != nullptr)
|
||||||
|
{
|
||||||
|
players->saveAll(Minecraft::GetInstance()->progressRenderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
players->broadcastAll(std::make_shared<UpdateProgressPacket>(20));
|
||||||
|
|
||||||
|
for (unsigned int j = 0; j < levels.length; j++)
|
||||||
|
{
|
||||||
if( s_bServerHalted ) break;
|
if( s_bServerHalted ) break;
|
||||||
// 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
|
// 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
|
||||||
// with the data from the nethers leveldata.
|
// with the data from the nethers leveldata.
|
||||||
// Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
|
// Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
|
||||||
ServerLevel *level = levels[levels.length - 1 - j];
|
ServerLevel *level = levels[levels.length - 1 - j];
|
||||||
level->save(true, Minecraft::GetInstance()->progressRenderer, (eAction==eXuiServerAction_AutoSaveGame));
|
level->save(true, Minecraft::GetInstance()->progressRenderer, (eAction==eXuiServerAction_AutoSaveGame));
|
||||||
|
|
||||||
players->broadcastAll(std::make_shared<UpdateProgressPacket>(33 + (j * 33)));
|
players->broadcastAll(std::make_shared<UpdateProgressPacket>(33 + (j * 33)));
|
||||||
}
|
}
|
||||||
if( !s_bServerHalted )
|
if( !s_bServerHalted )
|
||||||
{
|
{
|
||||||
saveGameRules();
|
saveGameRules();
|
||||||
|
|
||||||
levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, (eAction==eXuiServerAction_AutoSaveGame));
|
levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, (eAction==eXuiServerAction_AutoSaveGame));
|
||||||
}
|
}
|
||||||
app.LeaveSaveNotificationSection();
|
app.LeaveSaveNotificationSection();
|
||||||
break;
|
break;
|
||||||
case eXuiServerAction_DropItem:
|
case eXuiServerAction_DropItem:
|
||||||
// Find the player, and drop the id at their feet
|
// Find the player, and drop the id at their feet
|
||||||
{
|
{
|
||||||
shared_ptr<ServerPlayer> player = players->players.at(0);
|
shared_ptr<ServerPlayer> player = players->players.at(0);
|
||||||
size_t id = (size_t) param;
|
size_t id = (size_t) param;
|
||||||
player->drop(std::make_shared<ItemInstance>(id, 1, 0));
|
player->drop(std::make_shared<ItemInstance>(id, 1, 0));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case eXuiServerAction_SpawnMob:
|
case eXuiServerAction_SpawnMob:
|
||||||
{
|
{
|
||||||
shared_ptr<ServerPlayer> player = players->players.at(0);
|
shared_ptr<ServerPlayer> player = players->players.at(0);
|
||||||
eINSTANCEOF factory = static_cast<eINSTANCEOF>((size_t)param);
|
eINSTANCEOF factory = static_cast<eINSTANCEOF>((size_t)param);
|
||||||
shared_ptr<Mob> mob = dynamic_pointer_cast<Mob>(EntityIO::newByEnumType(factory,player->level ));
|
shared_ptr<Mob> mob = dynamic_pointer_cast<Mob>(EntityIO::newByEnumType(factory,player->level ));
|
||||||
mob->moveTo(player->x+1, player->y, player->z+1, player->level->random->nextFloat() * 360, 0);
|
mob->moveTo(player->x+1, player->y, player->z+1, player->level->random->nextFloat() * 360, 0);
|
||||||
mob->setDespawnProtected(); // 4J added, default to being protected against despawning (has to be done after initial position is set)
|
mob->setDespawnProtected(); // 4J added, default to being protected against despawning (has to be done after initial position is set)
|
||||||
player->level->addEntity(mob);
|
player->level->addEntity(mob);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case eXuiServerAction_PauseServer:
|
case eXuiServerAction_PauseServer:
|
||||||
m_isServerPaused = ( (size_t) param == TRUE );
|
m_isServerPaused = ( (size_t) param == TRUE );
|
||||||
if( m_isServerPaused )
|
if( m_isServerPaused )
|
||||||
{
|
{
|
||||||
m_serverPausedEvent->Set();
|
m_serverPausedEvent->Set();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case eXuiServerAction_ToggleRain:
|
case eXuiServerAction_ToggleRain:
|
||||||
{
|
{
|
||||||
bool isRaining = levels[0]->getLevelData()->isRaining();
|
bool isRaining = levels[0]->getLevelData()->isRaining();
|
||||||
levels[0]->getLevelData()->setRaining(!isRaining);
|
levels[0]->getLevelData()->setRaining(!isRaining);
|
||||||
levels[0]->getLevelData()->setRainTime(levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + Level::TICKS_PER_DAY / 2);
|
levels[0]->getLevelData()->setRainTime(levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + Level::TICKS_PER_DAY / 2);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case eXuiServerAction_ToggleThunder:
|
case eXuiServerAction_ToggleThunder:
|
||||||
{
|
{
|
||||||
bool isThundering = levels[0]->getLevelData()->isThundering();
|
bool isThundering = levels[0]->getLevelData()->isThundering();
|
||||||
levels[0]->getLevelData()->setThundering(!isThundering);
|
levels[0]->getLevelData()->setThundering(!isThundering);
|
||||||
levels[0]->getLevelData()->setThunderTime(levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + Level::TICKS_PER_DAY / 2);
|
levels[0]->getLevelData()->setThunderTime(levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + Level::TICKS_PER_DAY / 2);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case eXuiServerAction_ServerSettingChanged_Gamertags:
|
case eXuiServerAction_ServerSettingChanged_Gamertags:
|
||||||
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_OPTIONS, app.GetGameHostOption(eGameHostOption_Gamertags)));
|
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_OPTIONS, app.GetGameHostOption(eGameHostOption_Gamertags)));
|
||||||
break;
|
break;
|
||||||
case eXuiServerAction_ServerSettingChanged_BedrockFog:
|
case eXuiServerAction_ServerSettingChanged_BedrockFog:
|
||||||
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS, app.GetGameHostOption(eGameHostOption_All)));
|
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS, app.GetGameHostOption(eGameHostOption_All)));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case eXuiServerAction_ServerSettingChanged_Difficulty:
|
case eXuiServerAction_ServerSettingChanged_Difficulty:
|
||||||
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_DIFFICULTY, Minecraft::GetInstance()->options->difficulty));
|
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_DIFFICULTY, Minecraft::GetInstance()->options->difficulty));
|
||||||
break;
|
break;
|
||||||
case eXuiServerAction_ExportSchematic:
|
case eXuiServerAction_ExportSchematic:
|
||||||
#ifndef _CONTENT_PACKAGE
|
#ifndef _CONTENT_PACKAGE
|
||||||
app.EnterSaveNotificationSection();
|
app.EnterSaveNotificationSection();
|
||||||
|
|
||||||
//players->broadcastAll( shared_ptr<UpdateProgressPacket>( new UpdateProgressPacket(20) ) );
|
//players->broadcastAll( shared_ptr<UpdateProgressPacket>( new UpdateProgressPacket(20) ) );
|
||||||
|
|
||||||
if( !s_bServerHalted )
|
if( !s_bServerHalted )
|
||||||
{
|
{
|
||||||
ConsoleSchematicFile::XboxSchematicInitParam *initData = static_cast<ConsoleSchematicFile::XboxSchematicInitParam *>(param);
|
ConsoleSchematicFile::XboxSchematicInitParam *initData = static_cast<ConsoleSchematicFile::XboxSchematicInitParam *>(param);
|
||||||
#ifdef _XBOX
|
#ifdef _XBOX
|
||||||
File targetFileDir(File::pathRoot + File::pathSeparator + L"Schematics");
|
File targetFileDir(File::pathRoot + File::pathSeparator + L"Schematics");
|
||||||
#else
|
#else
|
||||||
File targetFileDir(L"Schematics");
|
File targetFileDir(L"Schematics");
|
||||||
#endif
|
#endif
|
||||||
if(!targetFileDir.exists()) targetFileDir.mkdir();
|
if(!targetFileDir.exists()) targetFileDir.mkdir();
|
||||||
|
|
||||||
wchar_t filename[128];
|
wchar_t filename[128];
|
||||||
swprintf(filename,128,L"%ls%dx%dx%d.sch",initData->name,(initData->endX - initData->startX + 1), (initData->endY - initData->startY + 1), (initData->endZ - initData->startZ + 1));
|
swprintf(filename,128,L"%ls%dx%dx%d.sch",initData->name,(initData->endX - initData->startX + 1), (initData->endY - initData->startY + 1), (initData->endZ - initData->startZ + 1));
|
||||||
|
|
||||||
File dataFile = File( targetFileDir, wstring(filename) );
|
File dataFile = File( targetFileDir, wstring(filename) );
|
||||||
if(dataFile.exists()) dataFile._delete();
|
if(dataFile.exists()) dataFile._delete();
|
||||||
FileOutputStream fos = FileOutputStream(dataFile);
|
FileOutputStream fos = FileOutputStream(dataFile);
|
||||||
DataOutputStream dos = DataOutputStream(&fos);
|
DataOutputStream dos = DataOutputStream(&fos);
|
||||||
ConsoleSchematicFile::generateSchematicFile(&dos, levels[0], initData->startX, initData->startY, initData->startZ, initData->endX, initData->endY, initData->endZ, initData->bSaveMobs, initData->compressionType);
|
ConsoleSchematicFile::generateSchematicFile(&dos, levels[0], initData->startX, initData->startY, initData->startZ, initData->endX, initData->endY, initData->endZ, initData->bSaveMobs, initData->compressionType);
|
||||||
dos.close();
|
dos.close();
|
||||||
|
|
||||||
delete initData;
|
delete initData;
|
||||||
}
|
}
|
||||||
app.LeaveSaveNotificationSection();
|
app.LeaveSaveNotificationSection();
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case eXuiServerAction_SetCameraLocation:
|
case eXuiServerAction_SetCameraLocation:
|
||||||
#ifndef _CONTENT_PACKAGE
|
#ifndef _CONTENT_PACKAGE
|
||||||
{
|
{
|
||||||
DebugSetCameraPosition *pos = static_cast<DebugSetCameraPosition *>(param);
|
DebugSetCameraPosition *pos = static_cast<DebugSetCameraPosition *>(param);
|
||||||
|
|
||||||
app.DebugPrintf( "DEBUG: Player=%i\n", pos->player );
|
app.DebugPrintf( "DEBUG: Player=%i\n", pos->player );
|
||||||
app.DebugPrintf( "DEBUG: Teleporting to pos=(%f.2, %f.2, %f.2), looking at=(%f.2,%f.2)\n",
|
app.DebugPrintf( "DEBUG: Teleporting to pos=(%f.2, %f.2, %f.2), looking at=(%f.2,%f.2)\n",
|
||||||
pos->m_camX, pos->m_camY, pos->m_camZ,
|
pos->m_camX, pos->m_camY, pos->m_camZ,
|
||||||
pos->m_yRot, pos->m_elev
|
pos->m_yRot, pos->m_elev
|
||||||
);
|
);
|
||||||
|
|
||||||
shared_ptr<ServerPlayer> player = players->players.at(pos->player);
|
shared_ptr<ServerPlayer> player = players->players.at(pos->player);
|
||||||
player->debug_setPosition( pos->m_camX, pos->m_camY, pos->m_camZ,
|
player->debug_setPosition( pos->m_camX, pos->m_camY, pos->m_camZ,
|
||||||
pos->m_yRot, pos->m_elev );
|
pos->m_yRot, pos->m_elev );
|
||||||
|
|
||||||
// Doesn't work
|
// Doesn't work
|
||||||
//player->setYHeadRot(pos->m_yRot);
|
//player->setYHeadRot(pos->m_yRot);
|
||||||
//player->absMoveTo(pos->m_camX, pos->m_camY, pos->m_camZ, pos->m_yRot, pos->m_elev);
|
//player->absMoveTo(pos->m_camX, pos->m_camY, pos->m_camZ, pos->m_yRot, pos->m_elev);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
app.SetXuiServerAction(i,eXuiServerAction_Idle);
|
app.SetXuiServerAction(i,eXuiServerAction_Idle);
|
||||||
}
|
}
|
||||||
|
|
||||||
Sleep(1);
|
Sleep(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//else
|
//else
|
||||||
//{
|
//{
|
||||||
// while (running)
|
// while (running)
|
||||||
// {
|
// {
|
||||||
// handleConsoleInputs();
|
// handleConsoleInputs();
|
||||||
// Sleep(10);
|
// Sleep(10);
|
||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
#if 0
|
#if 0
|
||||||
|
|
@ -2057,9 +2080,9 @@ void MinecraftServer::run(int64_t seed, void *lpParameter)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 4J Stu - Stop the server when the loops complete, as the finally would do
|
// 4J Stu - Stop the server when the loops complete, as the finally would do
|
||||||
stopServer(didInit);
|
stopServer(didInit);
|
||||||
stopped = true;
|
stopped = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MinecraftServer::broadcastStartSavingPacket()
|
void MinecraftServer::broadcastStartSavingPacket()
|
||||||
|
|
@ -2367,6 +2390,9 @@ bool MinecraftServer::chunkPacketManagement_CanSendTo(INetworkPlayer *player)
|
||||||
{
|
{
|
||||||
if( player == nullptr ) return false;
|
if( player == nullptr ) return false;
|
||||||
|
|
||||||
|
#ifdef MINECRAFT_SERVER_BUILD
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
int time = GetTickCount();
|
int time = GetTickCount();
|
||||||
DWORD currentPlayerCount = g_NetworkManager.GetPlayerCount();
|
DWORD currentPlayerCount = g_NetworkManager.GetPlayerCount();
|
||||||
if( currentPlayerCount == 0 ) return false;
|
if( currentPlayerCount == 0 ) return false;
|
||||||
|
|
@ -2378,6 +2404,7 @@ bool MinecraftServer::chunkPacketManagement_CanSendTo(INetworkPlayer *player)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void MinecraftServer::chunkPacketManagement_DidSendTo(INetworkPlayer *player)
|
void MinecraftServer::chunkPacketManagement_DidSendTo(INetworkPlayer *player)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
@ -325,4 +393,4 @@ bool PendingConnection::isServerPacketListener()
|
||||||
bool PendingConnection::isDisconnected()
|
bool PendingConnection::isDisconnected()
|
||||||
{
|
{
|
||||||
return done;
|
return done;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
{
|
{
|
||||||
// logger.warning(player->name + " moved too quickly!");
|
// Creative / flight-allowed players can move farther legitimately per tick.
|
||||||
disconnect(DisconnectPacket::eDisconnect_MovedTooQuickly);
|
maxAllowedSq *= 1.5;
|
||||||
// System.out.println("Moved too quickly at " + xt + ", " + yt + ", " + zt);
|
}
|
||||||
// teleport(player->x, player->y, player->z, player->yRot, player->xRot);
|
if (dist > maxAllowedSq)
|
||||||
return;
|
{
|
||||||
|
disconnect(DisconnectPacket::eDisconnect_MovedTooQuickly);
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,7 @@ void Screen::updateEvents()
|
||||||
// Map to Screen::keyPressed
|
// Map to Screen::keyPressed
|
||||||
int mappedKey = -1;
|
int mappedKey = -1;
|
||||||
wchar_t ch = 0;
|
wchar_t ch = 0;
|
||||||
if (vk == VK_ESCAPE) mappedKey = Keyboard::KEY_ESCAPE;
|
if (vk == VK_ESCAPE) mappedKey = Keyboard::KEY_ESCAPE;
|
||||||
else if (vk == VK_RETURN) mappedKey = Keyboard::KEY_RETURN;
|
else if (vk == VK_RETURN) mappedKey = Keyboard::KEY_RETURN;
|
||||||
else if (vk == VK_BACK) mappedKey = Keyboard::KEY_BACK;
|
else if (vk == VK_BACK) mappedKey = Keyboard::KEY_BACK;
|
||||||
else if (vk == VK_UP) mappedKey = Keyboard::KEY_UP;
|
else if (vk == VK_UP) mappedKey = Keyboard::KEY_UP;
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,8 @@ void ServerPlayer::flagEntitiesToBeRemoved(unsigned int *flags, bool *removedFou
|
||||||
if( ( *removedFound ) == false )
|
if( ( *removedFound ) == false )
|
||||||
{
|
{
|
||||||
*removedFound = true;
|
*removedFound = true;
|
||||||
memset(flags, 0, 2048/32);
|
// before this left 192 bytes uninitialized!!!!!
|
||||||
|
memset(flags, 0, (2048 / 32) * sizeof(unsigned int));
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int index : entitiesToRemove)
|
for(int index : entitiesToRemove)
|
||||||
|
|
@ -376,6 +377,9 @@ void ServerPlayer::doChunkSendingTick(bool dontDelayChunks)
|
||||||
// connection->done);
|
// connection->done);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
#ifdef MINECRAFT_SERVER_BUILD
|
||||||
|
if (dontDelayChunks || (canSendToPlayer && !connection->done))
|
||||||
|
#else
|
||||||
if( dontDelayChunks ||
|
if( dontDelayChunks ||
|
||||||
(canSendToPlayer &&
|
(canSendToPlayer &&
|
||||||
#ifdef _XBOX_ONE
|
#ifdef _XBOX_ONE
|
||||||
|
|
@ -390,21 +394,22 @@ void ServerPlayer::doChunkSendingTick(bool dontDelayChunks)
|
||||||
#endif
|
#endif
|
||||||
//(tickCount - lastBrupSendTickCount) > (connection->getNetworkPlayer()->GetCurrentRtt()>>4) &&
|
//(tickCount - lastBrupSendTickCount) > (connection->getNetworkPlayer()->GetCurrentRtt()>>4) &&
|
||||||
!connection->done) )
|
!connection->done) )
|
||||||
{
|
#endif
|
||||||
lastBrupSendTickCount = tickCount;
|
{
|
||||||
okToSend = true;
|
lastBrupSendTickCount = tickCount;
|
||||||
MinecraftServer::chunkPacketManagement_DidSendTo(connection->getNetworkPlayer());
|
okToSend = true;
|
||||||
|
MinecraftServer::chunkPacketManagement_DidSendTo(connection->getNetworkPlayer());
|
||||||
|
|
||||||
// static unordered_map<wstring,int64_t> mapLastTime;
|
// static unordered_map<wstring,int64_t> mapLastTime;
|
||||||
// int64_t thisTime = System::currentTimeMillis();
|
// int64_t thisTime = System::currentTimeMillis();
|
||||||
// int64_t lastTime = mapLastTime[connection->getNetworkPlayer()->GetUID().toString()];
|
// int64_t lastTime = mapLastTime[connection->getNetworkPlayer()->GetUID().toString()];
|
||||||
// app.DebugPrintf(" - OK to send (%d ms since last)\n", thisTime - lastTime);
|
// app.DebugPrintf(" - OK to send (%d ms since last)\n", thisTime - lastTime);
|
||||||
// mapLastTime[connection->getNetworkPlayer()->GetUID().toString()] = thisTime;
|
// mapLastTime[connection->getNetworkPlayer()->GetUID().toString()] = thisTime;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// app.DebugPrintf(" - <NOT OK>\n");
|
// app.DebugPrintf(" - <NOT OK>\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (okToSend)
|
if (okToSend)
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -393,4 +391,4 @@ void ServerPlayerGameMode::setGameRules(GameRulesInstance *rules)
|
||||||
{
|
{
|
||||||
if(m_gameRules != nullptr) delete m_gameRules;
|
if(m_gameRules != nullptr) delete m_gameRules;
|
||||||
m_gameRules = rules;
|
m_gameRules = rules;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,20 @@ public:
|
||||||
static const int KEY_DROP = 'Q';
|
static const int KEY_DROP = 'Q';
|
||||||
static const int KEY_CRAFTING = 'C';
|
static const int KEY_CRAFTING = 'C';
|
||||||
static const int KEY_CRAFTING_ALT = 'R';
|
static const int KEY_CRAFTING_ALT = 'R';
|
||||||
|
static const int KEY_CHAT = 'T';
|
||||||
static const int KEY_CONFIRM = VK_RETURN;
|
static const int KEY_CONFIRM = VK_RETURN;
|
||||||
static const int KEY_CANCEL = VK_ESCAPE;
|
static const int KEY_CANCEL = VK_ESCAPE;
|
||||||
static const int KEY_PAUSE = VK_ESCAPE;
|
static const int KEY_PAUSE = VK_ESCAPE;
|
||||||
static const int KEY_THIRD_PERSON = VK_F5;
|
static const int KEY_TOGGLE_HUD = VK_F1;
|
||||||
static const int KEY_DEBUG_INFO = VK_F3;
|
static const int KEY_DEBUG_INFO = VK_F3;
|
||||||
static const int KEY_DEBUG_MENU = VK_F4;
|
static const int KEY_DEBUG_MENU = VK_F4;
|
||||||
|
static const int KEY_THIRD_PERSON = VK_F5;
|
||||||
|
static const int KEY_DEBUG_CONSOLE = VK_F6;
|
||||||
|
static const int KEY_HOST_SETTINGS = VK_F8;
|
||||||
|
static const int KEY_FULLSCREEN = VK_F11;
|
||||||
|
|
||||||
|
// todo: implement and shi
|
||||||
|
static const int KEY_SCREENSHOT = VK_F2;
|
||||||
|
|
||||||
void Init();
|
void Init();
|
||||||
void Tick();
|
void Tick();
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
StartDiscovery();
|
// Dedicated Server does not use LAN session discovery and therefore does not initiate discovery.
|
||||||
|
if (!g_Win64DedicatedServer)
|
||||||
|
{
|
||||||
|
StartDiscovery();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -512,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);
|
||||||
|
|
@ -546,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)
|
||||||
|
|
@ -557,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)
|
||||||
{
|
{
|
||||||
app.DebugPrintf("Win64 LAN: Rejecting connection, game not ready\n");
|
#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");
|
||||||
|
}
|
||||||
closesocket(clientSocket);
|
closesocket(clientSocket);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -568,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())
|
||||||
{
|
{
|
||||||
app.DebugPrintf("Win64 LAN: Rejecting connection, server at max players\n");
|
#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");
|
||||||
|
}
|
||||||
SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_ServerFull);
|
SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_ServerFull);
|
||||||
closesocket(clientSocket);
|
closesocket(clientSocket);
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -588,7 +661,16 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LeaveCriticalSection(&s_freeSmallIdLock);
|
LeaveCriticalSection(&s_freeSmallIdLock);
|
||||||
app.DebugPrintf("Win64 LAN: Server full, rejecting connection\n");
|
#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");
|
||||||
|
}
|
||||||
SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_ServerFull);
|
SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_ServerFull);
|
||||||
closesocket(clientSocket);
|
closesocket(clientSocket);
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -616,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);
|
||||||
|
|
||||||
app.DebugPrintf("Win64 LAN: Client connected, assigned smallId=%d\n", assignedSmallId);
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
EnterCriticalSection(&s_smallIdToSocketLock);
|
EnterCriticalSection(&s_smallIdToSocketLock);
|
||||||
s_smallIdToSocket[assignedSmallId] = clientSocket;
|
s_smallIdToSocket[assignedSmallId] = clientSocket;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,6 @@ static WINDOWPLACEMENT g_wpPrev = { sizeof(g_wpPrev) };
|
||||||
struct Win64LaunchOptions
|
struct Win64LaunchOptions
|
||||||
{
|
{
|
||||||
int screenMode;
|
int screenMode;
|
||||||
bool serverMode;
|
|
||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -207,13 +206,9 @@ static Win64LaunchOptions ParseLaunchOptions()
|
||||||
{
|
{
|
||||||
Win64LaunchOptions options = {};
|
Win64LaunchOptions options = {};
|
||||||
options.screenMode = 0;
|
options.screenMode = 0;
|
||||||
options.serverMode = false;
|
|
||||||
|
|
||||||
g_Win64MultiplayerJoin = false;
|
g_Win64MultiplayerJoin = false;
|
||||||
g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT;
|
g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT;
|
||||||
g_Win64DedicatedServer = false;
|
|
||||||
g_Win64DedicatedServerPort = WIN64_NET_DEFAULT_PORT;
|
|
||||||
g_Win64DedicatedServerBindIP[0] = 0;
|
|
||||||
|
|
||||||
int argc = 0;
|
int argc = 0;
|
||||||
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||||
|
|
@ -226,17 +221,6 @@ static Win64LaunchOptions ParseLaunchOptions()
|
||||||
options.screenMode = argv[1][0] - L'0';
|
options.screenMode = argv[1][0] - L'0';
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 1; i < argc; ++i)
|
|
||||||
{
|
|
||||||
if (_wcsicmp(argv[i], L"-server") == 0)
|
|
||||||
{
|
|
||||||
options.serverMode = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_Win64DedicatedServer = options.serverMode;
|
|
||||||
|
|
||||||
for (int i = 1; i < argc; ++i)
|
for (int i = 1; i < argc; ++i)
|
||||||
{
|
{
|
||||||
if (_wcsicmp(argv[i], L"-name") == 0 && (i + 1) < argc)
|
if (_wcsicmp(argv[i], L"-name") == 0 && (i + 1) < argc)
|
||||||
|
|
@ -247,15 +231,8 @@ static Win64LaunchOptions ParseLaunchOptions()
|
||||||
{
|
{
|
||||||
char ipBuf[256];
|
char ipBuf[256];
|
||||||
CopyWideArgToAnsi(argv[++i], ipBuf, sizeof(ipBuf));
|
CopyWideArgToAnsi(argv[++i], ipBuf, sizeof(ipBuf));
|
||||||
if (options.serverMode)
|
strncpy_s(g_Win64MultiplayerIP, sizeof(g_Win64MultiplayerIP), ipBuf, _TRUNCATE);
|
||||||
{
|
g_Win64MultiplayerJoin = true;
|
||||||
strncpy_s(g_Win64DedicatedServerBindIP, sizeof(g_Win64DedicatedServerBindIP), ipBuf, _TRUNCATE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
strncpy_s(g_Win64MultiplayerIP, sizeof(g_Win64MultiplayerIP), ipBuf, _TRUNCATE);
|
|
||||||
g_Win64MultiplayerJoin = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (_wcsicmp(argv[i], L"-port") == 0 && (i + 1) < argc)
|
else if (_wcsicmp(argv[i], L"-port") == 0 && (i + 1) < argc)
|
||||||
{
|
{
|
||||||
|
|
@ -263,10 +240,7 @@ static Win64LaunchOptions ParseLaunchOptions()
|
||||||
const long port = wcstol(argv[++i], &endPtr, 10);
|
const long port = wcstol(argv[++i], &endPtr, 10);
|
||||||
if (endPtr != argv[i] && *endPtr == 0 && port > 0 && port <= 65535)
|
if (endPtr != argv[i] && *endPtr == 0 && port > 0 && port <= 65535)
|
||||||
{
|
{
|
||||||
if (options.serverMode)
|
g_Win64MultiplayerPort = static_cast<int>(port);
|
||||||
g_Win64DedicatedServerPort = static_cast<int>(port);
|
|
||||||
else
|
|
||||||
g_Win64MultiplayerPort = static_cast<int>(port);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (_wcsicmp(argv[i], L"-fullscreen") == 0)
|
else if (_wcsicmp(argv[i], L"-fullscreen") == 0)
|
||||||
|
|
@ -277,36 +251,6 @@ static Win64LaunchOptions ParseLaunchOptions()
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL WINAPI HeadlessServerCtrlHandler(DWORD ctrlType)
|
|
||||||
{
|
|
||||||
switch (ctrlType)
|
|
||||||
{
|
|
||||||
case CTRL_C_EVENT:
|
|
||||||
case CTRL_BREAK_EVENT:
|
|
||||||
case CTRL_CLOSE_EVENT:
|
|
||||||
case CTRL_SHUTDOWN_EVENT:
|
|
||||||
app.m_bShutdown = true;
|
|
||||||
MinecraftServer::HaltServer();
|
|
||||||
return TRUE;
|
|
||||||
default:
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SetupHeadlessServerConsole()
|
|
||||||
{
|
|
||||||
if (AllocConsole())
|
|
||||||
{
|
|
||||||
FILE* stream = nullptr;
|
|
||||||
freopen_s(&stream, "CONIN$", "r", stdin);
|
|
||||||
freopen_s(&stream, "CONOUT$", "w", stdout);
|
|
||||||
freopen_s(&stream, "CONOUT$", "w", stderr);
|
|
||||||
SetConsoleTitleA("Minecraft Server");
|
|
||||||
}
|
|
||||||
|
|
||||||
SetConsoleCtrlHandler(HeadlessServerCtrlHandler, TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DefineActions(void)
|
void DefineActions(void)
|
||||||
{
|
{
|
||||||
// The app needs to define the actions required, and the possible mappings for these
|
// The app needs to define the actions required, and the possible mappings for these
|
||||||
|
|
@ -1350,161 +1294,6 @@ static Minecraft* InitialiseMinecraftRuntime()
|
||||||
return pMinecraft;
|
return pMinecraft;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int HeadlessServerConsoleThreadProc(void* lpParameter)
|
|
||||||
{
|
|
||||||
UNREFERENCED_PARAMETER(lpParameter);
|
|
||||||
|
|
||||||
std::string line;
|
|
||||||
while (!app.m_bShutdown)
|
|
||||||
{
|
|
||||||
if (!std::getline(std::cin, line))
|
|
||||||
{
|
|
||||||
if (std::cin.eof())
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cin.clear();
|
|
||||||
Sleep(50);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
wstring command = trimString(convStringToWstring(line));
|
|
||||||
if (command.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
MinecraftServer* server = MinecraftServer::getInstance();
|
|
||||||
if (server != nullptr)
|
|
||||||
{
|
|
||||||
server->handleConsoleInput(command, server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int RunHeadlessServer()
|
|
||||||
{
|
|
||||||
SetupHeadlessServerConsole();
|
|
||||||
|
|
||||||
Settings serverSettings(new File(L"server.properties"));
|
|
||||||
const wstring configuredBindIp = serverSettings.getString(L"server-ip", L"");
|
|
||||||
|
|
||||||
const char* bindIp = "*";
|
|
||||||
if (g_Win64DedicatedServerBindIP[0] != 0)
|
|
||||||
{
|
|
||||||
bindIp = g_Win64DedicatedServerBindIP;
|
|
||||||
}
|
|
||||||
else if (!configuredBindIp.empty())
|
|
||||||
{
|
|
||||||
bindIp = wstringtochararray(configuredBindIp);
|
|
||||||
}
|
|
||||||
|
|
||||||
const int port = g_Win64DedicatedServerPort > 0 ? g_Win64DedicatedServerPort : serverSettings.getInt(L"server-port", WIN64_NET_DEFAULT_PORT);
|
|
||||||
|
|
||||||
printf("Starting headless server on %s:%d\n", bindIp, port);
|
|
||||||
fflush(stdout);
|
|
||||||
|
|
||||||
const Minecraft* pMinecraft = InitialiseMinecraftRuntime();
|
|
||||||
if (pMinecraft == nullptr)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to initialise the Minecraft runtime.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
app.SetGameHostOption(eGameHostOption_Difficulty, serverSettings.getInt(L"difficulty", 1));
|
|
||||||
app.SetGameHostOption(eGameHostOption_Gamertags, 1);
|
|
||||||
app.SetGameHostOption(eGameHostOption_GameType, serverSettings.getInt(L"gamemode", 0));
|
|
||||||
app.SetGameHostOption(eGameHostOption_LevelType, 0);
|
|
||||||
app.SetGameHostOption(eGameHostOption_Structures, serverSettings.getBoolean(L"generate-structures", true) ? 1 : 0);
|
|
||||||
app.SetGameHostOption(eGameHostOption_BonusChest, serverSettings.getBoolean(L"bonus-chest", false) ? 1 : 0);
|
|
||||||
app.SetGameHostOption(eGameHostOption_PvP, serverSettings.getBoolean(L"pvp", true) ? 1 : 0);
|
|
||||||
app.SetGameHostOption(eGameHostOption_TrustPlayers, serverSettings.getBoolean(L"trust-players", true) ? 1 : 0);
|
|
||||||
app.SetGameHostOption(eGameHostOption_FireSpreads, serverSettings.getBoolean(L"fire-spreads", true) ? 1 : 0);
|
|
||||||
app.SetGameHostOption(eGameHostOption_TNT, serverSettings.getBoolean(L"tnt", true) ? 1 : 0);
|
|
||||||
app.SetGameHostOption(eGameHostOption_HostCanFly, 1);
|
|
||||||
app.SetGameHostOption(eGameHostOption_HostCanChangeHunger, 1);
|
|
||||||
app.SetGameHostOption(eGameHostOption_HostCanBeInvisible, 1);
|
|
||||||
app.SetGameHostOption(eGameHostOption_MobGriefing, 1);
|
|
||||||
app.SetGameHostOption(eGameHostOption_KeepInventory, 0);
|
|
||||||
app.SetGameHostOption(eGameHostOption_DoMobSpawning, 1);
|
|
||||||
app.SetGameHostOption(eGameHostOption_DoMobLoot, 1);
|
|
||||||
app.SetGameHostOption(eGameHostOption_DoTileDrops, 1);
|
|
||||||
app.SetGameHostOption(eGameHostOption_NaturalRegeneration, 1);
|
|
||||||
app.SetGameHostOption(eGameHostOption_DoDaylightCycle, 1);
|
|
||||||
|
|
||||||
MinecraftServer::resetFlags();
|
|
||||||
g_NetworkManager.HostGame(0, false, true, MINECRAFT_NET_MAX_PLAYERS, 0);
|
|
||||||
|
|
||||||
if (!WinsockNetLayer::IsActive())
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to bind the server socket on %s:%d.\n", bindIp, port);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_NetworkManager.FakeLocalPlayerJoined();
|
|
||||||
|
|
||||||
NetworkGameInitData* param = new NetworkGameInitData();
|
|
||||||
param->seed = 0;
|
|
||||||
param->settings = app.GetGameHostOption(eGameHostOption_All);
|
|
||||||
|
|
||||||
g_NetworkManager.ServerStoppedCreate(true);
|
|
||||||
g_NetworkManager.ServerReadyCreate(true);
|
|
||||||
|
|
||||||
C4JThread* thread = new C4JThread(&CGameNetworkManager::ServerThreadProc, param, "Server", 256 * 1024);
|
|
||||||
thread->SetProcessor(CPU_CORE_SERVER);
|
|
||||||
thread->Run();
|
|
||||||
|
|
||||||
g_NetworkManager.ServerReadyWait();
|
|
||||||
g_NetworkManager.ServerReadyDestroy();
|
|
||||||
|
|
||||||
if (MinecraftServer::serverHalted())
|
|
||||||
{
|
|
||||||
fprintf(stderr, "The server halted during startup.\n");
|
|
||||||
g_NetworkManager.LeaveGame(false);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
app.SetGameStarted(true);
|
|
||||||
g_NetworkManager.DoWork();
|
|
||||||
|
|
||||||
printf("Server ready on %s:%d\n", bindIp, port);
|
|
||||||
printf("Type 'help' for server commands.\n");
|
|
||||||
fflush(stdout);
|
|
||||||
|
|
||||||
C4JThread* consoleThread = new C4JThread(&HeadlessServerConsoleThreadProc, nullptr, "Server console", 128 * 1024);
|
|
||||||
consoleThread->Run();
|
|
||||||
|
|
||||||
MSG msg = { 0 };
|
|
||||||
while (WM_QUIT != msg.message && !app.m_bShutdown && !MinecraftServer::serverHalted())
|
|
||||||
{
|
|
||||||
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
|
|
||||||
{
|
|
||||||
TranslateMessage(&msg);
|
|
||||||
DispatchMessage(&msg);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
app.UpdateTime();
|
|
||||||
ProfileManager.Tick();
|
|
||||||
StorageManager.Tick();
|
|
||||||
RenderManager.Tick();
|
|
||||||
ui.tick();
|
|
||||||
g_NetworkManager.DoWork();
|
|
||||||
app.HandleXuiActions();
|
|
||||||
|
|
||||||
Sleep(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Stopping server...\n");
|
|
||||||
fflush(stdout);
|
|
||||||
|
|
||||||
app.m_bShutdown = true;
|
|
||||||
MinecraftServer::HaltServer();
|
|
||||||
g_NetworkManager.LeaveGame(false);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||||
_In_opt_ HINSTANCE hPrevInstance,
|
_In_opt_ HINSTANCE hPrevInstance,
|
||||||
_In_ LPTSTR lpCmdLine,
|
_In_ LPTSTR lpCmdLine,
|
||||||
|
|
@ -1566,11 +1355,8 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||||
const Win64LaunchOptions launchOptions = ParseLaunchOptions();
|
const Win64LaunchOptions launchOptions = ParseLaunchOptions();
|
||||||
ApplyScreenMode(launchOptions.screenMode);
|
ApplyScreenMode(launchOptions.screenMode);
|
||||||
|
|
||||||
// Ensure uid.dat exists from startup in client mode (before any multiplayer/login path).
|
// Ensure uid.dat exists from startup (before any multiplayer/login path).
|
||||||
if (!launchOptions.serverMode)
|
Win64Xuid::ResolvePersistentXuid();
|
||||||
{
|
|
||||||
Win64Xuid::ResolvePersistentXuid();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no username, let's fall back
|
// If no username, let's fall back
|
||||||
if (g_Win64Username[0] == 0)
|
if (g_Win64Username[0] == 0)
|
||||||
|
|
@ -1651,7 +1437,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||||
MyRegisterClass(hInstance);
|
MyRegisterClass(hInstance);
|
||||||
|
|
||||||
// Perform application initialization:
|
// Perform application initialization:
|
||||||
if (!InitInstance (hInstance, launchOptions.serverMode ? SW_HIDE : nCmdShow))
|
if (!InitInstance (hInstance, nCmdShow))
|
||||||
{
|
{
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
@ -1670,13 +1456,6 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||||
ToggleFullscreen();
|
ToggleFullscreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (launchOptions.serverMode)
|
|
||||||
{
|
|
||||||
const int serverResult = RunHeadlessServer();
|
|
||||||
CleanupDevice();
|
|
||||||
return serverResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// Main message loop
|
// Main message loop
|
||||||
MSG msg = {0};
|
MSG msg = {0};
|
||||||
|
|
@ -1986,7 +1765,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||||
}
|
}
|
||||||
|
|
||||||
// F1 toggles the HUD
|
// F1 toggles the HUD
|
||||||
if (g_KBMInput.IsKeyPressed(VK_F1))
|
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_TOGGLE_HUD))
|
||||||
{
|
{
|
||||||
const int primaryPad = ProfileManager.GetPrimaryPad();
|
const int primaryPad = ProfileManager.GetPrimaryPad();
|
||||||
const unsigned char displayHud = app.GetGameSettings(primaryPad, eGameSetting_DisplayHUD);
|
const unsigned char displayHud = app.GetGameSettings(primaryPad, eGameSetting_DisplayHUD);
|
||||||
|
|
@ -1995,7 +1774,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||||
}
|
}
|
||||||
|
|
||||||
// F3 toggles onscreen debug info
|
// F3 toggles onscreen debug info
|
||||||
if (g_KBMInput.IsKeyPressed(VK_F3))
|
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_DEBUG_INFO))
|
||||||
{
|
{
|
||||||
if (const Minecraft* pMinecraft = Minecraft::GetInstance())
|
if (const Minecraft* pMinecraft = Minecraft::GetInstance())
|
||||||
{
|
{
|
||||||
|
|
@ -2008,7 +1787,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||||
|
|
||||||
#ifdef _DEBUG_MENUS_ENABLED
|
#ifdef _DEBUG_MENUS_ENABLED
|
||||||
// F6 Open debug console
|
// F6 Open debug console
|
||||||
if (g_KBMInput.IsKeyPressed(VK_F6))
|
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_DEBUG_CONSOLE))
|
||||||
{
|
{
|
||||||
static bool s_debugConsole = false;
|
static bool s_debugConsole = false;
|
||||||
s_debugConsole = !s_debugConsole;
|
s_debugConsole = !s_debugConsole;
|
||||||
|
|
@ -2016,14 +1795,14 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// F11 Toggle fullscreen
|
// toggle fullscreen
|
||||||
if (g_KBMInput.IsKeyPressed(VK_F11))
|
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_FULLSCREEN))
|
||||||
{
|
{
|
||||||
ToggleFullscreen();
|
ToggleFullscreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TAB opens game info menu. - Vvis :3 - Updated by detectiveren
|
// TAB opens game info menu. - Vvis :3 - Updated by detectiveren
|
||||||
if (g_KBMInput.IsKeyPressed(VK_TAB) && !ui.GetMenuDisplayed(0))
|
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_HOST_SETTINGS) && !ui.GetMenuDisplayed(0))
|
||||||
{
|
{
|
||||||
if (Minecraft* pMinecraft = Minecraft::GetInstance())
|
if (Minecraft* pMinecraft = Minecraft::GetInstance())
|
||||||
{
|
{
|
||||||
|
|
@ -2035,7 +1814,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open chat
|
// Open chat
|
||||||
if (g_KBMInput.IsKeyPressed('T') && app.GetGameStarted() && !ui.GetMenuDisplayed(0) && pMinecraft->screen == NULL)
|
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_CHAT) && app.GetGameStarted() && !ui.GetMenuDisplayed(0) && pMinecraft->screen == NULL)
|
||||||
{
|
{
|
||||||
g_KBMInput.ClearCharBuffer();
|
g_KBMInput.ClearCharBuffer();
|
||||||
pMinecraft->setScreen(new ChatScreen());
|
pMinecraft->setScreen(new ChatScreen());
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -8811,4 +8811,7 @@ All Ender Chests in a world are linked. Items placed into an Ender Chest are acc
|
||||||
<data name="IDS_TOOLTIPS_CURE">
|
<data name="IDS_TOOLTIPS_CURE">
|
||||||
<value>Cure</value>
|
<value>Cure</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="IDS_WINDOWS_EXIT">
|
||||||
|
<value>Exit Minecraft</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
// Auto-generated by StringTable builder — do not edit manually.
|
// Auto-generated by StringTable builder — do not edit manually.
|
||||||
// Source language: en-US
|
// Source language: en-US
|
||||||
// Total strings: 2286
|
// Total strings: 2287
|
||||||
|
|
||||||
#define IDS_NULL 0
|
#define IDS_NULL 0
|
||||||
#define IDS_OK 1
|
#define IDS_OK 1
|
||||||
|
|
@ -2165,127 +2165,128 @@
|
||||||
#define IDS_TUTORIAL_TASK_MINECART_PUSHING 2159
|
#define IDS_TUTORIAL_TASK_MINECART_PUSHING 2159
|
||||||
#define IDS_CONNECTION_FAILED_NO_SD_SPLITSCREEN 2160
|
#define IDS_CONNECTION_FAILED_NO_SD_SPLITSCREEN 2160
|
||||||
#define IDS_TOOLTIPS_CURE 2161
|
#define IDS_TOOLTIPS_CURE 2161
|
||||||
#define IDS_LANG_SYSTEM 2162
|
#define IDS_WINDOWS_EXIT 2162
|
||||||
#define IDS_LANG_ENGLISH 2163
|
#define IDS_LANG_SYSTEM 2163
|
||||||
#define IDS_LANG_GERMAN 2164
|
#define IDS_LANG_ENGLISH 2164
|
||||||
#define IDS_LANG_SPANISH 2165
|
#define IDS_LANG_GERMAN 2165
|
||||||
#define IDS_LANG_SPANISH_SPAIN 2166
|
#define IDS_LANG_SPANISH 2166
|
||||||
#define IDS_LANG_SPANISH_LATIN_AMERICA 2167
|
#define IDS_LANG_SPANISH_SPAIN 2167
|
||||||
#define IDS_LANG_FRENCH 2168
|
#define IDS_LANG_SPANISH_LATIN_AMERICA 2168
|
||||||
#define IDS_LANG_ITALIAN 2169
|
#define IDS_LANG_FRENCH 2169
|
||||||
#define IDS_LANG_PORTUGUESE 2170
|
#define IDS_LANG_ITALIAN 2170
|
||||||
#define IDS_LANG_PORTUGUESE_PORTUGAL 2171
|
#define IDS_LANG_PORTUGUESE 2171
|
||||||
#define IDS_LANG_PORTUGUESE_BRAZIL 2172
|
#define IDS_LANG_PORTUGUESE_PORTUGAL 2172
|
||||||
#define IDS_LANG_JAPANESE 2173
|
#define IDS_LANG_PORTUGUESE_BRAZIL 2173
|
||||||
#define IDS_LANG_KOREAN 2174
|
#define IDS_LANG_JAPANESE 2174
|
||||||
#define IDS_LANG_CHINESE_TRADITIONAL 2175
|
#define IDS_LANG_KOREAN 2175
|
||||||
#define IDS_LANG_CHINESE_SIMPLIFIED 2176
|
#define IDS_LANG_CHINESE_TRADITIONAL 2176
|
||||||
#define IDS_LANG_DANISH 2177
|
#define IDS_LANG_CHINESE_SIMPLIFIED 2177
|
||||||
#define IDS_LANG_FINISH 2178
|
#define IDS_LANG_DANISH 2178
|
||||||
#define IDS_LANG_DUTCH 2179
|
#define IDS_LANG_FINISH 2179
|
||||||
#define IDS_LANG_POLISH 2180
|
#define IDS_LANG_DUTCH 2180
|
||||||
#define IDS_LANG_RUSSIAN 2181
|
#define IDS_LANG_POLISH 2181
|
||||||
#define IDS_LANG_SWEDISH 2182
|
#define IDS_LANG_RUSSIAN 2182
|
||||||
#define IDS_LANG_NORWEGIAN 2183
|
#define IDS_LANG_SWEDISH 2183
|
||||||
#define IDS_LANG_GREEK 2184
|
#define IDS_LANG_NORWEGIAN 2184
|
||||||
#define IDS_LANG_TURKISH 2185
|
#define IDS_LANG_GREEK 2185
|
||||||
#define IDS_LEADERBOARD_KILLS_EASY 2186
|
#define IDS_LANG_TURKISH 2186
|
||||||
#define IDS_LEADERBOARD_KILLS_NORMAL 2187
|
#define IDS_LEADERBOARD_KILLS_EASY 2187
|
||||||
#define IDS_LEADERBOARD_KILLS_HARD 2188
|
#define IDS_LEADERBOARD_KILLS_NORMAL 2188
|
||||||
#define IDS_LEADERBOARD_MINING_BLOCKS_PEACEFUL 2189
|
#define IDS_LEADERBOARD_KILLS_HARD 2189
|
||||||
#define IDS_LEADERBOARD_MINING_BLOCKS_EASY 2190
|
#define IDS_LEADERBOARD_MINING_BLOCKS_PEACEFUL 2190
|
||||||
#define IDS_LEADERBOARD_MINING_BLOCKS_NORMAL 2191
|
#define IDS_LEADERBOARD_MINING_BLOCKS_EASY 2191
|
||||||
#define IDS_LEADERBOARD_MINING_BLOCKS_HARD 2192
|
#define IDS_LEADERBOARD_MINING_BLOCKS_NORMAL 2192
|
||||||
#define IDS_LEADERBOARD_FARMING_PEACEFUL 2193
|
#define IDS_LEADERBOARD_MINING_BLOCKS_HARD 2193
|
||||||
#define IDS_LEADERBOARD_FARMING_EASY 2194
|
#define IDS_LEADERBOARD_FARMING_PEACEFUL 2194
|
||||||
#define IDS_LEADERBOARD_FARMING_NORMAL 2195
|
#define IDS_LEADERBOARD_FARMING_EASY 2195
|
||||||
#define IDS_LEADERBOARD_FARMING_HARD 2196
|
#define IDS_LEADERBOARD_FARMING_NORMAL 2196
|
||||||
#define IDS_LEADERBOARD_TRAVELLING_PEACEFUL 2197
|
#define IDS_LEADERBOARD_FARMING_HARD 2197
|
||||||
#define IDS_LEADERBOARD_TRAVELLING_EASY 2198
|
#define IDS_LEADERBOARD_TRAVELLING_PEACEFUL 2198
|
||||||
#define IDS_LEADERBOARD_TRAVELLING_NORMAL 2199
|
#define IDS_LEADERBOARD_TRAVELLING_EASY 2199
|
||||||
#define IDS_LEADERBOARD_TRAVELLING_HARD 2200
|
#define IDS_LEADERBOARD_TRAVELLING_NORMAL 2200
|
||||||
#define IDS_TIPS_GAMETIP_0 2201
|
#define IDS_LEADERBOARD_TRAVELLING_HARD 2201
|
||||||
#define IDS_TIPS_GAMETIP_1 2202
|
#define IDS_TIPS_GAMETIP_0 2202
|
||||||
#define IDS_TIPS_GAMETIP_48 2203
|
#define IDS_TIPS_GAMETIP_1 2203
|
||||||
#define IDS_TIPS_GAMETIP_44 2204
|
#define IDS_TIPS_GAMETIP_48 2204
|
||||||
#define IDS_TIPS_GAMETIP_45 2205
|
#define IDS_TIPS_GAMETIP_44 2205
|
||||||
#define IDS_TIPS_TRIVIA_4 2206
|
#define IDS_TIPS_GAMETIP_45 2206
|
||||||
#define IDS_TIPS_TRIVIA_17 2207
|
#define IDS_TIPS_TRIVIA_4 2207
|
||||||
#define IDS_HOW_TO_PLAY_MULTIPLAYER 2208
|
#define IDS_TIPS_TRIVIA_17 2208
|
||||||
#define IDS_HOW_TO_PLAY_SOCIALMEDIA 2209
|
#define IDS_HOW_TO_PLAY_MULTIPLAYER 2209
|
||||||
#define IDS_HOW_TO_PLAY_CREATIVE 2210
|
#define IDS_HOW_TO_PLAY_SOCIALMEDIA 2210
|
||||||
#define IDS_TUTORIAL_TASK_FLY 2211
|
#define IDS_HOW_TO_PLAY_CREATIVE 2211
|
||||||
#define IDS_TOOLTIPS_SELECTDEVICE 2212
|
#define IDS_TUTORIAL_TASK_FLY 2212
|
||||||
#define IDS_TOOLTIPS_CHANGEDEVICE 2213
|
#define IDS_TOOLTIPS_SELECTDEVICE 2213
|
||||||
#define IDS_TOOLTIPS_VIEW_GAMERCARD 2214
|
#define IDS_TOOLTIPS_CHANGEDEVICE 2214
|
||||||
#define IDS_TOOLTIPS_VIEW_GAMERPROFILE 2215
|
#define IDS_TOOLTIPS_VIEW_GAMERCARD 2215
|
||||||
#define IDS_TOOLTIPS_INVITE_PARTY 2216
|
#define IDS_TOOLTIPS_VIEW_GAMERPROFILE 2216
|
||||||
#define IDS_CONFIRM_START_CREATIVE 2217
|
#define IDS_TOOLTIPS_INVITE_PARTY 2217
|
||||||
#define IDS_CONFIRM_START_SAVEDINCREATIVE 2218
|
#define IDS_CONFIRM_START_CREATIVE 2218
|
||||||
#define IDS_CONFIRM_START_SAVEDINCREATIVE_CONTINUE 2219
|
#define IDS_CONFIRM_START_SAVEDINCREATIVE 2219
|
||||||
#define IDS_CONFIRM_START_HOST_PRIVILEGES 2220
|
#define IDS_CONFIRM_START_SAVEDINCREATIVE_CONTINUE 2220
|
||||||
#define IDS_CONNECTION_LOST_LIVE 2221
|
#define IDS_CONFIRM_START_HOST_PRIVILEGES 2221
|
||||||
#define IDS_CONNECTION_LOST_LIVE_NO_EXIT 2222
|
#define IDS_CONNECTION_LOST_LIVE 2222
|
||||||
#define IDS_AWARD_AVATAR1 2223
|
#define IDS_CONNECTION_LOST_LIVE_NO_EXIT 2223
|
||||||
#define IDS_AWARD_AVATAR2 2224
|
#define IDS_AWARD_AVATAR1 2224
|
||||||
#define IDS_AWARD_AVATAR3 2225
|
#define IDS_AWARD_AVATAR2 2225
|
||||||
#define IDS_AWARD_THEME 2226
|
#define IDS_AWARD_AVATAR3 2226
|
||||||
#define IDS_UNLOCK_ACHIEVEMENT_TEXT 2227
|
#define IDS_AWARD_THEME 2227
|
||||||
#define IDS_UNLOCK_AVATAR_TEXT 2228
|
#define IDS_UNLOCK_ACHIEVEMENT_TEXT 2228
|
||||||
#define IDS_UNLOCK_GAMERPIC_TEXT 2229
|
#define IDS_UNLOCK_AVATAR_TEXT 2229
|
||||||
#define IDS_UNLOCK_THEME_TEXT 2230
|
#define IDS_UNLOCK_GAMERPIC_TEXT 2230
|
||||||
#define IDS_UNLOCK_ACCEPT_INVITE 2231
|
#define IDS_UNLOCK_THEME_TEXT 2231
|
||||||
#define IDS_UNLOCK_GUEST_TEXT 2232
|
#define IDS_UNLOCK_ACCEPT_INVITE 2232
|
||||||
#define IDS_LEADERBOARD_GAMERTAG 2233
|
#define IDS_UNLOCK_GUEST_TEXT 2233
|
||||||
#define IDS_GROUPNAME_POTIONS_480 2234
|
#define IDS_LEADERBOARD_GAMERTAG 2234
|
||||||
#define IDS_RETURNEDTOTITLESCREEN_TEXT 2235
|
#define IDS_GROUPNAME_POTIONS_480 2235
|
||||||
#define IDS_TRIALOVER_TEXT 2236
|
#define IDS_RETURNEDTOTITLESCREEN_TEXT 2236
|
||||||
#define IDS_FATAL_ERROR_TEXT 2237
|
#define IDS_TRIALOVER_TEXT 2237
|
||||||
#define IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT 2238
|
#define IDS_FATAL_ERROR_TEXT 2238
|
||||||
#define IDS_NO_MULTIPLAYER_PRIVILEGE_HOST_TEXT 2239
|
#define IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT 2239
|
||||||
#define IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL 2240
|
#define IDS_NO_MULTIPLAYER_PRIVILEGE_HOST_TEXT 2240
|
||||||
#define IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL 2241
|
#define IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL 2241
|
||||||
#define IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_REMOTE 2242
|
#define IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL 2242
|
||||||
#define IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_CREATE 2243
|
#define IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_REMOTE 2243
|
||||||
#define IDS_SAVE_ICON_MESSAGE 2244
|
#define IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_CREATE 2244
|
||||||
#define IDS_GAMEOPTION_HOST_PRIVILEGES 2245
|
#define IDS_SAVE_ICON_MESSAGE 2245
|
||||||
#define IDS_CHECKBOX_DISPLAY_SPLITSCREENGAMERTAGS 2246
|
#define IDS_GAMEOPTION_HOST_PRIVILEGES 2246
|
||||||
#define IDS_ACHIEVEMENTS 2247
|
#define IDS_CHECKBOX_DISPLAY_SPLITSCREENGAMERTAGS 2247
|
||||||
#define IDS_LABEL_GAMERTAGS 2248
|
#define IDS_ACHIEVEMENTS 2248
|
||||||
#define IDS_IN_GAME_GAMERTAGS 2249
|
#define IDS_LABEL_GAMERTAGS 2249
|
||||||
#define IDS_SOCIAL_DEFAULT_DESCRIPTION 2250
|
#define IDS_IN_GAME_GAMERTAGS 2250
|
||||||
#define IDS_TITLE_UPDATE_NAME 2251
|
#define IDS_SOCIAL_DEFAULT_DESCRIPTION 2251
|
||||||
#define IDS_PLATFORM_NAME 2252
|
#define IDS_TITLE_UPDATE_NAME 2252
|
||||||
#define IDS_BACK_BUTTON 2253
|
#define IDS_PLATFORM_NAME 2253
|
||||||
#define IDS_HOST_OPTION_DISABLES_ACHIEVEMENTS 2254
|
#define IDS_BACK_BUTTON 2254
|
||||||
#define IDS_KICK_PLAYER_DESCRIPTION 2255
|
#define IDS_HOST_OPTION_DISABLES_ACHIEVEMENTS 2255
|
||||||
#define IDS_USING_TRIAL_TEXUREPACK_WARNING 2256
|
#define IDS_KICK_PLAYER_DESCRIPTION 2256
|
||||||
#define IDS_WORLD_SIZE_TITLE_SMALL 2257
|
#define IDS_USING_TRIAL_TEXUREPACK_WARNING 2257
|
||||||
#define IDS_WORLD_SIZE_TITLE_MEDIUM 2258
|
#define IDS_WORLD_SIZE_TITLE_SMALL 2258
|
||||||
#define IDS_WORLD_SIZE_TITLE_LARGE 2259
|
#define IDS_WORLD_SIZE_TITLE_MEDIUM 2259
|
||||||
#define IDS_WORLD_SIZE_TITLE_CLASSIC 2260
|
#define IDS_WORLD_SIZE_TITLE_LARGE 2260
|
||||||
#define IDS_WORLD_SIZE 2261
|
#define IDS_WORLD_SIZE_TITLE_CLASSIC 2261
|
||||||
#define IDS_GAMEOPTION_WORLD_SIZE 2262
|
#define IDS_WORLD_SIZE 2262
|
||||||
#define IDS_DISABLE_SAVING 2263
|
#define IDS_GAMEOPTION_WORLD_SIZE 2263
|
||||||
#define IDS_GAMEOPTION_DISABLE_SAVING 2264
|
#define IDS_DISABLE_SAVING 2264
|
||||||
#define IDS_RICHPRESENCE_GAMESTATE 2265
|
#define IDS_GAMEOPTION_DISABLE_SAVING 2265
|
||||||
#define IDS_RICHPRESENCE_IDLE 2266
|
#define IDS_RICHPRESENCE_GAMESTATE 2266
|
||||||
#define IDS_RICHPRESENCE_MENUS 2267
|
#define IDS_RICHPRESENCE_IDLE 2267
|
||||||
#define IDS_RICHPRESENCE_MULTIPLAYER 2268
|
#define IDS_RICHPRESENCE_MENUS 2268
|
||||||
#define IDS_RICHPRESENCE_MULTIPLAYEROFFLINE 2269
|
#define IDS_RICHPRESENCE_MULTIPLAYER 2269
|
||||||
#define IDS_RICHPRESENCE_MULTIPLAYER_1P 2270
|
#define IDS_RICHPRESENCE_MULTIPLAYEROFFLINE 2270
|
||||||
#define IDS_RICHPRESENCE_MULTIPLAYER_1POFFLINE 2271
|
#define IDS_RICHPRESENCE_MULTIPLAYER_1P 2271
|
||||||
#define IDS_RICHPRESENCESTATE_BLANK 2272
|
#define IDS_RICHPRESENCE_MULTIPLAYER_1POFFLINE 2272
|
||||||
#define IDS_RICHPRESENCESTATE_RIDING_PIG 2273
|
#define IDS_RICHPRESENCESTATE_BLANK 2273
|
||||||
#define IDS_RICHPRESENCESTATE_RIDING_MINECART 2274
|
#define IDS_RICHPRESENCESTATE_RIDING_PIG 2274
|
||||||
#define IDS_RICHPRESENCESTATE_BOATING 2275
|
#define IDS_RICHPRESENCESTATE_RIDING_MINECART 2275
|
||||||
#define IDS_RICHPRESENCESTATE_FISHING 2276
|
#define IDS_RICHPRESENCESTATE_BOATING 2276
|
||||||
#define IDS_RICHPRESENCESTATE_CRAFTING 2277
|
#define IDS_RICHPRESENCESTATE_FISHING 2277
|
||||||
#define IDS_RICHPRESENCESTATE_FORGING 2278
|
#define IDS_RICHPRESENCESTATE_CRAFTING 2278
|
||||||
#define IDS_RICHPRESENCESTATE_NETHER 2279
|
#define IDS_RICHPRESENCESTATE_FORGING 2279
|
||||||
#define IDS_RICHPRESENCESTATE_CD 2280
|
#define IDS_RICHPRESENCESTATE_NETHER 2280
|
||||||
#define IDS_RICHPRESENCESTATE_MAP 2281
|
#define IDS_RICHPRESENCESTATE_CD 2281
|
||||||
#define IDS_RICHPRESENCESTATE_ENCHANTING 2282
|
#define IDS_RICHPRESENCESTATE_MAP 2282
|
||||||
#define IDS_RICHPRESENCESTATE_BREWING 2283
|
#define IDS_RICHPRESENCESTATE_ENCHANTING 2283
|
||||||
#define IDS_RICHPRESENCESTATE_ANVIL 2284
|
#define IDS_RICHPRESENCESTATE_BREWING 2284
|
||||||
#define IDS_RICHPRESENCESTATE_TRADING 2285
|
#define IDS_RICHPRESENCESTATE_ANVIL 2285
|
||||||
|
#define IDS_RICHPRESENCESTATE_TRADING 2286
|
||||||
|
|
|
||||||
460
Minecraft.Server/Access/Access.cpp
Normal file
460
Minecraft.Server/Access/Access.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
Minecraft.Server/Access/Access.h
Normal file
47
Minecraft.Server/Access/Access.h
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
631
Minecraft.Server/Access/BanManager.cpp
Normal file
631
Minecraft.Server/Access/BanManager.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
106
Minecraft.Server/Access/BanManager.h
Normal file
106
Minecraft.Server/Access/BanManager.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
297
Minecraft.Server/Access/WhitelistManager.cpp
Normal file
297
Minecraft.Server/Access/WhitelistManager.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
Minecraft.Server/Access/WhitelistManager.h
Normal file
64
Minecraft.Server/Access/WhitelistManager.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
105
Minecraft.Server/Common/AccessStorageUtils.h
Normal file
105
Minecraft.Server/Common/AccessStorageUtils.h
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
146
Minecraft.Server/Common/FileUtils.cpp
Normal file
146
Minecraft.Server/Common/FileUtils.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
Minecraft.Server/Common/FileUtils.h
Normal file
25
Minecraft.Server/Common/FileUtils.h
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
29
Minecraft.Server/Common/NetworkUtils.h
Normal file
29
Minecraft.Server/Common/NetworkUtils.h
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
212
Minecraft.Server/Common/StringUtils.cpp
Normal file
212
Minecraft.Server/Common/StringUtils.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
23
Minecraft.Server/Common/StringUtils.h
Normal file
23
Minecraft.Server/Common/StringUtils.h
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
44
Minecraft.Server/Console/ServerCli.cpp
Normal file
44
Minecraft.Server/Console/ServerCli.cpp
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
Minecraft.Server/Console/ServerCli.h
Normal file
50
Minecraft.Server/Console/ServerCli.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
395
Minecraft.Server/Console/ServerCliEngine.cpp
Normal file
395
Minecraft.Server/Console/ServerCliEngine.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
127
Minecraft.Server/Console/ServerCliEngine.h
Normal file
127
Minecraft.Server/Console/ServerCliEngine.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
285
Minecraft.Server/Console/ServerCliInput.cpp
Normal file
285
Minecraft.Server/Console/ServerCliInput.cpp
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
Minecraft.Server/Console/ServerCliInput.h
Normal file
63
Minecraft.Server/Console/ServerCliInput.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
116
Minecraft.Server/Console/ServerCliParser.cpp
Normal file
116
Minecraft.Server/Console/ServerCliParser.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
63
Minecraft.Server/Console/ServerCliParser.h
Normal file
63
Minecraft.Server/Console/ServerCliParser.h
Normal 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);
|
||||||
|
};
|
||||||
|
}
|
||||||
99
Minecraft.Server/Console/ServerCliRegistry.cpp
Normal file
99
Minecraft.Server/Console/ServerCliRegistry.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
58
Minecraft.Server/Console/ServerCliRegistry.h
Normal file
58
Minecraft.Server/Console/ServerCliRegistry.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
39
Minecraft.Server/Console/commands/CommandParsing.h
Normal file
39
Minecraft.Server/Console/commands/CommandParsing.h
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
Minecraft.Server/Console/commands/IServerCliCommand.h
Normal file
50
Minecraft.Server/Console/commands/IServerCliCommand.h
Normal 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
171
Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.cpp
Normal file
171
Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
19
Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.h
Normal file
19
Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
136
Minecraft.Server/Console/commands/ban-list/CliCommandBanList.cpp
Normal file
136
Minecraft.Server/Console/commands/ban-list/CliCommandBanList.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
145
Minecraft.Server/Console/commands/ban/CliCommandBan.cpp
Normal file
145
Minecraft.Server/Console/commands/ban/CliCommandBan.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
20
Minecraft.Server/Console/commands/ban/CliCommandBan.h
Normal file
20
Minecraft.Server/Console/commands/ban/CliCommandBan.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
103
Minecraft.Server/Console/commands/give/CliCommandGive.cpp
Normal file
103
Minecraft.Server/Console/commands/give/CliCommandGive.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Minecraft.Server/Console/commands/give/CliCommandGive.h
Normal file
16
Minecraft.Server/Console/commands/give/CliCommandGive.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
46
Minecraft.Server/Console/commands/help/CliCommandHelp.cpp
Normal file
46
Minecraft.Server/Console/commands/help/CliCommandHelp.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
17
Minecraft.Server/Console/commands/help/CliCommandHelp.h
Normal file
17
Minecraft.Server/Console/commands/help/CliCommandHelp.h
Normal 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);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
64
Minecraft.Server/Console/commands/kill/CliCommandKill.cpp
Normal file
64
Minecraft.Server/Console/commands/kill/CliCommandKill.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Minecraft.Server/Console/commands/kill/CliCommandKill.h
Normal file
16
Minecraft.Server/Console/commands/kill/CliCommandKill.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
49
Minecraft.Server/Console/commands/list/CliCommandList.cpp
Normal file
49
Minecraft.Server/Console/commands/list/CliCommandList.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
16
Minecraft.Server/Console/commands/list/CliCommandList.h
Normal file
16
Minecraft.Server/Console/commands/list/CliCommandList.h
Normal 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);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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;
|
||||||
|
};
|
||||||
|
}
|
||||||
173
Minecraft.Server/Console/commands/pardon/CliCommandPardon.cpp
Normal file
173
Minecraft.Server/Console/commands/pardon/CliCommandPardon.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
19
Minecraft.Server/Console/commands/pardon/CliCommandPardon.h
Normal file
19
Minecraft.Server/Console/commands/pardon/CliCommandPardon.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
32
Minecraft.Server/Console/commands/stop/CliCommandStop.cpp
Normal file
32
Minecraft.Server/Console/commands/stop/CliCommandStop.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
16
Minecraft.Server/Console/commands/stop/CliCommandStop.h
Normal file
16
Minecraft.Server/Console/commands/stop/CliCommandStop.h
Normal 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);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
118
Minecraft.Server/Console/commands/time/CliCommandTime.cpp
Normal file
118
Minecraft.Server/Console/commands/time/CliCommandTime.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Minecraft.Server/Console/commands/time/CliCommandTime.h
Normal file
16
Minecraft.Server/Console/commands/time/CliCommandTime.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
82
Minecraft.Server/Console/commands/tp/CliCommandTp.cpp
Normal file
82
Minecraft.Server/Console/commands/tp/CliCommandTp.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
18
Minecraft.Server/Console/commands/tp/CliCommandTp.h
Normal file
18
Minecraft.Server/Console/commands/tp/CliCommandTp.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
749
Minecraft.Server/Minecraft.Server.vcxproj
Normal file
749
Minecraft.Server/Minecraft.Server.vcxproj
Normal 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>
|
||||||
|
|
||||||
737
Minecraft.Server/Minecraft.Server.vcxproj.filters
Normal file
737
Minecraft.Server/Minecraft.Server.vcxproj.filters
Normal 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>
|
||||||
|
|
||||||
402
Minecraft.Server/ServerLogManager.cpp
Normal file
402
Minecraft.Server/ServerLogManager.cpp
Normal 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`
|
||||||
|
* 1つのsmallIdに紐づく接続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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
127
Minecraft.Server/ServerLogManager.h
Normal file
127
Minecraft.Server/ServerLogManager.h
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
258
Minecraft.Server/ServerLogger.cpp
Normal file
258
Minecraft.Server/ServerLogger.cpp
Normal file
|
|
@ -0,0 +1,258 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
#include "ServerLogger.h"
|
||||||
|
#include "Common\\StringUtils.h"
|
||||||
|
#include "vendor\\linenoise\\linenoise.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
namespace ServerRuntime
|
||||||
|
{
|
||||||
|
static volatile LONG g_minLogLevel = (LONG)eServerLogLevel_Info;
|
||||||
|
|
||||||
|
static const char *NormalizeCategory(const char *category)
|
||||||
|
{
|
||||||
|
if (category == NULL || category[0] == 0)
|
||||||
|
{
|
||||||
|
return "server";
|
||||||
|
}
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *LogLevelToString(EServerLogLevel level)
|
||||||
|
{
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case eServerLogLevel_Debug:
|
||||||
|
return "DEBUG";
|
||||||
|
case eServerLogLevel_Info:
|
||||||
|
return "INFO";
|
||||||
|
case eServerLogLevel_Warn:
|
||||||
|
return "WARN";
|
||||||
|
case eServerLogLevel_Error:
|
||||||
|
return "ERROR";
|
||||||
|
default:
|
||||||
|
return "INFO";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static WORD LogLevelToColor(EServerLogLevel level)
|
||||||
|
{
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case eServerLogLevel_Debug:
|
||||||
|
return FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
|
||||||
|
case eServerLogLevel_Warn:
|
||||||
|
return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
|
||||||
|
case eServerLogLevel_Error:
|
||||||
|
return FOREGROUND_RED | FOREGROUND_INTENSITY;
|
||||||
|
case eServerLogLevel_Info:
|
||||||
|
default:
|
||||||
|
return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BuildTimestamp(char *buffer, size_t bufferSize)
|
||||||
|
{
|
||||||
|
if (buffer == NULL || bufferSize == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SYSTEMTIME localTime;
|
||||||
|
GetLocalTime(&localTime);
|
||||||
|
sprintf_s(
|
||||||
|
buffer,
|
||||||
|
bufferSize,
|
||||||
|
"%04u-%02u-%02u %02u:%02u:%02u.%03u",
|
||||||
|
(unsigned)localTime.wYear,
|
||||||
|
(unsigned)localTime.wMonth,
|
||||||
|
(unsigned)localTime.wDay,
|
||||||
|
(unsigned)localTime.wHour,
|
||||||
|
(unsigned)localTime.wMinute,
|
||||||
|
(unsigned)localTime.wSecond,
|
||||||
|
(unsigned)localTime.wMilliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ShouldLog(EServerLogLevel level)
|
||||||
|
{
|
||||||
|
return ((LONG)level >= g_minLogLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteLogLine(EServerLogLevel level, const char *category, const char *message)
|
||||||
|
{
|
||||||
|
if (!ShouldLog(level))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
linenoiseExternalWriteBegin();
|
||||||
|
|
||||||
|
const char *safeCategory = NormalizeCategory(category);
|
||||||
|
const char *safeMessage = (message != NULL) ? message : "";
|
||||||
|
|
||||||
|
char timestamp[32] = {};
|
||||||
|
BuildTimestamp(timestamp, sizeof(timestamp));
|
||||||
|
|
||||||
|
HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
CONSOLE_SCREEN_BUFFER_INFO originalInfo;
|
||||||
|
bool hasColorConsole = false;
|
||||||
|
if (stdoutHandle != INVALID_HANDLE_VALUE && stdoutHandle != NULL)
|
||||||
|
{
|
||||||
|
if (GetConsoleScreenBufferInfo(stdoutHandle, &originalInfo))
|
||||||
|
{
|
||||||
|
hasColorConsole = true;
|
||||||
|
SetConsoleTextAttribute(stdoutHandle, LogLevelToColor(level));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(
|
||||||
|
"[%s][%s][%s] %s\n",
|
||||||
|
timestamp,
|
||||||
|
LogLevelToString(level),
|
||||||
|
safeCategory,
|
||||||
|
safeMessage);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
if (hasColorConsole)
|
||||||
|
{
|
||||||
|
SetConsoleTextAttribute(stdoutHandle, originalInfo.wAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
linenoiseExternalWriteEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteLogLineV(EServerLogLevel level, const char *category, const char *format, va_list args)
|
||||||
|
{
|
||||||
|
char messageBuffer[2048] = {};
|
||||||
|
if (format == NULL)
|
||||||
|
{
|
||||||
|
WriteLogLine(level, category, "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vsnprintf_s(messageBuffer, sizeof(messageBuffer), _TRUNCATE, format, args);
|
||||||
|
WriteLogLine(level, category, messageBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryParseServerLogLevel(const char *value, EServerLogLevel *outLevel)
|
||||||
|
{
|
||||||
|
if (value == NULL || outLevel == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_stricmp(value, "debug") == 0)
|
||||||
|
{
|
||||||
|
*outLevel = eServerLogLevel_Debug;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (_stricmp(value, "info") == 0)
|
||||||
|
{
|
||||||
|
*outLevel = eServerLogLevel_Info;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (_stricmp(value, "warn") == 0 || _stricmp(value, "warning") == 0)
|
||||||
|
{
|
||||||
|
*outLevel = eServerLogLevel_Warn;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (_stricmp(value, "error") == 0)
|
||||||
|
{
|
||||||
|
*outLevel = eServerLogLevel_Error;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetServerLogLevel(EServerLogLevel level)
|
||||||
|
{
|
||||||
|
if (level < eServerLogLevel_Debug)
|
||||||
|
{
|
||||||
|
level = eServerLogLevel_Debug;
|
||||||
|
}
|
||||||
|
else if (level > eServerLogLevel_Error)
|
||||||
|
{
|
||||||
|
level = eServerLogLevel_Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_minLogLevel = (LONG)level;
|
||||||
|
}
|
||||||
|
|
||||||
|
EServerLogLevel GetServerLogLevel()
|
||||||
|
{
|
||||||
|
return (EServerLogLevel)g_minLogLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogDebug(const char *category, const char *message)
|
||||||
|
{
|
||||||
|
WriteLogLine(eServerLogLevel_Debug, category, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogInfo(const char *category, const char *message)
|
||||||
|
{
|
||||||
|
WriteLogLine(eServerLogLevel_Info, category, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogWarn(const char *category, const char *message)
|
||||||
|
{
|
||||||
|
WriteLogLine(eServerLogLevel_Warn, category, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogError(const char *category, const char *message)
|
||||||
|
{
|
||||||
|
WriteLogLine(eServerLogLevel_Error, category, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogDebugf(const char *category, const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
WriteLogLineV(eServerLogLevel_Debug, category, format, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogInfof(const char *category, const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
WriteLogLineV(eServerLogLevel_Info, category, format, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogWarnf(const char *category, const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
WriteLogLineV(eServerLogLevel_Warn, category, format, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogErrorf(const char *category, const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
WriteLogLineV(eServerLogLevel_Error, category, format, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogStartupStep(const char *message)
|
||||||
|
{
|
||||||
|
LogInfo("startup", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogWorldIO(const char *message)
|
||||||
|
{
|
||||||
|
LogInfo("world-io", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogWorldName(const char *prefix, const std::wstring &name)
|
||||||
|
{
|
||||||
|
std::string utf8 = StringUtils::WideToUtf8(name);
|
||||||
|
LogInfof("world-io", "%s: %s", (prefix != NULL) ? prefix : "name", utf8.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
44
Minecraft.Server/ServerLogger.h
Normal file
44
Minecraft.Server/ServerLogger.h
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ServerRuntime
|
||||||
|
{
|
||||||
|
enum EServerLogLevel
|
||||||
|
{
|
||||||
|
eServerLogLevel_Debug = 0,
|
||||||
|
eServerLogLevel_Info = 1,
|
||||||
|
eServerLogLevel_Warn = 2,
|
||||||
|
eServerLogLevel_Error = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **Parse Log Level String**
|
||||||
|
*
|
||||||
|
* Converts a string value into log level (`debug`/`info`/`warn`/`error`)
|
||||||
|
* ログレベル文字列の変換処理
|
||||||
|
*
|
||||||
|
* @param value Source string
|
||||||
|
* @param outLevel Output location for parsed level
|
||||||
|
* @return `true` when conversion succeeds
|
||||||
|
*/
|
||||||
|
bool TryParseServerLogLevel(const char *value, EServerLogLevel *outLevel);
|
||||||
|
|
||||||
|
void SetServerLogLevel(EServerLogLevel level);
|
||||||
|
EServerLogLevel GetServerLogLevel();
|
||||||
|
|
||||||
|
void LogDebug(const char *category, const char *message);
|
||||||
|
void LogInfo(const char *category, const char *message);
|
||||||
|
void LogWarn(const char *category, const char *message);
|
||||||
|
void LogError(const char *category, const char *message);
|
||||||
|
|
||||||
|
/** Emit formatted log output with the specified level and category */
|
||||||
|
void LogDebugf(const char *category, const char *format, ...);
|
||||||
|
void LogInfof(const char *category, const char *format, ...);
|
||||||
|
void LogWarnf(const char *category, const char *format, ...);
|
||||||
|
void LogErrorf(const char *category, const char *format, ...);
|
||||||
|
|
||||||
|
void LogStartupStep(const char *message);
|
||||||
|
void LogWorldIO(const char *message);
|
||||||
|
void LogWorldName(const char *prefix, const std::wstring &name);
|
||||||
|
}
|
||||||
930
Minecraft.Server/ServerProperties.cpp
Normal file
930
Minecraft.Server/ServerProperties.cpp
Normal file
|
|
@ -0,0 +1,930 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
#include "ServerProperties.h"
|
||||||
|
|
||||||
|
#include "ServerLogger.h"
|
||||||
|
#include "Common\\StringUtils.h"
|
||||||
|
#include "Common\\FileUtils.h"
|
||||||
|
#include "..\\Minecraft.World\\ChunkSource.h"
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <map>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace ServerRuntime
|
||||||
|
{
|
||||||
|
using StringUtils::ToLowerAscii;
|
||||||
|
using StringUtils::TrimAscii;
|
||||||
|
using StringUtils::StripUtf8Bom;
|
||||||
|
using StringUtils::Utf8ToWide;
|
||||||
|
using StringUtils::WideToUtf8;
|
||||||
|
|
||||||
|
struct ServerPropertyDefault
|
||||||
|
{
|
||||||
|
const char *key;
|
||||||
|
const char *value;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *kServerPropertiesPath = "server.properties";
|
||||||
|
static const size_t kMaxSaveIdLength = 31;
|
||||||
|
|
||||||
|
static const int kDefaultServerPort = 25565;
|
||||||
|
static const int kDefaultMaxPlayers = 16;
|
||||||
|
static const int kMaxDedicatedPlayers = 256;
|
||||||
|
static const int kDefaultAutosaveIntervalSeconds = 60;
|
||||||
|
static const char *kLanAdvertisePropertyKey = "lan-advertise";
|
||||||
|
|
||||||
|
static const ServerPropertyDefault kServerPropertyDefaults[] =
|
||||||
|
{
|
||||||
|
{ "allow-flight", "true" },
|
||||||
|
{ "allow-nether", "true" },
|
||||||
|
{ "autosave-interval", "60" },
|
||||||
|
{ "bedrock-fog", "true" },
|
||||||
|
{ "bonus-chest", "false" },
|
||||||
|
{ "difficulty", "1" },
|
||||||
|
{ "disable-saving", "false" },
|
||||||
|
{ "do-daylight-cycle", "true" },
|
||||||
|
{ "do-mob-loot", "true" },
|
||||||
|
{ "do-mob-spawning", "true" },
|
||||||
|
{ "do-tile-drops", "true" },
|
||||||
|
{ "fire-spreads", "true" },
|
||||||
|
{ "friends-of-friends", "false" },
|
||||||
|
{ "gamemode", "0" },
|
||||||
|
{ "gamertags", "true" },
|
||||||
|
{ "generate-structures", "true" },
|
||||||
|
{ "host-can-be-invisible", "true" },
|
||||||
|
{ "host-can-change-hunger", "true" },
|
||||||
|
{ "host-can-fly", "true" },
|
||||||
|
{ "keep-inventory", "false" },
|
||||||
|
{ "level-id", "world" },
|
||||||
|
{ "level-name", "world" },
|
||||||
|
{ "level-seed", "" },
|
||||||
|
{ "level-type", "default" },
|
||||||
|
{ "world-size", "classic" },
|
||||||
|
{ "spawn-protection", "0" },
|
||||||
|
{ "log-level", "info" },
|
||||||
|
{ "max-build-height", "256" },
|
||||||
|
{ "max-players", "16" },
|
||||||
|
{ "mob-griefing", "true" },
|
||||||
|
{ "motd", "A Minecraft Server" },
|
||||||
|
{ "natural-regeneration", "true" },
|
||||||
|
{ "pvp", "true" },
|
||||||
|
{ "server-ip", "0.0.0.0" },
|
||||||
|
{ "server-name", "DedicatedServer" },
|
||||||
|
{ "server-port", "25565" },
|
||||||
|
{ "white-list", "false" },
|
||||||
|
{ "lan-advertise", "false" },
|
||||||
|
{ "spawn-animals", "true" },
|
||||||
|
{ "spawn-monsters", "true" },
|
||||||
|
{ "spawn-npcs", "true" },
|
||||||
|
{ "tnt", "true" },
|
||||||
|
{ "trust-players", "true" }
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::string BoolToString(bool value)
|
||||||
|
{
|
||||||
|
return value ? "true" : "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string IntToString(int value)
|
||||||
|
{
|
||||||
|
char buffer[32] = {};
|
||||||
|
sprintf_s(buffer, sizeof(buffer), "%d", value);
|
||||||
|
return std::string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string Int64ToString(__int64 value)
|
||||||
|
{
|
||||||
|
char buffer[64] = {};
|
||||||
|
_i64toa_s(value, buffer, sizeof(buffer), 10);
|
||||||
|
return std::string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ClampInt(int value, int minValue, int maxValue)
|
||||||
|
{
|
||||||
|
if (value < minValue)
|
||||||
|
{
|
||||||
|
return minValue;
|
||||||
|
}
|
||||||
|
if (value > maxValue)
|
||||||
|
{
|
||||||
|
return maxValue;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TryParseBool(const std::string &value, bool *outValue)
|
||||||
|
{
|
||||||
|
if (outValue == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string lowered = ToLowerAscii(TrimAscii(value));
|
||||||
|
if (lowered == "true" || lowered == "1" || lowered == "yes" || lowered == "on")
|
||||||
|
{
|
||||||
|
*outValue = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (lowered == "false" || lowered == "0" || lowered == "no" || lowered == "off")
|
||||||
|
{
|
||||||
|
*outValue = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TryParseInt(const std::string &value, int *outValue)
|
||||||
|
{
|
||||||
|
if (outValue == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string trimmed = TrimAscii(value);
|
||||||
|
if (trimmed.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *end = NULL;
|
||||||
|
long parsed = strtol(trimmed.c_str(), &end, 10);
|
||||||
|
if (end == trimmed.c_str() || *end != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*outValue = (int)parsed;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TryParseInt64(const std::string &value, __int64 *outValue)
|
||||||
|
{
|
||||||
|
if (outValue == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string trimmed = TrimAscii(value);
|
||||||
|
if (trimmed.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *end = NULL;
|
||||||
|
__int64 parsed = _strtoi64(trimmed.c_str(), &end, 10);
|
||||||
|
if (end == trimmed.c_str() || *end != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*outValue = parsed;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string LogLevelToPropertyValue(EServerLogLevel level)
|
||||||
|
{
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case eServerLogLevel_Debug:
|
||||||
|
return "debug";
|
||||||
|
case eServerLogLevel_Warn:
|
||||||
|
return "warn";
|
||||||
|
case eServerLogLevel_Error:
|
||||||
|
return "error";
|
||||||
|
case eServerLogLevel_Info:
|
||||||
|
default:
|
||||||
|
return "info";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **Normalize Save ID**
|
||||||
|
*
|
||||||
|
* Normalizes an arbitrary string into a safe save destination ID
|
||||||
|
* Conversion rules:
|
||||||
|
* - Lowercase alphabetic characters
|
||||||
|
* - Keep only `[a-z0-9_.-]`
|
||||||
|
* - Replace spaces and unsupported characters with `_`
|
||||||
|
* - Fallback to `world` when empty
|
||||||
|
* - Enforce max length to match storage constraints
|
||||||
|
* 保存先IDの正規化処理
|
||||||
|
*/
|
||||||
|
static std::string NormalizeSaveId(const std::string &source)
|
||||||
|
{
|
||||||
|
std::string out;
|
||||||
|
out.reserve(source.length());
|
||||||
|
|
||||||
|
// Normalize into a character set that is safe for storage save IDs
|
||||||
|
// Replace invalid characters with '_' and fold letter case to reduce collisions
|
||||||
|
for (size_t i = 0; i < source.length(); ++i)
|
||||||
|
{
|
||||||
|
unsigned char ch = (unsigned char)source[i];
|
||||||
|
if (ch >= 'A' && ch <= 'Z')
|
||||||
|
{
|
||||||
|
ch = (unsigned char)(ch - 'A' + 'a');
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool alnum = (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9');
|
||||||
|
const bool passthrough = (ch == '_') || (ch == '-') || (ch == '.');
|
||||||
|
if (alnum || passthrough)
|
||||||
|
{
|
||||||
|
out.push_back((char)ch);
|
||||||
|
}
|
||||||
|
else if (std::isspace(ch))
|
||||||
|
{
|
||||||
|
out.push_back('_');
|
||||||
|
}
|
||||||
|
else if (ch < 0x80)
|
||||||
|
{
|
||||||
|
out.push_back('_');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out.empty())
|
||||||
|
{
|
||||||
|
out = "world";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a prefix when needed to avoid awkward leading characters
|
||||||
|
if (!((out[0] >= 'a' && out[0] <= 'z') || (out[0] >= '0' && out[0] <= '9')))
|
||||||
|
{
|
||||||
|
out = std::string("w_") + out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clamp length to the 4J-side filename buffer constraint
|
||||||
|
if (out.length() > kMaxSaveIdLength)
|
||||||
|
{
|
||||||
|
out.resize(kMaxSaveIdLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ApplyDefaultServerProperties(std::unordered_map<std::string, std::string> *properties)
|
||||||
|
{
|
||||||
|
if (properties == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t defaultCount = sizeof(kServerPropertyDefaults) / sizeof(kServerPropertyDefaults[0]);
|
||||||
|
for (size_t i = 0; i < defaultCount; ++i)
|
||||||
|
{
|
||||||
|
(*properties)[kServerPropertyDefaults[i].key] = kServerPropertyDefaults[i].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **Parse server.properties Text**
|
||||||
|
*
|
||||||
|
* Extracts key/value pairs from `server.properties` format text
|
||||||
|
* - Ignores lines starting with `#` or `!` as comments
|
||||||
|
* - Accepts `=` or `:` as separators
|
||||||
|
* - Skips invalid lines and continues
|
||||||
|
* server.propertiesのパース処理
|
||||||
|
*/
|
||||||
|
static bool ReadServerPropertiesFile(const char *filePath, std::unordered_map<std::string, std::string> *properties, int *outParsedCount)
|
||||||
|
{
|
||||||
|
if (properties == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string text;
|
||||||
|
if (filePath == NULL || !FileUtils::ReadTextFile(filePath, &text))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
text = StripUtf8Bom(text);
|
||||||
|
|
||||||
|
int parsedCount = 0;
|
||||||
|
for (size_t start = 0; start <= text.length();)
|
||||||
|
{
|
||||||
|
size_t end = text.find_first_of("\r\n", start);
|
||||||
|
size_t nextStart = text.length() + 1;
|
||||||
|
if (end != std::string::npos)
|
||||||
|
{
|
||||||
|
nextStart = end + 1;
|
||||||
|
if (text[end] == '\r' && nextStart < text.length() && text[nextStart] == '\n')
|
||||||
|
{
|
||||||
|
++nextStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
if (end == std::string::npos)
|
||||||
|
{
|
||||||
|
line = text.substr(start);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
line = text.substr(start, end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string trimmedLine = TrimAscii(line);
|
||||||
|
if (trimmedLine.empty())
|
||||||
|
{
|
||||||
|
start = nextStart;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmedLine[0] == '#' || trimmedLine[0] == '!')
|
||||||
|
{
|
||||||
|
start = nextStart;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t eqPos = trimmedLine.find('=');
|
||||||
|
size_t colonPos = trimmedLine.find(':');
|
||||||
|
size_t sepPos = std::string::npos;
|
||||||
|
if (eqPos == std::string::npos)
|
||||||
|
{
|
||||||
|
sepPos = colonPos;
|
||||||
|
}
|
||||||
|
else if (colonPos == std::string::npos)
|
||||||
|
{
|
||||||
|
sepPos = eqPos;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sepPos = (eqPos < colonPos) ? eqPos : colonPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sepPos == std::string::npos)
|
||||||
|
{
|
||||||
|
start = nextStart;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string key = TrimAscii(trimmedLine.substr(0, sepPos));
|
||||||
|
if (key.empty())
|
||||||
|
{
|
||||||
|
start = nextStart;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string value = TrimAscii(trimmedLine.substr(sepPos + 1));
|
||||||
|
(*properties)[key] = value;
|
||||||
|
++parsedCount;
|
||||||
|
start = nextStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outParsedCount != NULL)
|
||||||
|
{
|
||||||
|
*outParsedCount = parsedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **Write server.properties Text**
|
||||||
|
*
|
||||||
|
* Writes key/value data back as `server.properties`
|
||||||
|
* Sorts keys before writing to keep output order stable
|
||||||
|
* server.propertiesの書き戻し処理
|
||||||
|
*/
|
||||||
|
static bool WriteServerPropertiesFile(const char *filePath, const std::unordered_map<std::string, std::string> &properties)
|
||||||
|
{
|
||||||
|
if (filePath == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string text;
|
||||||
|
text += "# Minecraft server properties\n";
|
||||||
|
text += "# Auto-generated and normalized when missing\n";
|
||||||
|
|
||||||
|
std::map<std::string, std::string> sortedProperties(properties.begin(), properties.end());
|
||||||
|
for (std::map<std::string, std::string>::const_iterator it = sortedProperties.begin(); it != sortedProperties.end(); ++it)
|
||||||
|
{
|
||||||
|
text += it->first;
|
||||||
|
text += "=";
|
||||||
|
text += it->second;
|
||||||
|
text += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return FileUtils::WriteTextFileAtomic(filePath, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ReadNormalizedBoolProperty(
|
||||||
|
std::unordered_map<std::string, std::string> *properties,
|
||||||
|
const char *key,
|
||||||
|
bool defaultValue,
|
||||||
|
bool *shouldWrite)
|
||||||
|
{
|
||||||
|
std::string raw = TrimAscii((*properties)[key]);
|
||||||
|
bool value = defaultValue;
|
||||||
|
if (!TryParseBool(raw, &value))
|
||||||
|
{
|
||||||
|
value = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string normalized = BoolToString(value);
|
||||||
|
if (raw != normalized)
|
||||||
|
{
|
||||||
|
(*properties)[key] = normalized;
|
||||||
|
if (shouldWrite != NULL)
|
||||||
|
{
|
||||||
|
*shouldWrite = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ReadNormalizedIntProperty(
|
||||||
|
std::unordered_map<std::string, std::string> *properties,
|
||||||
|
const char *key,
|
||||||
|
int defaultValue,
|
||||||
|
int minValue,
|
||||||
|
int maxValue,
|
||||||
|
bool *shouldWrite)
|
||||||
|
{
|
||||||
|
std::string raw = TrimAscii((*properties)[key]);
|
||||||
|
int value = defaultValue;
|
||||||
|
if (!TryParseInt(raw, &value))
|
||||||
|
{
|
||||||
|
value = defaultValue;
|
||||||
|
}
|
||||||
|
value = ClampInt(value, minValue, maxValue);
|
||||||
|
|
||||||
|
std::string normalized = IntToString(value);
|
||||||
|
if (raw != normalized)
|
||||||
|
{
|
||||||
|
(*properties)[key] = normalized;
|
||||||
|
if (shouldWrite != NULL)
|
||||||
|
{
|
||||||
|
*shouldWrite = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string ReadNormalizedStringProperty(
|
||||||
|
std::unordered_map<std::string, std::string> *properties,
|
||||||
|
const char *key,
|
||||||
|
const std::string &defaultValue,
|
||||||
|
size_t maxLength,
|
||||||
|
bool *shouldWrite)
|
||||||
|
{
|
||||||
|
std::string value = TrimAscii((*properties)[key]);
|
||||||
|
if (value.empty())
|
||||||
|
{
|
||||||
|
value = defaultValue;
|
||||||
|
}
|
||||||
|
if (maxLength > 0 && value.length() > maxLength)
|
||||||
|
{
|
||||||
|
value.resize(maxLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value != (*properties)[key])
|
||||||
|
{
|
||||||
|
(*properties)[key] = value;
|
||||||
|
if (shouldWrite != NULL)
|
||||||
|
{
|
||||||
|
*shouldWrite = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ReadNormalizedOptionalInt64Property(
|
||||||
|
std::unordered_map<std::string, std::string> *properties,
|
||||||
|
const char *key,
|
||||||
|
__int64 *outValue,
|
||||||
|
bool *shouldWrite)
|
||||||
|
{
|
||||||
|
std::string raw = TrimAscii((*properties)[key]);
|
||||||
|
if (raw.empty())
|
||||||
|
{
|
||||||
|
if ((*properties)[key] != "")
|
||||||
|
{
|
||||||
|
(*properties)[key] = "";
|
||||||
|
if (shouldWrite != NULL)
|
||||||
|
{
|
||||||
|
*shouldWrite = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
__int64 parsed = 0;
|
||||||
|
if (!TryParseInt64(raw, &parsed))
|
||||||
|
{
|
||||||
|
(*properties)[key] = "";
|
||||||
|
if (shouldWrite != NULL)
|
||||||
|
{
|
||||||
|
*shouldWrite = true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string normalized = Int64ToString(parsed);
|
||||||
|
if (raw != normalized)
|
||||||
|
{
|
||||||
|
(*properties)[key] = normalized;
|
||||||
|
if (shouldWrite != NULL)
|
||||||
|
{
|
||||||
|
*shouldWrite = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outValue != NULL)
|
||||||
|
{
|
||||||
|
*outValue = parsed;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EServerLogLevel ReadNormalizedLogLevelProperty(
|
||||||
|
std::unordered_map<std::string, std::string> *properties,
|
||||||
|
const char *key,
|
||||||
|
EServerLogLevel defaultValue,
|
||||||
|
bool *shouldWrite)
|
||||||
|
{
|
||||||
|
std::string raw = TrimAscii((*properties)[key]);
|
||||||
|
EServerLogLevel value = defaultValue;
|
||||||
|
if (!TryParseServerLogLevel(raw.c_str(), &value))
|
||||||
|
{
|
||||||
|
value = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string normalized = LogLevelToPropertyValue(value);
|
||||||
|
if (raw != normalized)
|
||||||
|
{
|
||||||
|
(*properties)[key] = normalized;
|
||||||
|
if (shouldWrite != NULL)
|
||||||
|
{
|
||||||
|
*shouldWrite = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string ReadNormalizedLevelTypeProperty(
|
||||||
|
std::unordered_map<std::string, std::string> *properties,
|
||||||
|
const char *key,
|
||||||
|
bool *outIsFlat,
|
||||||
|
bool *shouldWrite)
|
||||||
|
{
|
||||||
|
std::string raw = TrimAscii((*properties)[key]);
|
||||||
|
std::string lowered = ToLowerAscii(raw);
|
||||||
|
|
||||||
|
bool isFlat = false;
|
||||||
|
std::string normalized = "default";
|
||||||
|
if (lowered == "flat" || lowered == "superflat" || lowered == "1")
|
||||||
|
{
|
||||||
|
isFlat = true;
|
||||||
|
normalized = "flat";
|
||||||
|
}
|
||||||
|
else if (lowered == "default" || lowered == "normal" || lowered == "0")
|
||||||
|
{
|
||||||
|
isFlat = false;
|
||||||
|
normalized = "default";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raw != normalized)
|
||||||
|
{
|
||||||
|
(*properties)[key] = normalized;
|
||||||
|
if (shouldWrite != NULL)
|
||||||
|
{
|
||||||
|
*shouldWrite = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outIsFlat != NULL)
|
||||||
|
{
|
||||||
|
*outIsFlat = isFlat;
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string WorldSizeToPropertyValue(int worldSize)
|
||||||
|
{
|
||||||
|
switch (worldSize)
|
||||||
|
{
|
||||||
|
case e_worldSize_Small:
|
||||||
|
return "small";
|
||||||
|
case e_worldSize_Medium:
|
||||||
|
return "medium";
|
||||||
|
case e_worldSize_Large:
|
||||||
|
return "large";
|
||||||
|
case e_worldSize_Classic:
|
||||||
|
default:
|
||||||
|
return "classic";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int WorldSizeToXzChunks(int worldSize)
|
||||||
|
{
|
||||||
|
switch (worldSize)
|
||||||
|
{
|
||||||
|
case e_worldSize_Small:
|
||||||
|
return LEVEL_WIDTH_SMALL;
|
||||||
|
case e_worldSize_Medium:
|
||||||
|
return LEVEL_WIDTH_MEDIUM;
|
||||||
|
case e_worldSize_Large:
|
||||||
|
return LEVEL_WIDTH_LARGE;
|
||||||
|
case e_worldSize_Classic:
|
||||||
|
default:
|
||||||
|
return LEVEL_WIDTH_CLASSIC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int WorldSizeToHellScale(int worldSize)
|
||||||
|
{
|
||||||
|
switch (worldSize)
|
||||||
|
{
|
||||||
|
case e_worldSize_Small:
|
||||||
|
return HELL_LEVEL_SCALE_SMALL;
|
||||||
|
case e_worldSize_Medium:
|
||||||
|
return HELL_LEVEL_SCALE_MEDIUM;
|
||||||
|
case e_worldSize_Large:
|
||||||
|
return HELL_LEVEL_SCALE_LARGE;
|
||||||
|
case e_worldSize_Classic:
|
||||||
|
default:
|
||||||
|
return HELL_LEVEL_SCALE_CLASSIC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TryParseWorldSize(const std::string &lowered, int *outWorldSize)
|
||||||
|
{
|
||||||
|
if (outWorldSize == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lowered == "classic" || lowered == "54" || lowered == "1")
|
||||||
|
{
|
||||||
|
*outWorldSize = e_worldSize_Classic;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (lowered == "small" || lowered == "64" || lowered == "2")
|
||||||
|
{
|
||||||
|
*outWorldSize = e_worldSize_Small;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (lowered == "medium" || lowered == "192" || lowered == "3")
|
||||||
|
{
|
||||||
|
*outWorldSize = e_worldSize_Medium;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (lowered == "large" || lowered == "320" || lowered == "4")
|
||||||
|
{
|
||||||
|
*outWorldSize = e_worldSize_Large;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ReadNormalizedWorldSizeProperty(
|
||||||
|
std::unordered_map<std::string, std::string> *properties,
|
||||||
|
const char *key,
|
||||||
|
int defaultWorldSize,
|
||||||
|
int *outXzChunks,
|
||||||
|
int *outHellScale,
|
||||||
|
bool *shouldWrite)
|
||||||
|
{
|
||||||
|
std::string raw = TrimAscii((*properties)[key]);
|
||||||
|
std::string lowered = ToLowerAscii(raw);
|
||||||
|
|
||||||
|
int worldSize = defaultWorldSize;
|
||||||
|
if (!raw.empty())
|
||||||
|
{
|
||||||
|
int parsedWorldSize = defaultWorldSize;
|
||||||
|
if (TryParseWorldSize(lowered, &parsedWorldSize))
|
||||||
|
{
|
||||||
|
worldSize = parsedWorldSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string normalized = WorldSizeToPropertyValue(worldSize);
|
||||||
|
if (raw != normalized)
|
||||||
|
{
|
||||||
|
(*properties)[key] = normalized;
|
||||||
|
if (shouldWrite != NULL)
|
||||||
|
{
|
||||||
|
*shouldWrite = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outXzChunks != NULL)
|
||||||
|
{
|
||||||
|
*outXzChunks = WorldSizeToXzChunks(worldSize);
|
||||||
|
}
|
||||||
|
if (outHellScale != NULL)
|
||||||
|
{
|
||||||
|
*outHellScale = WorldSizeToHellScale(worldSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return worldSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **Load Effective Server Properties Config**
|
||||||
|
*
|
||||||
|
* Loads effective world settings, repairs missing or invalid values, and returns normalized config
|
||||||
|
* - Creates defaults when file is missing
|
||||||
|
* - Fills required keys when absent
|
||||||
|
* - Normalizes `level-id` to a safe format
|
||||||
|
* - Auto-saves when any fix is applied
|
||||||
|
* 実効設定の読み込みと補正処理
|
||||||
|
*/
|
||||||
|
ServerPropertiesConfig LoadServerPropertiesConfig()
|
||||||
|
{
|
||||||
|
ServerPropertiesConfig config;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, std::string> defaults;
|
||||||
|
std::unordered_map<std::string, std::string> loaded;
|
||||||
|
ApplyDefaultServerProperties(&defaults);
|
||||||
|
|
||||||
|
int parsedCount = 0;
|
||||||
|
bool readSuccess = ReadServerPropertiesFile(kServerPropertiesPath, &loaded, &parsedCount);
|
||||||
|
std::unordered_map<std::string, std::string> merged = defaults;
|
||||||
|
bool shouldWrite = false;
|
||||||
|
|
||||||
|
if (!readSuccess)
|
||||||
|
{
|
||||||
|
LogWorldIO("server.properties not found or unreadable; creating defaults");
|
||||||
|
shouldWrite = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (parsedCount == 0)
|
||||||
|
{
|
||||||
|
LogWorldIO("server.properties has no properties; applying defaults");
|
||||||
|
shouldWrite = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t defaultCount = sizeof(kServerPropertyDefaults) / sizeof(kServerPropertyDefaults[0]);
|
||||||
|
for (size_t i = 0; i < defaultCount; ++i)
|
||||||
|
{
|
||||||
|
if (loaded.find(kServerPropertyDefaults[i].key) == loaded.end())
|
||||||
|
{
|
||||||
|
shouldWrite = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::unordered_map<std::string, std::string>::const_iterator it = loaded.begin(); it != loaded.end(); ++it)
|
||||||
|
{
|
||||||
|
// Merge loaded values over defaults and keep unknown keys whenever possible
|
||||||
|
merged[it->first] = it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string worldName = TrimAscii(merged["level-name"]);
|
||||||
|
if (worldName.empty())
|
||||||
|
{
|
||||||
|
worldName = "world";
|
||||||
|
shouldWrite = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string worldSaveId = TrimAscii(merged["level-id"]);
|
||||||
|
if (worldSaveId.empty())
|
||||||
|
{
|
||||||
|
// If level-id is missing, derive it from level-name to lock save destination
|
||||||
|
worldSaveId = NormalizeSaveId(worldName);
|
||||||
|
shouldWrite = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Normalize existing level-id as well to avoid future inconsistencies
|
||||||
|
std::string normalized = NormalizeSaveId(worldSaveId);
|
||||||
|
if (normalized != worldSaveId)
|
||||||
|
{
|
||||||
|
worldSaveId = normalized;
|
||||||
|
shouldWrite = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
merged["level-name"] = worldName;
|
||||||
|
merged["level-id"] = worldSaveId;
|
||||||
|
|
||||||
|
config.worldName = Utf8ToWide(worldName.c_str());
|
||||||
|
config.worldSaveId = worldSaveId;
|
||||||
|
|
||||||
|
config.serverPort = ReadNormalizedIntProperty(&merged, "server-port", kDefaultServerPort, 1, 65535, &shouldWrite);
|
||||||
|
config.serverIp = ReadNormalizedStringProperty(&merged, "server-ip", "0.0.0.0", 255, &shouldWrite);
|
||||||
|
config.lanAdvertise = ReadNormalizedBoolProperty(&merged, kLanAdvertisePropertyKey, false, &shouldWrite);
|
||||||
|
config.whiteListEnabled = ReadNormalizedBoolProperty(&merged, "white-list", false, &shouldWrite);
|
||||||
|
config.serverName = ReadNormalizedStringProperty(&merged, "server-name", "DedicatedServer", 16, &shouldWrite);
|
||||||
|
config.maxPlayers = ReadNormalizedIntProperty(&merged, "max-players", kDefaultMaxPlayers, 1, kMaxDedicatedPlayers, &shouldWrite);
|
||||||
|
config.seed = 0;
|
||||||
|
config.hasSeed = ReadNormalizedOptionalInt64Property(&merged, "level-seed", &config.seed, &shouldWrite);
|
||||||
|
config.logLevel = ReadNormalizedLogLevelProperty(&merged, "log-level", eServerLogLevel_Info, &shouldWrite);
|
||||||
|
config.autosaveIntervalSeconds = ReadNormalizedIntProperty(&merged, "autosave-interval", kDefaultAutosaveIntervalSeconds, 5, 3600, &shouldWrite);
|
||||||
|
|
||||||
|
config.difficulty = ReadNormalizedIntProperty(&merged, "difficulty", 1, 0, 3, &shouldWrite);
|
||||||
|
config.gameMode = ReadNormalizedIntProperty(&merged, "gamemode", 0, 0, 1, &shouldWrite);
|
||||||
|
config.worldSize = ReadNormalizedWorldSizeProperty(
|
||||||
|
&merged,
|
||||||
|
"world-size",
|
||||||
|
e_worldSize_Classic,
|
||||||
|
&config.worldSizeChunks,
|
||||||
|
&config.worldHellScale,
|
||||||
|
&shouldWrite);
|
||||||
|
config.levelType = ReadNormalizedLevelTypeProperty(&merged, "level-type", &config.levelTypeFlat, &shouldWrite);
|
||||||
|
config.spawnProtectionRadius = ReadNormalizedIntProperty(&merged, "spawn-protection", 0, 0, 256, &shouldWrite);
|
||||||
|
config.generateStructures = ReadNormalizedBoolProperty(&merged, "generate-structures", true, &shouldWrite);
|
||||||
|
config.bonusChest = ReadNormalizedBoolProperty(&merged, "bonus-chest", false, &shouldWrite);
|
||||||
|
config.pvp = ReadNormalizedBoolProperty(&merged, "pvp", true, &shouldWrite);
|
||||||
|
config.trustPlayers = ReadNormalizedBoolProperty(&merged, "trust-players", true, &shouldWrite);
|
||||||
|
config.fireSpreads = ReadNormalizedBoolProperty(&merged, "fire-spreads", true, &shouldWrite);
|
||||||
|
config.tnt = ReadNormalizedBoolProperty(&merged, "tnt", true, &shouldWrite);
|
||||||
|
config.spawnAnimals = ReadNormalizedBoolProperty(&merged, "spawn-animals", true, &shouldWrite);
|
||||||
|
config.spawnNpcs = ReadNormalizedBoolProperty(&merged, "spawn-npcs", true, &shouldWrite);
|
||||||
|
config.spawnMonsters = ReadNormalizedBoolProperty(&merged, "spawn-monsters", true, &shouldWrite);
|
||||||
|
config.allowFlight = ReadNormalizedBoolProperty(&merged, "allow-flight", true, &shouldWrite);
|
||||||
|
config.allowNether = ReadNormalizedBoolProperty(&merged, "allow-nether", true, &shouldWrite);
|
||||||
|
config.friendsOfFriends = ReadNormalizedBoolProperty(&merged, "friends-of-friends", false, &shouldWrite);
|
||||||
|
config.gamertags = ReadNormalizedBoolProperty(&merged, "gamertags", true, &shouldWrite);
|
||||||
|
config.bedrockFog = ReadNormalizedBoolProperty(&merged, "bedrock-fog", true, &shouldWrite);
|
||||||
|
config.hostCanFly = ReadNormalizedBoolProperty(&merged, "host-can-fly", true, &shouldWrite);
|
||||||
|
config.hostCanChangeHunger = ReadNormalizedBoolProperty(&merged, "host-can-change-hunger", true, &shouldWrite);
|
||||||
|
config.hostCanBeInvisible = ReadNormalizedBoolProperty(&merged, "host-can-be-invisible", true, &shouldWrite);
|
||||||
|
config.disableSaving = ReadNormalizedBoolProperty(&merged, "disable-saving", false, &shouldWrite);
|
||||||
|
config.mobGriefing = ReadNormalizedBoolProperty(&merged, "mob-griefing", true, &shouldWrite);
|
||||||
|
config.keepInventory = ReadNormalizedBoolProperty(&merged, "keep-inventory", false, &shouldWrite);
|
||||||
|
config.doMobSpawning = ReadNormalizedBoolProperty(&merged, "do-mob-spawning", true, &shouldWrite);
|
||||||
|
config.doMobLoot = ReadNormalizedBoolProperty(&merged, "do-mob-loot", true, &shouldWrite);
|
||||||
|
config.doTileDrops = ReadNormalizedBoolProperty(&merged, "do-tile-drops", true, &shouldWrite);
|
||||||
|
config.naturalRegeneration = ReadNormalizedBoolProperty(&merged, "natural-regeneration", true, &shouldWrite);
|
||||||
|
config.doDaylightCycle = ReadNormalizedBoolProperty(&merged, "do-daylight-cycle", true, &shouldWrite);
|
||||||
|
|
||||||
|
config.maxBuildHeight = ReadNormalizedIntProperty(&merged, "max-build-height", 256, 64, 256, &shouldWrite);
|
||||||
|
config.motd = ReadNormalizedStringProperty(&merged, "motd", "A Minecraft Server", 255, &shouldWrite);
|
||||||
|
|
||||||
|
if (shouldWrite)
|
||||||
|
{
|
||||||
|
if (WriteServerPropertiesFile(kServerPropertiesPath, merged))
|
||||||
|
{
|
||||||
|
LogWorldIO("wrote server.properties");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogWorldIO("failed to write server.properties");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **Save World Identity While Preserving Other Keys**
|
||||||
|
*
|
||||||
|
* Saves world identity fields while preserving as many other settings as possible
|
||||||
|
* - Reads existing file and merges including unknown keys
|
||||||
|
* - Updates `level-name`, `level-id`, and `white-list` before writing back
|
||||||
|
* ワールド識別情報の保存処理
|
||||||
|
*/
|
||||||
|
bool SaveServerPropertiesConfig(const ServerPropertiesConfig &config)
|
||||||
|
{
|
||||||
|
std::unordered_map<std::string, std::string> merged;
|
||||||
|
ApplyDefaultServerProperties(&merged);
|
||||||
|
|
||||||
|
std::unordered_map<std::string, std::string> loaded;
|
||||||
|
int parsedCount = 0;
|
||||||
|
if (ReadServerPropertiesFile(kServerPropertiesPath, &loaded, &parsedCount))
|
||||||
|
{
|
||||||
|
for (std::unordered_map<std::string, std::string>::const_iterator it = loaded.begin(); it != loaded.end(); ++it)
|
||||||
|
{
|
||||||
|
// Keep existing content so keys untouched by caller are not dropped
|
||||||
|
merged[it->first] = it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string worldName = TrimAscii(WideToUtf8(config.worldName));
|
||||||
|
if (worldName.empty())
|
||||||
|
{
|
||||||
|
worldName = "world"; // Default world name
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string worldSaveId = TrimAscii(config.worldSaveId);
|
||||||
|
if (worldSaveId.empty())
|
||||||
|
{
|
||||||
|
worldSaveId = NormalizeSaveId(worldName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
worldSaveId = NormalizeSaveId(worldSaveId);
|
||||||
|
}
|
||||||
|
|
||||||
|
merged["level-name"] = worldName;
|
||||||
|
merged["level-id"] = worldSaveId;
|
||||||
|
merged["white-list"] = BoolToString(config.whiteListEnabled);
|
||||||
|
|
||||||
|
return WriteServerPropertiesFile(kServerPropertiesPath, merged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
105
Minecraft.Server/ServerProperties.h
Normal file
105
Minecraft.Server/ServerProperties.h
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "ServerLogger.h"
|
||||||
|
|
||||||
|
namespace ServerRuntime
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* `server.properties`
|
||||||
|
*/
|
||||||
|
struct ServerPropertiesConfig
|
||||||
|
{
|
||||||
|
/** world name `level-name` */
|
||||||
|
std::wstring worldName;
|
||||||
|
/** world save id `level-id` */
|
||||||
|
std::string worldSaveId;
|
||||||
|
|
||||||
|
/** `server-port` */
|
||||||
|
int serverPort;
|
||||||
|
/** `server-ip` */
|
||||||
|
std::string serverIp;
|
||||||
|
/** `lan-advertise` */
|
||||||
|
bool lanAdvertise;
|
||||||
|
/** `white-list` */
|
||||||
|
bool whiteListEnabled;
|
||||||
|
/** `server-name` (max 16 chars at runtime) */
|
||||||
|
std::string serverName;
|
||||||
|
/** `max-players` */
|
||||||
|
int maxPlayers;
|
||||||
|
/** `level-seed` is explicitly set */
|
||||||
|
bool hasSeed;
|
||||||
|
/** `level-seed` */
|
||||||
|
__int64 seed;
|
||||||
|
/** `log-level` */
|
||||||
|
EServerLogLevel logLevel;
|
||||||
|
/** `autosave-interval` (seconds) */
|
||||||
|
int autosaveIntervalSeconds;
|
||||||
|
|
||||||
|
/** host options / game settings */
|
||||||
|
int difficulty;
|
||||||
|
int gameMode;
|
||||||
|
/** `world-size` preset (`classic` / `small` / `medium` / `large`) */
|
||||||
|
int worldSize;
|
||||||
|
/** Overworld chunk width derived from `world-size` */
|
||||||
|
int worldSizeChunks;
|
||||||
|
/** Nether scale derived from `world-size` */
|
||||||
|
int worldHellScale;
|
||||||
|
bool levelTypeFlat;
|
||||||
|
/** `spawn-protection` radius in blocks (0 disables protection) */
|
||||||
|
int spawnProtectionRadius;
|
||||||
|
bool generateStructures;
|
||||||
|
bool bonusChest;
|
||||||
|
bool pvp;
|
||||||
|
bool trustPlayers;
|
||||||
|
bool fireSpreads;
|
||||||
|
bool tnt;
|
||||||
|
bool spawnAnimals;
|
||||||
|
bool spawnNpcs;
|
||||||
|
bool spawnMonsters;
|
||||||
|
bool allowFlight;
|
||||||
|
bool allowNether;
|
||||||
|
bool friendsOfFriends;
|
||||||
|
bool gamertags;
|
||||||
|
bool bedrockFog;
|
||||||
|
bool hostCanFly;
|
||||||
|
bool hostCanChangeHunger;
|
||||||
|
bool hostCanBeInvisible;
|
||||||
|
bool disableSaving;
|
||||||
|
bool mobGriefing;
|
||||||
|
bool keepInventory;
|
||||||
|
bool doMobSpawning;
|
||||||
|
bool doMobLoot;
|
||||||
|
bool doTileDrops;
|
||||||
|
bool naturalRegeneration;
|
||||||
|
bool doDaylightCycle;
|
||||||
|
|
||||||
|
/** other MinecraftServer runtime settings */
|
||||||
|
int maxBuildHeight;
|
||||||
|
std::string levelType;
|
||||||
|
std::string motd;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* server.properties loader
|
||||||
|
*
|
||||||
|
* - ファイル欠損時はデフォルト値で新規作成
|
||||||
|
* - 必須キー不足時は補完して再保存
|
||||||
|
* - `level-id` は保存先として安全な形式へ正規化
|
||||||
|
*
|
||||||
|
* @return `WorldManager` が利用するワールド設定
|
||||||
|
*/
|
||||||
|
ServerPropertiesConfig LoadServerPropertiesConfig();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* server.properties saver
|
||||||
|
*
|
||||||
|
* - `level-name` と `level-id` を更新
|
||||||
|
* - `white-list` を更新
|
||||||
|
* - それ以外の既存キーは極力保持
|
||||||
|
*
|
||||||
|
* @param config 保存するワールド識別情報と永続化対象設定
|
||||||
|
* @return 書き込み成功時 `true`
|
||||||
|
*/
|
||||||
|
bool SaveServerPropertiesConfig(const ServerPropertiesConfig &config);
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue