mirror of
https://github.com/neoStudiosLCE/neoLegacy.git
synced 2026-06-08 23:22:56 +00:00
Merge branch 'main-re' into upstream-merge
This commit is contained in:
commit
0ad7e383fa
|
Before Width: | Height: | Size: 496 KiB After Width: | Height: | Size: 496 KiB |
82
.github/workflows/nightly.yml
vendored
82
.github/workflows/nightly.yml
vendored
|
|
@ -40,8 +40,8 @@ jobs:
|
|||
shell: pwsh
|
||||
run: |
|
||||
$source = "./build/windows64/Minecraft.Client/Release"
|
||||
$zip = "LCREWindows64.zip"
|
||||
$topLevel = "LCREWindows64"
|
||||
$zip = "LCE-Revelations-Client-Win64.zip"
|
||||
$topLevel = "LCE-Revelations-Client-Win64"
|
||||
|
||||
# Collect files, excluding unwanted extensions
|
||||
$files = Get-ChildItem -Path $source -Recurse -File |
|
||||
|
|
@ -78,7 +78,7 @@ jobs:
|
|||
shell: pwsh
|
||||
run: |
|
||||
New-Item -ItemType Directory -Force -Path staging
|
||||
Copy-Item LCREWindows64.zip staging/
|
||||
Copy-Item LCE-Revelations-Client-Win64.zip staging/
|
||||
Copy-Item ./build/windows64/Minecraft.Client/Release/Minecraft.Client.exe staging/
|
||||
|
||||
- name: Upload artifacts
|
||||
|
|
@ -101,6 +101,11 @@ jobs:
|
|||
- name: Setup CMake
|
||||
uses: lukka/get-cmake@latest
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
global-json-file: global.json
|
||||
|
||||
- name: Run CMake
|
||||
uses: lukka/run-cmake@v10
|
||||
env:
|
||||
|
|
@ -108,14 +113,49 @@ jobs:
|
|||
with:
|
||||
configurePreset: windows64
|
||||
buildPreset: windows64-release
|
||||
buildPresetAdditionalArgs: "['--target', 'Minecraft.Server']"
|
||||
buildPresetAdditionalArgs: "['--target', 'Minecraft.Server', '--target', 'Minecraft.Server.FourKit']"
|
||||
|
||||
- name: Zip Build
|
||||
- name: Zip Build (vanilla)
|
||||
shell: pwsh
|
||||
run: |
|
||||
$source = "./build/windows64/Minecraft.Server/Release"
|
||||
$zip = "LCREServerWindows64.zip"
|
||||
$topLevel = "LCREServerWindows64"
|
||||
$zip = "LCE-Revelations-Server-Win64.zip"
|
||||
$topLevel = "LCE-Revelations-Server-Win64"
|
||||
|
||||
$files = Get-ChildItem -Path $source -Recurse -File |
|
||||
Where-Object { $_.Extension -notin '.pch', '.zip', '.ipdb', '.iobj' }
|
||||
|
||||
Add-Type -AssemblyName System.IO.Compression
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
|
||||
$basePath = (Resolve-Path $source).Path
|
||||
$fs = [System.IO.File]::Open($zip, [System.IO.FileMode]::Create)
|
||||
try {
|
||||
$archive = New-Object System.IO.Compression.ZipArchive($fs, [System.IO.Compression.ZipArchiveMode]::Create)
|
||||
try {
|
||||
Get-ChildItem -Path $basePath -Recurse -Directory | ForEach-Object {
|
||||
$rel = $_.FullName.Substring($basePath.Length).TrimStart('\', '/')
|
||||
$archive.CreateEntry("$topLevel/$($rel -replace '\\','/')/") | Out-Null
|
||||
}
|
||||
foreach ($file in $files) {
|
||||
$rel = $file.FullName.Substring($basePath.Length).TrimStart('\', '/')
|
||||
$entryName = "$topLevel/$($rel -replace '\\','/')"
|
||||
[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile(
|
||||
$archive, $file.FullName, $entryName,
|
||||
[System.IO.Compression.CompressionLevel]::Optimal
|
||||
) | Out-Null
|
||||
}
|
||||
} finally { $archive.Dispose() }
|
||||
} finally { $fs.Dispose() }
|
||||
|
||||
Write-Host "Created $zip"
|
||||
|
||||
- name: Zip Build (FourKit)
|
||||
shell: pwsh
|
||||
run: |
|
||||
$source = "./build/windows64/Minecraft.Server.FourKit/Release"
|
||||
$zip = "LCE-Revelations-Server-Win64-FourKit.zip"
|
||||
$topLevel = "LCE-Revelations-Server-Win64-FourKit"
|
||||
|
||||
$files = Get-ChildItem -Path $source -Recurse -File |
|
||||
Where-Object { $_.Extension -notin '.pch', '.zip', '.ipdb', '.iobj' }
|
||||
|
|
@ -149,7 +189,8 @@ jobs:
|
|||
shell: pwsh
|
||||
run: |
|
||||
New-Item -ItemType Directory -Force -Path staging
|
||||
Copy-Item LCREServerWindows64.zip staging/
|
||||
Copy-Item LCE-Revelations-Server-Win64.zip staging/
|
||||
Copy-Item LCE-Revelations-Server-Win64-FourKit.zip staging/
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v6
|
||||
|
|
@ -176,7 +217,8 @@ jobs:
|
|||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-path: |
|
||||
artifacts/LCREServerWindows64.zip
|
||||
artifacts/LCE-Revelations-Server-Win64.zip
|
||||
artifacts/LCE-Revelations-Server-Win64-FourKit.zip
|
||||
|
||||
- name: Get short SHA
|
||||
id: sha
|
||||
|
|
@ -213,7 +255,11 @@ jobs:
|
|||
--title "Server: ${{ steps.sha.outputs.short }}" \
|
||||
--notes "Dedicated Server runtime for Windows64.
|
||||
|
||||
Download \`LCREServerWindows64.zip\` and extract it to a folder where you'd like to keep the server runtime." \
|
||||
Two flavours are attached:
|
||||
- \`LCE-Revelations-Server-Win64.zip\`: vanilla server, no plugin support, smallest download.
|
||||
- \`LCE-Revelations-Server-Win64-FourKit.zip\`: server with the FourKit plugin host, bundled .NET 10 runtime, and an empty \`plugins/\` folder ready for plugin authors to drop DLLs into.
|
||||
|
||||
Pick the flavour you want and extract it to a folder where you'd like to keep the server runtime." \
|
||||
--latest=false
|
||||
|
||||
release-client:
|
||||
|
|
@ -235,7 +281,7 @@ jobs:
|
|||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-path: |
|
||||
artifacts/LCREWindows64.zip
|
||||
artifacts/LCE-Revelations-Client-Win64.zip
|
||||
artifacts/Minecraft.Client.exe
|
||||
|
||||
- name: Get short SHA
|
||||
|
|
@ -270,15 +316,15 @@ jobs:
|
|||
cat > notes.md <<'NOTES'
|
||||
# Instructions:
|
||||
**Newcomers:**
|
||||
- If this is your first time, download `LCREWindows64.zip` and extract it wherever you would like to keep it.
|
||||
- If this is your first time, download `LCE-Revelations-Client-Win64.zip` and extract it wherever you would like to keep it.
|
||||
- I would recommend to set your username prior to launch (create a file called `username.txt`, put your desired username into the file, and save).
|
||||
- To play, simply run `Minecraft.Client.exe`.
|
||||
|
||||
**For those that wish to update their existing installation with the latest build:**
|
||||
- Download `Minecraft.Client.exe` and `Minecraft.Client.pdb` and copy them over to your existing LCREWindows64 build (overwrite your old version of Minecraft.Client.exe and Minecraft.Client.pdb).
|
||||
- Download `Minecraft.Client.exe` and `Minecraft.Client.pdb` and copy them over to your existing LCE-Revelations-Client-Win64 build (overwrite your old version of Minecraft.Client.exe and Minecraft.Client.pdb).
|
||||
|
||||
**Steam Deck & Linux:**
|
||||
- Y'all know the drill. Download the `LCREWindows64.zip`, extract it, add the `Minecraft.Client.exe` as a "Non-Steam Game" within the Steam library, turn on compatibility mode with Proton Experimental, and then run it!
|
||||
- Y'all know the drill. Download the `LCE-Revelations-Client-Win64.zip`, extract it, add the `Minecraft.Client.exe` as a "Non-Steam Game" within the Steam library, turn on compatibility mode with Proton Experimental, and then run it!
|
||||
|
||||
# Multiplayer instructions:
|
||||
LAN games are natively supported, and any LAN games will appear automatically on the right. However, if you'd like to play with your friends online (and if you don't want to require them to setup a vpn, and/or if you don't want to port forward), I would recommend the following setup. Please keep in mind, you do NOT need to do this to enjoy the game. This is just how I have it setup for me so my friends can join without any hassle:
|
||||
|
|
@ -289,18 +335,12 @@ jobs:
|
|||
|
||||
How-to:
|
||||
- Ensure your playit.gg agent is connected to your playit.gg account
|
||||
- On the playit.gg website, setup a new tunnel (choose TCP). Ensure the configurable settings are set to the below values, assuming your agent is installed on the same computer as your online LCREMinecraft game is hosted from.
|
||||
- On the playit.gg website, setup a new tunnel (choose TCP). Ensure the configurable settings are set to the below values, assuming your agent is installed on the same computer as your online LCE Revelations game is hosted from.
|
||||
- Configurable settings:
|
||||
- Local IP: `127.0.0.1`
|
||||
- Local Port: `25565`
|
||||
- Proxy Protocol: `None`
|
||||
- After creating your tunnel, navigate to the "Tunnels" main page. You'll see the IP address and port for your tunnel. This is what your friends will input when adding your server in order to join your online game!
|
||||
|
||||
|
||||
# Why this fork exists:
|
||||
Changes/additions that stray from the upstream repo (`smartcmd/MinecraftConsoles`:
|
||||
- See: https://github.com/itsRevela/MinecraftConsoles?tab=readme-ov-file#latest
|
||||
- I can tweak this fork while staying compatible with the upstream repo without needing to wait on my pull requests to get accepted upstream (while keeping this fork updated with the latest and greatest from upstream)
|
||||
NOTES
|
||||
|
||||
- name: Create release
|
||||
|
|
|
|||
33
.github/workflows/pull-request.yml
vendored
33
.github/workflows/pull-request.yml
vendored
|
|
@ -1,7 +1,7 @@
|
|||
name: Pull Request Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
paths-ignore:
|
||||
|
|
@ -14,19 +14,24 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
- name: Setup MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Setup CMake
|
||||
uses: lukka/get-cmake@latest
|
||||
- name: Setup CMake
|
||||
uses: lukka/get-cmake@latest
|
||||
|
||||
- name: Run CMake
|
||||
uses: lukka/run-cmake@v10
|
||||
env:
|
||||
VCPKG_ROOT: "" # Disable vcpkg for CI builds
|
||||
with:
|
||||
configurePreset: windows64
|
||||
buildPreset: windows64-debug
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
global-json-file: global.json
|
||||
|
||||
- name: Run CMake
|
||||
uses: lukka/run-cmake@v10
|
||||
env:
|
||||
VCPKG_ROOT: "" # Disable vcpkg for CI builds
|
||||
with:
|
||||
configurePreset: windows64
|
||||
buildPreset: windows64-debug
|
||||
|
|
|
|||
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -428,3 +428,11 @@ tools/*.class
|
|||
tools/*.swf
|
||||
tools/staging/
|
||||
tools/server-monitor/
|
||||
|
||||
|
||||
# Nix
|
||||
result
|
||||
result-*
|
||||
.direnv/
|
||||
.xwin-cache/
|
||||
.xwin/
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ list(APPEND MINECRAFT_SHARED_DEFINES ${PLATFORM_DEFINES})
|
|||
add_subdirectory(Minecraft.World)
|
||||
add_subdirectory(Minecraft.Client)
|
||||
if(PLATFORM_NAME STREQUAL "Windows64") # Server is only supported on Windows for now
|
||||
add_subdirectory(Minecraft.Server.FourKit)
|
||||
add_subdirectory(Minecraft.Server)
|
||||
endif()
|
||||
|
||||
|
|
|
|||
144
COMPILE.md
144
COMPILE.md
|
|
@ -1,24 +1,55 @@
|
|||
# Compile Instructions
|
||||
|
||||
## Visual Studio
|
||||
## Prerequisites
|
||||
|
||||
1. Clone or download the repository
|
||||
1. Open the repo folder in Visual Studio 2022+.
|
||||
2. Wait for cmake to configure the project and load all assets (this may take a few minutes on the first run).
|
||||
3. Right click a folder in the solution explorer and switch to the 'CMake Targets View'
|
||||
4. Select platform and configuration from the dropdown. EG: `Windows64 - Debug` or `Windows64 - Release`
|
||||
5. Pick the startup project `Minecraft.Client.exe` or `Minecraft.Server.exe` using the debug targets dropdown
|
||||
6. Build and run the project:
|
||||
- `Build > Build Solution` (or `Ctrl+Shift+B`)
|
||||
- Start debugging with `F5`.
|
||||
- **Visual Studio 2022** with the **Desktop development with C++** workload (this includes the CMake tools, MSVC toolchain, and Windows 10 SDK).
|
||||
- **.NET 10 SDK**, required to build the FourKit plugin host (`Minecraft.Server.FourKit`).
|
||||
- Download: https://dotnet.microsoft.com/download/dotnet/10.0 (pick the **x64 SDK** installer)
|
||||
- The exact SDK version is pinned in `global.json` at the repo root.
|
||||
- CMake will fail configure with a clear error message if .NET 10 is not installed, so you find out immediately rather than partway through a build.
|
||||
- The build invokes `dotnet publish ... --runtime win-x64 --self-contained true`, so the published output bundles a complete .NET 10 runtime alongside the FourKit assembly. End users running the produced server do **not** need to install .NET themselves.
|
||||
- All FourKit runtime files (DLL + .NET runtime + `hostfxr.dll`) land in a `runtime/` subfolder next to `Minecraft.Server.exe`. An empty `plugins/` folder is also created. Both are produced automatically by the build.
|
||||
|
||||
## Visual Studio 2022 quick start (recommended)
|
||||
|
||||
VS 2022 has built-in CMake support, so there is no need to generate a `.sln` file by hand.
|
||||
|
||||
1. Install the prerequisites above.
|
||||
2. Clone the repo.
|
||||
3. In Visual Studio: `File > Open > Folder...` and select the **repo root** (the folder that contains `CMakeLists.txt`).
|
||||
4. Wait for CMake to configure (~5 seconds on a warm cache, a few minutes on the first run while assets copy).
|
||||
5. Pick a build configuration in the dropdown, for example `windows64-release`.
|
||||
6. `Build > Build All` (or `F7`). Targets of interest:
|
||||
- `Minecraft.Client`: the game client.
|
||||
- `Minecraft.Server`: the **vanilla** dedicated server. Standalone C++ binary, no plugin host, no .NET dependency at runtime, smallest distribution.
|
||||
- `Minecraft.Server.FourKit`: the **FourKit-enabled** dedicated server. Bundles the .NET 10 plugin host alongside the exe (in `runtime/`) and creates an empty `plugins/` folder for end users to drop plugin DLLs into. Building this target also triggers the `Minecraft.Server.FourKit.Managed` target which publishes the C# project.
|
||||
7. Use the debug target dropdown to pick `Minecraft.Client.exe` or whichever server flavour you want, then `F5` to launch.
|
||||
|
||||
### Server flavours
|
||||
|
||||
Both server targets compile from the same source tree and produce a binary literally named `Minecraft.Server.exe`. The variant identity lives in the build directory:
|
||||
|
||||
```
|
||||
build/<preset>/Minecraft.Server/Release/
|
||||
Minecraft.Server.exe (vanilla, no plugin support)
|
||||
Common/, Windows64/, ...
|
||||
|
||||
build/<preset>/Minecraft.Server.FourKit/Release/
|
||||
Minecraft.Server.exe (FourKit-enabled, same exe name on purpose)
|
||||
runtime/ (self-contained .NET 10 + Minecraft.Server.FourKit.dll)
|
||||
plugins/ (empty drop point)
|
||||
Common/, Windows64/, ...
|
||||
```
|
||||
|
||||
The FourKit target gets the `MINECRAFT_SERVER_FOURKIT_BUILD` preprocessor define. Inside `FourKitBridge.h`, the real plugin entry points are conditional on that define; the vanilla target sees inline no-op stubs instead, so gameplay code can call `FourKitBridge::Fire*` unconditionally and produce the right behaviour for each flavour without per-call-site `#ifdef`s.
|
||||
|
||||
### Dedicated server debug arguments
|
||||
|
||||
- Default debugger arguments for `Minecraft.Server`:
|
||||
- Default debugger arguments for both `Minecraft.Server` and `Minecraft.Server.FourKit`:
|
||||
- `-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:
|
||||
- Both server targets post-build copy the dedicated-server asset set:
|
||||
- `Common/Media/MediaWindows64.arc`
|
||||
- `Common/res`
|
||||
- `Windows64/GameHDD`
|
||||
|
|
@ -45,18 +76,36 @@ Build Release:
|
|||
cmake --build --preset windows64-release --target Minecraft.Client
|
||||
```
|
||||
|
||||
Build Dedicated Server (Debug):
|
||||
Build vanilla Dedicated Server (Debug):
|
||||
|
||||
```powershell
|
||||
cmake --build --preset windows64-debug --target Minecraft.Server
|
||||
```
|
||||
|
||||
Build Dedicated Server (Release):
|
||||
Build vanilla Dedicated Server (Release):
|
||||
|
||||
```powershell
|
||||
cmake --build --preset windows64-release --target Minecraft.Server
|
||||
```
|
||||
|
||||
Build FourKit Dedicated Server (Debug):
|
||||
|
||||
```powershell
|
||||
cmake --build --preset windows64-debug --target Minecraft.Server.FourKit
|
||||
```
|
||||
|
||||
Build FourKit Dedicated Server (Release):
|
||||
|
||||
```powershell
|
||||
cmake --build --preset windows64-release --target Minecraft.Server.FourKit
|
||||
```
|
||||
|
||||
Build everything (client + both server flavours):
|
||||
|
||||
```powershell
|
||||
cmake --build --preset windows64-release
|
||||
```
|
||||
|
||||
Run executable:
|
||||
|
||||
```powershell
|
||||
|
|
@ -64,15 +113,76 @@ cd .\build\windows64\Minecraft.Client\Debug
|
|||
.\Minecraft.Client.exe
|
||||
```
|
||||
|
||||
Run dedicated server:
|
||||
Run vanilla dedicated server:
|
||||
|
||||
```powershell
|
||||
cd .\build\windows64\Minecraft.Server\Debug
|
||||
.\Minecraft.Server.exe -port 25565 -bind 0.0.0.0 -name DedicatedServer
|
||||
```
|
||||
|
||||
Run FourKit dedicated server:
|
||||
|
||||
```powershell
|
||||
cd .\build\windows64\Minecraft.Server.FourKit\Debug
|
||||
.\Minecraft.Server.exe -port 25565 -bind 0.0.0.0 -name DedicatedServer
|
||||
```
|
||||
|
||||
Notes:
|
||||
- The CMake build is Windows-only and x64-only.
|
||||
- Contributors on macOS or Linux need a Windows machine or VM to build the project. Running the game via Wine is separate from having a supported build environment.
|
||||
- Post-build asset copy is automatic for `Minecraft.Client` in CMake (Debug and Release variants).
|
||||
- The game relies on relative paths (for example `Common\Media\...`), so launching from the output directory is required.
|
||||
|
||||
## CMake (Linux x64 Cross-Compile with Clang)
|
||||
|
||||
Cross-compile Windows x64 binaries on Linux using LLVM/Clang and the Windows SDK obtained via xwin.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Install the following packages (example for Ubuntu):
|
||||
|
||||
```bash
|
||||
sudo apt install clang lld llvm cmake ninja-build rsync cargo
|
||||
```
|
||||
|
||||
Install xwin for downloading the Windows SDK:
|
||||
|
||||
```bash
|
||||
cargo install xwin
|
||||
```
|
||||
|
||||
### Compile
|
||||
|
||||
Run this (Release):
|
||||
```bash
|
||||
./build-linux.sh
|
||||
```
|
||||
|
||||
Or, for debug:
|
||||
```bash
|
||||
./build-linux.sh . Debug
|
||||
```
|
||||
|
||||
|
||||
### NixOS / Nix
|
||||
|
||||
For NixOS or systems with Nix installed, use the provided flake:
|
||||
|
||||
```bash
|
||||
nix build .#client
|
||||
nix build .#server
|
||||
```
|
||||
|
||||
Or enter the development shell with all dependencies:
|
||||
|
||||
```bash
|
||||
nix develop
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Requires LLVM 15+ with clang-cl, lld-link, llvm-rc, and llvm-mt.
|
||||
- Wine is required to run the compiled Windows executables on Linux.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
- **`'vswhere.exe' is not recognized`**: harmless warning. This appears if you ran `vcvars64.bat` from a plain command prompt instead of `Developer PowerShell for VS`. The Visual Studio Installer's `vswhere.exe` lives at `C:\Program Files (x86)\Microsoft Visual Studio\Installer\` and is not on the default `PATH`. Use the Developer PowerShell shortcut, or open the repo folder directly in VS (which handles the dev env for you).
|
||||
- **`.NET 10 SDK not found` at configure time**: install the x64 SDK from https://dotnet.microsoft.com/download/dotnet/10.0 and re-run CMake configure (`Project > Configure Cache` in VS, or `cmake --preset windows64` from a shell).
|
||||
- **Server starts but logs `hostfxr_initialize_for_dotnet_command_line failed`**: the `runtime/` folder next to `Minecraft.Server.exe` is missing or stale. Rebuild the `Minecraft.Server.FourKit` target (which re-stages `runtime/`), or do a clean rebuild of `Minecraft.Server`.
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@
|
|||
#ifdef _WINDOWS64
|
||||
#include "Xbox/Network/NetworkPlayerXbox.h"
|
||||
#include "Common/Network/PlatformNetworkManagerStub.h"
|
||||
#include "Windows64\Network\WinsockNetLayer.h"
|
||||
#include "Windows64/Network/WinsockNetLayer.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -4251,7 +4251,8 @@ void ClientConnection::handleSetPlayerTeamPacket(shared_ptr<SetPlayerTeamPacket>
|
|||
|
||||
void ClientConnection::handleParticleEvent(shared_ptr<LevelParticlesPacket> packet)
|
||||
{
|
||||
ePARTICLE_TYPE particleId = (ePARTICLE_TYPE)Integer::parseInt(packet->getName());
|
||||
wstring particleName = packet->getName();
|
||||
ePARTICLE_TYPE particleId = (ePARTICLE_TYPE)Integer::parseInt(particleName);
|
||||
|
||||
for (int i = 0; i < packet->getCount(); i++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
#include <vector>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <lce_filesystem\lce_filesystem.h>
|
||||
#include <lce_filesystem/lce_filesystem.h>
|
||||
|
||||
#ifdef __ORBIS__
|
||||
#include <audioout.h>
|
||||
|
|
@ -1523,4 +1523,4 @@ char *SoundEngine::ConvertSoundPathToName(const wstring& name, bool bConvertSpac
|
|||
return buf;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -574,11 +574,10 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
|
|||
}
|
||||
else if ( connection->isClosed() || !IsInSession())
|
||||
{
|
||||
// assert(false);
|
||||
// Set to NULL because we're returning to home
|
||||
// The level is not reset when you leave the progress UI which causes a crash
|
||||
Minecraft::GetInstance()->setLevel(NULL);
|
||||
|
||||
// assert(false);
|
||||
// Set to NULL because we're returning to home
|
||||
// The level is not reset when you leave the progress UI which causes a crash
|
||||
Minecraft::GetInstance()->setLevel(NULL);
|
||||
MinecraftServer::HaltServer();
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ UIControl_BeaconEffectButton::UIControl_BeaconEffectButton()
|
|||
m_selected = false;
|
||||
m_active = false;
|
||||
m_focus = false;
|
||||
m_lastState = eState_Disabled;
|
||||
}
|
||||
|
||||
bool UIControl_BeaconEffectButton::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#include "stdafx.h"
|
||||
#include "UI.h"
|
||||
#include "UIControl_Label.h"
|
||||
#include "..\..\..\Minecraft.World\StringHelpers.h"
|
||||
#include "..\..\..\Minecraft.World\ArabicShaping.h"
|
||||
#include "../../../Minecraft.World/StringHelpers.h"
|
||||
#include "../../../Minecraft.World/ArabicShaping.h"
|
||||
|
||||
UIControl_Label::UIControl_Label()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include "stdafx.h"
|
||||
#include "UI.h"
|
||||
#include "UIControl_SaveList.h"
|
||||
#include "..\..\..\Minecraft.World\ArabicShaping.h"
|
||||
#include "../../../Minecraft.World/ArabicShaping.h"
|
||||
|
||||
bool UIControl_SaveList::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
#include "stdafx.h"
|
||||
#include "UI.h"
|
||||
#include "UIScene_ConnectingProgress.h"
|
||||
#include "..\..\Minecraft.h"
|
||||
#include "../../Minecraft.h"
|
||||
#ifdef _WINDOWS64
|
||||
#include "..\..\Windows64\Network\WinsockNetLayer.h"
|
||||
#include "../../Windows64/Network/WinsockNetLayer.h"
|
||||
|
||||
static int ConnectingProgress_OnRejectedDialogOK(LPVOID, int iPad, const C4JStorage::EMessageResult)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -226,4 +226,4 @@ void UIScene_DeathMenu::handlePress(F64 controlId, F64 childId)
|
|||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -909,4 +909,4 @@ void UIScene_HUD::handleGameTick()
|
|||
|
||||
updateFrameTick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#include "stdafx.h"
|
||||
#include "XUI_Ctrl_4JList.h"
|
||||
#include "..\..\..\Minecraft.World\ArabicShaping.h"
|
||||
#include "../../../Minecraft.World/ArabicShaping.h"
|
||||
|
||||
static bool TimeSortFn(const void *a, const void *b);
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
#include "../Minecraft.World/net.minecraft.h"
|
||||
#include "../Minecraft.World/StringHelpers.h"
|
||||
#include "../Minecraft.World/Random.h"
|
||||
#include "..\Minecraft.World\ArabicShaping.h"
|
||||
#include "../Minecraft.World/ArabicShaping.h"
|
||||
|
||||
Font::Font(Options *options, const wstring& name, Textures* textures, bool enforceUnicode, ResourceLocation *textureLocation, int cols, int rows, int charWidth, int charHeight, unsigned short charMap[]/* = nullptr */) : textures(textures)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include "stdafx.h"
|
||||
#include "HumanoidModel.h"
|
||||
#include "..\Minecraft.World\Mth.h"
|
||||
#include "..\Minecraft.World\Entity.h"
|
||||
#include "../Minecraft.World/Mth.h"
|
||||
#include "../Minecraft.World/Entity.h"
|
||||
#include "ModelPart.h"
|
||||
|
||||
// 4J added
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include "../Minecraft.World/Player.h"
|
||||
#include "Skins.h"
|
||||
|
||||
|
||||
ResourceLocation LivingEntityRenderer::ENCHANT_GLINT_LOCATION = ResourceLocation(TN__BLUR__MISC_GLINT);
|
||||
int LivingEntityRenderer::MAX_ARMOR_LAYERS = 4;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
#include "Windows64/Network/WinsockNetLayer.h"
|
||||
#endif
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
#include "..\Minecraft.Server\ServerLogger.h"
|
||||
#include "../Minecraft.Server/ServerLogger.h"
|
||||
#endif
|
||||
#include <sstream>
|
||||
#ifdef SPLIT_SAVES
|
||||
|
|
@ -2401,36 +2401,7 @@ void MinecraftServer::chunkPacketManagement_PreTick()
|
|||
s_tickStartTime = System::currentTimeMillis();
|
||||
s_sentTo.clear();
|
||||
|
||||
vector< shared_ptr<PlayerConnection> > *players = connection->getPlayers();
|
||||
|
||||
if( players->size() )
|
||||
{
|
||||
vector< shared_ptr<PlayerConnection> > playersOrig = *players;
|
||||
players->clear();
|
||||
|
||||
do
|
||||
{
|
||||
int longestTime = 0;
|
||||
auto playerConnectionBest = playersOrig.begin();
|
||||
for( auto it = playersOrig.begin(); it != playersOrig.end(); it++)
|
||||
{
|
||||
int thisTime = 0;
|
||||
INetworkPlayer *np = (*it)->getNetworkPlayer();
|
||||
if( np )
|
||||
{
|
||||
thisTime = np->GetTimeSinceLastChunkPacket_ms();
|
||||
}
|
||||
|
||||
if( thisTime > longestTime )
|
||||
{
|
||||
playerConnectionBest = it;
|
||||
longestTime = thisTime;
|
||||
}
|
||||
}
|
||||
players->push_back(*playerConnectionBest);
|
||||
playersOrig.erase(playerConnectionBest);
|
||||
} while ( playersOrig.size() > 0 );
|
||||
}
|
||||
connection->sortPlayersByChunkPriority();
|
||||
}
|
||||
|
||||
void MinecraftServer::chunkPacketManagement_PostTick()
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
// #include <sysutil/sysutil_common.h>
|
||||
// #include <sys/timer.h>
|
||||
// #include <sys/paths.h>
|
||||
// #include <sysutil\sysutil_savedata.h>
|
||||
// #include <sysutil/sysutil_savedata.h>
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
// #include <sysutil/sysutil_common.h>
|
||||
// #include <sys/timer.h>
|
||||
// #include <sys/paths.h>
|
||||
// #include <sysutil\sysutil_savedata.h>
|
||||
// #include <sysutil/sysutil_savedata.h>
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -453,7 +453,7 @@ void print_head(Out& out)
|
|||
<< "\n"
|
||||
<< " AUTOGENERATED. DO NOT EDIT!!!\n"
|
||||
<< "==============================================================================*/\n"
|
||||
<< "#include <boost/cstdint.hpp>\n"
|
||||
<< "#include <boost/cstdint.hpp>/n"
|
||||
<< "\n"
|
||||
<< "namespace boost { namespace spirit { namespace ucd { namespace detail\n"
|
||||
<< "{"
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
// #include <sysutil/sysutil_common.h>
|
||||
// #include <sys/timer.h>
|
||||
// #include <sys/paths.h>
|
||||
// #include <sysutil\sysutil_savedata.h>
|
||||
// #include <sysutil/sysutil_savedata.h>
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@
|
|||
#include "../Minecraft.Server/ServerLogManager.h"
|
||||
#include "../Minecraft.Server/Access/Access.h"
|
||||
#include "..\Minecraft.Server/Security/SecurityConfig.h"
|
||||
#include "..\Minecraft.World\Socket.h"
|
||||
#include "../Minecraft.World/Socket.h"
|
||||
#include <FourKitBridge.h>
|
||||
#include <Windows64/Network/WinsockNetLayer.h>
|
||||
#endif
|
||||
// #ifdef __PS3__
|
||||
// #include "PS3/Network/NetworkPlayerSony.h"
|
||||
|
|
@ -112,6 +114,54 @@ void PendingConnection::handlePreLogin(shared_ptr<PreLoginPacket> packet)
|
|||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
{
|
||||
std::string connectionIp = "";
|
||||
int connectionPort = 0;
|
||||
|
||||
if (connection && connection->getSocket()) {
|
||||
unsigned char smallId = connection->getSocket()->getSmallId();
|
||||
if (smallId != 0) {
|
||||
if (!ServerRuntime::ServerLogManager::TryGetConnectionRemoteIp(smallId, &connectionIp))
|
||||
{
|
||||
SOCKET sock = WinsockNetLayer::GetSocketForSmallId(smallId);
|
||||
if (sock != INVALID_SOCKET)
|
||||
{
|
||||
sockaddr_in addr;
|
||||
int addrLen = sizeof(addr);
|
||||
if (getpeername(sock, (sockaddr*)&addr, &addrLen) == 0)
|
||||
{
|
||||
char ipBuf[64] = {};
|
||||
if (inet_ntop(AF_INET, &addr.sin_addr, ipBuf, sizeof(ipBuf)))
|
||||
{
|
||||
connectionIp = ipBuf;
|
||||
connectionPort = (int)ntohs(addr.sin_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SOCKET sock = WinsockNetLayer::GetSocketForSmallId(smallId);
|
||||
if (sock != INVALID_SOCKET)
|
||||
{
|
||||
sockaddr_in addr;
|
||||
int addrLen = sizeof(addr);
|
||||
if (getpeername(sock, (sockaddr*)&addr, &addrLen) == 0)
|
||||
connectionPort = (int)ntohs(addr.sin_port);
|
||||
}
|
||||
}
|
||||
|
||||
if (!connectionIp.empty()) {
|
||||
if (FourKitBridge::FirePlayerPreLogin(packet->loginKey, connectionIp, connectionPort)) {
|
||||
disconnect(DisconnectPacket::eDisconnect_EndOfStream);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// printf("Server: handlePreLogin\n");
|
||||
name = packet->loginKey; // 4J Stu - Change from the login packet as we know better on client end during the pre-login packet
|
||||
sendPreLoginResponse();
|
||||
|
|
@ -201,6 +251,62 @@ void PendingConnection::handleLogin(shared_ptr<LoginPacket> packet)
|
|||
//if (true)// 4J removed !server->onlineMode)
|
||||
bool sentDisconnect = false;
|
||||
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
{
|
||||
std::string connectionIp = "";
|
||||
int connectionPort = 0;
|
||||
|
||||
if (!connection || !connection->getSocket()) {
|
||||
disconnect(DisconnectPacket::eDisconnect_EndOfStream);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char smallId = connection->getSocket()->getSmallId();
|
||||
if (smallId == 0) {
|
||||
disconnect(DisconnectPacket::eDisconnect_EndOfStream);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ServerRuntime::ServerLogManager::TryGetConnectionRemoteIp(smallId, &connectionIp))
|
||||
{
|
||||
SOCKET sock = WinsockNetLayer::GetSocketForSmallId(smallId);
|
||||
if (sock != INVALID_SOCKET)
|
||||
{
|
||||
sockaddr_in addr;
|
||||
int addrLen = sizeof(addr);
|
||||
if (getpeername(sock, (sockaddr*)&addr, &addrLen) == 0)
|
||||
{
|
||||
char ipBuf[64] = {};
|
||||
if (inet_ntop(AF_INET, &addr.sin_addr, ipBuf, sizeof(ipBuf)))
|
||||
{
|
||||
connectionIp = ipBuf;
|
||||
connectionPort = (int)ntohs(addr.sin_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (connectionIp.empty()) {
|
||||
disconnect(DisconnectPacket::eDisconnect_EndOfStream);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
SOCKET sock = WinsockNetLayer::GetSocketForSmallId(smallId);
|
||||
if (sock != INVALID_SOCKET)
|
||||
{
|
||||
sockaddr_in addr;
|
||||
int addrLen = sizeof(addr);
|
||||
if (getpeername(sock, (sockaddr*)&addr, &addrLen) == 0)
|
||||
connectionPort = (int)ntohs(addr.sin_port);
|
||||
}
|
||||
}
|
||||
|
||||
if (FourKitBridge::FirePlayerLogin(packet->userName, connectionIp, connectionPort, 1, &packet->m_onlineXuid, &packet->m_offlineXuid)) {
|
||||
disconnect(DisconnectPacket::eDisconnect_EndOfStream);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Use the same Xuid choice as handleAcceptedLogin (offline first, online fallback).
|
||||
//
|
||||
PlayerUID loginXuid = packet->m_offlineXuid;
|
||||
|
|
@ -395,6 +501,62 @@ void PendingConnection::handleAcceptedLogin(shared_ptr<LoginPacket> packet)
|
|||
return;
|
||||
}
|
||||
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
{
|
||||
std::string connectionIp = "";
|
||||
int connectionPort = 0;
|
||||
|
||||
if (!connection || !connection->getSocket()) {
|
||||
disconnect(DisconnectPacket::eDisconnect_EndOfStream);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char smallId = connection->getSocket()->getSmallId();
|
||||
if (smallId == 0) {
|
||||
disconnect(DisconnectPacket::eDisconnect_EndOfStream);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ServerRuntime::ServerLogManager::TryGetConnectionRemoteIp(smallId, &connectionIp))
|
||||
{
|
||||
SOCKET sock = WinsockNetLayer::GetSocketForSmallId(smallId);
|
||||
if (sock != INVALID_SOCKET)
|
||||
{
|
||||
sockaddr_in addr;
|
||||
int addrLen = sizeof(addr);
|
||||
if (getpeername(sock, (sockaddr*)&addr, &addrLen) == 0)
|
||||
{
|
||||
char ipBuf[64] = {};
|
||||
if (inet_ntop(AF_INET, &addr.sin_addr, ipBuf, sizeof(ipBuf)))
|
||||
{
|
||||
connectionIp = ipBuf;
|
||||
connectionPort = (int)ntohs(addr.sin_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (connectionIp.empty()) {
|
||||
disconnect(DisconnectPacket::eDisconnect_EndOfStream);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
SOCKET sock = WinsockNetLayer::GetSocketForSmallId(smallId);
|
||||
if (sock != INVALID_SOCKET)
|
||||
{
|
||||
sockaddr_in addr;
|
||||
int addrLen = sizeof(addr);
|
||||
if (getpeername(sock, (sockaddr*)&addr, &addrLen) == 0)
|
||||
connectionPort = (int)ntohs(addr.sin_port);
|
||||
}
|
||||
}
|
||||
|
||||
if (FourKitBridge::FirePlayerLogin(packet->userName, connectionIp, connectionPort, 2, &packet->m_onlineXuid, &packet->m_offlineXuid)) {
|
||||
disconnect(DisconnectPacket::eDisconnect_EndOfStream);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Guests use the online xuid, everyone else uses the offline one
|
||||
PlayerUID playerXuid = packet->m_offlineXuid;
|
||||
if(playerXuid == INVALID_XUID) playerXuid = packet->m_onlineXuid;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@
|
|||
#include "../Minecraft.World/Socket.h"
|
||||
#include "../Minecraft.World/net.minecraft.h"
|
||||
#include "../Minecraft.World/LevelData.h"
|
||||
#include "../Minecraft.World/Pos.h"
|
||||
#include "../Minecraft.World/Achievements.h"
|
||||
#include "EntityTracker.h"
|
||||
#include "ServerConnection.h"
|
||||
#include "../Minecraft.World/GenericStats.h"
|
||||
#include "../Minecraft.World/JavaMath.h"
|
||||
|
|
@ -37,6 +40,7 @@
|
|||
#include "../Minecraft.Server/Security/IdentityTokenManager.h"
|
||||
#include "../Minecraft.Server/Security/SecurityConfig.h"
|
||||
#include "../Minecraft.Server/Security/ConnectionCipher.h"
|
||||
#include "../Minecraft.Server/FourKitBridge.h"
|
||||
extern bool g_Win64DedicatedServer;
|
||||
#endif
|
||||
|
||||
|
|
@ -64,6 +68,7 @@ PlayerConnection::PlayerConnection(MinecraftServer *server, Connection *connecti
|
|||
aboveGroundTickCount = 0;
|
||||
xLastOk = yLastOk = zLastOk = 0;
|
||||
synched = true;
|
||||
hasDoneFirstTickFourKit = false;
|
||||
didTick = false;
|
||||
lastKeepAliveId = 0;
|
||||
lastKeepAliveTime = 0;
|
||||
|
|
@ -118,6 +123,14 @@ unsigned char PlayerConnection::getLogSmallId()
|
|||
|
||||
void PlayerConnection::tick()
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (!hasDoneFirstTickFourKit)
|
||||
{
|
||||
FourKitBridge::FirePlayerJoin(player->entityId, player->name, player->getUUID(), (unsigned long long)player->getOnlineXuid(), (unsigned long long)player->getXuid());
|
||||
hasDoneFirstTickFourKit = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if( done ) return;
|
||||
|
||||
if( m_bCloseOnTick )
|
||||
|
|
@ -166,34 +179,39 @@ void PlayerConnection::disconnect(DisconnectPacket::eDisconnectReason reason)
|
|||
return;
|
||||
}
|
||||
|
||||
std::wstring kickLeaveMessage;
|
||||
bool fourKitHandledQuit = false;
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (reason != DisconnectPacket::eDisconnect_Closed &&
|
||||
reason != DisconnectPacket::eDisconnect_ConnectionCreationFailed &&
|
||||
reason != DisconnectPacket::eDisconnect_Quitting)
|
||||
{
|
||||
if (FourKitBridge::FirePlayerKick(player->entityId, (int)reason, kickLeaveMessage))
|
||||
{
|
||||
m_bWasKicked.store(false);
|
||||
LeaveCriticalSection(&done_cs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ServerRuntime::ServerLogManager::OnPlayerDisconnected(
|
||||
getLogSmallId(),
|
||||
(player != NULL) ? player->name : std::wstring(),
|
||||
reason,
|
||||
true);
|
||||
fourKitHandledQuit = FourKitBridge::FirePlayerQuit(player->entityId);
|
||||
#endif
|
||||
app.DebugPrintf("PlayerConnection disconect reason: %d\n", reason );
|
||||
player->disconnect();
|
||||
|
||||
// 4J Stu - Need to remove the player from the receiving list before their socket is NULLed so that we can find another player on their system
|
||||
server->getPlayers()->removePlayerFromReceiving( player );
|
||||
send(std::make_shared<DisconnectPacket>(reason));
|
||||
connection->sendAndQuit();
|
||||
// 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.") ) );
|
||||
if(getWasKicked())
|
||||
{
|
||||
server->getPlayers()->broadcastAll(std::make_shared<ChatPacket>(player->name, ChatPacket::e_ChatPlayerKickedFromGame));
|
||||
}
|
||||
else
|
||||
{
|
||||
server->getPlayers()->broadcastAll(std::make_shared<ChatPacket>(player->name, ChatPacket::e_ChatPlayerLeftGame));
|
||||
}
|
||||
|
||||
server->getPlayers()->remove(player);
|
||||
// Mark done and release the lock BEFORE the heavy PlayerList work.
|
||||
// The actual removal, broadcast, and socket teardown are queued for
|
||||
// the next tick, which processes them without holding done_cs.
|
||||
done = true;
|
||||
LeaveCriticalSection(&done_cs);
|
||||
|
||||
server->getPlayers()->queueDisconnect(player, static_cast<int>(reason),
|
||||
kickLeaveMessage, getWasKicked(), fourKitHandledQuit);
|
||||
}
|
||||
|
||||
void PlayerConnection::handlePlayerInput(shared_ptr<PlayerInputPacket> packet)
|
||||
|
|
@ -277,6 +295,8 @@ void PlayerConnection::handleMovePlayer(shared_ptr<MovePlayerPacket> packet)
|
|||
|
||||
float yRotT = player->yRot;
|
||||
float xRotT = player->xRot;
|
||||
const float yRotOld = yRotT;
|
||||
const float xRotOld = xRotT;
|
||||
|
||||
if (packet->hasPos && packet->y == -999 && packet->yView == -999)
|
||||
{
|
||||
|
|
@ -340,7 +360,33 @@ void PlayerConnection::handleMovePlayer(shared_ptr<MovePlayerPacket> packet)
|
|||
return;
|
||||
}
|
||||
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (xLastOk != xt || yLastOk != yt || zLastOk != zt || yRotT != yRotOld || xRotT != xRotOld)
|
||||
{
|
||||
double moveToX, moveToY, moveToZ;
|
||||
bool cancelled = FourKitBridge::FirePlayerMove(player->entityId,
|
||||
xLastOk, yLastOk, zLastOk,
|
||||
xt, yt, zt,
|
||||
&moveToX, &moveToY, &moveToZ);
|
||||
if (cancelled)
|
||||
{
|
||||
teleport(xLastOk, yLastOk, zLastOk, yRotT, xRotT);
|
||||
return;
|
||||
}
|
||||
if (moveToX != xt || moveToY != yt || moveToZ != zt)
|
||||
{
|
||||
xt = moveToX;
|
||||
yt = moveToY;
|
||||
zt = moveToZ;
|
||||
xDist = xt - player->x;
|
||||
yDist = yt - player->y;
|
||||
zDist = zt - player->z;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
float r = 1 / 16.0f;
|
||||
if (player->bb == nullptr) return;
|
||||
bool oldOk = level->getCubes(player, player->bb->copy()->shrink(r, r, r))->empty();
|
||||
|
||||
if (player->onGround && !packet->onGround && yDist > 0)
|
||||
|
|
@ -388,13 +434,19 @@ void PlayerConnection::handleMovePlayer(shared_ptr<MovePlayerPacket> packet)
|
|||
}
|
||||
player->absMoveTo(xt, yt, zt, yRotT, xRotT);
|
||||
|
||||
bool newOk = level->getCubes(player, player->bb->copy()->shrink(r, r, r))->empty();
|
||||
AABB *playerBB = player->bb;
|
||||
if (playerBB == nullptr)
|
||||
{
|
||||
teleport(xLastOk, yLastOk, zLastOk, yRotT, xRotT);
|
||||
return;
|
||||
}
|
||||
bool newOk = level->getCubes(player, playerBB->copy()->shrink(r, r, r))->empty();
|
||||
if (oldOk && (fail || !newOk) && !player->isSleeping())
|
||||
{
|
||||
teleport(xLastOk, yLastOk, zLastOk, yRotT, xRotT);
|
||||
return;
|
||||
}
|
||||
AABB *testBox = player->bb->copy()->grow(r, r, r)->expand(0, -0.55, 0);
|
||||
AABB *testBox = playerBB->copy()->grow(r, r, r)->expand(0, -0.55, 0);
|
||||
// && server.level.getCubes(player, testBox).size() == 0
|
||||
if (!server->isFlightAllowed() && !player->gameMode->isCreative() && !level->containsAnyBlocks(testBox) && !player->isAllowedToFly() )
|
||||
{
|
||||
|
|
@ -446,11 +498,55 @@ void PlayerConnection::handlePlayerAction(shared_ptr<PlayerActionPacket> packet)
|
|||
|
||||
if (packet->action == PlayerActionPacket::DROP_ITEM)
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
{
|
||||
shared_ptr<ItemInstance> selected = player->inventory->getSelected();
|
||||
if (selected != nullptr && selected->count > 0)
|
||||
{
|
||||
int outId = selected->id, outCount = 1, outAux = selected->getAuxValue();
|
||||
bool cancelled = FourKitBridge::FirePlayerDropItem(
|
||||
player->entityId, selected->id, 1, selected->getAuxValue(),
|
||||
&outId, &outCount, &outAux);
|
||||
if (cancelled)
|
||||
return;
|
||||
player->inventory->removeItem(player->inventory->selected, 1);
|
||||
shared_ptr<ItemInstance> dropItem = (outId == selected->id)
|
||||
? selected->copy()
|
||||
: std::make_shared<ItemInstance>(outId, outCount, outAux);
|
||||
dropItem->count = outCount;
|
||||
if (outAux != selected->getAuxValue()) dropItem->setAuxValue(outAux);
|
||||
player->drop(dropItem);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
player->drop(false);
|
||||
return;
|
||||
}
|
||||
else if (packet->action == PlayerActionPacket::DROP_ALL_ITEMS)
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
{
|
||||
shared_ptr<ItemInstance> selected = player->inventory->getSelected();
|
||||
if (selected != nullptr && selected->count > 0)
|
||||
{
|
||||
int outId = selected->id, outCount = selected->count, outAux = selected->getAuxValue();
|
||||
bool cancelled = FourKitBridge::FirePlayerDropItem(
|
||||
player->entityId, selected->id, selected->count, selected->getAuxValue(),
|
||||
&outId, &outCount, &outAux);
|
||||
if (cancelled)
|
||||
return;
|
||||
player->inventory->removeItem(player->inventory->selected, selected->count);
|
||||
shared_ptr<ItemInstance> dropItem = (outId == selected->id)
|
||||
? selected->copy()
|
||||
: std::make_shared<ItemInstance>(outId, outCount, outAux);
|
||||
dropItem->count = outCount;
|
||||
if (outAux != selected->getAuxValue()) dropItem->setAuxValue(outAux);
|
||||
player->drop(dropItem);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
player->drop(true);
|
||||
return;
|
||||
}
|
||||
|
|
@ -488,6 +584,23 @@ void PlayerConnection::handlePlayerAction(shared_ptr<PlayerActionPacket> packet)
|
|||
|
||||
if (packet->action == PlayerActionPacket::START_DESTROY_BLOCK)
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
lastLeftClickTick = tickCount;
|
||||
{
|
||||
shared_ptr<ItemInstance> heldItem = player->inventory->getSelected();
|
||||
int iId = heldItem ? heldItem->id : 0;
|
||||
int iCount = heldItem ? heldItem->count : 0;
|
||||
int iAux = heldItem ? heldItem->getAuxValue() : 0;
|
||||
int useItemInHand = 1;
|
||||
bool cancelled = FourKitBridge::FirePlayerInteract(
|
||||
player->entityId, 1 /* LEFT_CLICK_BLOCK */,
|
||||
iId, iCount, iAux,
|
||||
x, y, z, packet->face, player->dimension,
|
||||
&useItemInHand);
|
||||
if (cancelled)
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// 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));
|
||||
|
|
@ -495,9 +608,9 @@ void PlayerConnection::handlePlayerAction(shared_ptr<PlayerActionPacket> packet)
|
|||
}
|
||||
else if (packet->action == PlayerActionPacket::STOP_DESTROY_BLOCK)
|
||||
{
|
||||
bool destroyed = player->gameMode->stopDestroyBlock(x, y, z);
|
||||
player->gameMode->stopDestroyBlock(x, y, z);
|
||||
server->getPlayers()->prioritiseTileChanges(x, y, z, level->dimension->id); // 4J added - make sure that the update packets for this get prioritised over other general world updates
|
||||
if (!destroyed && level->getTile(x, y, z) != 0) player->connection->send(std::make_shared<TileUpdatePacket>(x, y, z, level));
|
||||
if (level->getTile(x, y, z) != 0) player->connection->send(std::make_shared<TileUpdatePacket>(x, y, z, level));
|
||||
}
|
||||
else if (packet->action == PlayerActionPacket::ABORT_DESTROY_BLOCK)
|
||||
{
|
||||
|
|
@ -520,19 +633,128 @@ void PlayerConnection::handleUseItem(shared_ptr<UseItemPacket> packet)
|
|||
if (packet->getFace() == 255)
|
||||
{
|
||||
if (item == nullptr) return;
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
{
|
||||
int iId = item->id;
|
||||
int iCount = item->count;
|
||||
int iAux = item->getAuxValue();
|
||||
int useItemInHand = 1;
|
||||
bool cancelled = FourKitBridge::FirePlayerInteract(
|
||||
player->entityId, 2,
|
||||
iId, iCount, iAux,
|
||||
0, 0, 0, 6, player->dimension,
|
||||
&useItemInHand);
|
||||
if (cancelled || !useItemInHand)
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
player->gameMode->useItem(player, level, item);
|
||||
}
|
||||
else if ((packet->getY() < server->getMaxBuildHeight() - 1) || (packet->getFace() != Facing::UP && packet->getY() < server->getMaxBuildHeight()))
|
||||
{
|
||||
if (synched && player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) < 8 * 8)
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
{
|
||||
int iId = item ? item->id : 0;
|
||||
int iCount = item ? item->count : 0;
|
||||
int iAux = item ? item->getAuxValue() : 0;
|
||||
int useItemInHand = 1;
|
||||
bool cancelled = FourKitBridge::FirePlayerInteract(
|
||||
player->entityId, 3 /* RIGHT_CLICK_BLOCK */,
|
||||
iId, iCount, iAux,
|
||||
x, y, z, face, player->dimension,
|
||||
&useItemInHand);
|
||||
if (cancelled || !useItemInHand)
|
||||
{
|
||||
informClient = true;
|
||||
goto skipUseItemOn;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Anti-cheat: block placement/use must pass server-side spawn protection.
|
||||
if (!server->isUnderSpawnProtection(level, x, y, z, player))
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
int placeX = x, placeY = y, placeZ = z;
|
||||
bool validFace = true;
|
||||
|
||||
if (face == 0) placeY--;
|
||||
else if (face == 1) placeY++;
|
||||
else if (face == 2) placeZ--;
|
||||
else if (face == 3) placeZ++;
|
||||
else if (face == 4) placeX--;
|
||||
else if (face == 5) placeX++;
|
||||
else validFace = false;
|
||||
|
||||
int oldTileId = validFace ? level->getTile(placeX, placeY, placeZ) : 0;
|
||||
int oldTileData = validFace ? level->getData(placeX, placeY, placeZ) : 0;
|
||||
int oldClickedId = level->getTile(x, y, z);
|
||||
int oldClickedData = level->getData(x, y, z);
|
||||
int savedItemId = item ? item->id : 0;
|
||||
int savedItemCount = item ? item->count : 0;
|
||||
#endif
|
||||
|
||||
player->gameMode->useItemOn(player, level, item, x, y, z, face, packet->getClickX(), packet->getClickY(), packet->getClickZ());
|
||||
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (validFace)
|
||||
{
|
||||
int newTileId = level->getTile(placeX, placeY, placeZ);
|
||||
int newClickedId = level->getTile(x, y, z);
|
||||
|
||||
int fireX = placeX, fireY = placeY, fireZ = placeZ;
|
||||
int againstX = x, againstY = y, againstZ = z;
|
||||
int revertId = oldTileId, revertData = oldTileData;
|
||||
bool shouldFire = false;
|
||||
|
||||
if (newTileId != 0 && newTileId != oldTileId)
|
||||
{
|
||||
shouldFire = true;
|
||||
}
|
||||
else if (newClickedId != 0 && newClickedId != oldClickedId)
|
||||
{
|
||||
shouldFire = true;
|
||||
fireX = x; fireY = y; fireZ = z;
|
||||
revertId = oldClickedId; revertData = oldClickedData;
|
||||
againstX = x; againstY = y; againstZ = z;
|
||||
if (face == 0) againstY++;
|
||||
else if (face == 1) againstY--;
|
||||
else if (face == 2) againstZ++;
|
||||
else if (face == 3) againstZ--;
|
||||
else if (face == 4) againstX++;
|
||||
else if (face == 5) againstX--;
|
||||
}
|
||||
|
||||
if (shouldFire)
|
||||
{
|
||||
bool cancelled = FourKitBridge::FireBlockPlace(
|
||||
player->entityId, player->dimension,
|
||||
fireX, fireY, fireZ,
|
||||
againstX, againstY, againstZ,
|
||||
savedItemId, savedItemCount, true);
|
||||
|
||||
if (cancelled)
|
||||
{
|
||||
level->setTileAndData(fireX, fireY, fireZ, revertId, revertData, Tile::UPDATE_ALL);
|
||||
auto &slot = player->inventory->items[player->inventory->selected];
|
||||
if (slot != nullptr)
|
||||
{
|
||||
slot->count = savedItemCount;
|
||||
}
|
||||
else if (savedItemId > 0 && savedItemId < 256 && Tile::tiles[savedItemId] != nullptr && savedItemCount > 0)
|
||||
{
|
||||
slot = std::make_shared<ItemInstance>(Tile::tiles[savedItemId], savedItemCount);
|
||||
}
|
||||
informClient = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
skipUseItemOn:
|
||||
informClient = true;
|
||||
}
|
||||
else
|
||||
|
|
@ -601,27 +823,21 @@ void PlayerConnection::onDisconnect(DisconnectPacket::eDisconnectReason reason,
|
|||
LeaveCriticalSection(&done_cs);
|
||||
return;
|
||||
}
|
||||
bool fourKitHandledQuit = false;
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
ServerRuntime::ServerLogManager::OnPlayerDisconnected(
|
||||
getLogSmallId(),
|
||||
(player != NULL) ? player->name : std::wstring(),
|
||||
reason,
|
||||
false);
|
||||
fourKitHandledQuit = FourKitBridge::FirePlayerQuit(player->entityId);
|
||||
#endif
|
||||
// logger.info(player.name + " lost connection: " + reason);
|
||||
// 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.") ) );
|
||||
if(getWasKicked())
|
||||
{
|
||||
server->getPlayers()->broadcastAll(std::make_shared<ChatPacket>(player->name, ChatPacket::e_ChatPlayerKickedFromGame));
|
||||
}
|
||||
else
|
||||
{
|
||||
server->getPlayers()->broadcastAll(std::make_shared<ChatPacket>(player->name, ChatPacket::e_ChatPlayerLeftGame));
|
||||
}
|
||||
server->getPlayers()->remove(player);
|
||||
|
||||
done = true;
|
||||
LeaveCriticalSection(&done_cs);
|
||||
|
||||
server->getPlayers()->queueDisconnect(player, static_cast<int>(reason),
|
||||
L"", getWasKicked(), fourKitHandledQuit);
|
||||
}
|
||||
|
||||
void PlayerConnection::openSecurityGate()
|
||||
|
|
@ -740,8 +956,20 @@ void PlayerConnection::handleChat(shared_ptr<ChatPacket> packet)
|
|||
handleCommand(message);
|
||||
return;
|
||||
}
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
{
|
||||
std::wstring formatted;
|
||||
if (!FourKitBridge::FirePlayerChat(player->entityId, message, formatted))
|
||||
{
|
||||
if (formatted.empty())
|
||||
formatted = L"<" + player->name + L"> " + message;
|
||||
server->getPlayers()->broadcastAll(std::make_shared<ChatPacket>(formatted));
|
||||
}
|
||||
}
|
||||
#else
|
||||
wstring formatted = L"<" + player->name + L"> " + message;
|
||||
server->getPlayers()->broadcastAll(shared_ptr<ChatPacket>(new ChatPacket(formatted)));
|
||||
#endif
|
||||
chatSpamTickCount += SharedConstants::TICKS_PER_SECOND;
|
||||
if (chatSpamTickCount > SharedConstants::TICKS_PER_SECOND * 10)
|
||||
{
|
||||
|
|
@ -751,6 +979,13 @@ void PlayerConnection::handleChat(shared_ptr<ChatPacket> packet)
|
|||
|
||||
void PlayerConnection::handleCommand(const wstring& message)
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
std::wstring commandLine = message;
|
||||
if (FourKitBridge::FireCommandPreprocess(player->entityId, commandLine, commandLine))
|
||||
return;
|
||||
if (FourKitBridge::HandlePlayerCommand(player->entityId, commandLine))
|
||||
return;
|
||||
#endif
|
||||
// 4J - TODO
|
||||
#if 0
|
||||
server.getCommandDispatcher().performCommand(player, message);
|
||||
|
|
@ -762,6 +997,23 @@ void PlayerConnection::handleAnimate(shared_ptr<AnimatePacket> packet)
|
|||
player->resetLastActionTime();
|
||||
if (packet->action == AnimatePacket::SWING)
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (lastLeftClickTick != tickCount)
|
||||
{
|
||||
int useItemInHand = 1;
|
||||
auto item = player->inventory->getSelected();
|
||||
int iId = item ? item->id : 0;
|
||||
int iCount = item ? item->count : 0;
|
||||
int iAux = item ? item->getAuxValue() : 0;
|
||||
bool cancelled = FourKitBridge::FirePlayerInteract(
|
||||
player->entityId, 0,
|
||||
iId, iCount, iAux,
|
||||
0, 0, 0, 6, player->dimension,
|
||||
&useItemInHand);
|
||||
if (cancelled)
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
player->swing();
|
||||
}
|
||||
}
|
||||
|
|
@ -872,10 +1124,31 @@ void PlayerConnection::handleInteract(shared_ptr<InteractPacket> packet)
|
|||
|
||||
if (packet->action == InteractPacket::INTERACT)
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
{
|
||||
int mappedType = FourKitBridge::MapEntityType((int)target->GetType());
|
||||
float targetHealth = 0, targetMaxHealth = 0, targetEyeHeight = 0;
|
||||
auto living = dynamic_pointer_cast<LivingEntity>(target);
|
||||
if (living)
|
||||
{
|
||||
targetHealth = living->getHealth();
|
||||
targetMaxHealth = living->getMaxHealth();
|
||||
targetEyeHeight = living->getHeadHeight();
|
||||
}
|
||||
if (FourKitBridge::FirePlayerInteractEntity(
|
||||
player->entityId, target->entityId, mappedType,
|
||||
player->dimension, target->x, target->y, target->z,
|
||||
targetHealth, targetMaxHealth, targetEyeHeight))
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
player->interact(target);
|
||||
}
|
||||
else if (packet->action == InteractPacket::ATTACK)
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
lastLeftClickTick = tickCount;
|
||||
#endif
|
||||
if ((target->GetType() == eTYPE_ITEMENTITY) || (target->GetType() == eTYPE_EXPERIENCEORB) || (target->GetType() == eTYPE_ARROW) || target == player)
|
||||
{
|
||||
return;
|
||||
|
|
@ -1243,7 +1516,13 @@ void PlayerConnection::handleClientCommand(shared_ptr<ClientCommandPacket> packe
|
|||
{
|
||||
if (player->wonGame)
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
int oldEntityId = player->entityId;
|
||||
#endif
|
||||
player = server->getPlayers()->respawn(player, player->m_enteredEndExitPortal?0:player->dimension, true);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
FourKitBridge::UpdatePlayerEntityId(oldEntityId, player->entityId);
|
||||
#endif
|
||||
}
|
||||
else if (player->level->getLevelData()->isHardcore())
|
||||
{
|
||||
|
|
@ -1254,7 +1533,13 @@ void PlayerConnection::handleClientCommand(shared_ptr<ClientCommandPacket> packe
|
|||
else
|
||||
{
|
||||
if (player->getHealth() > 0) return;
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
int oldEntityId = player->entityId;
|
||||
#endif
|
||||
player = server->getPlayers()->respawn(player, 0, false);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
FourKitBridge::UpdatePlayerEntityId(oldEntityId, player->entityId);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1314,6 +1599,20 @@ void PlayerConnection::handleContainerClick(shared_ptr<ContainerClickPacket> pac
|
|||
player->resetLastActionTime();
|
||||
if (player->containerMenu->containerId == packet->containerId && player->containerMenu->isSynched(player))
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
int fourKitClickResult = FourKitBridge::FireInventoryClick(player->entityId, packet->slotNum, packet->buttonNum, packet->clickType);
|
||||
if (fourKitClickResult == 1)
|
||||
{
|
||||
expectedAcks[player->containerMenu->containerId] = packet->uid;
|
||||
player->connection->send(std::make_shared<ContainerAckPacket>(packet->containerId, packet->uid, false));
|
||||
player->containerMenu->setSynched(player, false);
|
||||
vector<shared_ptr<ItemInstance>> items;
|
||||
for (unsigned int i = 0; i < player->containerMenu->slots.size(); i++)
|
||||
items.push_back(player->containerMenu->slots.at(i)->getItem());
|
||||
player->refreshContainer(player->containerMenu, &items);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
shared_ptr<ItemInstance> clicked = player->containerMenu->clicked(packet->slotNum, packet->buttonNum, packet->clickType, player);
|
||||
|
||||
if (ItemInstance::matches(packet->item, clicked))
|
||||
|
|
@ -1341,6 +1640,15 @@ void PlayerConnection::handleContainerClick(shared_ptr<ContainerClickPacket> pac
|
|||
|
||||
// player.containerMenu.broadcastChanges();
|
||||
}
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (fourKitClickResult == 2)
|
||||
{
|
||||
vector<shared_ptr<ItemInstance>> refreshItems;
|
||||
for (unsigned int i = 0; i < player->containerMenu->slots.size(); i++)
|
||||
refreshItems.push_back(player->containerMenu->slots.at(i)->getItem());
|
||||
player->refreshContainer(player->containerMenu, &refreshItems);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1478,10 +1786,31 @@ void PlayerConnection::handleSignUpdate(shared_ptr<SignUpdatePacket> packet)
|
|||
int y = packet->y;
|
||||
int z = packet->z;
|
||||
shared_ptr<SignTileEntity> ste = dynamic_pointer_cast<SignTileEntity>(te);
|
||||
|
||||
wstring lines[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
wstring lineText = packet->lines[i].substr(0,15);
|
||||
ste->SetMessage( i, lineText );
|
||||
lines[i] = packet->lines[i].substr(0,15);
|
||||
}
|
||||
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
{
|
||||
std::wstring outLines[4];
|
||||
bool cancelled = FourKitBridge::FireSignChange(
|
||||
player->entityId, player->dimension,
|
||||
x, y, z,
|
||||
lines[0], lines[1], lines[2], lines[3],
|
||||
outLines);
|
||||
if (cancelled)
|
||||
return;
|
||||
for (int i = 0; i < 4; i++)
|
||||
lines[i] = outLines[i];
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
ste->SetMessage( i, lines[i] );
|
||||
}
|
||||
ste->SetVerified(false);
|
||||
ste->setChanged();
|
||||
|
|
@ -1774,45 +2103,58 @@ void PlayerConnection::handleCustomPayload(shared_ptr<CustomPayloadPacket> custo
|
|||
}
|
||||
else if (CustomPayloadPacket::SET_BEACON_PACKET.compare(customPayloadPacket->identifier) == 0)
|
||||
{
|
||||
if ( dynamic_cast<BeaconMenu *>( player->containerMenu) != nullptr)
|
||||
{
|
||||
ByteArrayInputStream bais(customPayloadPacket->data);
|
||||
DataInputStream input(&bais);
|
||||
int primary = input.readInt();
|
||||
int secondary = input.readInt();
|
||||
if (dynamic_cast<BeaconMenu *>(player->containerMenu) != nullptr)
|
||||
{
|
||||
ByteArrayInputStream bais(customPayloadPacket->data);
|
||||
DataInputStream input(&bais);
|
||||
int primary = input.readInt();
|
||||
int secondary = input.readInt();
|
||||
|
||||
BeaconMenu *beaconMenu = static_cast<BeaconMenu *>(player->containerMenu);
|
||||
Slot *slot = beaconMenu->getSlot(0);
|
||||
if (slot->hasItem())
|
||||
{
|
||||
slot->remove(1);
|
||||
shared_ptr<BeaconTileEntity> beacon = beaconMenu->getBeacon();
|
||||
beacon->setPrimaryPower(primary);
|
||||
beacon->setSecondaryPower(secondary);
|
||||
beacon->setChanged();
|
||||
}
|
||||
}
|
||||
BeaconMenu *beaconMenu = static_cast<BeaconMenu *>(player->containerMenu);
|
||||
Slot *slot = beaconMenu->getSlot(0);
|
||||
|
||||
if (slot != nullptr && slot->hasItem())
|
||||
{
|
||||
shared_ptr<BeaconTileEntity> beacon = beaconMenu->getBeacon();
|
||||
|
||||
int prevPrimary = beacon->getPrimaryPower();
|
||||
int prevSecondary = beacon->getSecondaryPower();
|
||||
|
||||
beacon->setPrimaryPower(primary);
|
||||
beacon->setSecondaryPower(secondary);
|
||||
|
||||
// Only consume the payment item if the powers actually changed
|
||||
if (beacon->getPrimaryPower() != prevPrimary ||
|
||||
beacon->getSecondaryPower() != prevSecondary)
|
||||
{
|
||||
slot->remove(1);
|
||||
}
|
||||
|
||||
beacon->setChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (CustomPayloadPacket::SET_ITEM_NAME_PACKET.compare(customPayloadPacket->identifier) == 0)
|
||||
{
|
||||
AnvilMenu *menu = dynamic_cast<AnvilMenu *>( player->containerMenu);
|
||||
if (menu)
|
||||
{
|
||||
if (customPayloadPacket->data.data == nullptr || customPayloadPacket->data.length < 1)
|
||||
{
|
||||
menu->setItemName(L"");
|
||||
}
|
||||
else
|
||||
{
|
||||
ByteArrayInputStream bais(customPayloadPacket->data);
|
||||
DataInputStream dis(&bais);
|
||||
wstring name = dis.readUTF();
|
||||
if (name.length() <= 30)
|
||||
{
|
||||
menu->setItemName(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
AnvilMenu *menu = dynamic_cast<AnvilMenu *>(player->containerMenu);
|
||||
if (menu)
|
||||
{
|
||||
if (customPayloadPacket->data.data == nullptr || customPayloadPacket->data.length < 1)
|
||||
{
|
||||
menu->setItemName(L"");
|
||||
}
|
||||
else
|
||||
{
|
||||
ByteArrayInputStream bais(customPayloadPacket->data);
|
||||
DataInputStream dis(&bais);
|
||||
wstring name = dis.readUTF();
|
||||
|
||||
if (name.length() <= 30)
|
||||
{
|
||||
menu->setItemName(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ private:
|
|||
int aboveGroundTickCount;
|
||||
|
||||
bool didTick;
|
||||
bool hasDoneFirstTickFourKit;
|
||||
int lastLeftClickTick = 0;
|
||||
int lastKeepAliveId;
|
||||
int64_t lastKeepAliveTime;
|
||||
static Random random;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
#include "../Minecraft.Server/Access/Access.h"
|
||||
#include "../Minecraft.Server/Common\StringUtils.h"
|
||||
#include "../Minecraft.Server/Common/StringUtils.h"
|
||||
#include "../Minecraft.Server/ServerLogger.h"
|
||||
#include "../Minecraft.Server/ServerLogManager.h"
|
||||
#include "../Minecraft.Server/ServerProperties.h"
|
||||
|
|
@ -77,6 +77,8 @@ PlayerList::PlayerList(MinecraftServer *server)
|
|||
int rawMax = server->settings->getInt(L"max-players", 8);
|
||||
maxPlayers = static_cast<unsigned int>(Mth::clamp(rawMax, 1, MINECRAFT_NET_MAX_PLAYERS));
|
||||
doWhiteList = false;
|
||||
InitializeCriticalSection(&m_playersCS);
|
||||
InitializeCriticalSection(&m_disconnectCS);
|
||||
InitializeCriticalSection(&m_banCS);
|
||||
InitializeCriticalSection(&m_kickPlayersCS);
|
||||
InitializeCriticalSection(&m_closePlayersCS);
|
||||
|
|
@ -91,11 +93,34 @@ PlayerList::~PlayerList()
|
|||
player->gameMode = nullptr;
|
||||
}
|
||||
|
||||
DeleteCriticalSection(&m_playersCS);
|
||||
DeleteCriticalSection(&m_disconnectCS);
|
||||
DeleteCriticalSection(&m_banCS);
|
||||
DeleteCriticalSection(&m_kickPlayersCS);
|
||||
DeleteCriticalSection(&m_closePlayersCS);
|
||||
}
|
||||
|
||||
vector<shared_ptr<ServerPlayer> > PlayerList::getPlayersSnapshot()
|
||||
{
|
||||
EnterCriticalSection(&m_playersCS);
|
||||
vector<shared_ptr<ServerPlayer> > snapshot = players;
|
||||
LeaveCriticalSection(&m_playersCS);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
void PlayerList::queueDisconnect(shared_ptr<ServerPlayer> player, int reason, const wstring& kickMessage, bool wasKicked, bool fourKitHandledQuit)
|
||||
{
|
||||
PendingDisconnect pd;
|
||||
pd.player = player;
|
||||
pd.reason = reason;
|
||||
pd.kickMessage = kickMessage;
|
||||
pd.wasKicked = wasKicked;
|
||||
pd.fourKitHandledQuit = fourKitHandledQuit;
|
||||
EnterCriticalSection(&m_disconnectCS);
|
||||
m_pendingDisconnects.push_back(pd);
|
||||
LeaveCriticalSection(&m_disconnectCS);
|
||||
}
|
||||
|
||||
bool PlayerList::placeNewPlayer(Connection *connection, shared_ptr<ServerPlayer> player, shared_ptr<LoginPacket> packet)
|
||||
{
|
||||
CompoundTag *playerTag = load(player);
|
||||
|
|
@ -531,7 +556,9 @@ void PlayerList::add(shared_ptr<ServerPlayer> player)
|
|||
broadcastAll(std::make_shared<PlayerInfoPacket>(player));
|
||||
}
|
||||
|
||||
EnterCriticalSection(&m_playersCS);
|
||||
players.push_back(player);
|
||||
LeaveCriticalSection(&m_playersCS);
|
||||
|
||||
// 4J Added
|
||||
addPlayerToReceiving(player);
|
||||
|
|
@ -548,13 +575,16 @@ void PlayerList::add(shared_ptr<ServerPlayer> player)
|
|||
changeDimension(player, nullptr);
|
||||
level->addEntity(player);
|
||||
|
||||
for (size_t i = 0; i < players.size(); i++)
|
||||
{
|
||||
shared_ptr<ServerPlayer> op = players.at(i);
|
||||
//player->connection->send(shared_ptr<PlayerInfoPacket>( new PlayerInfoPacket(op->name, true, op->latency) ) );
|
||||
if( op->connection->getNetworkPlayer() )
|
||||
vector<shared_ptr<ServerPlayer> > snapshot = getPlayersSnapshot();
|
||||
for (size_t i = 0; i < snapshot.size(); i++)
|
||||
{
|
||||
player->connection->send(std::make_shared<PlayerInfoPacket>(op));
|
||||
shared_ptr<ServerPlayer> op = snapshot.at(i);
|
||||
//player->connection->send(shared_ptr<PlayerInfoPacket>( new PlayerInfoPacket(op->name, true, op->latency) ) );
|
||||
if( op->connection->getNetworkPlayer() )
|
||||
{
|
||||
player->connection->send(std::make_shared<PlayerInfoPacket>(op));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -580,9 +610,10 @@ void PlayerList::add(shared_ptr<ServerPlayer> player)
|
|||
broadcastAll(std::make_shared<AddPlayerPacket>(player, xuid, onlineXuid, xp, yp, zp, yRotp, xRotp, yHeadRotp));
|
||||
|
||||
// Send all existing players to the new player
|
||||
for (size_t i = 0; i < players.size(); i++)
|
||||
vector<shared_ptr<ServerPlayer> > snapshot = getPlayersSnapshot();
|
||||
for (size_t i = 0; i < snapshot.size(); i++)
|
||||
{
|
||||
shared_ptr<ServerPlayer> op = players.at(i);
|
||||
shared_ptr<ServerPlayer> op = snapshot.at(i);
|
||||
if (op != player && op->connection->getNetworkPlayer())
|
||||
{
|
||||
PlayerUID opXuid = INVALID_XUID;
|
||||
|
|
@ -605,9 +636,10 @@ void PlayerList::add(shared_ptr<ServerPlayer> player)
|
|||
if(level->isAtLeastOnePlayerSleeping())
|
||||
{
|
||||
shared_ptr<ServerPlayer> firstSleepingPlayer = nullptr;
|
||||
for (unsigned int i = 0; i < players.size(); i++)
|
||||
vector<shared_ptr<ServerPlayer> > snapshot = getPlayersSnapshot();
|
||||
for (unsigned int i = 0; i < snapshot.size(); i++)
|
||||
{
|
||||
shared_ptr<ServerPlayer> thisPlayer = players[i];
|
||||
shared_ptr<ServerPlayer> thisPlayer = snapshot[i];
|
||||
if(thisPlayer->isSleeping())
|
||||
{
|
||||
if(firstSleepingPlayer == nullptr) firstSleepingPlayer = thisPlayer;
|
||||
|
|
@ -655,11 +687,13 @@ if (player->riding != nullptr)
|
|||
level->getTracker()->removeEntity(player);
|
||||
level->removeEntity(player);
|
||||
level->getChunkMap()->remove(player);
|
||||
EnterCriticalSection(&m_playersCS);
|
||||
auto it = find(players.begin(), players.end(), player);
|
||||
if( it != players.end() )
|
||||
{
|
||||
players.erase(it);
|
||||
}
|
||||
LeaveCriticalSection(&m_playersCS);
|
||||
// Notify fork clients that this player has left the server so they can
|
||||
// clean up IQNet/Tab list entries. Uses a custom payload channel so the
|
||||
// wire format of existing packets is unchanged (upstream clients simply
|
||||
|
|
@ -781,11 +815,13 @@ shared_ptr<ServerPlayer> PlayerList::respawn(shared_ptr<ServerPlayer> serverPlay
|
|||
}
|
||||
|
||||
serverPlayer->getLevel()->getChunkMap()->remove(serverPlayer);
|
||||
EnterCriticalSection(&m_playersCS);
|
||||
auto it = find(players.begin(), players.end(), serverPlayer);
|
||||
if( it != players.end() )
|
||||
{
|
||||
players.erase(it);
|
||||
}
|
||||
LeaveCriticalSection(&m_playersCS);
|
||||
server->getLevel(serverPlayer->dimension)->removeEntityImmediately(serverPlayer);
|
||||
|
||||
Pos *bedPosition = serverPlayer->getRespawnPosition();
|
||||
|
|
@ -899,7 +935,9 @@ shared_ptr<ServerPlayer> PlayerList::respawn(shared_ptr<ServerPlayer> serverPlay
|
|||
|
||||
level->getChunkMap()->add(player);
|
||||
level->addEntity(player);
|
||||
EnterCriticalSection(&m_playersCS);
|
||||
players.push_back(player);
|
||||
LeaveCriticalSection(&m_playersCS);
|
||||
|
||||
player->initMenu();
|
||||
player->setHealth(player->getHealth());
|
||||
|
|
@ -1135,10 +1173,11 @@ void PlayerList::tick()
|
|||
}
|
||||
|
||||
// Report cipher completion and open security gate for all completed handshakes
|
||||
vector<shared_ptr<ServerPlayer> > tickSnapshot = getPlayersSnapshot();
|
||||
for (unsigned char smallId : completed)
|
||||
{
|
||||
// Open the security gate -- flush buffered game packets now that cipher is active
|
||||
for (auto &p : players)
|
||||
for (auto &p : tickSnapshot)
|
||||
{
|
||||
if (p == nullptr || p->connection == nullptr) continue;
|
||||
INetworkPlayer *np = p->connection->getNetworkPlayer();
|
||||
|
|
@ -1168,7 +1207,7 @@ void PlayerList::tick()
|
|||
for (unsigned char smallId : completed)
|
||||
{
|
||||
// Find the player by smallId
|
||||
for (auto &p : players)
|
||||
for (auto &p : tickSnapshot)
|
||||
{
|
||||
if (p == nullptr || p->connection == nullptr) continue;
|
||||
INetworkPlayer *np = p->connection->getNetworkPlayer();
|
||||
|
|
@ -1212,7 +1251,7 @@ void PlayerList::tick()
|
|||
}
|
||||
|
||||
// Enforce identity token response timeout
|
||||
for (auto &p : players)
|
||||
for (auto &p : tickSnapshot)
|
||||
{
|
||||
if (p == nullptr || p->connection == nullptr) continue;
|
||||
int challengeTick = p->connection->getIdentityChallengeTick();
|
||||
|
|
@ -1244,27 +1283,78 @@ void PlayerList::tick()
|
|||
sendAllPlayerInfoIn = 0;
|
||||
}
|
||||
|
||||
if (sendAllPlayerInfoIn < players.size())
|
||||
{
|
||||
shared_ptr<ServerPlayer> op = players[sendAllPlayerInfoIn];
|
||||
vector<shared_ptr<ServerPlayer> > infoSnapshot = getPlayersSnapshot();
|
||||
if (sendAllPlayerInfoIn < infoSnapshot.size())
|
||||
{
|
||||
shared_ptr<ServerPlayer> op = infoSnapshot[sendAllPlayerInfoIn];
|
||||
//broadcastAll(shared_ptr<PlayerInfoPacket>( new PlayerInfoPacket(op->name, true, op->latency) ) );
|
||||
if( op->connection->getNetworkPlayer() )
|
||||
{
|
||||
broadcastAll(std::make_shared<PlayerInfoPacket>(op));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EnterCriticalSection(&m_closePlayersCS);
|
||||
while(!m_smallIdsToClose.empty())
|
||||
// Drain the pending disconnect queue. disconnect() enqueues here so it
|
||||
// can release done_cs before the heavy cleanup runs on the tick thread.
|
||||
{
|
||||
BYTE smallId = m_smallIdsToClose.front();
|
||||
m_smallIdsToClose.pop_front();
|
||||
std::deque<PendingDisconnect> dcCopy;
|
||||
EnterCriticalSection(&m_disconnectCS);
|
||||
dcCopy.swap(m_pendingDisconnects);
|
||||
LeaveCriticalSection(&m_disconnectCS);
|
||||
|
||||
while (!dcCopy.empty())
|
||||
{
|
||||
PendingDisconnect pd = dcCopy.front();
|
||||
dcCopy.pop_front();
|
||||
|
||||
server->getPlayers()->removePlayerFromReceiving(pd.player);
|
||||
if (pd.player->connection != nullptr)
|
||||
{
|
||||
pd.player->connection->send(std::make_shared<DisconnectPacket>(static_cast<DisconnectPacket::eDisconnectReason>(pd.reason)));
|
||||
pd.player->connection->connection->sendAndQuit();
|
||||
}
|
||||
|
||||
if (!pd.kickMessage.empty())
|
||||
{
|
||||
broadcastAll(std::make_shared<ChatPacket>(pd.kickMessage));
|
||||
}
|
||||
else if (!pd.fourKitHandledQuit)
|
||||
{
|
||||
if (pd.wasKicked)
|
||||
{
|
||||
broadcastAll(std::make_shared<ChatPacket>(pd.player->name, ChatPacket::e_ChatPlayerKickedFromGame));
|
||||
}
|
||||
else
|
||||
{
|
||||
broadcastAll(std::make_shared<ChatPacket>(pd.player->name, ChatPacket::e_ChatPlayerLeftGame));
|
||||
}
|
||||
}
|
||||
|
||||
remove(pd.player);
|
||||
}
|
||||
}
|
||||
|
||||
// Drain the close queue: snapshot the deque, then release the CS before
|
||||
// calling disconnect() which may itself try to acquire other locks.
|
||||
std::deque<BYTE> closeCopy;
|
||||
EnterCriticalSection(&m_closePlayersCS);
|
||||
closeCopy.swap(m_smallIdsToClose);
|
||||
LeaveCriticalSection(&m_closePlayersCS);
|
||||
|
||||
{
|
||||
vector<shared_ptr<ServerPlayer> > closeSnapshot = getPlayersSnapshot();
|
||||
while(!closeCopy.empty())
|
||||
{
|
||||
BYTE smallId = closeCopy.front();
|
||||
closeCopy.pop_front();
|
||||
|
||||
shared_ptr<ServerPlayer> player = nullptr;
|
||||
|
||||
for(unsigned int i = 0; i < players.size(); i++)
|
||||
for(unsigned int i = 0; i < closeSnapshot.size(); i++)
|
||||
{
|
||||
shared_ptr<ServerPlayer> p = players.at(i);
|
||||
shared_ptr<ServerPlayer> p = closeSnapshot.at(i);
|
||||
// 4J Stu - May be being a bit overprotective with all the nullptr checks, but adding late in TU7 so want to be safe
|
||||
if (p != nullptr && p->connection != nullptr && p->connection->connection != nullptr && p->connection->connection->getSocket() != nullptr && p->connection->connection->getSocket()->getSmallId() == smallId )
|
||||
{
|
||||
|
|
@ -1286,13 +1376,19 @@ void PlayerList::tick()
|
|||
WinsockNetLayer::ClearSocketForSmallId(smallId);
|
||||
#endif
|
||||
}
|
||||
LeaveCriticalSection(&m_closePlayersCS);
|
||||
}
|
||||
|
||||
std::deque<BYTE> kickCopy;
|
||||
EnterCriticalSection(&m_kickPlayersCS);
|
||||
while(!m_smallIdsToKick.empty())
|
||||
kickCopy.swap(m_smallIdsToKick);
|
||||
LeaveCriticalSection(&m_kickPlayersCS);
|
||||
|
||||
{
|
||||
BYTE smallId = m_smallIdsToKick.front();
|
||||
m_smallIdsToKick.pop_front();
|
||||
vector<shared_ptr<ServerPlayer> > kickSnapshot = getPlayersSnapshot();
|
||||
while(!kickCopy.empty())
|
||||
{
|
||||
BYTE smallId = kickCopy.front();
|
||||
kickCopy.pop_front();
|
||||
INetworkPlayer *selectedPlayer = g_NetworkManager.GetPlayerBySmallId(smallId);
|
||||
if( selectedPlayer != nullptr )
|
||||
{
|
||||
|
|
@ -1303,9 +1399,9 @@ void PlayerList::tick()
|
|||
// Kick this player from the game
|
||||
shared_ptr<ServerPlayer> player = nullptr;
|
||||
|
||||
for(unsigned int i = 0; i < players.size(); i++)
|
||||
for(unsigned int i = 0; i < kickSnapshot.size(); i++)
|
||||
{
|
||||
shared_ptr<ServerPlayer> p = players.at(i);
|
||||
shared_ptr<ServerPlayer> p = kickSnapshot.at(i);
|
||||
PlayerUID playersXuid = p->getOnlineXuid();
|
||||
if (p != nullptr && ProfileManager.AreXUIDSEqual(playersXuid, xuid ) )
|
||||
{
|
||||
|
|
@ -1326,7 +1422,7 @@ void PlayerList::tick()
|
|||
}
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&m_kickPlayersCS);
|
||||
}
|
||||
|
||||
// Check our receiving players, and if they are dead see if we can replace them
|
||||
for(unsigned int dim = 0; dim < 2; ++dim)
|
||||
|
|
@ -1360,29 +1456,32 @@ void PlayerList::prioritiseTileChanges(int x, int y, int z, int dimension)
|
|||
|
||||
void PlayerList::broadcastAll(shared_ptr<Packet> packet)
|
||||
{
|
||||
for (unsigned int i = 0; i < players.size(); i++)
|
||||
vector<shared_ptr<ServerPlayer> > snapshot = getPlayersSnapshot();
|
||||
for (unsigned int i = 0; i < snapshot.size(); i++)
|
||||
{
|
||||
shared_ptr<ServerPlayer> player = players[i];
|
||||
shared_ptr<ServerPlayer> player = snapshot[i];
|
||||
player->connection->send(packet);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerList::broadcastAll(shared_ptr<Packet> packet, int dimension)
|
||||
{
|
||||
for (unsigned int i = 0; i < players.size(); i++)
|
||||
vector<shared_ptr<ServerPlayer> > snapshot = getPlayersSnapshot();
|
||||
for (unsigned int i = 0; i < snapshot.size(); i++)
|
||||
{
|
||||
shared_ptr<ServerPlayer> player = players[i];
|
||||
shared_ptr<ServerPlayer> player = snapshot[i];
|
||||
if (player->dimension == dimension) player->connection->send(packet);
|
||||
}
|
||||
}
|
||||
|
||||
wstring PlayerList::getPlayerNames()
|
||||
{
|
||||
vector<shared_ptr<ServerPlayer> > snapshot = getPlayersSnapshot();
|
||||
wstring msg;
|
||||
for (unsigned int i = 0; i < players.size(); i++)
|
||||
for (unsigned int i = 0; i < snapshot.size(); i++)
|
||||
{
|
||||
if (i > 0) msg += L", ";
|
||||
msg += players[i]->name;
|
||||
msg += snapshot[i]->name;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
|
@ -1410,9 +1509,10 @@ bool PlayerList::isOp(shared_ptr<ServerPlayer> player)
|
|||
|
||||
shared_ptr<ServerPlayer> PlayerList::getPlayer(const wstring& name)
|
||||
{
|
||||
for (unsigned int i = 0; i < players.size(); i++)
|
||||
vector<shared_ptr<ServerPlayer> > snapshot = getPlayersSnapshot();
|
||||
for (unsigned int i = 0; i < snapshot.size(); i++)
|
||||
{
|
||||
shared_ptr<ServerPlayer> p = players[i];
|
||||
shared_ptr<ServerPlayer> p = snapshot[i];
|
||||
if (p->name == name) // 4J - used to be case insensitive (using equalsIgnoreCase) - imagine we'll be shifting to XUIDs anyway
|
||||
{
|
||||
return p;
|
||||
|
|
@ -1424,9 +1524,10 @@ shared_ptr<ServerPlayer> PlayerList::getPlayer(const wstring& name)
|
|||
// 4J Added
|
||||
shared_ptr<ServerPlayer> PlayerList::getPlayer(PlayerUID uid)
|
||||
{
|
||||
for (unsigned int i = 0; i < players.size(); i++)
|
||||
vector<shared_ptr<ServerPlayer> > snapshot = getPlayersSnapshot();
|
||||
for (unsigned int i = 0; i < snapshot.size(); i++)
|
||||
{
|
||||
shared_ptr<ServerPlayer> p = players[i];
|
||||
shared_ptr<ServerPlayer> p = snapshot[i];
|
||||
if (p->getXuid() == uid || p->getOnlineXuid() == uid) // 4J - used to be case insensitive (using equalsIgnoreCase) - imagine we'll be shifting to XUIDs anyway
|
||||
{
|
||||
return p;
|
||||
|
|
@ -1437,15 +1538,16 @@ shared_ptr<ServerPlayer> PlayerList::getPlayer(PlayerUID uid)
|
|||
|
||||
shared_ptr<ServerPlayer> PlayerList::getNearestPlayer(Pos *position, int range)
|
||||
{
|
||||
if (players.empty()) return nullptr;
|
||||
if (position == nullptr) return players.at(0);
|
||||
vector<shared_ptr<ServerPlayer> > snapshot = getPlayersSnapshot();
|
||||
if (snapshot.empty()) return nullptr;
|
||||
if (position == nullptr) return snapshot.at(0);
|
||||
shared_ptr<ServerPlayer> current = nullptr;
|
||||
double dist = -1;
|
||||
int rangeSqr = range * range;
|
||||
|
||||
for (size_t i = 0; i < players.size(); i++)
|
||||
for (size_t i = 0; i < snapshot.size(); i++)
|
||||
{
|
||||
shared_ptr<ServerPlayer> next = players.at(i);
|
||||
shared_ptr<ServerPlayer> next = snapshot.at(i);
|
||||
double newDist = position->distSqr(next->getCommandSenderWorldPosition());
|
||||
|
||||
if ((dist == -1 || newDist < dist) && (range <= 0 || newDist <= rangeSqr))
|
||||
|
|
@ -1566,9 +1668,10 @@ void PlayerList::broadcast(shared_ptr<Player> except, double x, double y, double
|
|||
sentTo.push_back(dynamic_pointer_cast<ServerPlayer>(except));
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < players.size(); i++)
|
||||
vector<shared_ptr<ServerPlayer> > snapshot = getPlayersSnapshot();
|
||||
for (unsigned int i = 0; i < snapshot.size(); i++)
|
||||
{
|
||||
shared_ptr<ServerPlayer> p = players[i];
|
||||
shared_ptr<ServerPlayer> p = snapshot[i];
|
||||
if (p == except) continue;
|
||||
if (p->dimension != dimension) continue;
|
||||
|
||||
|
|
@ -1630,14 +1733,15 @@ void PlayerList::saveAll(ProgressListener *progressListener, bool bDeleteGuestMa
|
|||
if(playerIo)
|
||||
{
|
||||
playerIo->saveAllCachedData();
|
||||
for (unsigned int i = 0; i < players.size(); i++)
|
||||
vector<shared_ptr<ServerPlayer> > snapshot = getPlayersSnapshot();
|
||||
for (unsigned int i = 0; i < snapshot.size(); i++)
|
||||
{
|
||||
playerIo->save(players[i]);
|
||||
playerIo->save(snapshot[i]);
|
||||
|
||||
//4J Stu - We don't want to save the map data for guests, so when we are sure that the player is gone delete the map
|
||||
if(bDeleteGuestMaps && players[i]->isGuest()) playerIo->deleteMapFilesForPlayer(players[i]);
|
||||
if(bDeleteGuestMaps && snapshot[i]->isGuest()) playerIo->deleteMapFilesForPlayer(snapshot[i]);
|
||||
|
||||
if(progressListener != nullptr) progressListener->progressStagePercentage((i * 100)/ static_cast<int>(players.size()));
|
||||
if(progressListener != nullptr) progressListener->progressStagePercentage((i * 100)/ static_cast<int>(snapshot.size()));
|
||||
}
|
||||
playerIo->clearOldPlayerFiles();
|
||||
playerIo->saveMapIdLookup();
|
||||
|
|
@ -1794,9 +1898,15 @@ void PlayerList::removePlayerFromReceiving(shared_ptr<ServerPlayer> player, bool
|
|||
}
|
||||
|
||||
INetworkPlayer *thisPlayer = player->connection->getNetworkPlayer();
|
||||
|
||||
// Snapshot the players vector to avoid iterator invalidation during concurrent modifications
|
||||
EnterCriticalSection(&m_playersCS);
|
||||
vector<shared_ptr<ServerPlayer> > playersSnapshot = players;
|
||||
LeaveCriticalSection(&m_playersCS);
|
||||
|
||||
if( thisPlayer && playerRemoved )
|
||||
{
|
||||
for(auto& newPlayer : players)
|
||||
for(auto& newPlayer : playersSnapshot)
|
||||
{
|
||||
INetworkPlayer *otherPlayer = newPlayer->connection->getNetworkPlayer();
|
||||
|
||||
|
|
@ -1821,7 +1931,7 @@ void PlayerList::removePlayerFromReceiving(shared_ptr<ServerPlayer> player, bool
|
|||
#endif
|
||||
// 4J Stu - Something went wrong, or possibly the QNet player left before we got here.
|
||||
// Re-check all active players and make sure they have someone on their system to receive all packets
|
||||
for(auto& newPlayer : players)
|
||||
for(auto& newPlayer : playersSnapshot)
|
||||
{
|
||||
INetworkPlayer *checkingPlayer = newPlayer->connection->getNetworkPlayer();
|
||||
|
||||
|
|
|
|||
|
|
@ -24,11 +24,26 @@ private:
|
|||
// public static Logger logger = Logger.getLogger("Minecraft");
|
||||
public:
|
||||
vector<shared_ptr<ServerPlayer> > players;
|
||||
CRITICAL_SECTION m_playersCS; // Protects players vector for concurrent access
|
||||
vector<shared_ptr<ServerPlayer> > getPlayersSnapshot();
|
||||
|
||||
private:
|
||||
MinecraftServer *server;
|
||||
unsigned int maxPlayers;
|
||||
|
||||
// Pending disconnect queue: disconnect() enqueues here, tick() drains it.
|
||||
// This avoids holding done_cs across PlayerList operations (deadlock fix).
|
||||
struct PendingDisconnect
|
||||
{
|
||||
shared_ptr<ServerPlayer> player;
|
||||
int reason;
|
||||
wstring kickMessage;
|
||||
bool wasKicked;
|
||||
bool fourKitHandledQuit;
|
||||
};
|
||||
deque<PendingDisconnect> m_pendingDisconnects;
|
||||
CRITICAL_SECTION m_disconnectCS;
|
||||
|
||||
// 4J Added
|
||||
vector<PlayerUID> m_bannedXuids;
|
||||
CRITICAL_SECTION m_banCS; // 4J Added - protects m_bannedXuids for concurrent access
|
||||
|
|
@ -82,6 +97,7 @@ public:
|
|||
void add(shared_ptr<ServerPlayer> player);
|
||||
void move(shared_ptr<ServerPlayer> player);
|
||||
void remove(shared_ptr<ServerPlayer> player);
|
||||
void queueDisconnect(shared_ptr<ServerPlayer> player, int reason, const wstring& kickMessage, bool wasKicked, bool fourKitHandledQuit);
|
||||
shared_ptr<ServerPlayer> getPlayerForLogin(PendingConnection *pendingConnection, const wstring& userName, PlayerUID xuid, PlayerUID OnlineXuid);
|
||||
shared_ptr<ServerPlayer> respawn(shared_ptr<ServerPlayer> serverPlayer, int targetDimension, bool keepAllPlayerData);
|
||||
void toggleDimension(shared_ptr<ServerPlayer> player, int targetDimension);
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
#include "../Minecraft.World/net.minecraft.world.level.h"
|
||||
#include "MultiPlayerLevel.h"
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
#include "..\Minecraft.Server\Security\SecurityConfig.h"
|
||||
#include "..\Minecraft.Server\ServerLogManager.h"
|
||||
#include "../Minecraft.Server/Security/SecurityConfig.h"
|
||||
#include "../Minecraft.Server/ServerLogManager.h"
|
||||
#endif
|
||||
|
||||
ServerConnection::ServerConnection(MinecraftServer *server)
|
||||
|
|
@ -20,6 +20,7 @@ ServerConnection::ServerConnection(MinecraftServer *server)
|
|||
// 4J - added initialiser
|
||||
connectionCounter = 0;
|
||||
InitializeCriticalSection(&pending_cs);
|
||||
InitializeCriticalSection(&players_cs);
|
||||
|
||||
this->server = server;
|
||||
}
|
||||
|
|
@ -27,6 +28,7 @@ ServerConnection::ServerConnection(MinecraftServer *server)
|
|||
ServerConnection::~ServerConnection()
|
||||
{
|
||||
DeleteCriticalSection(&pending_cs);
|
||||
DeleteCriticalSection(&players_cs);
|
||||
}
|
||||
|
||||
// 4J - added to handle incoming connections, to replace thread that original used to have
|
||||
|
|
@ -38,7 +40,9 @@ void ServerConnection::NewIncomingSocket(Socket *socket)
|
|||
|
||||
void ServerConnection::addPlayerConnection(shared_ptr<PlayerConnection> uc)
|
||||
{
|
||||
EnterCriticalSection(&players_cs);
|
||||
players.push_back(uc);
|
||||
LeaveCriticalSection(&players_cs);
|
||||
}
|
||||
|
||||
void ServerConnection::handleConnection(shared_ptr<PendingConnection> uc)
|
||||
|
|
@ -76,7 +80,9 @@ void ServerConnection::stop()
|
|||
}
|
||||
|
||||
// Snapshot to avoid iterator invalidation if disconnect modifies the vector.
|
||||
EnterCriticalSection(&players_cs);
|
||||
std::vector<shared_ptr<PlayerConnection> > playerSnapshot = players;
|
||||
LeaveCriticalSection(&players_cs);
|
||||
for (unsigned int i = 0; i < playerSnapshot.size(); i++)
|
||||
{
|
||||
shared_ptr<PlayerConnection> player = playerSnapshot[i];
|
||||
|
|
@ -118,26 +124,34 @@ void ServerConnection::tick()
|
|||
}
|
||||
LeaveCriticalSection(&pending_cs);
|
||||
|
||||
for (unsigned int i = 0; i < players.size(); i++)
|
||||
{
|
||||
shared_ptr<PlayerConnection> player = players[i];
|
||||
shared_ptr<ServerPlayer> serverPlayer = player->getPlayer();
|
||||
if( serverPlayer )
|
||||
EnterCriticalSection(&players_cs);
|
||||
vector< shared_ptr<PlayerConnection> > tempPlayers = players;
|
||||
LeaveCriticalSection(&players_cs);
|
||||
|
||||
for (unsigned int i = 0; i < tempPlayers.size(); i++)
|
||||
{
|
||||
serverPlayer->updateFrameTick();
|
||||
serverPlayer->doChunkSendingTick(false);
|
||||
shared_ptr<PlayerConnection> player = tempPlayers[i];
|
||||
shared_ptr<ServerPlayer> serverPlayer = player->getPlayer();
|
||||
if( serverPlayer )
|
||||
{
|
||||
serverPlayer->updateFrameTick();
|
||||
serverPlayer->doChunkSendingTick(false);
|
||||
}
|
||||
player->tick();
|
||||
if (player->done)
|
||||
{
|
||||
EnterCriticalSection(&players_cs);
|
||||
auto it = find(players.begin(), players.end(), player);
|
||||
if (it != players.end()) players.erase(it);
|
||||
LeaveCriticalSection(&players_cs);
|
||||
}
|
||||
else
|
||||
{
|
||||
player->connection->flush();
|
||||
}
|
||||
}
|
||||
player->tick();
|
||||
if (player->done)
|
||||
{
|
||||
players.erase(players.begin()+i);
|
||||
i--;
|
||||
}
|
||||
else
|
||||
{
|
||||
player->connection->flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -163,7 +177,10 @@ void ServerConnection::handleTextureReceived(const wstring &textureName)
|
|||
{
|
||||
m_pendingTextureRequests.erase(it);
|
||||
}
|
||||
for (auto& player : players)
|
||||
EnterCriticalSection(&players_cs);
|
||||
vector< shared_ptr<PlayerConnection> > tempPlayers = players;
|
||||
LeaveCriticalSection(&players_cs);
|
||||
for (auto& player : tempPlayers)
|
||||
{
|
||||
if (!player->done)
|
||||
{
|
||||
|
|
@ -179,7 +196,10 @@ void ServerConnection::handleTextureAndGeometryReceived(const wstring &textureNa
|
|||
{
|
||||
m_pendingTextureRequests.erase(it);
|
||||
}
|
||||
for (auto& player : players)
|
||||
EnterCriticalSection(&players_cs);
|
||||
vector< shared_ptr<PlayerConnection> > tempPlayers = players;
|
||||
LeaveCriticalSection(&players_cs);
|
||||
for (auto& player : tempPlayers)
|
||||
{
|
||||
if (!player->done)
|
||||
{
|
||||
|
|
@ -231,4 +251,46 @@ void ServerConnection::handleServerSettingsChanged(shared_ptr<ServerSettingsChan
|
|||
vector< shared_ptr<PlayerConnection> > * ServerConnection::getPlayers()
|
||||
{
|
||||
return &players;
|
||||
}
|
||||
|
||||
vector< shared_ptr<PlayerConnection> > ServerConnection::getPlayersSnapshot()
|
||||
{
|
||||
EnterCriticalSection(&players_cs);
|
||||
vector< shared_ptr<PlayerConnection> > snapshot = players;
|
||||
LeaveCriticalSection(&players_cs);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
void ServerConnection::sortPlayersByChunkPriority()
|
||||
{
|
||||
EnterCriticalSection(&players_cs);
|
||||
if( players.size() )
|
||||
{
|
||||
vector< shared_ptr<PlayerConnection> > playersOrig = players;
|
||||
players.clear();
|
||||
|
||||
do
|
||||
{
|
||||
int longestTime = 0;
|
||||
auto playerConnectionBest = playersOrig.begin();
|
||||
for( auto it = playersOrig.begin(); it != playersOrig.end(); it++)
|
||||
{
|
||||
int thisTime = 0;
|
||||
INetworkPlayer *np = (*it)->getNetworkPlayer();
|
||||
if( np )
|
||||
{
|
||||
thisTime = np->GetTimeSinceLastChunkPacket_ms();
|
||||
}
|
||||
|
||||
if( thisTime > longestTime )
|
||||
{
|
||||
playerConnectionBest = it;
|
||||
longestTime = thisTime;
|
||||
}
|
||||
}
|
||||
players.push_back(*playerConnectionBest);
|
||||
playersOrig.erase(playerConnectionBest);
|
||||
} while ( playersOrig.size() > 0 );
|
||||
}
|
||||
LeaveCriticalSection(&players_cs);
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ private:
|
|||
int connectionCounter;
|
||||
private:
|
||||
CRITICAL_SECTION pending_cs; // 4J added
|
||||
CRITICAL_SECTION players_cs; // Protects players vector for concurrent access
|
||||
vector< shared_ptr<PendingConnection> > pending;
|
||||
vector< shared_ptr<PlayerConnection> > players;
|
||||
|
||||
|
|
@ -47,4 +48,6 @@ public:
|
|||
void handleTextureAndGeometryReceived(const wstring &textureName);
|
||||
void handleServerSettingsChanged(shared_ptr<ServerSettingsChangedPacket> packet);
|
||||
vector< shared_ptr<PlayerConnection> > *getPlayers();
|
||||
vector< shared_ptr<PlayerConnection> > getPlayersSnapshot();
|
||||
void sortPlayersByChunkPriority();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@
|
|||
#include "../Minecraft.World/ProgressListener.h"
|
||||
#include "PS3/PS3Extras/ShutdownManager.h"
|
||||
#include "PlayerChunkMap.h"
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
#include "../Minecraft.Server/FourKitBridge.h"
|
||||
#endif
|
||||
|
||||
WeighedTreasureArray ServerLevel::RANDOM_BONUS_ITEMS;
|
||||
|
||||
|
|
@ -324,6 +327,8 @@ void ServerLevel::updateSleepingPlayerList()
|
|||
|
||||
for (auto& player : players)
|
||||
{
|
||||
if (player->fk_sleepingIgnored)
|
||||
continue;
|
||||
if (!player->isSleeping())
|
||||
{
|
||||
allPlayersSleeping = false;
|
||||
|
|
@ -368,6 +373,8 @@ bool ServerLevel::allPlayersAreSleeping()
|
|||
// all players are sleeping, but have they slept long enough?
|
||||
for (auto& player : players)
|
||||
{
|
||||
if (player->fk_sleepingIgnored)
|
||||
continue;
|
||||
// System.out.println(player->entityId + ": " + player->getSleepTimer());
|
||||
if (!player->isSleepingLongEnough())
|
||||
{
|
||||
|
|
@ -507,15 +514,21 @@ void ServerLevel::tickTiles()
|
|||
int val = (randValue >> 2);
|
||||
int x = (val & 15);
|
||||
int z = ((val >> 8) & 15);
|
||||
int yy = getTopRainBlock(x + xo, z + zo);
|
||||
if (shouldFreeze(x + xo, yy - 1, z + zo))
|
||||
{
|
||||
setTileAndUpdate(x + xo, yy - 1, z + zo, Tile::ice_Id);
|
||||
}
|
||||
if (isRaining() && shouldSnow(x + xo, yy, z + zo))
|
||||
{
|
||||
setTileAndUpdate(x + xo, yy, z + zo, Tile::topSnow_Id);
|
||||
}
|
||||
int yy = getTopRainBlock(x + xo, z + zo);
|
||||
if (shouldFreeze(x + xo, yy - 1, z + zo))
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (!FourKitBridge::FireBlockForm(dimension->id, x + xo, yy - 1, z + zo, Tile::ice_Id, 0))
|
||||
#endif
|
||||
setTileAndUpdate(x + xo, yy - 1, z + zo, Tile::ice_Id);
|
||||
}
|
||||
if (isRaining() && shouldSnow(x + xo, yy, z + zo))
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (!FourKitBridge::FireBlockForm(dimension->id, x + xo, yy, z + zo, Tile::topSnow_Id, 0))
|
||||
#endif
|
||||
setTileAndUpdate(x + xo, yy, z + zo, Tile::topSnow_Id);
|
||||
}
|
||||
if (isRaining())
|
||||
{
|
||||
Biome *b = getBiome(x + xo, z + zo);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,154 @@
|
|||
#include "../Minecraft.World/LevelChunk.h"
|
||||
#include "LevelRenderer.h"
|
||||
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
#include "../Minecraft.Server/FourKitBridge.h"
|
||||
#include "../Minecraft.World/ChatPacket.h"
|
||||
|
||||
static std::wstring FormatDeathMessage(const shared_ptr<ChatPacket>& packet)
|
||||
{
|
||||
if (!packet) return L"";
|
||||
|
||||
std::wstring message;
|
||||
bool replacePlayer = false;
|
||||
bool replaceEntitySource = false;
|
||||
bool replaceItem = false;
|
||||
// coug chough
|
||||
// de hättn echt an gscheidern string konverter für de todesmeldungen macha soin
|
||||
// a globaler helfer waar wahrscheinlich ganz bärig gwen
|
||||
// waaaah
|
||||
|
||||
switch (packet->m_messageType)
|
||||
{
|
||||
case ChatPacket::e_ChatCustom:
|
||||
return packet->m_stringArgs.size() > 0 ? packet->m_stringArgs[0] : L"";
|
||||
case ChatPacket::e_ChatDeathInFire:
|
||||
message = app.GetString(IDS_DEATH_INFIRE); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathOnFire:
|
||||
message = app.GetString(IDS_DEATH_ONFIRE); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathLava:
|
||||
message = app.GetString(IDS_DEATH_LAVA); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathInWall:
|
||||
message = app.GetString(IDS_DEATH_INWALL); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathDrown:
|
||||
message = app.GetString(IDS_DEATH_DROWN); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathStarve:
|
||||
message = app.GetString(IDS_DEATH_STARVE); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathCactus:
|
||||
message = app.GetString(IDS_DEATH_CACTUS); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathFall:
|
||||
message = app.GetString(IDS_DEATH_FALL); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathOutOfWorld:
|
||||
message = app.GetString(IDS_DEATH_OUTOFWORLD); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathGeneric:
|
||||
message = app.GetString(IDS_DEATH_GENERIC); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathExplosion:
|
||||
message = app.GetString(IDS_DEATH_EXPLOSION); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathMagic:
|
||||
message = app.GetString(IDS_DEATH_MAGIC); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathWither:
|
||||
message = app.GetString(IDS_DEATH_WITHER); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathDragonBreath:
|
||||
message = app.GetString(IDS_DEATH_DRAGON_BREATH); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathAnvil:
|
||||
message = app.GetString(IDS_DEATH_FALLING_ANVIL); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathFallingBlock:
|
||||
message = app.GetString(IDS_DEATH_FALLING_TILE); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathFellAccidentLadder:
|
||||
message = app.GetString(IDS_DEATH_FELL_ACCIDENT_LADDER); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathFellAccidentVines:
|
||||
message = app.GetString(IDS_DEATH_FELL_ACCIDENT_VINES); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathFellAccidentWater:
|
||||
message = app.GetString(IDS_DEATH_FELL_ACCIDENT_WATER); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathFellAccidentGeneric:
|
||||
message = app.GetString(IDS_DEATH_FELL_ACCIDENT_GENERIC); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathFellKiller:
|
||||
message = app.GetString(IDS_DEATH_FALL); replacePlayer = true; break;
|
||||
case ChatPacket::e_ChatDeathMob:
|
||||
message = app.GetString(IDS_DEATH_MOB); replacePlayer = true; replaceEntitySource = true; break;
|
||||
case ChatPacket::e_ChatDeathPlayer:
|
||||
message = app.GetString(IDS_DEATH_PLAYER); replacePlayer = true; replaceEntitySource = true; break;
|
||||
case ChatPacket::e_ChatDeathArrow:
|
||||
message = app.GetString(IDS_DEATH_ARROW); replacePlayer = true; replaceEntitySource = true; break;
|
||||
case ChatPacket::e_ChatDeathFireball:
|
||||
message = app.GetString(IDS_DEATH_FIREBALL); replacePlayer = true; replaceEntitySource = true; break;
|
||||
case ChatPacket::e_ChatDeathThrown:
|
||||
message = app.GetString(IDS_DEATH_THROWN); replacePlayer = true; replaceEntitySource = true; break;
|
||||
case ChatPacket::e_ChatDeathIndirectMagic:
|
||||
message = app.GetString(IDS_DEATH_INDIRECT_MAGIC); replacePlayer = true; replaceEntitySource = true; break;
|
||||
case ChatPacket::e_ChatDeathThorns:
|
||||
message = app.GetString(IDS_DEATH_THORNS); replacePlayer = true; replaceEntitySource = true; break;
|
||||
case ChatPacket::e_ChatDeathExplosionPlayer:
|
||||
message = app.GetString(IDS_DEATH_EXPLOSION_PLAYER); replacePlayer = true; replaceEntitySource = true; break;
|
||||
case ChatPacket::e_ChatDeathInFirePlayer:
|
||||
message = app.GetString(IDS_DEATH_INFIRE_PLAYER); replacePlayer = true; replaceEntitySource = true; break;
|
||||
case ChatPacket::e_ChatDeathOnFirePlayer:
|
||||
message = app.GetString(IDS_DEATH_ONFIRE_PLAYER); replacePlayer = true; replaceEntitySource = true; break;
|
||||
case ChatPacket::e_ChatDeathLavaPlayer:
|
||||
message = app.GetString(IDS_DEATH_LAVA_PLAYER); replacePlayer = true; replaceEntitySource = true; break;
|
||||
case ChatPacket::e_ChatDeathDrownPlayer:
|
||||
message = app.GetString(IDS_DEATH_DROWN_PLAYER); replacePlayer = true; replaceEntitySource = true; break;
|
||||
case ChatPacket::e_ChatDeathCactusPlayer:
|
||||
message = app.GetString(IDS_DEATH_CACTUS_PLAYER); replacePlayer = true; replaceEntitySource = true; break;
|
||||
case ChatPacket::e_ChatDeathFellAssist:
|
||||
message = app.GetString(IDS_DEATH_FELL_ASSIST); replacePlayer = true; replaceEntitySource = true; break;
|
||||
case ChatPacket::e_ChatDeathFellFinish:
|
||||
message = app.GetString(IDS_DEATH_FELL_FINISH); replacePlayer = true; replaceEntitySource = true; break;
|
||||
case ChatPacket::e_ChatDeathPlayerItem:
|
||||
message = app.GetString(IDS_DEATH_PLAYER_ITEM); replacePlayer = true; replaceEntitySource = true; replaceItem = true; break;
|
||||
case ChatPacket::e_ChatDeathArrowItem:
|
||||
message = app.GetString(IDS_DEATH_ARROW_ITEM); replacePlayer = true; replaceEntitySource = true; replaceItem = true; break;
|
||||
case ChatPacket::e_ChatDeathFireballItem:
|
||||
message = app.GetString(IDS_DEATH_FIREBALL_ITEM); replacePlayer = true; replaceEntitySource = true; replaceItem = true; break;
|
||||
case ChatPacket::e_ChatDeathThrownItem:
|
||||
message = app.GetString(IDS_DEATH_THROWN_ITEM); replacePlayer = true; replaceEntitySource = true; replaceItem = true; break;
|
||||
case ChatPacket::e_ChatDeathIndirectMagicItem:
|
||||
message = app.GetString(IDS_DEATH_INDIRECT_MAGIC_ITEM); replacePlayer = true; replaceEntitySource = true; replaceItem = true; break;
|
||||
case ChatPacket::e_ChatDeathFellAssistItem:
|
||||
message = app.GetString(IDS_DEATH_FELL_ASSIST_ITEM); replacePlayer = true; replaceEntitySource = true; replaceItem = true; break;
|
||||
case ChatPacket::e_ChatDeathFellFinishItem:
|
||||
message = app.GetString(IDS_DEATH_FELL_FINISH_ITEM); replacePlayer = true; replaceEntitySource = true; replaceItem = true; break;
|
||||
default:
|
||||
message = app.GetString(IDS_DEATH_GENERIC); replacePlayer = true; break;
|
||||
}
|
||||
|
||||
if (replacePlayer)
|
||||
{
|
||||
std::wstring playerName = packet->m_stringArgs.size() > 0 ? packet->m_stringArgs[0] : L"";
|
||||
message = replaceAll(message, L"{*PLAYER*}", playerName);
|
||||
}
|
||||
|
||||
if (replaceEntitySource)
|
||||
{
|
||||
std::wstring sourceName;
|
||||
if (!packet->m_intArgs.empty() && packet->m_intArgs[0] == eTYPE_SERVERPLAYER)
|
||||
{
|
||||
sourceName = packet->m_stringArgs.size() > 1 ? packet->m_stringArgs[1] : L"";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (packet->m_stringArgs.size() > 1 && !packet->m_stringArgs[1].empty())
|
||||
{
|
||||
sourceName = packet->m_stringArgs[1];
|
||||
}
|
||||
else if (!packet->m_intArgs.empty())
|
||||
{
|
||||
sourceName = app.getEntityName((eINSTANCEOF)packet->m_intArgs[0]);
|
||||
}
|
||||
}
|
||||
message = replaceAll(message, L"{*SOURCE*}", sourceName);
|
||||
}
|
||||
|
||||
if (replaceItem)
|
||||
{
|
||||
std::wstring itemName = packet->m_stringArgs.size() > 2 ? packet->m_stringArgs[2] : L"";
|
||||
message = replaceAll(message, L"{*ITEM*}", itemName);
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
ServerPlayer::ServerPlayer(MinecraftServer *server, Level *level, const wstring& name, ServerPlayerGameMode *gameMode) : Player(level, name)
|
||||
{
|
||||
|
|
@ -567,6 +715,45 @@ shared_ptr<ItemInstance> ServerPlayer::getCarried(int slot)
|
|||
|
||||
void ServerPlayer::die(DamageSource *source)
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
shared_ptr<ChatPacket> deathPacket = getCombatTracker()->getDeathMessagePacket();
|
||||
std::wstring deathMsg = FormatDeathMessage(deathPacket);
|
||||
|
||||
int exp = getExperienceReward(nullptr);
|
||||
std::wstring outDeathMsg;
|
||||
int keepInventory = 0;
|
||||
int outNewExp = 0, outNewLevel = 0, outKeepLevel = 0;
|
||||
int outExp = FourKitBridge::FirePlayerDeath(entityId, deathMsg, exp, outDeathMsg, &keepInventory,
|
||||
&outNewExp, &outNewLevel, &outKeepLevel);
|
||||
|
||||
fk_hasDeathState = true;
|
||||
fk_deathKeepInventory = (keepInventory != 0);
|
||||
fk_deathKeepLevel = (outKeepLevel != 0);
|
||||
fk_deathNewExp = outNewExp;
|
||||
fk_deathNewLevel = outNewLevel;
|
||||
|
||||
if (!outDeathMsg.empty())
|
||||
server->getPlayers()->broadcastAll(std::make_shared<ChatPacket>(outDeathMsg));
|
||||
|
||||
// LCE-Revelations: Hardcore mode enforcement. Donor's FourKit-aware die()
|
||||
// rewrite dropped this branch; we restore it AFTER FirePlayerDeath so
|
||||
// plugins still see the death event before the player is banned and
|
||||
// switched to Adventure mode.
|
||||
if (level->getLevelData()->isHardcore())
|
||||
{
|
||||
setGameMode(GameType::ADVENTURE);
|
||||
// Ban this player's XUID and queue disconnect.
|
||||
// The force-save is triggered inside banPlayerForHardcoreDeath after
|
||||
// the disconnect is queued, so the client doesn't get stuck on a save
|
||||
// screen.
|
||||
server->getPlayers()->banPlayerForHardcoreDeath(this);
|
||||
}
|
||||
|
||||
if (keepInventory == 0 && !level->getGameRules()->getBoolean(GameRules::RULE_KEEPINVENTORY))
|
||||
{
|
||||
inventory->dropAll();
|
||||
}
|
||||
#else
|
||||
server->getPlayers()->broadcastAll(getCombatTracker()->getDeathMessagePacket());
|
||||
|
||||
// 4J Added: Hardcore mode — switch to Adventure mode on death (can look but not break/place blocks)
|
||||
|
|
@ -584,6 +771,7 @@ void ServerPlayer::die(DamageSource *source)
|
|||
{
|
||||
inventory->dropAll();
|
||||
}
|
||||
#endif
|
||||
|
||||
vector<Objective *> *objectives = level->getScoreboard()->findObjectiveFor(ObjectiveCriteria::DEATH_COUNT);
|
||||
if(objectives)
|
||||
|
|
@ -777,6 +965,10 @@ void ServerPlayer::changeDimension(int i)
|
|||
}
|
||||
else
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
bool portalDestModified = false;
|
||||
double portalOutX = 0, portalOutY = 0, portalOutZ = 0;
|
||||
#endif
|
||||
if (dimension == 0 && i == 1)
|
||||
{
|
||||
awardStat(GenericStats::theEnd(), GenericStats::param_theEnd());
|
||||
|
|
@ -784,7 +976,24 @@ void ServerPlayer::changeDimension(int i)
|
|||
Pos *pos = server->getLevel(i)->getDimensionSpecificSpawn();
|
||||
if (pos != nullptr)
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
{
|
||||
double outX, outY, outZ;
|
||||
bool cancelled = FourKitBridge::FirePlayerPortal(entityId,
|
||||
x, y, z, dimension,
|
||||
pos->x, pos->y, pos->z, i,
|
||||
4,
|
||||
&outX, &outY, &outZ);
|
||||
if (cancelled)
|
||||
{
|
||||
delete pos;
|
||||
return;
|
||||
}
|
||||
connection->teleport(outX, outY, outZ, 0, 0);
|
||||
}
|
||||
#else
|
||||
connection->teleport(pos->x, pos->y, pos->z, 0, 0);
|
||||
#endif
|
||||
delete pos;
|
||||
}
|
||||
|
||||
|
|
@ -792,10 +1001,48 @@ void ServerPlayer::changeDimension(int i)
|
|||
}
|
||||
else
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
{
|
||||
double scale = server->getLevel(i)->getLevelData()->getHellScale();
|
||||
double toX = x, toY = y, toZ = z;
|
||||
if (i == -1)
|
||||
{
|
||||
toX = x / scale;
|
||||
toZ = z / scale;
|
||||
}
|
||||
else if (dimension == -1 && i == 0)
|
||||
{
|
||||
toX = x * scale;
|
||||
toZ = z * scale;
|
||||
}
|
||||
|
||||
double outX, outY, outZ;
|
||||
bool cancelled = FourKitBridge::FirePlayerPortal(entityId,
|
||||
x, y, z, dimension,
|
||||
toX, toY, toZ, i,
|
||||
3,
|
||||
&outX, &outY, &outZ);
|
||||
if (cancelled)
|
||||
return;
|
||||
if (outX != toX || outY != toY || outZ != toZ)
|
||||
{
|
||||
portalDestModified = true;
|
||||
portalOutX = outX;
|
||||
portalOutY = outY;
|
||||
portalOutZ = outZ;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// 4J: Removed on the advice of the mighty King of Achievments (JV)
|
||||
// awardStat(GenericStats::portal(), GenericStats::param_portal());
|
||||
}
|
||||
server->getPlayers()->toggleDimension( dynamic_pointer_cast<ServerPlayer>(shared_from_this()), i);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (portalDestModified)
|
||||
{
|
||||
connection->teleport(portalOutX, portalOutY, portalOutZ, yRot, xRot);
|
||||
}
|
||||
#endif
|
||||
lastSentExp = -1;
|
||||
lastSentHealth = -1;
|
||||
lastSentFood = -1;
|
||||
|
|
@ -825,6 +1072,10 @@ void ServerPlayer::take(shared_ptr<Entity> e, int orgCount)
|
|||
|
||||
Player::BedSleepingResult ServerPlayer::startSleepInBed(int x, int y, int z, bool bTestUse)
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (!bTestUse && FourKitBridge::FireBedEnter(entityId, dimension, x, y, z))
|
||||
return OTHER_PROBLEM;
|
||||
#endif
|
||||
BedSleepingResult result = Player::startSleepInBed(x, y, z, bTestUse);
|
||||
if (result == OK)
|
||||
{
|
||||
|
|
@ -838,12 +1089,22 @@ Player::BedSleepingResult ServerPlayer::startSleepInBed(int x, int y, int z, boo
|
|||
|
||||
void ServerPlayer::stopSleepInBed(bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint)
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
int bedX = bedPosition ? bedPosition->x : 0;
|
||||
int bedY = bedPosition ? bedPosition->y : 0;
|
||||
int bedZ = bedPosition ? bedPosition->z : 0;
|
||||
bool wasSleeping = isSleeping();
|
||||
#endif
|
||||
if (isSleeping())
|
||||
{
|
||||
getLevel()->getTracker()->broadcastAndSend(shared_from_this(), std::make_shared<AnimatePacket>(shared_from_this(), AnimatePacket::WAKE_UP));
|
||||
}
|
||||
Player::stopSleepInBed(forcefulWakeUp, updateLevelList, saveRespawnPoint);
|
||||
if (connection != nullptr) connection->teleport(x, y, z, yRot, xRot);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (wasSleeping)
|
||||
FourKitBridge::FireBedLeave(entityId, dimension, bedX, bedY, bedZ);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ServerPlayer::ride(shared_ptr<Entity> e)
|
||||
|
|
@ -886,10 +1147,18 @@ bool ServerPlayer::startCrafting(int x, int y, int z)
|
|||
if(containerMenu == inventoryMenu)
|
||||
{
|
||||
nextContainerCounter();
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::WORKBENCH, L"", 9, false));
|
||||
containerMenu = new CraftingMenu(inventory, level, x, y, z);
|
||||
containerMenu->containerId = containerCounter;
|
||||
containerMenu->addSlotListener(this);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::WORKBENCH, L"", 9))
|
||||
{
|
||||
doCloseContainer();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::WORKBENCH, L"", 9, false));
|
||||
refreshContainer(containerMenu);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -904,20 +1173,36 @@ bool ServerPlayer::openFireworks(int x, int y, int z)
|
|||
if(containerMenu == inventoryMenu)
|
||||
{
|
||||
nextContainerCounter();
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::FIREWORKS, L"", 9, false));
|
||||
containerMenu = new FireworksMenu(inventory, level, x, y, z);
|
||||
containerMenu->containerId = containerCounter;
|
||||
containerMenu->addSlotListener(this);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::FIREWORKS, L"", 9))
|
||||
{
|
||||
doCloseContainer();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::FIREWORKS, L"", 9, false));
|
||||
refreshContainer(containerMenu);
|
||||
}
|
||||
else if(dynamic_cast<CraftingMenu *>(containerMenu) != nullptr)
|
||||
{
|
||||
closeContainer();
|
||||
|
||||
nextContainerCounter();
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::FIREWORKS, L"", 9, false));
|
||||
containerMenu = new FireworksMenu(inventory, level, x, y, z);
|
||||
containerMenu->containerId = containerCounter;
|
||||
containerMenu->addSlotListener(this);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::FIREWORKS, L"", 9))
|
||||
{
|
||||
doCloseContainer();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::FIREWORKS, L"", 9, false));
|
||||
refreshContainer(containerMenu);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -932,10 +1217,18 @@ bool ServerPlayer::startEnchanting(int x, int y, int z, const wstring &name)
|
|||
if(containerMenu == inventoryMenu)
|
||||
{
|
||||
nextContainerCounter();
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::ENCHANTMENT, name.empty() ? L"" : name, 9, !name.empty()));
|
||||
containerMenu = new EnchantmentMenu(inventory, level, x, y, z);
|
||||
containerMenu->containerId = containerCounter;
|
||||
containerMenu->addSlotListener(this);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::ENCHANTMENT, name.empty() ? L"" : name, 9))
|
||||
{
|
||||
doCloseContainer();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::ENCHANTMENT, name.empty() ? L"" : name, 9, !name.empty()));
|
||||
refreshContainer(containerMenu);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -950,10 +1243,18 @@ bool ServerPlayer::startRepairing(int x, int y, int z)
|
|||
if(containerMenu == inventoryMenu)
|
||||
{
|
||||
nextContainerCounter();
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::REPAIR_TABLE, L"", 9, false));
|
||||
containerMenu = new AnvilMenu(inventory, level, x, y, z, dynamic_pointer_cast<Player>(shared_from_this()));
|
||||
containerMenu->containerId = containerCounter;
|
||||
containerMenu->addSlotListener(this);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::REPAIR_TABLE, L"", 9))
|
||||
{
|
||||
doCloseContainer();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::REPAIR_TABLE, L"", 9, false));
|
||||
refreshContainer(containerMenu);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -973,11 +1274,18 @@ bool ServerPlayer::openContainer(shared_ptr<Container> container)
|
|||
int containerType = container->getContainerType();
|
||||
assert(containerType >= 0);
|
||||
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, containerType, container->getCustomName(), container->getContainerSize(), container->hasCustomName()));
|
||||
|
||||
containerMenu = new ContainerMenu(inventory, container);
|
||||
containerMenu->containerId = containerCounter;
|
||||
containerMenu->addSlotListener(this);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (FourKitBridge::FireInventoryOpen(entityId, containerType, container->getCustomName(), container->getContainerSize()))
|
||||
{
|
||||
doCloseContainer();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, containerType, container->getCustomName(), container->getContainerSize(), container->hasCustomName()));
|
||||
refreshContainer(containerMenu);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -992,10 +1300,18 @@ bool ServerPlayer::openHopper(shared_ptr<HopperTileEntity> container)
|
|||
if(containerMenu == inventoryMenu)
|
||||
{
|
||||
nextContainerCounter();
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::HOPPER, container->getCustomName(), container->getContainerSize(), container->hasCustomName()));
|
||||
containerMenu = new HopperMenu(inventory, container);
|
||||
containerMenu->containerId = containerCounter;
|
||||
containerMenu->addSlotListener(this);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::HOPPER, container->getCustomName(), container->getContainerSize()))
|
||||
{
|
||||
doCloseContainer();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::HOPPER, container->getCustomName(), container->getContainerSize(), container->hasCustomName()));
|
||||
refreshContainer(containerMenu);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1010,10 +1326,18 @@ bool ServerPlayer::openHopper(shared_ptr<MinecartHopper> container)
|
|||
if(containerMenu == inventoryMenu)
|
||||
{
|
||||
nextContainerCounter();
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::HOPPER, container->getCustomName(), container->getContainerSize(), container->hasCustomName()));
|
||||
containerMenu = new HopperMenu(inventory, container);
|
||||
containerMenu->containerId = containerCounter;
|
||||
containerMenu->addSlotListener(this);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::HOPPER, container->getCustomName(), container->getContainerSize()))
|
||||
{
|
||||
doCloseContainer();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::HOPPER, container->getCustomName(), container->getContainerSize(), container->hasCustomName()));
|
||||
refreshContainer(containerMenu);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1028,10 +1352,18 @@ bool ServerPlayer::openFurnace(shared_ptr<FurnaceTileEntity> furnace)
|
|||
if(containerMenu == inventoryMenu)
|
||||
{
|
||||
nextContainerCounter();
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::FURNACE, furnace->getCustomName(), furnace->getContainerSize(), furnace->hasCustomName()));
|
||||
containerMenu = new FurnaceMenu(inventory, furnace);
|
||||
containerMenu->containerId = containerCounter;
|
||||
containerMenu->addSlotListener(this);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::FURNACE, furnace->getCustomName(), furnace->getContainerSize()))
|
||||
{
|
||||
doCloseContainer();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::FURNACE, furnace->getCustomName(), furnace->getContainerSize(), furnace->hasCustomName()));
|
||||
refreshContainer(containerMenu);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1046,10 +1378,18 @@ bool ServerPlayer::openTrap(shared_ptr<DispenserTileEntity> trap)
|
|||
if(containerMenu == inventoryMenu)
|
||||
{
|
||||
nextContainerCounter();
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, trap->GetType() == eTYPE_DROPPERTILEENTITY ? ContainerOpenPacket::DROPPER : ContainerOpenPacket::TRAP, trap->getCustomName(), trap->getContainerSize(), trap->hasCustomName()));
|
||||
containerMenu = new TrapMenu(inventory, trap);
|
||||
containerMenu->containerId = containerCounter;
|
||||
containerMenu->addSlotListener(this);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (FourKitBridge::FireInventoryOpen(entityId, trap->GetType() == eTYPE_DROPPERTILEENTITY ? ContainerOpenPacket::DROPPER : ContainerOpenPacket::TRAP, trap->getCustomName(), trap->getContainerSize()))
|
||||
{
|
||||
doCloseContainer();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, trap->GetType() == eTYPE_DROPPERTILEENTITY ? ContainerOpenPacket::DROPPER : ContainerOpenPacket::TRAP, trap->getCustomName(), trap->getContainerSize(), trap->hasCustomName()));
|
||||
refreshContainer(containerMenu);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1064,10 +1404,18 @@ bool ServerPlayer::openBrewingStand(shared_ptr<BrewingStandTileEntity> brewingSt
|
|||
if(containerMenu == inventoryMenu)
|
||||
{
|
||||
nextContainerCounter();
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::BREWING_STAND, brewingStand->getCustomName(), brewingStand->getContainerSize(), brewingStand->hasCustomName()));
|
||||
containerMenu = new BrewingStandMenu(inventory, brewingStand);
|
||||
containerMenu->containerId = containerCounter;
|
||||
containerMenu->addSlotListener(this);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::BREWING_STAND, brewingStand->getCustomName(), brewingStand->getContainerSize()))
|
||||
{
|
||||
doCloseContainer();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::BREWING_STAND, brewingStand->getCustomName(), brewingStand->getContainerSize(), brewingStand->hasCustomName()));
|
||||
refreshContainer(containerMenu);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1082,10 +1430,20 @@ bool ServerPlayer::openBeacon(shared_ptr<BeaconTileEntity> beacon)
|
|||
if(containerMenu == inventoryMenu)
|
||||
{
|
||||
nextContainerCounter();
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::BEACON, beacon->getCustomName(), beacon->getContainerSize(), beacon->hasCustomName()));
|
||||
containerMenu = new BeaconMenu(inventory, beacon);
|
||||
containerMenu->containerId = containerCounter;
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::BEACON, beacon->getCustomName(), beacon->getContainerSize()))
|
||||
{
|
||||
doCloseContainer();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
// Send the open packet BEFORE addSlotListener so the client has the
|
||||
// menu ready when the beacon data (levels, powers) arrives.
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::BEACON, beacon->getCustomName(), beacon->getContainerSize(), beacon->hasCustomName()));
|
||||
containerMenu->addSlotListener(this);
|
||||
refreshContainer(containerMenu);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1105,7 +1463,15 @@ bool ServerPlayer::openTrading(shared_ptr<Merchant> traderTarget, const wstring
|
|||
containerMenu->addSlotListener(this);
|
||||
shared_ptr<Container> container = static_cast<MerchantMenu *>(containerMenu)->getTradeContainer();
|
||||
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::TRADER_NPC, name.empty() ? L"" : name, container->getContainerSize()))
|
||||
{
|
||||
doCloseContainer();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::TRADER_NPC, name.empty() ? L"" : name, container->getContainerSize(), !name.empty()));
|
||||
refreshContainer(containerMenu);
|
||||
|
||||
MerchantRecipeList *offers = traderTarget->getOffers(dynamic_pointer_cast<Player>(shared_from_this()));
|
||||
if (offers != nullptr)
|
||||
|
|
@ -1135,10 +1501,18 @@ bool ServerPlayer::openHorseInventory(shared_ptr<EntityHorse> horse, shared_ptr<
|
|||
closeContainer();
|
||||
}
|
||||
nextContainerCounter();
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::HORSE, horse->getCustomName(), container->getContainerSize(), container->hasCustomName(), horse->entityId));
|
||||
containerMenu = new HorseInventoryMenu(inventory, container, horse);
|
||||
containerMenu->containerId = containerCounter;
|
||||
containerMenu->addSlotListener(this);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::HORSE, horse->getCustomName(), container->getContainerSize()))
|
||||
{
|
||||
doCloseContainer();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::HORSE, horse->getCustomName(), container->getContainerSize(), container->hasCustomName(), horse->entityId));
|
||||
refreshContainer(containerMenu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1621,9 +1995,9 @@ bool ServerPlayer::hasPermission(EGameCommand command)
|
|||
//
|
||||
// // 4J - Don't need
|
||||
// //if (server.isSingleplayer() && server.getSingleplayerName().equals(name))
|
||||
/// //{
|
||||
// //{
|
||||
// // server.setDifficulty(packet.getDifficulty());
|
||||
/// //}
|
||||
// //}
|
||||
//}
|
||||
|
||||
int ServerPlayer::getViewDistance()
|
||||
|
|
|
|||
|
|
@ -10,9 +10,18 @@
|
|||
#include "../Minecraft.World/net.minecraft.world.level.h"
|
||||
#include "../Minecraft.World/net.minecraft.world.level.chunk.h"
|
||||
#include "../Minecraft.World/net.minecraft.world.level.dimension.h"
|
||||
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
#include "../Minecraft.World/EnchantmentHelper.h"
|
||||
#include "../Minecraft.World/ExperienceOrb.h"
|
||||
#include "../Minecraft.Server/FourKitBridge.h"
|
||||
#endif
|
||||
|
||||
#include "MultiPlayerLevel.h"
|
||||
#include "LevelRenderer.h"
|
||||
|
||||
extern bool g_suppressExpDrops;
|
||||
|
||||
ServerPlayerGameMode::ServerPlayerGameMode(Level *level)
|
||||
{
|
||||
// 4J - added initialisers
|
||||
|
|
@ -171,7 +180,7 @@ void ServerPlayerGameMode::startDestroyBlock(int x, int y, int z, int face)
|
|||
}
|
||||
}
|
||||
|
||||
bool ServerPlayerGameMode::stopDestroyBlock(int x, int y, int z)
|
||||
void ServerPlayerGameMode::stopDestroyBlock(int x, int y, int z)
|
||||
{
|
||||
if (x == xDestroyBlock && y == yDestroyBlock && z == zDestroyBlock)
|
||||
{
|
||||
|
|
@ -187,7 +196,6 @@ bool ServerPlayerGameMode::stopDestroyBlock(int x, int y, int z)
|
|||
isDestroyingBlock = false;
|
||||
level->destroyTileProgress(player->entityId, x, y, z, -1);
|
||||
destroyBlock(x, y, z);
|
||||
return true;
|
||||
}
|
||||
else if (!hasDelayedDestroy)
|
||||
{
|
||||
|
|
@ -198,11 +206,9 @@ bool ServerPlayerGameMode::stopDestroyBlock(int x, int y, int z)
|
|||
delayedDestroyY = y;
|
||||
delayedDestroyZ = z;
|
||||
delayedTickStart = destroyProgressStart;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ServerPlayerGameMode::abortDestroyBlock(int x, int y, int z)
|
||||
|
|
@ -249,7 +255,43 @@ bool ServerPlayerGameMode::destroyBlock(int x, int y, int z)
|
|||
|
||||
int t = level->getTile(x, y, z);
|
||||
int data = level->getData(x, y, z);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
int eventExp = 0;
|
||||
if (!isCreative() && !gameModeForPlayer->isAdventureRestricted())
|
||||
{
|
||||
Tile *tile = Tile::tiles[t];
|
||||
if (tile != nullptr && player->canDestroy(tile))
|
||||
{
|
||||
if (!EnchantmentHelper::hasSilkTouch(player))
|
||||
{
|
||||
// (SYLV)todo: shouldnt we get these values from the actual blocks?
|
||||
if (t == Tile::coalOre_Id)
|
||||
eventExp = Mth::nextInt(level->random, 0, 2);
|
||||
else if (t == Tile::diamondOre_Id)
|
||||
eventExp = Mth::nextInt(level->random, 3, 7);
|
||||
else if (t == Tile::emeraldOre_Id)
|
||||
eventExp = Mth::nextInt(level->random, 3, 7);
|
||||
else if (t == Tile::lapisOre_Id)
|
||||
eventExp = Mth::nextInt(level->random, 2, 5);
|
||||
else if (t == Tile::netherQuartz_Id)
|
||||
eventExp = Mth::nextInt(level->random, 2, 5);
|
||||
else if (t == Tile::redStoneOre_Id || t == Tile::redStoneOre_lit_Id)
|
||||
eventExp = 1 + level->random->nextInt(5);
|
||||
else if (t == Tile::mobSpawner_Id)
|
||||
eventExp = 15 + level->random->nextInt(15) + level->random->nextInt(15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int dimId = level->dimension ? level->dimension->id : 0;
|
||||
int breakResult = FourKitBridge::FireBlockBreak(player->entityId, dimId, x, y, z, t, data, eventExp);
|
||||
if (breakResult < 0)
|
||||
{
|
||||
player->connection->send(std::make_shared<TileUpdatePacket>(x, y, z, level));
|
||||
return false;
|
||||
}
|
||||
int finalExp = breakResult;
|
||||
#endif
|
||||
level->levelEvent(player, LevelEvent::PARTICLES_DESTROY_BLOCK, x, y, z, t + (level->getData(x, y, z) << Tile::TILE_NUM_SHIFT));
|
||||
|
||||
// 4J - In creative mode, the point where we need to tell the renderer that we are about to destroy a tile via destroyingTileAt is quite complicated.
|
||||
|
|
@ -307,8 +349,25 @@ bool ServerPlayerGameMode::destroyBlock(int x, int y, int z)
|
|||
}
|
||||
}
|
||||
if (changed && canDestroy)
|
||||
{
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
g_suppressExpDrops = true;
|
||||
#endif
|
||||
|
||||
Tile::tiles[t]->playerDestroy(level, player, x, y, z, data);
|
||||
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
g_suppressExpDrops = false;
|
||||
if (finalExp > 0)
|
||||
{
|
||||
while (finalExp > 0)
|
||||
{
|
||||
int xpDrop = ExperienceOrb::getExperienceValue(finalExp);
|
||||
finalExp -= xpDrop;
|
||||
level->addEntity(std::make_shared<ExperienceOrb>(level, x + .5, y + .5, z + .5, xpDrop));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public:
|
|||
|
||||
void tick();
|
||||
void startDestroyBlock(int x, int y, int z, int face);
|
||||
bool stopDestroyBlock(int x, int y, int z);
|
||||
void stopDestroyBlock(int x, int y, int z);
|
||||
void abortDestroyBlock(int x, int y, int z);
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
#include "../Minecraft.World/net.minecraft.world.level.h"
|
||||
#include "../Minecraft.World/net.minecraft.world.level.dimension.h"
|
||||
#include "TeleportCommand.h"
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
#include "../Minecraft.Server/FourKitBridge.h"
|
||||
#endif
|
||||
|
||||
EGameCommand TeleportCommand::getId()
|
||||
{
|
||||
|
|
@ -32,7 +35,21 @@ void TeleportCommand::execute(shared_ptr<CommandSender> source, byteArray comman
|
|||
if(subject != nullptr && destination != nullptr && subject->level->dimension->id == destination->level->dimension->id && subject->isAlive() )
|
||||
{
|
||||
subject->ride(nullptr);
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
{
|
||||
double outX, outY, outZ;
|
||||
bool cancelled = FourKitBridge::FirePlayerTeleport(subject->entityId,
|
||||
subject->x, subject->y, subject->z, subject->dimension,
|
||||
destination->x, destination->y, destination->z, destination->dimension,
|
||||
1 /* COMMAND */,
|
||||
&outX, &outY, &outZ);
|
||||
if (cancelled)
|
||||
return;
|
||||
subject->connection->teleport(outX, outY, outZ, destination->yRot, destination->xRot);
|
||||
}
|
||||
#else
|
||||
subject->connection->teleport(destination->x, destination->y, destination->z, destination->yRot, destination->xRot);
|
||||
#endif
|
||||
//logAdminAction(source, "commands.tp.success", subject->getAName(), destination->getAName());
|
||||
logAdminAction(source, ChatPacket::e_ChatCommandTeleportSuccess, subject->getName(), eTYPE_SERVERPLAYER, destination->getName());
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include "stdafx.h"
|
||||
#include "ModelPart.h"
|
||||
#include "VillagerZombieModel.h"
|
||||
#include "..\Minecraft.World\Mth.h"
|
||||
#include "../Minecraft.World/Mth.h"
|
||||
|
||||
void VillagerZombieModel::_init(float g, float yOffset, bool isArmor)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
#include "../../Minecraft.h"
|
||||
|
||||
#include <windns.h>
|
||||
#pragma comment(lib, "Dnsapi.lib")
|
||||
#pragma comment(lib, "dnsapi.lib")
|
||||
#include "../4JLibs/inc/4J_Profile.h"
|
||||
|
||||
#include <string>
|
||||
|
|
|
|||
4
Minecraft.Server.FourKit/.editorconfig
Normal file
4
Minecraft.Server.FourKit/.editorconfig
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
[*.cs]
|
||||
|
||||
# IDE1006: Naming Styles
|
||||
dotnet_diagnostic.IDE1006.severity = suggestion
|
||||
26
Minecraft.Server.FourKit/Block/Action.cs
Normal file
26
Minecraft.Server.FourKit/Block/Action.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
namespace Minecraft.Server.FourKit.Block;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the action type for a player interaction.
|
||||
/// </summary>
|
||||
public enum Action
|
||||
{
|
||||
/// <summary>Left-clicking the air.</summary>
|
||||
LEFT_CLICK_AIR = 0,
|
||||
|
||||
/// <summary>Left-clicking a block.</summary>
|
||||
LEFT_CLICK_BLOCK = 1,
|
||||
|
||||
/// <summary>Right-clicking the air.</summary>
|
||||
RIGHT_CLICK_AIR = 2,
|
||||
|
||||
/// <summary>Right-clicking a block.</summary>
|
||||
RIGHT_CLICK_BLOCK = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Stepping onto or into a block (Ass-pressure).
|
||||
/// Examples: Jumping on soil, Standing on pressure plate,
|
||||
/// Triggering redstone ore, Triggering tripwire.
|
||||
/// </summary>
|
||||
PHYSICAL = 4
|
||||
}
|
||||
166
Minecraft.Server.FourKit/Block/Block.cs
Normal file
166
Minecraft.Server.FourKit/Block/Block.cs
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
namespace Minecraft.Server.FourKit.Block;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a block. This is a live object, and only one Block may exist for
|
||||
/// any given location in a world.
|
||||
/// </summary>
|
||||
public class Block
|
||||
{
|
||||
private readonly World _world;
|
||||
private readonly int _x;
|
||||
private readonly int _y;
|
||||
private readonly int _z;
|
||||
|
||||
internal Block(World world, int x, int y, int z)
|
||||
{
|
||||
_world = world;
|
||||
_x = x;
|
||||
_y = y;
|
||||
_z = z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Location of the block.
|
||||
/// </summary>
|
||||
/// <returns>Location of the block.</returns>
|
||||
public Location getLocation()
|
||||
{
|
||||
return new Location(_world, _x, _y, _z, 0f, 0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of this block.
|
||||
/// </summary>
|
||||
/// <returns>Block type.</returns>
|
||||
public Material getType()
|
||||
{
|
||||
int id = getTypeId();
|
||||
return Enum.IsDefined(typeof(Material), id) ? (Material)id : Material.AIR;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type ID of this block.
|
||||
/// </summary>
|
||||
/// <returns>Block type ID.</returns>
|
||||
public int getTypeId()
|
||||
{
|
||||
if (NativeBridge.GetTileId != null)
|
||||
return NativeBridge.GetTileId(_world.getDimensionId(), _x, _y, _z);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the world which contains this Block.
|
||||
/// </summary>
|
||||
/// <returns>World containing this block.</returns>
|
||||
public World getWorld() => _world;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the x-coordinate of this block.
|
||||
/// </summary>
|
||||
/// <returns>X-coordinate.</returns>
|
||||
public int getX() => _x;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the y-coordinate of this block.
|
||||
/// </summary>
|
||||
/// <returns>Y-coordinate.</returns>
|
||||
public int getY() => _y;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the z-coordinate of this block.
|
||||
/// </summary>
|
||||
/// <returns>Z-coordinate.</returns>
|
||||
public int getZ() => _z;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the type of this block.
|
||||
/// </summary>
|
||||
/// <param name="type">Material to change this block to.</param>
|
||||
public void setType(Material type)
|
||||
{
|
||||
setTypeId((int)type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the type ID of this block.
|
||||
/// </summary>
|
||||
/// <param name="type">Type ID to change this block to.</param>
|
||||
/// <returns>Whether the change was successful.</returns>
|
||||
public bool setTypeId(int type)
|
||||
{
|
||||
NativeBridge.SetTile?.Invoke(_world.getDimensionId(), _x, _y, _z, type, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the metadata value for this block.
|
||||
/// </summary>
|
||||
/// <returns>Block specific metadata.</returns>
|
||||
public byte getData()
|
||||
{
|
||||
return (byte)NativeBridge.GetTileData(_world.getDimensionId(), _x, _y, _z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the metadata value for this block.
|
||||
/// </summary>
|
||||
/// <param name="data">New block specific metadata.</param>
|
||||
public void setData(byte data)
|
||||
{
|
||||
NativeBridge.SetTileData?.Invoke(_world.getDimensionId(), _x, _y, _z, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Breaks the block and spawns items as if a player had digged it.
|
||||
/// </summary>
|
||||
/// <returns>true if the block was destroyed.</returns>
|
||||
public bool breakNaturally()
|
||||
{
|
||||
if (NativeBridge.BreakBlock != null)
|
||||
return NativeBridge.BreakBlock(_world.getDimensionId(), _x, _y, _z) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the block at the given offsets
|
||||
/// </summary>
|
||||
/// <param name="modX">X offset</param>
|
||||
/// <param name="modY">Y offset</param>
|
||||
/// <param name="modZ">Z offset</param>
|
||||
/// <returns>Block at the given offsets</returns>
|
||||
public Block getRelative(int modX, int modY, int modZ)
|
||||
{
|
||||
return getWorld().getBlockAt(getX() + modX, getY() + modY, getZ() + modZ);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the block at the given face
|
||||
/// <para>This method is equal to getRelative(face, 1)</para>
|
||||
/// </summary>
|
||||
/// <param name="face">BlockFace to get relative to</param>
|
||||
/// <returns>Block at the given face</returns>
|
||||
public Block getRelative(BlockFace face)
|
||||
{
|
||||
return getRelative(face, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the block at the given distance of the given face
|
||||
/// <para>For example, the following method places water at 100,102,100; two
|
||||
/// blocks above 100,100,100.</para>
|
||||
/// <code>
|
||||
/// Block block = world.getBlockAt(100, 100, 100);
|
||||
/// Block shower = block.getRelative(BlockFace.UP, 2);
|
||||
/// shower.setType(Material.WATER);
|
||||
/// </code>
|
||||
/// </summary>
|
||||
/// <param name="face">BlockFace to get relative to</param>
|
||||
/// <param name="distance">Distance to get relative to</param>
|
||||
/// <returns>Block at the given distance of the given face</returns>
|
||||
public Block getRelative(BlockFace face, int distance)
|
||||
{
|
||||
return getRelative(face.getModX() * distance, face.getModY() * distance, face.getModZ() * distance);
|
||||
}
|
||||
|
||||
}
|
||||
118
Minecraft.Server.FourKit/Block/BlockFace.cs
Normal file
118
Minecraft.Server.FourKit/Block/BlockFace.cs
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
namespace Minecraft.Server.FourKit.Block;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the face of a block.
|
||||
/// </summary>
|
||||
public enum BlockFace
|
||||
{
|
||||
DOWN = 0,
|
||||
UP = 1,
|
||||
NORTH = 2,
|
||||
SOUTH = 3,
|
||||
WEST = 4,
|
||||
EAST = 5,
|
||||
SELF = 6,
|
||||
NORTH_EAST,
|
||||
NORTH_WEST,
|
||||
SOUTH_EAST,
|
||||
SOUTH_WEST,
|
||||
WEST_NORTH_WEST,
|
||||
NORTH_NORTH_WEST,
|
||||
NORTH_NORTH_EAST,
|
||||
EAST_NORTH_EAST,
|
||||
EAST_SOUTH_EAST,
|
||||
SOUTH_SOUTH_EAST,
|
||||
SOUTH_SOUTH_WEST,
|
||||
WEST_SOUTH_WEST
|
||||
}
|
||||
|
||||
public static class BlockFaceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the amount of X-coordinates to modify to get the represented block.
|
||||
/// </summary>
|
||||
/// <param name="face">The block face.</param>
|
||||
/// <returns>Amount of X-coordinates to modify.</returns>
|
||||
public static int getModX(this BlockFace face) => face switch
|
||||
{
|
||||
BlockFace.EAST => 1,
|
||||
BlockFace.WEST => -1,
|
||||
BlockFace.NORTH_EAST => 1,
|
||||
BlockFace.NORTH_WEST => -1,
|
||||
BlockFace.SOUTH_EAST => 1,
|
||||
BlockFace.SOUTH_WEST => -1,
|
||||
BlockFace.EAST_NORTH_EAST => 1,
|
||||
BlockFace.EAST_SOUTH_EAST => 1,
|
||||
BlockFace.NORTH_NORTH_EAST => 1,
|
||||
BlockFace.SOUTH_SOUTH_EAST => 1,
|
||||
BlockFace.WEST_NORTH_WEST => -1,
|
||||
BlockFace.WEST_SOUTH_WEST => -1,
|
||||
BlockFace.NORTH_NORTH_WEST => -1,
|
||||
BlockFace.SOUTH_SOUTH_WEST => -1,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Get the amount of Y-coordinates to modify to get the represented block.
|
||||
/// </summary>
|
||||
/// <param name="face">The block face.</param>
|
||||
/// <returns>Amount of Y-coordinates to modify.</returns>
|
||||
public static int getModY(this BlockFace face) => face switch
|
||||
{
|
||||
BlockFace.UP => 1,
|
||||
BlockFace.DOWN => -1,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Get the amount of Z-coordinates to modify to get the represented block.
|
||||
/// </summary>
|
||||
/// <param name="face">The block face.</param>
|
||||
/// <returns>Amount of Z-coordinates to modify.</returns>
|
||||
public static int getModZ(this BlockFace face) => face switch
|
||||
{
|
||||
BlockFace.NORTH => -1,
|
||||
BlockFace.SOUTH => 1,
|
||||
BlockFace.NORTH_EAST => -1,
|
||||
BlockFace.NORTH_WEST => -1,
|
||||
BlockFace.SOUTH_EAST => 1,
|
||||
BlockFace.SOUTH_WEST => 1,
|
||||
BlockFace.NORTH_NORTH_EAST => -1,
|
||||
BlockFace.NORTH_NORTH_WEST => -1,
|
||||
BlockFace.EAST_NORTH_EAST => -1,
|
||||
BlockFace.WEST_NORTH_WEST => -1,
|
||||
BlockFace.EAST_SOUTH_EAST => 1,
|
||||
BlockFace.WEST_SOUTH_WEST => 1,
|
||||
BlockFace.SOUTH_SOUTH_EAST => 1,
|
||||
BlockFace.SOUTH_SOUTH_WEST => 1,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the opposite face of this block face.
|
||||
/// </summary>
|
||||
/// <param name="face">The block face.</param>
|
||||
/// <returns>The opposite block face.</returns>
|
||||
public static BlockFace getOppositeFace(this BlockFace face) => face switch
|
||||
{
|
||||
BlockFace.NORTH => BlockFace.SOUTH,
|
||||
BlockFace.SOUTH => BlockFace.NORTH,
|
||||
BlockFace.EAST => BlockFace.WEST,
|
||||
BlockFace.WEST => BlockFace.EAST,
|
||||
BlockFace.UP => BlockFace.DOWN,
|
||||
BlockFace.DOWN => BlockFace.UP,
|
||||
BlockFace.NORTH_EAST => BlockFace.SOUTH_WEST,
|
||||
BlockFace.NORTH_WEST => BlockFace.SOUTH_EAST,
|
||||
BlockFace.SOUTH_EAST => BlockFace.NORTH_WEST,
|
||||
BlockFace.SOUTH_WEST => BlockFace.NORTH_EAST,
|
||||
BlockFace.WEST_NORTH_WEST => BlockFace.EAST_SOUTH_EAST,
|
||||
BlockFace.NORTH_NORTH_WEST => BlockFace.SOUTH_SOUTH_EAST,
|
||||
BlockFace.NORTH_NORTH_EAST => BlockFace.SOUTH_SOUTH_WEST,
|
||||
BlockFace.EAST_NORTH_EAST => BlockFace.WEST_SOUTH_WEST,
|
||||
BlockFace.EAST_SOUTH_EAST => BlockFace.WEST_NORTH_WEST,
|
||||
BlockFace.SOUTH_SOUTH_EAST => BlockFace.NORTH_NORTH_WEST,
|
||||
BlockFace.SOUTH_SOUTH_WEST => BlockFace.NORTH_NORTH_EAST,
|
||||
BlockFace.WEST_SOUTH_WEST => BlockFace.EAST_NORTH_EAST,
|
||||
_ => BlockFace.SELF
|
||||
};
|
||||
}
|
||||
197
Minecraft.Server.FourKit/Block/BlockState.cs
Normal file
197
Minecraft.Server.FourKit/Block/BlockState.cs
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
namespace Minecraft.Server.FourKit.Block;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a captured state of a block, which will not change
|
||||
/// automatically.
|
||||
///
|
||||
/// <para>Unlike <see cref="Block"/>, which only one object can exist per
|
||||
/// coordinate, BlockState can exist multiple times for any given Block.
|
||||
/// Note that another plugin may change the state of the block and you will
|
||||
/// not know, or they may change the block to another type entirely, causing
|
||||
/// your BlockState to become invalid.</para>
|
||||
/// </summary>
|
||||
public class BlockState
|
||||
{
|
||||
private readonly World _world;
|
||||
private readonly int _x;
|
||||
private readonly int _y;
|
||||
private readonly int _z;
|
||||
private int _typeId;
|
||||
private int _data;
|
||||
|
||||
internal BlockState(World world, int x, int y, int z, int typeId, int data)
|
||||
{
|
||||
_world = world;
|
||||
_x = x;
|
||||
_y = y;
|
||||
_z = z;
|
||||
_typeId = typeId;
|
||||
_data = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the block represented by this BlockState.
|
||||
/// </summary>
|
||||
/// <returns>Block that this BlockState represents.</returns>
|
||||
public Block getBlock()
|
||||
{
|
||||
return new Block(_world, _x, _y, _z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the metadata for this block.
|
||||
/// </summary>
|
||||
/// <returns>Block specific metadata.</returns>
|
||||
public int getData() => _data;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the metadata for this block.
|
||||
/// </summary>
|
||||
/// <param name="data">New block specific metadata.</param>
|
||||
public void setData(int data)
|
||||
{
|
||||
_data = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of this block.
|
||||
/// </summary>
|
||||
/// <returns>Block type.</returns>
|
||||
public Material getType()
|
||||
{
|
||||
return Enum.IsDefined(typeof(Material), _typeId) ? (Material)_typeId : Material.AIR;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type ID of this block.
|
||||
/// </summary>
|
||||
/// <returns>Block type ID.</returns>
|
||||
public int getTypeId() => _typeId;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the world which contains this Block.
|
||||
/// </summary>
|
||||
/// <returns>World containing this block.</returns>
|
||||
public World getWorld() => _world;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the x-coordinate of this block.
|
||||
/// </summary>
|
||||
/// <returns>X-coordinate.</returns>
|
||||
public int getX() => _x;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the y-coordinate of this block.
|
||||
/// </summary>
|
||||
/// <returns>Y-coordinate.</returns>
|
||||
public int getY() => _y;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the z-coordinate of this block.
|
||||
/// </summary>
|
||||
/// <returns>Z-coordinate.</returns>
|
||||
public int getZ() => _z;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the location of this block.
|
||||
/// </summary>
|
||||
/// <returns>Location.</returns>
|
||||
public Location getLocation()
|
||||
{
|
||||
return new Location(_world, _x, _y, _z, 0f, 0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores the location of this block in the provided Location object.
|
||||
/// If the provided Location is null this method does nothing and returns
|
||||
/// null.
|
||||
/// </summary>
|
||||
/// <param name="loc">The location object to store in.</param>
|
||||
/// <returns>The Location object provided or null.</returns>
|
||||
public Location? getLocation(Location? loc)
|
||||
{
|
||||
if (loc == null) return null;
|
||||
loc.X = _x;
|
||||
loc.Y = _y;
|
||||
loc.Z = _z;
|
||||
loc.LocationWorld = _world;
|
||||
return loc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the type of this block.
|
||||
/// </summary>
|
||||
/// <param name="type">Material to change this block to.</param>
|
||||
public void setType(Material type)
|
||||
{
|
||||
_typeId = (int)type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the type ID of this block.
|
||||
/// </summary>
|
||||
/// <param name="type">Type ID to change this block to.</param>
|
||||
/// <returns>Whether the change was accepted.</returns>
|
||||
public bool setTypeId(int type)
|
||||
{
|
||||
_typeId = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to update the block represented by this state, setting it to
|
||||
/// the new values as defined by this state.
|
||||
/// <para>This has the same effect as calling <c>update(false)</c>.</para>
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if the update was successful, otherwise
|
||||
/// <c>false</c>.</returns>
|
||||
public bool update()
|
||||
{
|
||||
return update(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to update the block represented by this state, setting it to
|
||||
/// the new values as defined by this state.
|
||||
/// <para>This has the same effect as calling
|
||||
/// <c>update(force, true)</c>.</para>
|
||||
/// </summary>
|
||||
/// <param name="force"><c>true</c> to forcefully set the state.</param>
|
||||
/// <returns><c>true</c> if the update was successful, otherwise
|
||||
/// <c>false</c>.</returns>
|
||||
public bool update(bool force)
|
||||
{
|
||||
return update(force, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to update the block represented by this state, setting it to
|
||||
/// the new values as defined by this state.
|
||||
/// <para>Unless <paramref name="force"/> is true, this will not modify the
|
||||
/// state of a block if it is no longer the same type as it was when this
|
||||
/// state was taken. It will return false in this eventuality.</para>
|
||||
/// <para>If <paramref name="force"/> is true, it will set the type of the
|
||||
/// block to match the new state, set the state data and then return
|
||||
/// true.</para>
|
||||
/// <para>If <paramref name="applyPhysics"/> is true, it will trigger a
|
||||
/// physics update on surrounding blocks which could cause them to update
|
||||
/// or disappear.</para>
|
||||
/// </summary>
|
||||
/// <param name="force"><c>true</c> to forcefully set the state.</param>
|
||||
/// <param name="applyPhysics"><c>false</c> to cancel updating physics on
|
||||
/// surrounding blocks.</param>
|
||||
/// <returns><c>true</c> if the update was successful, otherwise
|
||||
/// <c>false</c>.</returns>
|
||||
public bool update(bool force, bool applyPhysics)
|
||||
{
|
||||
if (NativeBridge.GetTileId == null || NativeBridge.SetTile == null)
|
||||
return false;
|
||||
|
||||
int currentType = NativeBridge.GetTileId(_world.getDimensionId(), _x, _y, _z);
|
||||
if (!force && currentType != _typeId)
|
||||
return false;
|
||||
|
||||
NativeBridge.SetTile(_world.getDimensionId(), _x, _y, _z, _typeId, _data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
117
Minecraft.Server.FourKit/CMakeLists.txt
Normal file
117
Minecraft.Server.FourKit/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
set(FOURKIT_PROJECT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
set(FOURKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/bin/$<CONFIG>")
|
||||
set(FOURKIT_CSPROJ "${FOURKIT_PROJECT_DIR}/Minecraft.Server.FourKit.csproj")
|
||||
|
||||
# ---
|
||||
# .NET 10 SDK check
|
||||
# ---
|
||||
# Fail configure with a clear message if .NET 10 SDK isn't installed. Otherwise
|
||||
# the failure happens deep inside the dotnet publish step with a much less
|
||||
# obvious error. The version pin in /global.json governs which 10.x SDK is
|
||||
# selected at build time; this check just confirms one is installed at all.
|
||||
find_program(DOTNET_EXECUTABLE NAMES dotnet
|
||||
HINTS "$ENV{ProgramFiles}/dotnet" "$ENV{ProgramW6432}/dotnet"
|
||||
DOC ".NET CLI executable")
|
||||
if(NOT DOTNET_EXECUTABLE)
|
||||
message(FATAL_ERROR
|
||||
"FourKit requires the .NET 10 SDK but the 'dotnet' command was not found on PATH.\n"
|
||||
"Install it from https://dotnet.microsoft.com/download/dotnet/10.0 (x64 SDK)\n"
|
||||
"and reconfigure CMake.")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND "${DOTNET_EXECUTABLE}" --list-sdks
|
||||
OUTPUT_VARIABLE FOURKIT_DOTNET_SDKS
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(NOT FOURKIT_DOTNET_SDKS MATCHES "(^|\n)10\\.[0-9]+\\.[0-9]+")
|
||||
if(FOURKIT_DOTNET_SDKS STREQUAL "")
|
||||
set(_sdk_list " (none installed)")
|
||||
else()
|
||||
set(_sdk_list "${FOURKIT_DOTNET_SDKS}")
|
||||
endif()
|
||||
message(FATAL_ERROR
|
||||
"FourKit requires a .NET 10 SDK but none was detected.\n"
|
||||
"Install it from https://dotnet.microsoft.com/download/dotnet/10.0 (x64 SDK)\n"
|
||||
"and reconfigure CMake.\n"
|
||||
"\n"
|
||||
"Currently installed .NET SDKs:\n"
|
||||
"${_sdk_list}")
|
||||
endif()
|
||||
message(STATUS "FourKit: .NET CLI at ${DOTNET_EXECUTABLE}")
|
||||
|
||||
file(GLOB_RECURSE FOURKIT_SOURCES RELATIVE "${FOURKIT_PROJECT_DIR}" "${FOURKIT_PROJECT_DIR}/*.cs")
|
||||
list(FILTER FOURKIT_SOURCES EXCLUDE REGEX "([/\\](obj|bin)[/\\])|^(obj|bin)[/\\]")
|
||||
|
||||
set(DOTNET_CONFIG "$<IF:$<CONFIG:Debug>,Debug,Release>")
|
||||
|
||||
foreach(src_file IN LISTS FOURKIT_SOURCES)
|
||||
get_filename_component(src_path "${src_file}" PATH)
|
||||
if(src_path)
|
||||
string(REPLACE "/" "\\" group_path "${src_path}")
|
||||
source_group("${group_path}" FILES "${FOURKIT_PROJECT_DIR}/${src_file}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
list(TRANSFORM FOURKIT_SOURCES PREPEND "${FOURKIT_PROJECT_DIR}/")
|
||||
|
||||
# Self-contained publish: bundles .NET 10 runtime so end users need nothing pre-installed.
|
||||
# The publish AND the staging copy both live on this target so they re-run together
|
||||
# whenever any FourKit C# source changes, even on incremental builds where the
|
||||
# Minecraft.Server.FourKit C++ exe does not re-link.
|
||||
#
|
||||
# Target name note: the CMake target is "Minecraft.Server.FourKit.Managed" so it
|
||||
# does not collide with the C++ executable target named "Minecraft.Server.FourKit".
|
||||
# The .csproj file name, the assembly name, and the C# namespace all stay
|
||||
# Minecraft.Server.FourKit because those are the donor's identifiers.
|
||||
add_custom_target(Minecraft.Server.FourKit.Managed ALL
|
||||
COMMAND dotnet publish "${FOURKIT_CSPROJ}"
|
||||
--configuration "${DOTNET_CONFIG}"
|
||||
--runtime win-x64
|
||||
--self-contained true
|
||||
--output "${FOURKIT_OUTPUT_DIR}"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
"${FOURKIT_OUTPUT_DIR}"
|
||||
"$<TARGET_FILE_DIR:Minecraft.Server.FourKit>/runtime"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||
"$<TARGET_FILE_DIR:Minecraft.Server.FourKit>/plugins"
|
||||
WORKING_DIRECTORY "${FOURKIT_PROJECT_DIR}"
|
||||
SOURCES ${FOURKIT_SOURCES}
|
||||
COMMENT "dotnet publish Minecraft.Server.FourKit + stage runtime/plugins next to server"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
set_target_properties(Minecraft.Server.FourKit.Managed PROPERTIES
|
||||
OUTPUT_NAME "Minecraft.Server.FourKit.Managed"
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# FourKit-enabled C++ server executable.
|
||||
#
|
||||
# Built from the same shared source list as the vanilla Minecraft.Server,
|
||||
# plus the seven FourKit native bridge files. The MINECRAFT_SERVER_FOURKIT_BUILD
|
||||
# preprocessor define switches FourKitBridge.h from inline no-op stubs to its
|
||||
# real declarations, and the FourKitRuntime.cpp / FourKitBridge.cpp /
|
||||
# FourKitNatives.cpp / FourKitMappers.cpp impls link in.
|
||||
#
|
||||
# This target is defined in the Minecraft.Server.FourKit/ source dir (not
|
||||
# Minecraft.Server/) so the Visual Studio generator emits its .vcxproj files
|
||||
# into build/<dir>/Minecraft.Server.FourKit/, keeping the variant identity
|
||||
# consistent across source dir, project file, runtime output dir, and Docker
|
||||
# image base.
|
||||
# ---------------------------------------------------------------------------
|
||||
include("${CMAKE_SOURCE_DIR}/Minecraft.Server/cmake/sources/Common.cmake")
|
||||
include("${CMAKE_SOURCE_DIR}/cmake/CommonSources.cmake")
|
||||
include("${CMAKE_SOURCE_DIR}/cmake/ServerTarget.cmake")
|
||||
|
||||
set(MINECRAFT_SERVER_FOURKIT_SOURCES
|
||||
${MINECRAFT_SERVER_COMMON}
|
||||
${SOURCES_COMMON}
|
||||
${_MINECRAFT_SERVER_COMMON_SERVER_FOURKIT}
|
||||
)
|
||||
|
||||
add_executable(Minecraft.Server.FourKit ${MINECRAFT_SERVER_FOURKIT_SOURCES})
|
||||
configure_lce_server_target(Minecraft.Server.FourKit)
|
||||
target_compile_definitions(Minecraft.Server.FourKit PRIVATE MINECRAFT_SERVER_FOURKIT_BUILD)
|
||||
add_dependencies(Minecraft.Server.FourKit Minecraft.Server.FourKit.Managed)
|
||||
110
Minecraft.Server.FourKit/Command/Command.cs
Normal file
110
Minecraft.Server.FourKit/Command/Command.cs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
namespace Minecraft.Server.FourKit.Command;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Command, which executes various tasks upon user input.
|
||||
/// </summary>
|
||||
public abstract class Command
|
||||
{
|
||||
private string _name;
|
||||
private string _description;
|
||||
private string _usage;
|
||||
private List<string> _aliases;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new command with the given name and no aliases.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of this command.</param>
|
||||
protected Command(string name)
|
||||
{
|
||||
_name = name;
|
||||
_description = string.Empty;
|
||||
_usage = "/" + name;
|
||||
_aliases = new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new command with the given name, description, and aliases.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of this command.</param>
|
||||
/// <param name="description">A brief description of this command.</param>
|
||||
/// <param name="aliases">A list of aliases for this command.</param>
|
||||
protected Command(string name, string description, List<string> aliases)
|
||||
{
|
||||
_name = name;
|
||||
_description = description ?? string.Empty;
|
||||
_usage = "/" + name;
|
||||
_aliases = aliases ?? new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the command, returning its success.
|
||||
/// </summary>
|
||||
/// <param name="sender">Source of the command.</param>
|
||||
/// <param name="commandLabel">Alias of the command which was used.</param>
|
||||
/// <param name="args">Passed command arguments.</param>
|
||||
/// <returns><c>true</c> if the command was successful, otherwise <c>false</c>.</returns>
|
||||
public abstract bool execute(CommandSender sender, string commandLabel, string[] args);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of active aliases of this command.
|
||||
/// </summary>
|
||||
/// <returns>List of aliases.</returns>
|
||||
public List<string> getAliases() => new(_aliases);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a brief description of this command.
|
||||
/// </summary>
|
||||
/// <returns>Description of this command.</returns>
|
||||
public string getDescription() => _description;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current label for this command.
|
||||
/// </summary>
|
||||
/// <returns>Current label.</returns>
|
||||
public string getLabel() => _name;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name of this command.
|
||||
/// </summary>
|
||||
/// <returns>Name of this command.</returns>
|
||||
public string getName() => _name;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an example usage of this command.
|
||||
/// </summary>
|
||||
/// <returns>Usage string.</returns>
|
||||
public string getUsage() => _usage;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the list of aliases to request on registration for this command.
|
||||
/// </summary>
|
||||
/// <param name="aliases">Aliases to register.</param>
|
||||
/// <returns>This command.</returns>
|
||||
public Command setAliases(List<string> aliases)
|
||||
{
|
||||
_aliases = aliases ?? new List<string>();
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a brief description of this command.
|
||||
/// </summary>
|
||||
/// <param name="description">New command description.</param>
|
||||
/// <returns>This command.</returns>
|
||||
public Command setDescription(string description)
|
||||
{
|
||||
_description = description ?? string.Empty;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the example usage of this command.
|
||||
/// </summary>
|
||||
/// <param name="usage">New example usage.</param>
|
||||
/// <returns>This command.</returns>
|
||||
public Command setUsage(string usage)
|
||||
{
|
||||
_usage = usage ?? string.Empty;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
17
Minecraft.Server.FourKit/Command/CommandExecutor.cs
Normal file
17
Minecraft.Server.FourKit/Command/CommandExecutor.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
namespace Minecraft.Server.FourKit.Command;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a class which contains a single method for executing commands.
|
||||
/// </summary>
|
||||
public interface CommandExecutor
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the given command, returning its success.
|
||||
/// </summary>
|
||||
/// <param name="sender">Source of the command.</param>
|
||||
/// <param name="command">Command which was executed.</param>
|
||||
/// <param name="label">Alias of the command which was used.</param>
|
||||
/// <param name="args">Passed command arguments.</param>
|
||||
/// <returns><c>true</c> if a valid command, otherwise <c>false</c>.</returns>
|
||||
bool onCommand(CommandSender sender, Command command, string label, string[] args);
|
||||
}
|
||||
25
Minecraft.Server.FourKit/Command/CommandSender.cs
Normal file
25
Minecraft.Server.FourKit/Command/CommandSender.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
namespace Minecraft.Server.FourKit.Command;
|
||||
|
||||
/// <summary>
|
||||
/// Represents something that can send commands and receive messages.
|
||||
/// </summary>
|
||||
public interface CommandSender
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends this sender a message.
|
||||
/// </summary>
|
||||
/// <param name="message">Message to be displayed.</param>
|
||||
void sendMessage(string message);
|
||||
|
||||
/// <summary>
|
||||
/// Sends this sender multiple messages.
|
||||
/// </summary>
|
||||
/// <param name="messages">An array of messages to be displayed.</param>
|
||||
void sendMessage(string[] messages);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of this command sender.
|
||||
/// </summary>
|
||||
/// <returns>Name of the sender.</returns>
|
||||
string getName();
|
||||
}
|
||||
27
Minecraft.Server.FourKit/Command/ConsoleCommandSender.cs
Normal file
27
Minecraft.Server.FourKit/Command/ConsoleCommandSender.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
namespace Minecraft.Server.FourKit.Command;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the server console as a command sender.
|
||||
/// </summary>
|
||||
public class ConsoleCommandSender : CommandSender
|
||||
{
|
||||
internal static readonly ConsoleCommandSender Instance = new();
|
||||
|
||||
private ConsoleCommandSender() { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void sendMessage(string message)
|
||||
{
|
||||
ServerLog.Info("console", message);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void sendMessage(string[] messages)
|
||||
{
|
||||
foreach (var msg in messages)
|
||||
sendMessage(msg);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string getName() => "CONSOLE";
|
||||
}
|
||||
44
Minecraft.Server.FourKit/Command/PluginCommand.cs
Normal file
44
Minecraft.Server.FourKit/Command/PluginCommand.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
namespace Minecraft.Server.FourKit.Command;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a <see cref="Command"/> belonging to a plugin.
|
||||
/// </summary>
|
||||
public class PluginCommand : Command
|
||||
{
|
||||
private CommandExecutor? _executor;
|
||||
|
||||
// should this remain internal?
|
||||
/// <summary>
|
||||
/// Creates a new plugin command with the given name.
|
||||
/// Use <see cref="FourKit.getCommand"/> to obtain instances.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of this command.</param>
|
||||
internal PluginCommand(string name) : base(name)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool execute(CommandSender sender, string commandLabel, string[] args)
|
||||
{
|
||||
if (_executor != null)
|
||||
return _executor.onCommand(sender, this, commandLabel, args);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="CommandExecutor"/> associated with this command.
|
||||
/// </summary>
|
||||
/// <returns>The command executor, or <c>null</c>.</returns>
|
||||
public CommandExecutor? getExecutor() => _executor;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="CommandExecutor"/> to run when the command is dispatched.
|
||||
/// </summary>
|
||||
/// <param name="executor">New executor to set.</param>
|
||||
/// <returns><c>true</c> if the executor was set.</returns>
|
||||
public bool setExecutor(CommandExecutor executor)
|
||||
{
|
||||
_executor = executor;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class AquaAffinityEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.LEATHER_HELMET, Material.CHAINMAIL_HELMET, Material.GOLD_HELMET, Material.IRON_HELMET, Material.DIAMOND_HELMET,
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = { };
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.ARMOR_HEAD;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.WATER_WORKER;
|
||||
|
||||
public override int getMaxLevel() => 1;
|
||||
|
||||
public override string getName() => "aquaaffinity";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
//name could be changed
|
||||
public class BaneOfArthropodsEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.WOOD_SWORD, Material.STONE_SWORD, Material.IRON_SWORD, Material.GOLD_SWORD, Material.DIAMOND_SWORD,
|
||||
Material.WOOD_AXE, Material.STONE_AXE, Material.IRON_AXE, Material.GOLD_AXE, Material.DIAMOND_AXE,
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = {
|
||||
EnchantmentType.DAMAGE_ALL,
|
||||
EnchantmentType.DAMAGE_UNDEAD,
|
||||
};
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.WEAPON;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.DAMAGE_ARTHOPODS;
|
||||
|
||||
public override int getMaxLevel() => 5;
|
||||
|
||||
public override string getName() => "arthopods"; //could be changed
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class BlastProtectionEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS,
|
||||
Material.CHAINMAIL_HELMET, Material.CHAINMAIL_CHESTPLATE, Material.CHAINMAIL_LEGGINGS, Material.CHAINMAIL_BOOTS,
|
||||
Material.GOLD_HELMET, Material.GOLD_CHESTPLATE, Material.GOLD_LEGGINGS, Material.GOLD_BOOTS,
|
||||
Material.IRON_HELMET, Material.IRON_CHESTPLATE, Material.IRON_LEGGINGS, Material.IRON_BOOTS,
|
||||
Material.DIAMOND_HELMET, Material.DIAMOND_CHESTPLATE, Material.DIAMOND_LEGGINGS, Material.DIAMOND_BOOTS,
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = {
|
||||
EnchantmentType.PROTECTION_ENVIRONMENTAL,
|
||||
EnchantmentType.PROTECTION_FIRE,
|
||||
EnchantmentType.PROTECTION_PROJECTILE,
|
||||
};
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.ARMOR;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.PROTECTION_EXPLOSIVE;
|
||||
|
||||
public override int getMaxLevel() => 4;
|
||||
|
||||
public override string getName() => "blastprotection";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class EfficiencyEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.WOOD_SWORD, Material.STONE_SWORD, Material.IRON_SWORD, Material.GOLD_SWORD, Material.DIAMOND_SWORD,
|
||||
Material.WOOD_PICKAXE, Material.STONE_PICKAXE, Material.IRON_PICKAXE, Material.GOLD_PICKAXE, Material.DIAMOND_PICKAXE,
|
||||
Material.WOOD_AXE, Material.STONE_AXE, Material.IRON_AXE, Material.GOLD_AXE, Material.DIAMOND_AXE,
|
||||
Material.WOOD_SPADE, Material.STONE_SPADE, Material.IRON_SPADE, Material.GOLD_SPADE, Material.DIAMOND_SPADE,
|
||||
Material.WOOD_HOE, Material.STONE_HOE, Material.IRON_HOE, Material.GOLD_HOE, Material.DIAMOND_HOE,
|
||||
|
||||
Material.SHEARS
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = { };
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.TOOL;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.DIG_SPEAD;
|
||||
|
||||
public override int getMaxLevel() => 5;
|
||||
|
||||
public override string getName() => "efficiency";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
274
Minecraft.Server.FourKit/Enchantments/Enchantment.cs
Normal file
274
Minecraft.Server.FourKit/Enchantments/Enchantment.cs
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the applicable target for a Enchantment
|
||||
/// </summary>
|
||||
public enum EnchantmentTarget
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows the Enchantment to be placed on all items
|
||||
/// </summary>
|
||||
ALL,
|
||||
/// <summary>
|
||||
/// Allows the Enchantment to be placed on armor
|
||||
/// </summary>
|
||||
ARMOR,
|
||||
/// <summary>
|
||||
/// Allows the Enchantment to be placed on feet slot armor
|
||||
/// </summary>
|
||||
ARMOR_FEET,
|
||||
/// <summary>
|
||||
/// Allows the Enchantment to be placed on head slot armor
|
||||
/// </summary>
|
||||
ARMOR_HEAD,
|
||||
/// <summary>
|
||||
/// Allows the Enchantment to be placed on leg slot armor
|
||||
/// </summary>
|
||||
ARMOR_LEGS,
|
||||
/// <summary>
|
||||
/// Allows the Enchantment to be placed on torso slot armor
|
||||
/// </summary>
|
||||
ARMOR_TORSO,
|
||||
/// <summary>
|
||||
/// Allows the Enchantment to be placed on bows.
|
||||
/// </summary>
|
||||
BOW,
|
||||
/// <summary>
|
||||
/// Allows the Enchantment to be placed on tools (spades, pickaxe, hoes, axes)
|
||||
/// </summary>
|
||||
TOOL,
|
||||
/// <summary>
|
||||
/// Allows the Enchantment to be placed on weapons (swords)
|
||||
/// </summary>
|
||||
WEAPON,
|
||||
}
|
||||
|
||||
//these numbers match the same ones that the enchants register as, Enchantment.cpp - 41
|
||||
|
||||
/// <summary>
|
||||
/// The various type of enchantments that may be added to armour or weapons
|
||||
/// </summary>
|
||||
public enum EnchantmentType
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extra damage when shooting arrows from bows
|
||||
/// </summary>
|
||||
ARROW_DAMAGE = 48,
|
||||
/// <summary>
|
||||
/// Sets entities on fire when hit by arrows shot from a bow
|
||||
/// </summary>
|
||||
ARROW_FIRE = 50,
|
||||
/// <summary>
|
||||
/// Provides infinite arrows when shooting a bow
|
||||
/// </summary>
|
||||
ARROW_INFINITE = 51,
|
||||
/// <summary>
|
||||
/// Provides a knockback when an entity is hit by an arrow from a bow
|
||||
/// </summary>
|
||||
ARROW_KNOCKBACK = 49,
|
||||
/// <summary>
|
||||
/// Increases damage against all targets
|
||||
/// </summary>
|
||||
DAMAGE_ALL = 16,
|
||||
/// <summary>
|
||||
/// Increases damage against arthropod targets
|
||||
/// </summary>
|
||||
DAMAGE_ARTHOPODS = 18,
|
||||
/// <summary>
|
||||
/// Increases damage against undead targets
|
||||
/// </summary>
|
||||
DAMAGE_UNDEAD = 17,
|
||||
/// <summary>
|
||||
/// Increases the rate at which you mine/dig
|
||||
/// </summary>
|
||||
DIG_SPEAD = 32,
|
||||
/// <summary>
|
||||
/// Decreases the rate at which a tool looses durability
|
||||
/// </summary>
|
||||
DURABILITY = 34,
|
||||
/// <summary>
|
||||
/// When attacking a target, has a chance to set them on fire
|
||||
/// </summary>
|
||||
FIRE_ASPECT = 20,
|
||||
/// <summary>
|
||||
/// All damage to other targets will knock them back when hit
|
||||
/// </summary>
|
||||
KNOCKBACK = 19,
|
||||
/// <summary>
|
||||
/// Provides a chance of gaining extra loot when destroying blocks
|
||||
/// </summary>
|
||||
LOOT_BONUS_BLOCKS = 35,
|
||||
/// <summary>
|
||||
/// Provides a chance of gaining extra loot when killing monsters
|
||||
/// </summary>
|
||||
LOOT_BONUS_MOBS = 21,
|
||||
/// <summary>
|
||||
/// Decreases the rate of air loss whilst underwater
|
||||
/// </summary>
|
||||
OXYGEN = 5,
|
||||
/// <summary>
|
||||
/// Provides protection against environmental damage
|
||||
/// </summary>
|
||||
PROTECTION_ENVIRONMENTAL = 0,
|
||||
/// <summary>
|
||||
/// Provides protection against explosive damage
|
||||
/// </summary>
|
||||
PROTECTION_EXPLOSIVE = 3,
|
||||
/// <summary>
|
||||
/// Provides protection against fall damage
|
||||
/// </summary>
|
||||
PROTECTION_FALL = 2,
|
||||
/// <summary>
|
||||
/// Provides protection against fire damage
|
||||
/// </summary>
|
||||
PROTECTION_FIRE = 1,
|
||||
/// <summary>
|
||||
/// Provides protection against projectile damage
|
||||
/// </summary>
|
||||
PROTECTION_PROJECTILE = 4,
|
||||
/// <summary>
|
||||
/// Allows blocks to drop themselves instead of fragments (for example, stone instead of cobblestone)
|
||||
/// </summary>
|
||||
SILK_TOUCH = 33,
|
||||
/// <summary>
|
||||
/// Damages the attacker
|
||||
/// </summary>
|
||||
THORNS = 7,
|
||||
/// <summary>
|
||||
/// Increases the speed at which a player may mine underwater
|
||||
/// </summary>
|
||||
WATER_WORKER = 6
|
||||
}
|
||||
|
||||
public abstract class Enchantment
|
||||
{
|
||||
public static Enchantment PowerEnchantment => _registry[EnchantmentType.ARROW_DAMAGE];
|
||||
public static Enchantment FlameEnchantment => _registry[EnchantmentType.ARROW_FIRE];
|
||||
public static Enchantment InfinityEnchantment => _registry[EnchantmentType.ARROW_INFINITE];
|
||||
public static Enchantment PunchEnchantment => _registry[EnchantmentType.ARROW_KNOCKBACK];
|
||||
public static Enchantment SharpnessEnchantment => _registry[EnchantmentType.DAMAGE_ALL];
|
||||
public static Enchantment BaneOfArthropodsEnchantment => _registry[EnchantmentType.DAMAGE_ARTHOPODS];
|
||||
public static Enchantment SmiteEnchantment => _registry[EnchantmentType.DAMAGE_UNDEAD];
|
||||
public static Enchantment EfficiencyEnchantment => _registry[EnchantmentType.DIG_SPEAD];
|
||||
public static Enchantment UnbreakingEnchantment => _registry[EnchantmentType.DURABILITY];
|
||||
public static Enchantment FireAspectEnchantment => _registry[EnchantmentType.FIRE_ASPECT];
|
||||
public static Enchantment KnockbackEnchantment => _registry[EnchantmentType.KNOCKBACK];
|
||||
public static Enchantment FortuneEnchantment => _registry[EnchantmentType.LOOT_BONUS_BLOCKS];
|
||||
public static Enchantment LootingEnchantment => _registry[EnchantmentType.LOOT_BONUS_MOBS];
|
||||
public static Enchantment RespirationEnchantment => _registry[EnchantmentType.OXYGEN];
|
||||
public static Enchantment ProtectionEnchantment => _registry[EnchantmentType.PROTECTION_ENVIRONMENTAL];
|
||||
public static Enchantment BlastProtectionEnchantment => _registry[EnchantmentType.PROTECTION_EXPLOSIVE];
|
||||
public static Enchantment FeatherFallingEnchantment => _registry[EnchantmentType.PROTECTION_FALL];
|
||||
public static Enchantment FireProtectionEnchantment => _registry[EnchantmentType.PROTECTION_FIRE];
|
||||
public static Enchantment ProjectileProtectionEnchantment => _registry[EnchantmentType.PROTECTION_PROJECTILE];
|
||||
public static Enchantment SilkTouchEnchantment => _registry[EnchantmentType.SILK_TOUCH];
|
||||
public static Enchantment ThornsEnchantment => _registry[EnchantmentType.THORNS];
|
||||
public static Enchantment AquaAffinityEnchantment => _registry[EnchantmentType.WATER_WORKER];
|
||||
|
||||
|
||||
private static Dictionary<EnchantmentType, Enchantment> _registry = new Dictionary<EnchantmentType, Enchantment>()
|
||||
{
|
||||
{ EnchantmentType.ARROW_DAMAGE, new PowerEnchantment() },
|
||||
{ EnchantmentType.ARROW_FIRE, new FlameEnchantment() },
|
||||
{ EnchantmentType.ARROW_INFINITE, new InfinityEnchantment() },
|
||||
{ EnchantmentType.ARROW_KNOCKBACK, new PunchEnchantment() },
|
||||
{ EnchantmentType.DAMAGE_ALL, new SharpnessEnchantment() },
|
||||
{ EnchantmentType.DAMAGE_ARTHOPODS, new BaneOfArthropodsEnchantment() },
|
||||
{ EnchantmentType.DAMAGE_UNDEAD, new SmiteEnchantment() },
|
||||
{ EnchantmentType.DIG_SPEAD, new EfficiencyEnchantment() },
|
||||
{ EnchantmentType.DURABILITY, new UnbreakingEnchantment() },
|
||||
{ EnchantmentType.FIRE_ASPECT, new FireAspectEnchantment() },
|
||||
{ EnchantmentType.KNOCKBACK, new KnockbackEnchantment() },
|
||||
{ EnchantmentType.LOOT_BONUS_BLOCKS, new FortuneEnchantment() },
|
||||
{ EnchantmentType.LOOT_BONUS_MOBS, new LootingEnchantment() },
|
||||
{ EnchantmentType.OXYGEN, new RespirationEnchantment() },
|
||||
{ EnchantmentType.PROTECTION_ENVIRONMENTAL, new ProtectionEnchantment() },
|
||||
{ EnchantmentType.PROTECTION_EXPLOSIVE, new BlastProtectionEnchantment() },
|
||||
{ EnchantmentType.PROTECTION_FALL, new FeatherFallingEnchantment() },
|
||||
{ EnchantmentType.PROTECTION_FIRE, new FireProtectionEnchantment() },
|
||||
{ EnchantmentType.PROTECTION_PROJECTILE, new ProjectileProtectionEnchantment() },
|
||||
{ EnchantmentType.SILK_TOUCH, new SilkTouchEnchantment() },
|
||||
{ EnchantmentType.THORNS, new ThornsEnchantment() },
|
||||
{ EnchantmentType.WATER_WORKER, new AquaAffinityEnchantment() },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this Enchantment may be applied to the given <see cref="ItemStack"/>. This does not check if it conflicts with any enchantments already applied to the item.
|
||||
/// </summary>
|
||||
/// <param name="item">Item to test</param>
|
||||
/// <returns>True if the enchantment may be applied, otherwise False</returns>
|
||||
public abstract bool canEnchantItem(ItemStack item);
|
||||
|
||||
/// <summary>
|
||||
/// Check if this enchantment conflicts with another enchantment.
|
||||
/// </summary>
|
||||
/// <param name="other">The enchantment to check against </param>
|
||||
/// <returns>True if there is a conflict.</returns>
|
||||
public abstract bool conflictsWith(Enchantment other);
|
||||
//public abstract Enchantment getById(int id); //deprecated by bukkit
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Enchantment at the specified name
|
||||
/// </summary>
|
||||
/// <param name="name">Name to fetch.</param>
|
||||
/// <returns>Resulting Enchantment, or null if not found</returns>
|
||||
public static Enchantment? getByName(string name)
|
||||
{
|
||||
foreach (KeyValuePair<EnchantmentType, Enchantment> enchantmentPair in _registry)
|
||||
{
|
||||
if (enchantmentPair.Value.getName().Equals(name)) return enchantmentPair.Value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Enchantment at the specified type
|
||||
/// </summary>
|
||||
/// <param name="type">Type to fetch.</param>
|
||||
/// <returns>Resulting Enchantment, or null if not found</returns>
|
||||
public static Enchantment getByType(EnchantmentType type)
|
||||
{
|
||||
return _registry[type]; //we should always have the enchant based on the type
|
||||
}
|
||||
|
||||
//public abstract int getId(); //deprecated by bukkit
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of <see cref="ItemStack"/> that may fit this Enchantment.
|
||||
/// </summary>
|
||||
/// <returns>Gets the type of <see cref="ItemStack"/> that may fit this Enchantment.</returns>
|
||||
public abstract EnchantmentTarget getItemTarget();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="EnchantmentType"/>.
|
||||
/// </summary>
|
||||
/// <returns>Gets the enchantment type.</returns>
|
||||
public abstract EnchantmentType getEnchantType();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum level that this Enchantment may become.
|
||||
/// </summary>
|
||||
/// <returns>Maximum level of the Enchantment</returns>
|
||||
public abstract int getMaxLevel();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique name of this enchantment
|
||||
/// </summary>
|
||||
/// <returns>Unique name</returns>
|
||||
public abstract string getName();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the level that this Enchantment should start at
|
||||
/// </summary>
|
||||
/// <returns>Starting level of the Enchantment</returns>
|
||||
public abstract int getStartLevel();
|
||||
|
||||
//public static bool isAcceptingRegistrations(); //we dont have enchant registrations
|
||||
//public static void registerEnchantment(Enchantment enchantment); //we dont have enchant registrations
|
||||
//public static void stopAcceptingRegistrations(); //we dont have enchant registrations
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class FeatherFallingEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.LEATHER_BOOTS, Material.LEATHER_BOOTS, Material.CHAINMAIL_BOOTS, Material.GOLD_BOOTS, Material.IRON_BOOTS, Material.DIAMOND_BOOTS,
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = { };
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.ARMOR_FEET;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.PROTECTION_FALL;
|
||||
|
||||
public override int getMaxLevel() => 4;
|
||||
|
||||
public override string getName() => "featherfalling";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class FireAspectEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.WOOD_SWORD, Material.STONE_SWORD, Material.IRON_SWORD, Material.GOLD_SWORD, Material.DIAMOND_SWORD,
|
||||
Material.WOOD_AXE, Material.STONE_AXE, Material.IRON_AXE, Material.GOLD_AXE, Material.DIAMOND_AXE,
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = { };
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.WEAPON;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.FIRE_ASPECT;
|
||||
|
||||
public override int getMaxLevel() => 2;
|
||||
|
||||
public override string getName() => "fireaspect";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class FireProtectionEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS,
|
||||
Material.CHAINMAIL_HELMET, Material.CHAINMAIL_CHESTPLATE, Material.CHAINMAIL_LEGGINGS, Material.CHAINMAIL_BOOTS,
|
||||
Material.GOLD_HELMET, Material.GOLD_CHESTPLATE, Material.GOLD_LEGGINGS, Material.GOLD_BOOTS,
|
||||
Material.IRON_HELMET, Material.IRON_CHESTPLATE, Material.IRON_LEGGINGS, Material.IRON_BOOTS,
|
||||
Material.DIAMOND_HELMET, Material.DIAMOND_CHESTPLATE, Material.DIAMOND_LEGGINGS, Material.DIAMOND_BOOTS,
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = {
|
||||
EnchantmentType.PROTECTION_ENVIRONMENTAL,
|
||||
EnchantmentType.PROTECTION_EXPLOSIVE,
|
||||
EnchantmentType.PROTECTION_PROJECTILE,
|
||||
};
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.ARMOR;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.PROTECTION_FIRE;
|
||||
|
||||
public override int getMaxLevel() => 4;
|
||||
|
||||
public override string getName() => "fireprotection";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
26
Minecraft.Server.FourKit/Enchantments/FlameEnchantment.cs
Normal file
26
Minecraft.Server.FourKit/Enchantments/FlameEnchantment.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class FlameEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.BOW
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = { };
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.BOW;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.ARROW_FIRE;
|
||||
|
||||
public override int getMaxLevel() => 1;
|
||||
|
||||
public override string getName() => "flame";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
31
Minecraft.Server.FourKit/Enchantments/FortuneEnchantment.cs
Normal file
31
Minecraft.Server.FourKit/Enchantments/FortuneEnchantment.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class FortuneEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.WOOD_PICKAXE, Material.STONE_PICKAXE, Material.IRON_PICKAXE, Material.GOLD_PICKAXE, Material.DIAMOND_PICKAXE,
|
||||
Material.WOOD_AXE, Material.STONE_AXE, Material.IRON_AXE, Material.GOLD_AXE, Material.DIAMOND_AXE,
|
||||
Material.WOOD_SPADE, Material.STONE_SPADE, Material.IRON_SPADE, Material.GOLD_SPADE, Material.DIAMOND_SPADE,
|
||||
Material.WOOD_HOE, Material.STONE_HOE, Material.IRON_HOE, Material.GOLD_HOE, Material.DIAMOND_HOE,
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = {
|
||||
EnchantmentType.SILK_TOUCH
|
||||
};
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.TOOL;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.LOOT_BONUS_BLOCKS;
|
||||
|
||||
public override int getMaxLevel() => 3;
|
||||
|
||||
public override string getName() => "fortune";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
26
Minecraft.Server.FourKit/Enchantments/InfinityEnchantment.cs
Normal file
26
Minecraft.Server.FourKit/Enchantments/InfinityEnchantment.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class InfinityEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.BOW
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = { };
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.BOW;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.ARROW_INFINITE;
|
||||
|
||||
public override int getMaxLevel() => 1;
|
||||
|
||||
public override string getName() => "infinity";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class KnockbackEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.WOOD_SWORD, Material.STONE_SWORD, Material.IRON_SWORD, Material.GOLD_SWORD, Material.DIAMOND_SWORD,
|
||||
Material.WOOD_AXE, Material.STONE_AXE, Material.IRON_AXE, Material.GOLD_AXE, Material.DIAMOND_AXE,
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = { };
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.WEAPON;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.KNOCKBACK;
|
||||
|
||||
public override int getMaxLevel() => 2;
|
||||
|
||||
public override string getName() => "knockback";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
27
Minecraft.Server.FourKit/Enchantments/LootingEnchantment.cs
Normal file
27
Minecraft.Server.FourKit/Enchantments/LootingEnchantment.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class LootingEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.WOOD_PICKAXE, Material.STONE_PICKAXE, Material.IRON_PICKAXE, Material.GOLD_PICKAXE, Material.DIAMOND_PICKAXE,
|
||||
Material.WOOD_AXE, Material.STONE_AXE, Material.IRON_AXE, Material.GOLD_AXE, Material.DIAMOND_AXE,
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = { };
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.WEAPON;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.LOOT_BONUS_MOBS;
|
||||
|
||||
public override int getMaxLevel() => 3;
|
||||
|
||||
public override string getName() => "looting";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
26
Minecraft.Server.FourKit/Enchantments/PowerEnchantment.cs
Normal file
26
Minecraft.Server.FourKit/Enchantments/PowerEnchantment.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class PowerEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.BOW
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = { };
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.BOW;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.ARROW_DAMAGE;
|
||||
|
||||
public override int getMaxLevel() => 5;
|
||||
|
||||
public override string getName() => "power";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class ProjectileProtectionEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS,
|
||||
Material.CHAINMAIL_HELMET, Material.CHAINMAIL_CHESTPLATE, Material.CHAINMAIL_LEGGINGS, Material.CHAINMAIL_BOOTS,
|
||||
Material.GOLD_HELMET, Material.GOLD_CHESTPLATE, Material.GOLD_LEGGINGS, Material.GOLD_BOOTS,
|
||||
Material.IRON_HELMET, Material.IRON_CHESTPLATE, Material.IRON_LEGGINGS, Material.IRON_BOOTS,
|
||||
Material.DIAMOND_HELMET, Material.DIAMOND_CHESTPLATE, Material.DIAMOND_LEGGINGS, Material.DIAMOND_BOOTS,
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = {
|
||||
EnchantmentType.PROTECTION_ENVIRONMENTAL,
|
||||
EnchantmentType.PROTECTION_EXPLOSIVE,
|
||||
EnchantmentType.PROTECTION_FIRE,
|
||||
};
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.ARMOR;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.PROTECTION_PROJECTILE;
|
||||
|
||||
public override int getMaxLevel() => 4;
|
||||
|
||||
public override string getName() => "projectileprotection";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class ProtectionEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS,
|
||||
Material.CHAINMAIL_HELMET, Material.CHAINMAIL_CHESTPLATE, Material.CHAINMAIL_LEGGINGS, Material.CHAINMAIL_BOOTS,
|
||||
Material.GOLD_HELMET, Material.GOLD_CHESTPLATE, Material.GOLD_LEGGINGS, Material.GOLD_BOOTS,
|
||||
Material.IRON_HELMET, Material.IRON_CHESTPLATE, Material.IRON_LEGGINGS, Material.IRON_BOOTS,
|
||||
Material.DIAMOND_HELMET, Material.DIAMOND_CHESTPLATE, Material.DIAMOND_LEGGINGS, Material.DIAMOND_BOOTS,
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = {
|
||||
EnchantmentType.PROTECTION_EXPLOSIVE,
|
||||
EnchantmentType.PROTECTION_FIRE,
|
||||
EnchantmentType.PROTECTION_PROJECTILE,
|
||||
};
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.ARMOR;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.PROTECTION_ENVIRONMENTAL;
|
||||
|
||||
public override int getMaxLevel() => 4;
|
||||
|
||||
public override string getName() => "protection";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
26
Minecraft.Server.FourKit/Enchantments/PunchEnchantment.cs
Normal file
26
Minecraft.Server.FourKit/Enchantments/PunchEnchantment.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class PunchEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.BOW
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = { };
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.BOW;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.ARROW_KNOCKBACK;
|
||||
|
||||
public override int getMaxLevel() => 2;
|
||||
|
||||
public override string getName() => "punch";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class RespirationEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.LEATHER_HELMET, Material.CHAINMAIL_HELMET, Material.GOLD_HELMET, Material.IRON_HELMET, Material.DIAMOND_HELMET,
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = { };
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.ARMOR_HEAD;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.OXYGEN;
|
||||
|
||||
public override int getMaxLevel() => 3;
|
||||
|
||||
public override string getName() => "respiration";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class SharpnessEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.WOOD_SWORD, Material.STONE_SWORD, Material.IRON_SWORD, Material.GOLD_SWORD, Material.DIAMOND_SWORD,
|
||||
Material.WOOD_AXE, Material.STONE_AXE, Material.IRON_AXE, Material.GOLD_AXE, Material.DIAMOND_AXE,
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = {
|
||||
EnchantmentType.DAMAGE_ARTHOPODS,
|
||||
EnchantmentType.DAMAGE_UNDEAD
|
||||
};
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.WEAPON;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.DAMAGE_ALL;
|
||||
|
||||
public override int getMaxLevel() => 5;
|
||||
|
||||
public override string getName() => "sharpness";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class SilkTouchEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.WOOD_PICKAXE, Material.STONE_PICKAXE, Material.IRON_PICKAXE, Material.GOLD_PICKAXE, Material.DIAMOND_PICKAXE,
|
||||
Material.WOOD_AXE, Material.STONE_AXE, Material.IRON_AXE, Material.GOLD_AXE, Material.DIAMOND_AXE,
|
||||
Material.WOOD_SPADE, Material.STONE_SPADE, Material.IRON_SPADE, Material.GOLD_SPADE, Material.DIAMOND_SPADE,
|
||||
Material.WOOD_HOE, Material.STONE_HOE, Material.IRON_HOE, Material.GOLD_HOE, Material.DIAMOND_HOE,
|
||||
|
||||
Material.SHEARS
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = {
|
||||
EnchantmentType.LOOT_BONUS_BLOCKS
|
||||
};
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.TOOL;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.SILK_TOUCH;
|
||||
|
||||
public override int getMaxLevel() => 1;
|
||||
|
||||
public override string getName() => "silktouch";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
30
Minecraft.Server.FourKit/Enchantments/SmiteEnchantment.cs
Normal file
30
Minecraft.Server.FourKit/Enchantments/SmiteEnchantment.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class SmiteEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.WOOD_SWORD, Material.STONE_SWORD, Material.IRON_SWORD, Material.GOLD_SWORD, Material.DIAMOND_SWORD,
|
||||
Material.WOOD_AXE, Material.STONE_AXE, Material.IRON_AXE, Material.GOLD_AXE, Material.DIAMOND_AXE,
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = {
|
||||
EnchantmentType.DAMAGE_ALL,
|
||||
EnchantmentType.DAMAGE_ARTHOPODS
|
||||
};
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.WEAPON;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.DAMAGE_UNDEAD;
|
||||
|
||||
public override int getMaxLevel() => 5;
|
||||
|
||||
public override string getName() => "smite";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
30
Minecraft.Server.FourKit/Enchantments/ThornsEnchantment.cs
Normal file
30
Minecraft.Server.FourKit/Enchantments/ThornsEnchantment.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class ThornsEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS,
|
||||
Material.CHAINMAIL_HELMET, Material.CHAINMAIL_CHESTPLATE, Material.CHAINMAIL_LEGGINGS, Material.CHAINMAIL_BOOTS,
|
||||
Material.GOLD_HELMET, Material.GOLD_CHESTPLATE, Material.GOLD_LEGGINGS, Material.GOLD_BOOTS,
|
||||
Material.IRON_HELMET, Material.IRON_CHESTPLATE, Material.IRON_LEGGINGS, Material.IRON_BOOTS,
|
||||
Material.DIAMOND_HELMET, Material.DIAMOND_CHESTPLATE, Material.DIAMOND_LEGGINGS, Material.DIAMOND_BOOTS,
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = { };
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.ARMOR;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.THORNS;
|
||||
|
||||
public override int getMaxLevel() => 3;
|
||||
|
||||
public override string getName() => "thorns";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enchantments;
|
||||
|
||||
public class UnbreakingEnchantment : Enchantment
|
||||
{
|
||||
static readonly Material[] supportedItems = {
|
||||
Material.WOOD_SWORD, Material.STONE_SWORD, Material.IRON_SWORD, Material.GOLD_SWORD, Material.DIAMOND_SWORD,
|
||||
Material.WOOD_PICKAXE, Material.STONE_PICKAXE, Material.IRON_PICKAXE, Material.GOLD_PICKAXE, Material.DIAMOND_PICKAXE,
|
||||
Material.WOOD_AXE, Material.STONE_AXE, Material.IRON_AXE, Material.GOLD_AXE, Material.DIAMOND_AXE,
|
||||
Material.WOOD_SPADE, Material.STONE_SPADE, Material.IRON_SPADE, Material.GOLD_SPADE, Material.DIAMOND_SPADE,
|
||||
Material.WOOD_HOE, Material.STONE_HOE, Material.IRON_HOE, Material.GOLD_HOE, Material.DIAMOND_HOE,
|
||||
|
||||
Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS,
|
||||
Material.CHAINMAIL_HELMET, Material.CHAINMAIL_CHESTPLATE, Material.CHAINMAIL_LEGGINGS, Material.CHAINMAIL_BOOTS,
|
||||
Material.GOLD_HELMET, Material.GOLD_CHESTPLATE, Material.GOLD_LEGGINGS, Material.GOLD_BOOTS,
|
||||
Material.IRON_HELMET, Material.IRON_CHESTPLATE, Material.IRON_LEGGINGS, Material.IRON_BOOTS,
|
||||
Material.DIAMOND_HELMET, Material.DIAMOND_CHESTPLATE, Material.DIAMOND_LEGGINGS, Material.DIAMOND_BOOTS,
|
||||
|
||||
Material.FISHING_ROD, Material.BOW,
|
||||
Material.SHEARS, Material.FLINT_AND_STEEL, Material.CARROT_STICK
|
||||
};
|
||||
|
||||
static readonly EnchantmentType[] conflictedEnchants = { };
|
||||
|
||||
public override bool canEnchantItem(ItemStack item) => supportedItems.Contains(item.getType());
|
||||
|
||||
public override bool conflictsWith(Enchantment other) => conflictedEnchants.Contains(other.getEnchantType());
|
||||
|
||||
public override EnchantmentTarget getItemTarget() => EnchantmentTarget.ALL;
|
||||
|
||||
public override EnchantmentType getEnchantType() => EnchantmentType.DURABILITY;
|
||||
|
||||
public override int getMaxLevel() => 3;
|
||||
|
||||
public override string getName() => "unbreaking";
|
||||
|
||||
public override int getStartLevel() => 1;
|
||||
}
|
||||
79
Minecraft.Server.FourKit/Entity/Damageable.cs
Normal file
79
Minecraft.Server.FourKit/Entity/Damageable.cs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
namespace Minecraft.Server.FourKit.Entity;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an <see cref="Entity"/> that can take damage and has health.
|
||||
/// </summary>
|
||||
public class Damageable : Entity
|
||||
{
|
||||
private double _health = 20.0;
|
||||
private double _maxHealth = 20.0;
|
||||
private readonly double _originalMaxHealth = 20.0;
|
||||
|
||||
/// <summary>
|
||||
/// Deals the given amount of damage to this entity.
|
||||
/// This calls into the native server to apply real damage.
|
||||
/// </summary>
|
||||
/// <param name="amount">Amount of damage to deal.</param>
|
||||
public void damage(double amount)
|
||||
{
|
||||
NativeBridge.DamagePlayer?.Invoke(getEntityId(), (float)amount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the entity's health from 0 to <see cref="getMaxHealth"/>, where 0 is dead.
|
||||
/// </summary>
|
||||
/// <returns>The current health.</returns>
|
||||
public double getHealth() => _health;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum health this entity has.
|
||||
/// </summary>
|
||||
/// <returns>The maximum health.</returns>
|
||||
public double getMaxHealth() => _maxHealth;
|
||||
|
||||
/// <summary>
|
||||
/// Resets the max health to the original amount.
|
||||
/// </summary>
|
||||
public void resetMaxHealth()
|
||||
{
|
||||
_maxHealth = _originalMaxHealth;
|
||||
if (_health > _maxHealth)
|
||||
_health = _maxHealth;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the entity's health from 0 to <see cref="getMaxHealth"/>, where 0 is dead.
|
||||
/// This calls into the native server to apply the health change.
|
||||
/// </summary>
|
||||
/// <param name="health">New health value.</param>
|
||||
public void setHealth(double health)
|
||||
{
|
||||
NativeBridge.SetPlayerHealth?.Invoke(getEntityId(), (float)Math.Clamp(health, 0.0, _maxHealth));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the maximum health this entity can have.
|
||||
/// If the entity's current health exceeds the new maximum, it is clamped.
|
||||
/// </summary>
|
||||
/// <param name="health">New maximum health value.</param>
|
||||
public void setMaxHealth(double health)
|
||||
{
|
||||
_maxHealth = health;
|
||||
if (_health > _maxHealth)
|
||||
_health = _maxHealth;
|
||||
}
|
||||
|
||||
// --- Internal setter used by the bridge ---
|
||||
|
||||
/// <summary>
|
||||
/// Updates health directly. Called internally by the bridge.
|
||||
/// </summary>
|
||||
/// <param name="health">The new health value.</param>
|
||||
internal void SetHealthInternal(double health) => _health = health;
|
||||
|
||||
/// <summary>
|
||||
/// Updates max health directly. Called internally by the bridge.
|
||||
/// </summary>
|
||||
/// <param name="maxHealth">The new max health value.</param>
|
||||
internal void SetMaxHealthInternal(double maxHealth) => _maxHealth = maxHealth;
|
||||
}
|
||||
66
Minecraft.Server.FourKit/Entity/DisconnectReason.cs
Normal file
66
Minecraft.Server.FourKit/Entity/DisconnectReason.cs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
namespace Minecraft.Server.FourKit.Entity;
|
||||
// eh
|
||||
|
||||
/// <summary>
|
||||
/// Enum representing the reason a player was disconnected from the server.
|
||||
/// mirrored from <c>DisconnectPacket::eDisconnectReason</c>.
|
||||
/// </summary>
|
||||
public enum DisconnectReason
|
||||
{
|
||||
/// <summary>No specific reason.</summary>
|
||||
NONE = 0,
|
||||
/// <summary>The player quit voluntarily.</summary>
|
||||
QUITTING = 1,
|
||||
/// <summary>The connection was closed.</summary>
|
||||
CLOSED = 2,
|
||||
/// <summary>The login took too long.</summary>
|
||||
LOGIN_TOO_LONG = 3,
|
||||
/// <summary>The player had an illegal stance.</summary>
|
||||
ILLEGAL_STANCE = 4,
|
||||
/// <summary>The player had an illegal position.</summary>
|
||||
ILLEGAL_POSITION = 5,
|
||||
/// <summary>The player moved too quickly.</summary>
|
||||
MOVED_TOO_QUICKLY = 6,
|
||||
/// <summary>The player was flying when not allowed.</summary>
|
||||
NO_FLYING = 7,
|
||||
/// <summary>The player was kicked by an operator or plugin.</summary>
|
||||
KICKED = 8,
|
||||
/// <summary>The connection timed out.</summary>
|
||||
TIME_OUT = 9,
|
||||
/// <summary>Packet overflow.</summary>
|
||||
OVERFLOW = 10,
|
||||
/// <summary>End of stream reached unexpectedly.</summary>
|
||||
END_OF_STREAM = 11,
|
||||
/// <summary>The server is full.</summary>
|
||||
SERVER_FULL = 12,
|
||||
/// <summary>The server is outdated.</summary>
|
||||
OUTDATED_SERVER = 13,
|
||||
/// <summary>The client is outdated.</summary>
|
||||
OUTDATED_CLIENT = 14,
|
||||
/// <summary>An unexpected packet was received.</summary>
|
||||
UNEXPECTED_PACKET = 15,
|
||||
/// <summary>Connection creation failed.</summary>
|
||||
CONNECTION_CREATION_FAILED = 16,
|
||||
/// <summary>The host does not have multiplayer privileges.</summary>
|
||||
NO_MULTIPLAYER_PRIVILEGES_HOST = 17,
|
||||
/// <summary>The joining player does not have multiplayer privileges.</summary>
|
||||
NO_MULTIPLAYER_PRIVILEGES_JOIN = 18,
|
||||
/// <summary>All local players lack UGC permissions.</summary>
|
||||
NO_UGC_ALL_LOCAL = 19,
|
||||
/// <summary>A single local player lacks UGC permissions.</summary>
|
||||
NO_UGC_SINGLE_LOCAL = 20,
|
||||
/// <summary>All local players have content restrictions.</summary>
|
||||
CONTENT_RESTRICTED_ALL_LOCAL = 21,
|
||||
/// <summary>A single local player has content restrictions.</summary>
|
||||
CONTENT_RESTRICTED_SINGLE_LOCAL = 22,
|
||||
/// <summary>A remote player lacks UGC permissions.</summary>
|
||||
NO_UGC_REMOTE = 23,
|
||||
/// <summary>No friends in the game.</summary>
|
||||
NO_FRIENDS_IN_GAME = 24,
|
||||
/// <summary>The player was banned.</summary>
|
||||
BANNED = 25,
|
||||
/// <summary>The player is not friends with the host.</summary>
|
||||
NOT_FRIENDS_WITH_HOST = 26,
|
||||
/// <summary>NAT type mismatch.</summary>
|
||||
NAT_MISMATCH = 27,
|
||||
}
|
||||
192
Minecraft.Server.FourKit/Entity/Entity.cs
Normal file
192
Minecraft.Server.FourKit/Entity/Entity.cs
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
namespace Minecraft.Server.FourKit.Entity;
|
||||
|
||||
using Minecraft.Server.FourKit.Util;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a base entity in the world
|
||||
/// </summary>
|
||||
public class Entity
|
||||
{
|
||||
private Location _location = new();
|
||||
private Guid _uniqueId = Guid.NewGuid();
|
||||
private float _fallDistance;
|
||||
private int _dimensionId;
|
||||
private int _entityId;
|
||||
private EntityType _entityType = EntityType.UNKNOWN;
|
||||
private bool _onGround;
|
||||
private double _velocityX, _velocityY, _velocityZ;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the entity's current position.
|
||||
/// </summary>
|
||||
/// <returns>a new copy of <see cref="Location"/> containing the position of this entity</returns>
|
||||
public Location getLocation() => _location;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a unique id for this entity
|
||||
/// </summary>
|
||||
/// <returns>Entity id</returns>
|
||||
public virtual int getEntityId() => _entityId;
|
||||
|
||||
/// <summary>
|
||||
/// Get the type of the entity.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="EntityType"/> of this entity.</returns>
|
||||
public new virtual EntityType getType() => _entityType;
|
||||
public new virtual EntityType GetType() => _entityType;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a unique and persistent id for this entity. Note that this is not the standard UUID for players.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Guid"/> unique to this entity.</returns>
|
||||
public Guid getUniqueId() => _uniqueId;
|
||||
|
||||
/// <summary>
|
||||
/// Teleports this entity to the given location.
|
||||
/// This calls into the native server to perform the actual teleport.
|
||||
/// </summary>
|
||||
/// <param name="location">The destination location.</param>
|
||||
/// <returns><c>true</c> if the teleport was successful.</returns>
|
||||
public virtual bool teleport(Location location)
|
||||
{
|
||||
int targetDimId = location.LocationWorld?.getDimensionId() ?? _dimensionId;
|
||||
NativeBridge.TeleportEntity?.Invoke(getEntityId(), targetDimId, location.getX(), location.getY(), location.getZ());
|
||||
SetLocation(location);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the fall distance for this entity.
|
||||
/// </summary>
|
||||
/// <param name="distance">The fall distance value.</param>
|
||||
public void setFallDistance(float distance)
|
||||
{
|
||||
_fallDistance = distance;
|
||||
NativeBridge.SetFallDistance?.Invoke(getEntityId(), distance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the distance this entity has fallen.
|
||||
/// </summary>
|
||||
/// <returns>The current fall distance.</returns>
|
||||
public float getFallDistance() => _fallDistance;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current world this entity resides in.
|
||||
/// </summary>
|
||||
/// <returns>World containing this entity.</returns>
|
||||
public World getWorld() => FourKit.getWorld(_dimensionId);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the entity is supported by a block. This value is a
|
||||
/// state updated by the server and is not recalculated unless the entity moves.
|
||||
/// </summary>
|
||||
/// <returns>True if entity is on ground.</returns>
|
||||
public bool isOnGround() => _onGround;
|
||||
|
||||
/// <summary>
|
||||
/// Gets this entity's current velocity.
|
||||
/// </summary>
|
||||
/// <returns>Current travelling velocity of this entity.</returns>
|
||||
public Vector getVelocity() => new Vector(_velocityX, _velocityY, _velocityZ);
|
||||
|
||||
/// <summary>
|
||||
/// Sets this entity's velocity.
|
||||
/// </summary>
|
||||
/// <param name="velocity">New velocity to travel with.</param>
|
||||
public void setVelocity(Vector velocity)
|
||||
{
|
||||
_velocityX = velocity.getX();
|
||||
_velocityY = velocity.getY();
|
||||
_velocityZ = velocity.getZ();
|
||||
NativeBridge.SetVelocity?.Invoke(getEntityId(), velocity.getX(), velocity.getY(), velocity.getZ());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether this entity is inside a vehicle.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if the entity is in a vehicle.</returns>
|
||||
public bool isInsideVehicle()
|
||||
{
|
||||
return (NativeBridge.GetVehicleId?.Invoke(getEntityId()) ?? -1) >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Leave the current vehicle. If the entity is currently in a vehicle
|
||||
/// (and is removed from it), <c>true</c> will be returned, otherwise
|
||||
/// <c>false</c> will be returned.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if the entity was in a vehicle.</returns>
|
||||
public bool leaveVehicle()
|
||||
{
|
||||
return NativeBridge.LeaveVehicle?.Invoke(getEntityId()) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the vehicle that this entity is inside. If there is no vehicle,
|
||||
/// <c>null</c> will be returned.
|
||||
/// </summary>
|
||||
/// <returns>The current vehicle, or <c>null</c>.</returns>
|
||||
public Entity? getVehicle()
|
||||
{
|
||||
int vehicleId = NativeBridge.GetVehicleId?.Invoke(getEntityId()) ?? -1;
|
||||
if (vehicleId < 0) return null;
|
||||
return FourKit.GetEntityByEntityId(vehicleId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Eject any passenger.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if there was a passenger.</returns>
|
||||
public bool eject()
|
||||
{
|
||||
return NativeBridge.Eject?.Invoke(getEntityId()) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the primary passenger of a vehicle. For vehicles that could
|
||||
/// have multiple passengers, this will only return the primary passenger.
|
||||
/// </summary>
|
||||
/// <returns>The passenger entity, or <c>null</c>.</returns>
|
||||
public Entity? getPassenger()
|
||||
{
|
||||
int passengerId = NativeBridge.GetPassengerId?.Invoke(getEntityId()) ?? -1;
|
||||
if (passengerId < 0) return null;
|
||||
return FourKit.GetEntityByEntityId(passengerId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the passenger of a vehicle.
|
||||
/// </summary>
|
||||
/// <param name="passenger">The new passenger.</param>
|
||||
/// <returns><c>false</c> if it could not be done for whatever reason.</returns>
|
||||
public bool setPassenger(Entity passenger)
|
||||
{
|
||||
if (passenger == null || NativeBridge.SetPassenger == null) return false;
|
||||
return NativeBridge.SetPassenger(getEntityId(), passenger.getEntityId()) != 0;
|
||||
}
|
||||
|
||||
// INTERNAL
|
||||
internal void SetLocation(Location location)
|
||||
{
|
||||
_location = location;
|
||||
}
|
||||
|
||||
internal void SetFallDistanceInternal(float distance) => _fallDistance = distance;
|
||||
|
||||
internal void SetUniqueId(Guid id)
|
||||
{
|
||||
_uniqueId = id;
|
||||
}
|
||||
|
||||
internal void SetDimensionInternal(int dimensionId) => _dimensionId = dimensionId;
|
||||
internal void SetEntityIdInternal(int entityId) => _entityId = entityId;
|
||||
internal void SetEntityTypeInternal(EntityType entityType) => _entityType = entityType;
|
||||
internal void SetOnGroundInternal(bool onGround) => _onGround = onGround;
|
||||
internal void SetVelocityInternal(double x, double y, double z)
|
||||
{
|
||||
_velocityX = x;
|
||||
_velocityY = y;
|
||||
_velocityZ = z;
|
||||
}
|
||||
}
|
||||
132
Minecraft.Server.FourKit/Entity/EntityType.cs
Normal file
132
Minecraft.Server.FourKit/Entity/EntityType.cs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
namespace Minecraft.Server.FourKit.Entity;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the type of an <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
public enum EntityType
|
||||
{
|
||||
/// <summary>An arrow projectile; may get stuck in the ground.</summary>
|
||||
ARROW,
|
||||
/// <summary>A bat.</summary>
|
||||
BAT,
|
||||
/// <summary>A blaze.</summary>
|
||||
BLAZE,
|
||||
/// <summary>A placed boat.</summary>
|
||||
BOAT,
|
||||
/// <summary>A cave spider.</summary>
|
||||
CAVE_SPIDER,
|
||||
/// <summary>A chicken.</summary>
|
||||
CHICKEN,
|
||||
/// <summary>A complex entity part.</summary>
|
||||
COMPLEX_PART,
|
||||
/// <summary>A cow.</summary>
|
||||
COW,
|
||||
/// <summary>A creeper.</summary>
|
||||
CREEPER,
|
||||
/// <summary>An item resting on the ground.</summary>
|
||||
DROPPED_ITEM,
|
||||
/// <summary>A flying chicken egg.</summary>
|
||||
EGG,
|
||||
/// <summary>An ender crystal.</summary>
|
||||
ENDER_CRYSTAL,
|
||||
/// <summary>An ender dragon.</summary>
|
||||
ENDER_DRAGON,
|
||||
/// <summary>A flying ender pearl.</summary>
|
||||
ENDER_PEARL,
|
||||
/// <summary>An ender eye signal.</summary>
|
||||
ENDER_SIGNAL,
|
||||
/// <summary>An enderman.</summary>
|
||||
ENDERMAN,
|
||||
/// <summary>An experience orb.</summary>
|
||||
EXPERIENCE_ORB,
|
||||
/// <summary>A block that is going to or is about to fall.</summary>
|
||||
FALLING_BLOCK,
|
||||
/// <summary>A flying large fireball, as thrown by a Ghast for example.</summary>
|
||||
FIREBALL,
|
||||
/// <summary>A firework rocket.</summary>
|
||||
FIREWORK,
|
||||
/// <summary>A fishing line and bobber.</summary>
|
||||
FISHING_HOOK,
|
||||
/// <summary>A ghast.</summary>
|
||||
GHAST,
|
||||
/// <summary>A giant.</summary>
|
||||
GIANT,
|
||||
/// <summary>A horse.</summary>
|
||||
HORSE,
|
||||
/// <summary>An iron golem.</summary>
|
||||
IRON_GOLEM,
|
||||
/// <summary>An item frame on a wall.</summary>
|
||||
ITEM_FRAME,
|
||||
/// <summary>A leash attached to a fencepost.</summary>
|
||||
LEASH_HITCH,
|
||||
/// <summary>A bolt of lightning.</summary>
|
||||
LIGHTNING,
|
||||
/// <summary>A magma cube.</summary>
|
||||
MAGMA_CUBE,
|
||||
/// <summary>A minecart.</summary>
|
||||
MINECART,
|
||||
/// <summary>A minecart with a chest.</summary>
|
||||
MINECART_CHEST,
|
||||
/// <summary>A minecart with a command block.</summary>
|
||||
MINECART_COMMAND,
|
||||
/// <summary>A minecart with a furnace.</summary>
|
||||
MINECART_FURNACE,
|
||||
/// <summary>A minecart with a hopper.</summary>
|
||||
MINECART_HOPPER,
|
||||
/// <summary>A minecart with a mob spawner.</summary>
|
||||
MINECART_MOB_SPAWNER,
|
||||
/// <summary>A minecart with TNT.</summary>
|
||||
MINECART_TNT,
|
||||
/// <summary>A mooshroom.</summary>
|
||||
MUSHROOM_COW,
|
||||
/// <summary>An ocelot.</summary>
|
||||
OCELOT,
|
||||
/// <summary>A painting on a wall.</summary>
|
||||
PAINTING,
|
||||
/// <summary>A pig.</summary>
|
||||
PIG,
|
||||
/// <summary>A zombie pigman.</summary>
|
||||
PIG_ZOMBIE,
|
||||
/// <summary>A player.</summary>
|
||||
PLAYER,
|
||||
/// <summary>Primed TNT that is about to explode.</summary>
|
||||
PRIMED_TNT,
|
||||
/// <summary>A sheep.</summary>
|
||||
SHEEP,
|
||||
/// <summary>A silverfish.</summary>
|
||||
SILVERFISH,
|
||||
/// <summary>A skeleton.</summary>
|
||||
SKELETON,
|
||||
/// <summary>A slime.</summary>
|
||||
SLIME,
|
||||
/// <summary>A flying small fireball, such as thrown by a Blaze or player.</summary>
|
||||
SMALL_FIREBALL,
|
||||
/// <summary>A flying snowball.</summary>
|
||||
SNOWBALL,
|
||||
/// <summary>A snowman.</summary>
|
||||
SNOWMAN,
|
||||
/// <summary>A spider.</summary>
|
||||
SPIDER,
|
||||
/// <summary>A flying splash potion.</summary>
|
||||
SPLASH_POTION,
|
||||
/// <summary>A squid.</summary>
|
||||
SQUID,
|
||||
/// <summary>A flying experience bottle.</summary>
|
||||
THROWN_EXP_BOTTLE,
|
||||
/// <summary>An unknown entity without an Entity Class.</summary>
|
||||
UNKNOWN,
|
||||
/// <summary>A villager.</summary>
|
||||
VILLAGER,
|
||||
/// <summary>A weather entity.</summary>
|
||||
WEATHER,
|
||||
/// <summary>A witch.</summary>
|
||||
WITCH,
|
||||
/// <summary>A wither.</summary>
|
||||
WITHER,
|
||||
/// <summary>A flying wither skull projectile.</summary>
|
||||
WITHER_SKULL,
|
||||
/// <summary>A wolf.</summary>
|
||||
WOLF,
|
||||
/// <summary>A zombie.</summary>
|
||||
ZOMBIE,
|
||||
}
|
||||
175
Minecraft.Server.FourKit/Entity/HumanEntity.cs
Normal file
175
Minecraft.Server.FourKit/Entity/HumanEntity.cs
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
namespace Minecraft.Server.FourKit.Entity;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a human entity in the world (e.g. a player).
|
||||
/// </summary>
|
||||
public abstract class HumanEntity : LivingEntity, InventoryHolder
|
||||
{
|
||||
private GameMode _gameMode = GameMode.SURVIVAL;
|
||||
private string _name = string.Empty;
|
||||
internal PlayerInventory _playerInventory = new();
|
||||
internal Inventory _enderChestInventory = new("Ender Chest", InventoryType.ENDER_CHEST, 27);
|
||||
private ItemStack? _cursorItem;
|
||||
private bool _sleeping;
|
||||
private int _sleepTicks;
|
||||
|
||||
/// <summary>
|
||||
/// Gets this human's current <see cref="GameMode"/>.
|
||||
/// </summary>
|
||||
/// <returns>The current game mode.</returns>
|
||||
public GameMode getGameMode() => _gameMode;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name of this player.
|
||||
/// </summary>
|
||||
/// <returns>The display name.</returns>
|
||||
public string getName() => _name;
|
||||
|
||||
/// <summary>
|
||||
/// Sets this human's current <see cref="GameMode"/>.
|
||||
/// </summary>
|
||||
/// <param name="mode">The new game mode.</param>
|
||||
public void setGameMode(GameMode mode)
|
||||
{
|
||||
NativeBridge.SetPlayerGameMode?.Invoke(getEntityId(), (int)mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the player's inventory.
|
||||
/// </summary>
|
||||
/// <returns>The inventory of the player, this also contains the armor slots.</returns>
|
||||
Inventory InventoryHolder.getInventory() => getInventory();
|
||||
|
||||
/// <summary>
|
||||
/// Get the player's inventory.
|
||||
/// This also contains the armor slots.
|
||||
/// </summary>
|
||||
/// <returns>The player's inventory.</returns>
|
||||
public PlayerInventory getInventory()
|
||||
{
|
||||
return _playerInventory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the player's EnderChest inventory.
|
||||
/// </summary>
|
||||
/// <returns>The EnderChest of the player.</returns>
|
||||
public Inventory getEnderChest()
|
||||
{
|
||||
return _enderChestInventory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the ItemStack currently in your hand, can be empty.
|
||||
/// </summary>
|
||||
/// <returns>The ItemStack of the item you are currently holding.</returns>
|
||||
public ItemStack? getItemInHand()
|
||||
{
|
||||
return _playerInventory.getItemInHand();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the item to the given ItemStack, this will replace whatever the
|
||||
/// user was holding.
|
||||
/// </summary>
|
||||
/// <param name="item">The ItemStack which will end up in the hand.</param>
|
||||
public void setItemInHand(ItemStack? item)
|
||||
{
|
||||
_playerInventory.setItemInHand(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the ItemStack currently on your cursor, can be empty.
|
||||
/// Will always be empty if the player currently has no open window.
|
||||
/// </summary>
|
||||
/// <returns>The ItemStack of the item you are currently moving around.</returns>
|
||||
public ItemStack? getItemOnCursor() => _cursorItem;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the item to the given ItemStack, this will replace whatever the
|
||||
/// user was moving. Will always be empty if the player currently has no open window.
|
||||
/// </summary>
|
||||
/// <param name="item">The ItemStack which will end up in the hand.</param>
|
||||
public void setItemOnCursor(ItemStack? item) => _cursorItem = item;
|
||||
|
||||
/// <summary>
|
||||
/// If the player currently has an inventory window open, this method will
|
||||
/// close it on both the server and client side.
|
||||
/// </summary>
|
||||
public void closeInventory()
|
||||
{
|
||||
NativeBridge.CloseContainer?.Invoke(getEntityId());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens an inventory window with the specified inventory on the top.
|
||||
/// </summary>
|
||||
/// <param name="inventory">The inventory to open.</param>
|
||||
/// <returns>The newly opened InventoryView, or null if it could not be opened.</returns>
|
||||
public InventoryView? openInventory(Inventory inventory)
|
||||
{
|
||||
if (NativeBridge.OpenVirtualContainer == null)
|
||||
return null;
|
||||
|
||||
closeInventory();
|
||||
|
||||
int nativeType = inventory.getType() switch
|
||||
{
|
||||
InventoryType.CHEST => 0,
|
||||
InventoryType.DISPENSER => 3,
|
||||
InventoryType.DROPPER => 10,
|
||||
InventoryType.HOPPER => 5,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
int size = inventory.getSize();
|
||||
int[] buf = new int[size * 3];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
var item = inventory._items[i];
|
||||
buf[i * 3] = item?.getTypeId() ?? 0;
|
||||
buf[i * 3 + 1] = item?.getAmount() ?? 0;
|
||||
buf[i * 3 + 2] = item?.getDurability() ?? 0;
|
||||
}
|
||||
|
||||
string title = inventory.getName();
|
||||
int titleByteLen = System.Text.Encoding.UTF8.GetByteCount(title);
|
||||
IntPtr titlePtr = Marshal.StringToCoTaskMemUTF8(title);
|
||||
var gh = GCHandle.Alloc(buf, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
NativeBridge.OpenVirtualContainer(getEntityId(), nativeType, titlePtr, titleByteLen, size, gh.AddrOfPinnedObject());
|
||||
}
|
||||
finally
|
||||
{
|
||||
gh.Free();
|
||||
Marshal.FreeCoTaskMem(titlePtr);
|
||||
}
|
||||
|
||||
var view = new InventoryView(inventory, getInventory(), this, inventory.getType());
|
||||
return view;
|
||||
}
|
||||
|
||||
internal void SetGameModeInternal(GameMode mode) => _gameMode = mode;
|
||||
|
||||
internal void SetNameInternal(string name) => _name = name;
|
||||
|
||||
internal void SetSleepingInternal(bool sleeping) => _sleeping = sleeping;
|
||||
|
||||
internal void SetSleepTicksInternal(int ticks) => _sleepTicks = ticks;
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether this player is slumbering.
|
||||
/// </summary>
|
||||
/// <returns>slumber state</returns>
|
||||
public bool isSleeping() => _sleeping;
|
||||
|
||||
/// <summary>
|
||||
/// Get the sleep ticks of the player. This value may be capped.
|
||||
/// </summary>
|
||||
/// <returns>slumber ticks</returns>
|
||||
public int getSleepTicks() => _sleepTicks;
|
||||
}
|
||||
32
Minecraft.Server.FourKit/Entity/Item.cs
Normal file
32
Minecraft.Server.FourKit/Entity/Item.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
namespace Minecraft.Server.FourKit.Entity;
|
||||
|
||||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a dropped item on the ground.
|
||||
/// </summary>
|
||||
public class Item : Entity
|
||||
{
|
||||
private ItemStack _itemStack;
|
||||
|
||||
internal Item(int entityId, int dimId, double x, double y, double z, ItemStack itemStack)
|
||||
{
|
||||
SetEntityIdInternal(entityId);
|
||||
SetEntityTypeInternal(EntityType.DROPPED_ITEM);
|
||||
SetDimensionInternal(dimId);
|
||||
SetLocation(new Location(FourKit.getWorld(dimId), x, y, z));
|
||||
_itemStack = itemStack;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item stack associated with this item.
|
||||
/// </summary>
|
||||
/// <returns>An item stack.</returns>
|
||||
public ItemStack getItemStack() => _itemStack;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the item stack of this item.
|
||||
/// </summary>
|
||||
/// <param name="stack">The new item stack.</param>
|
||||
public void setItemStack(ItemStack stack) => _itemStack = stack;
|
||||
}
|
||||
51
Minecraft.Server.FourKit/Entity/LivingEntity.cs
Normal file
51
Minecraft.Server.FourKit/Entity/LivingEntity.cs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
namespace Minecraft.Server.FourKit.Entity;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a living entity in the world that has health and can take damage.
|
||||
/// </summary>
|
||||
public class LivingEntity : Damageable
|
||||
{
|
||||
private double _eyeHeight = 1.62;
|
||||
|
||||
internal LivingEntity() { }
|
||||
|
||||
internal LivingEntity(int entityId, EntityType entityType, int dimId, double x, double y, double z,
|
||||
float health = 20f, float maxHealth = 20f)
|
||||
{
|
||||
SetEntityIdInternal(entityId);
|
||||
SetEntityTypeInternal(entityType);
|
||||
SetDimensionInternal(dimId);
|
||||
SetLocation(new Location(FourKit.getWorld(dimId), x, y, z));
|
||||
if (maxHealth > 0)
|
||||
SetMaxHealthInternal(maxHealth);
|
||||
SetHealthInternal(health);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height of the living entity's eyes above its <see cref="Location"/>.
|
||||
/// </summary>
|
||||
/// <returns>The eye height.</returns>
|
||||
public double getEyeHeight() => _eyeHeight;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height of the living entity's eyes above its <see cref="Location"/>.
|
||||
/// </summary>
|
||||
/// <param name="ignoreSneaking">If <c>true</c>, returns the standing eye height regardless of sneak state.</param>
|
||||
/// <returns>The eye height.</returns>
|
||||
public double getEyeHeight(bool ignoreSneaking)
|
||||
{
|
||||
if (ignoreSneaking)
|
||||
return _eyeHeight;
|
||||
|
||||
// When sneaking the eye height is slightly lower
|
||||
return _eyeHeight - 0.08;
|
||||
}
|
||||
|
||||
// --- Internal setter used by the bridge ---
|
||||
|
||||
/// <summary>
|
||||
/// Updates the eye height. Called internally by the bridge.
|
||||
/// </summary>
|
||||
/// <param name="eyeHeight">The new eye height.</param>
|
||||
internal void SetEyeHeightInternal(double eyeHeight) => _eyeHeight = eyeHeight;
|
||||
}
|
||||
26
Minecraft.Server.FourKit/Entity/OfflinePlayer.cs
Normal file
26
Minecraft.Server.FourKit/Entity/OfflinePlayer.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
namespace Minecraft.Server.FourKit.Entity;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a player identity that may or may not currently be online.
|
||||
/// </summary>
|
||||
public interface OfflinePlayer
|
||||
{
|
||||
/// <summary>Returns the name of this player.</summary>
|
||||
/// <returns>The player's name.</returns>
|
||||
string getName();
|
||||
|
||||
/// <summary>Gets a Player object that this represents, if there is one.</summary>
|
||||
/// <returns>A <see cref="Player"/> instance if the player is online; otherwise <c>null</c>.</returns>
|
||||
Player? getPlayer();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the UUID that uniquely identifies this player across sessions.
|
||||
/// This is the player-specific UUID, not the entity UUID.
|
||||
/// </summary>
|
||||
/// <returns>The player's unique identifier.</returns>
|
||||
Guid getUniqueId();
|
||||
|
||||
/// <summary>Checks if this player is currently online.</summary>
|
||||
/// <returns><c>true</c> if the player is online; otherwise <c>false</c>.</returns>
|
||||
bool isOnline();
|
||||
}
|
||||
665
Minecraft.Server.FourKit/Entity/Player.cs
Normal file
665
Minecraft.Server.FourKit/Entity/Player.cs
Normal file
|
|
@ -0,0 +1,665 @@
|
|||
namespace Minecraft.Server.FourKit.Entity;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using Minecraft.Server.FourKit.Command;
|
||||
using Minecraft.Server.FourKit.Experimental;
|
||||
using Minecraft.Server.FourKit.Inventory;
|
||||
using Minecraft.Server.FourKit.Net;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a player connected to the server.
|
||||
/// </summary>
|
||||
public class Player : HumanEntity, OfflinePlayer, CommandSender
|
||||
{
|
||||
private float _saturation = 5.0f;
|
||||
private float _walkSpeed = 0.2f;
|
||||
private float _exhaustion;
|
||||
private int _foodLevel = 20;
|
||||
private int _level;
|
||||
private float _exp;
|
||||
private int _totalExperience;
|
||||
private Guid _playerUniqueId;
|
||||
private ulong _playerRawOnlineXUID;
|
||||
private ulong _playerRawOfflineXUID;
|
||||
private string? _displayName;
|
||||
private bool _sneaking;
|
||||
private bool _sprinting;
|
||||
private bool _allowFlight;
|
||||
private bool _sleepingIgnored;
|
||||
|
||||
private PlayerConnection _connection;
|
||||
|
||||
internal bool IsOnline { get; set; }
|
||||
|
||||
internal Player(int entityId, string name)
|
||||
{
|
||||
SetEntityIdInternal(entityId);
|
||||
SetEntityTypeInternal(EntityType.PLAYER);
|
||||
SetNameInternal(name);
|
||||
IsOnline = true;
|
||||
_playerInventory._holder = this;
|
||||
_connection = new PlayerConnection(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override EntityType getType() => EntityType.PLAYER;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override EntityType GetType() => EntityType.PLAYER;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool teleport(Location location)
|
||||
{
|
||||
int targetDimId = location.LocationWorld?.getDimensionId() ?? getLocation().LocationWorld?.getDimensionId() ?? 0;
|
||||
NativeBridge.TeleportEntity?.Invoke(getEntityId(), targetDimId, location.X, location.Y, location.Z);
|
||||
SetLocation(location);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <b>Experimental.</b> Gets the player's <see cref="PlayerConnection"/>, which can be used
|
||||
/// to send raw packet data directly to the client.
|
||||
/// </summary>
|
||||
/// <returns>The player's connection.</returns>
|
||||
public PlayerConnection getConnection() => _connection;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Player? getPlayer() => IsOnline ? this : null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the "friendly" name to display of this player.
|
||||
/// This may include color. If no custom display name has been set,
|
||||
/// this returns the player's <see cref="HumanEntity.getName"/>.
|
||||
/// </summary>
|
||||
/// <returns>The display name.</returns>
|
||||
public string getDisplayName() => _displayName ?? getName();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the "friendly" name to display of this player.
|
||||
/// </summary>
|
||||
/// <param name="name">The display name, or <c>null</c> to reset to <see cref="HumanEntity.getName"/>.</param>
|
||||
public void setDisplayName(string? name)
|
||||
{
|
||||
_displayName = name;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool isOnline() => IsOnline;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the UUID that uniquely identifies this player across sessions.
|
||||
/// This is the player-specific UUID, not the entity UUID.
|
||||
/// </summary>
|
||||
/// <returns>The player's unique identifier.</returns>
|
||||
public new Guid getUniqueId() => _playerUniqueId;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <b>Experimental.</b> Gets the raw online XUID (Xbox User ID) for this player.
|
||||
/// The online XUID is used for guests.
|
||||
/// </summary>
|
||||
/// <returns>The raw online XUID value.</returns>
|
||||
public ulong getRawOnlineXUID() => _playerRawOnlineXUID;
|
||||
|
||||
/// <summary>
|
||||
/// <b>Experimental.</b> Gets the raw offline XUID (Xbox User ID) for this player.
|
||||
/// The offline XUID is the main XUID used by the client.
|
||||
/// </summary>
|
||||
/// <returns>The raw offline XUID value.</returns>
|
||||
public ulong getRawOfflineXUID() => _playerRawOfflineXUID;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the player's estimated ping in milliseconds.
|
||||
/// This value represents a weighted average of the response time to application layer ping packets sent. This value does not represent the network round trip time and as such may have less granularity and be impacted by other sources. For these reasons it should not be used for anti-cheat purposes. Its recommended use is only as a qualitative indicator of connection quality.
|
||||
/// </summary>
|
||||
/// <returns>The player's estimated ping in milliseconds.</returns>
|
||||
public int getPing()
|
||||
{
|
||||
if (NativeBridge.GetPlayerLatency == null)
|
||||
return -1;
|
||||
|
||||
return NativeBridge.GetPlayerLatency(getEntityId());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the player's current saturation level.
|
||||
/// Saturation acts as a buffer before hunger begins to deplete.
|
||||
/// </summary>
|
||||
/// <returns>The current saturation level.</returns>
|
||||
public float getSaturation() => _saturation;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current allowed speed that a client can walk.
|
||||
/// The default value is 0.2.
|
||||
/// </summary>
|
||||
/// <returns>The current walk speed.</returns>
|
||||
public float getWalkSpeed() => _walkSpeed;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the speed at which a client will walk.
|
||||
/// This calls into the native server to apply the change.
|
||||
/// </summary>
|
||||
/// <param name="value">The new walk speed.</param>
|
||||
public void setWalkSpeed(float value)
|
||||
{
|
||||
_walkSpeed = value;
|
||||
NativeBridge.SetWalkSpeed?.Invoke(getEntityId(), value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the player is in sneak mode.
|
||||
/// </summary>
|
||||
/// <returns>True if player is in sneak mode.</returns>
|
||||
public bool isSneaking() => _sneaking;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the player is sprinting or not.
|
||||
/// </summary>
|
||||
/// <returns>True if player is sprinting.</returns>
|
||||
public bool isSprinting() => _sprinting;
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether the player is ignored as not sleeping. If everyone is
|
||||
/// either sleeping or has this flag set, then time will advance to the
|
||||
/// next day. If everyone has this flag set but no one is actually in
|
||||
/// bed, then nothing will happen.
|
||||
/// </summary>
|
||||
/// <param name="isSleeping">Whether to ignore.</param>
|
||||
public void setSleepingIgnored(bool isSleeping)
|
||||
{
|
||||
_sleepingIgnored = isSleeping;
|
||||
NativeBridge.SetSleepingIgnored?.Invoke(getEntityId(), isSleeping ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the player is sleeping ignored.
|
||||
/// </summary>
|
||||
/// <returns>Whether player is ignoring sleep.</returns>
|
||||
public bool isSleepingIgnored() => _sleepingIgnored;
|
||||
|
||||
/// <summary>
|
||||
/// Play a sound for a player at the location.
|
||||
/// This function will fail silently if Location or Sound are null.
|
||||
/// </summary>
|
||||
/// <param name="location">The location to play the sound.</param>
|
||||
/// <param name="sound">The sound to play.</param>
|
||||
/// <param name="volume">The volume of the sound.</param>
|
||||
/// <param name="pitch">The pitch of the sound.</param>
|
||||
public void playSound(Location location, Sound sound, float volume, float pitch)
|
||||
{
|
||||
if (location == null)
|
||||
return;
|
||||
NativeBridge.PlaySound?.Invoke(getEntityId(), (int)sound, location.X, location.Y, location.Z, volume, pitch);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the Player is allowed to fly via jump key double-tap
|
||||
/// like in creative mode.
|
||||
/// </summary>
|
||||
/// <returns>True if the player is allowed to fly.</returns>
|
||||
public bool getAllowFlight() => _allowFlight;
|
||||
|
||||
/// <summary>
|
||||
/// Sets if the Player is allowed to fly via jump key double-tap like
|
||||
/// in creative mode.
|
||||
/// </summary>
|
||||
/// <param name="flight">If flight should be allowed.</param>
|
||||
public void setAllowFlight(bool flight)
|
||||
{
|
||||
_allowFlight = flight;
|
||||
NativeBridge.SetAllowFlight?.Invoke(getEntityId(), flight ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void sendMessage(string message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message) || NativeBridge.SendMessage == null)
|
||||
return;
|
||||
if (message.Length > FourKit.MAX_CHAT_LENGTH)
|
||||
message = message[..FourKit.MAX_CHAT_LENGTH];
|
||||
|
||||
IntPtr ptr = Marshal.StringToCoTaskMemUTF8(message);
|
||||
try
|
||||
{
|
||||
NativeBridge.SendMessage(getEntityId(), ptr, System.Text.Encoding.UTF8.GetByteCount(message));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeCoTaskMem(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void sendMessage(string[] messages)
|
||||
{
|
||||
foreach (var msg in messages)
|
||||
sendMessage(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kicks player with the default <see cref="DisconnectReason.KICKED"/> reason.
|
||||
/// </summary>
|
||||
public void kickPlayer()
|
||||
{
|
||||
NativeBridge.KickPlayer?.Invoke(getEntityId(), (int)DisconnectReason.KICKED);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bans the player by UID with the specified reason and disconnects them.
|
||||
/// </summary>
|
||||
/// <param name="reason">The ban reason.</param>
|
||||
/// <returns><c>true</c> if the ban was applied successfully.</returns>
|
||||
public bool banPlayer(string reason)
|
||||
{
|
||||
if (NativeBridge.BanPlayer == null) return false;
|
||||
IntPtr ptr = Marshal.StringToCoTaskMemUTF8(reason ?? string.Empty);
|
||||
try
|
||||
{
|
||||
int byteLen = System.Text.Encoding.UTF8.GetByteCount(reason ?? string.Empty);
|
||||
return NativeBridge.BanPlayer(getEntityId(), ptr, byteLen) != 0;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeCoTaskMem(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bans the player's IP address with the specified reason.
|
||||
/// </summary>
|
||||
/// <param name="reason">The ban reason.</param>
|
||||
/// <returns><c>true</c> if the IP ban was applied successfully.</returns>
|
||||
public bool banPlayerIp(string reason)
|
||||
{
|
||||
if (NativeBridge.BanPlayerIp == null) return false;
|
||||
IntPtr ptr = Marshal.StringToCoTaskMemUTF8(reason ?? string.Empty);
|
||||
try
|
||||
{
|
||||
int byteLen = System.Text.Encoding.UTF8.GetByteCount(reason ?? string.Empty);
|
||||
return NativeBridge.BanPlayerIp(getEntityId(), ptr, byteLen) != 0;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeCoTaskMem(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the socket address of this player.
|
||||
/// </summary>
|
||||
/// <returns>The player's socket address, or <c>null</c> if the address could not be determined.</returns>
|
||||
public InetSocketAddress? getAddress()
|
||||
{
|
||||
if (NativeBridge.GetPlayerAddress == null)
|
||||
return null;
|
||||
|
||||
const int ipBufSize = 64;
|
||||
IntPtr ipBuf = Marshal.AllocCoTaskMem(ipBufSize);
|
||||
IntPtr portBuf = Marshal.AllocCoTaskMem(sizeof(int));
|
||||
try
|
||||
{
|
||||
int result = NativeBridge.GetPlayerAddress(getEntityId(), ipBuf, ipBufSize, portBuf);
|
||||
if (result == 0)
|
||||
return null;
|
||||
|
||||
string? ip = Marshal.PtrToStringAnsi(ipBuf);
|
||||
int port = Marshal.ReadInt32(portBuf);
|
||||
|
||||
if (string.IsNullOrEmpty(ip))
|
||||
return null;
|
||||
|
||||
return new InetSocketAddress(new InetAddress(ip), port);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeCoTaskMem(ipBuf);
|
||||
Marshal.FreeCoTaskMem(portBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the players current experience level.
|
||||
/// </summary>
|
||||
/// <returns>Current experience level.</returns>
|
||||
public int getLevel() => _level;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the players current experience level.
|
||||
/// </summary>
|
||||
/// <param name="level">New experience level.</param>
|
||||
public void setLevel(int level)
|
||||
{
|
||||
_level = level;
|
||||
NativeBridge.SetLevel?.Invoke(getEntityId(), level);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the players current experience points towards the next level.
|
||||
/// This is a percentage value. 0 is "no progress" and 1 is "next level".
|
||||
/// </summary>
|
||||
/// <returns>Current experience points.</returns>
|
||||
public float getExp() => _exp;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the players current experience points towards the next level.
|
||||
/// This is a percentage value. 0 is "no progress" and 1 is "next level".
|
||||
/// </summary>
|
||||
/// <param name="exp">New experience points.</param>
|
||||
public void setExp(float exp)
|
||||
{
|
||||
_exp = exp;
|
||||
NativeBridge.SetExp?.Invoke(getEntityId(), exp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gives the player the amount of experience specified.
|
||||
/// </summary>
|
||||
/// <param name="amount">Exp amount to give.</param>
|
||||
public void giveExp(int amount)
|
||||
{
|
||||
NativeBridge.GiveExp?.Invoke(getEntityId(), amount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gives the player the amount of experience levels specified.
|
||||
/// Levels can be taken by specifying a negative amount.
|
||||
/// </summary>
|
||||
/// <param name="amount">Amount of experience levels to give or take.</param>
|
||||
public void giveExpLevels(int amount)
|
||||
{
|
||||
NativeBridge.GiveExpLevels?.Invoke(getEntityId(), amount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the players current exhaustion level.
|
||||
/// Exhaustion controls how fast the food level drops. While you have a
|
||||
/// certain amount of exhaustion, your saturation will drop to zero, and
|
||||
/// then your food will drop to zero.
|
||||
/// </summary>
|
||||
/// <returns>Exhaustion level.</returns>
|
||||
public float getExhaustion() => _exhaustion;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the players current exhaustion level.
|
||||
/// </summary>
|
||||
/// <param name="value">Exhaustion level.</param>
|
||||
public void setExhaustion(float value)
|
||||
{
|
||||
_exhaustion = value;
|
||||
NativeBridge.SetExhaustion?.Invoke(getEntityId(), value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the players current saturation level.
|
||||
/// </summary>
|
||||
/// <param name="value">Saturation level.</param>
|
||||
public void setSaturation(float value)
|
||||
{
|
||||
_saturation = value;
|
||||
NativeBridge.SetSaturation?.Invoke(getEntityId(), value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the players current food level.
|
||||
/// </summary>
|
||||
/// <returns>Food level.</returns>
|
||||
public int getFoodLevel() => _foodLevel;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the players current food level.
|
||||
/// </summary>
|
||||
/// <param name="value">New food level.</param>
|
||||
public void setFoodLevel(int value)
|
||||
{
|
||||
_foodLevel = value;
|
||||
NativeBridge.SetFoodLevel?.Invoke(getEntityId(), value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the particle (the number of times specified by count)
|
||||
/// at the target location. Only this player will see the particle.
|
||||
/// </summary>
|
||||
/// <param name="particle">The particle to spawn.</param>
|
||||
/// <param name="location">The location to spawn at.</param>
|
||||
/// <param name="count">The number of particles.</param>
|
||||
public void spawnParticle(Particle particle, Location location, int count)
|
||||
{
|
||||
spawnParticleInternal(particle, location.X, location.Y, location.Z, count, 0, 0, 0, 0, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the particle (the number of times specified by count)
|
||||
/// at the target location. Only this player will see the particle.
|
||||
/// </summary>
|
||||
/// <param name="particle">The particle to spawn.</param>
|
||||
/// <param name="x">The position on the x axis to spawn at.</param>
|
||||
/// <param name="y">The position on the y axis to spawn at.</param>
|
||||
/// <param name="z">The position on the z axis to spawn at.</param>
|
||||
/// <param name="count">The number of particles.</param>
|
||||
public void spawnParticle(Particle particle, double x, double y, double z, int count)
|
||||
{
|
||||
spawnParticleInternal(particle, x, y, z, count, 0, 0, 0, 0, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the particle (the number of times specified by count)
|
||||
/// at the target location. Only this player will see the particle.
|
||||
/// </summary>
|
||||
/// <param name="particle">The particle to spawn.</param>
|
||||
/// <param name="location">The location to spawn at.</param>
|
||||
/// <param name="count">The number of particles.</param>
|
||||
/// <param name="data">The data to use for the particle or null.</param>
|
||||
/// <typeparam name="T">The type of the particle data.</typeparam>
|
||||
public void spawnParticle<T>(Particle particle, Location location, int count, T? data)
|
||||
{
|
||||
spawnParticleInternal(particle, location.X, location.Y, location.Z, count, 0, 0, 0, 0, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the particle (the number of times specified by count)
|
||||
/// at the target location. Only this player will see the particle.
|
||||
/// </summary>
|
||||
/// <param name="particle">The particle to spawn.</param>
|
||||
/// <param name="x">The position on the x axis to spawn at.</param>
|
||||
/// <param name="y">The position on the y axis to spawn at.</param>
|
||||
/// <param name="z">The position on the z axis to spawn at.</param>
|
||||
/// <param name="count">The number of particles.</param>
|
||||
/// <param name="data">The data to use for the particle or null.</param>
|
||||
/// <typeparam name="T">The type of the particle data.</typeparam>
|
||||
public void spawnParticle<T>(Particle particle, double x, double y, double z, int count, T? data)
|
||||
{
|
||||
spawnParticleInternal(particle, x, y, z, count, 0, 0, 0, 0, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the particle (the number of times specified by count)
|
||||
/// at the target location. The position of each particle will be
|
||||
/// randomized positively and negatively by the offset parameters
|
||||
/// on each axis. Only this player will see the particle.
|
||||
/// </summary>
|
||||
/// <param name="particle">The particle to spawn.</param>
|
||||
/// <param name="location">The location to spawn at.</param>
|
||||
/// <param name="count">The number of particles.</param>
|
||||
/// <param name="offsetX">The maximum random offset on the X axis.</param>
|
||||
/// <param name="offsetY">The maximum random offset on the Y axis.</param>
|
||||
/// <param name="offsetZ">The maximum random offset on the Z axis.</param>
|
||||
public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ)
|
||||
{
|
||||
spawnParticleInternal(particle, location.X, location.Y, location.Z, count, offsetX, offsetY, offsetZ, 0, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the particle (the number of times specified by count)
|
||||
/// at the target location. The position of each particle will be
|
||||
/// randomized positively and negatively by the offset parameters
|
||||
/// on each axis. Only this player will see the particle.
|
||||
/// </summary>
|
||||
/// <param name="particle">The particle to spawn.</param>
|
||||
/// <param name="x">The position on the x axis to spawn at.</param>
|
||||
/// <param name="y">The position on the y axis to spawn at.</param>
|
||||
/// <param name="z">The position on the z axis to spawn at.</param>
|
||||
/// <param name="count">The number of particles.</param>
|
||||
/// <param name="offsetX">The maximum random offset on the X axis.</param>
|
||||
/// <param name="offsetY">The maximum random offset on the Y axis.</param>
|
||||
/// <param name="offsetZ">The maximum random offset on the Z axis.</param>
|
||||
public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ)
|
||||
{
|
||||
spawnParticleInternal(particle, x, y, z, count, offsetX, offsetY, offsetZ, 0, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the particle (the number of times specified by count)
|
||||
/// at the target location. The position of each particle will be
|
||||
/// randomized positively and negatively by the offset parameters
|
||||
/// on each axis. Only this player will see the particle.
|
||||
/// </summary>
|
||||
/// <param name="particle">The particle to spawn.</param>
|
||||
/// <param name="location">The location to spawn at.</param>
|
||||
/// <param name="count">The number of particles.</param>
|
||||
/// <param name="offsetX">The maximum random offset on the X axis.</param>
|
||||
/// <param name="offsetY">The maximum random offset on the Y axis.</param>
|
||||
/// <param name="offsetZ">The maximum random offset on the Z axis.</param>
|
||||
/// <param name="data">The data to use for the particle or null.</param>
|
||||
/// <typeparam name="T">The type of the particle data.</typeparam>
|
||||
public void spawnParticle<T>(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ, T? data)
|
||||
{
|
||||
spawnParticleInternal(particle, location.X, location.Y, location.Z, count, offsetX, offsetY, offsetZ, 0, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the particle (the number of times specified by count)
|
||||
/// at the target location. The position of each particle will be
|
||||
/// randomized positively and negatively by the offset parameters
|
||||
/// on each axis. Only this player will see the particle.
|
||||
/// </summary>
|
||||
/// <param name="particle">The particle to spawn.</param>
|
||||
/// <param name="x">The position on the x axis to spawn at.</param>
|
||||
/// <param name="y">The position on the y axis to spawn at.</param>
|
||||
/// <param name="z">The position on the z axis to spawn at.</param>
|
||||
/// <param name="count">The number of particles.</param>
|
||||
/// <param name="offsetX">The maximum random offset on the X axis.</param>
|
||||
/// <param name="offsetY">The maximum random offset on the Y axis.</param>
|
||||
/// <param name="offsetZ">The maximum random offset on the Z axis.</param>
|
||||
/// <param name="data">The data to use for the particle or null.</param>
|
||||
/// <typeparam name="T">The type of the particle data.</typeparam>
|
||||
public void spawnParticle<T>(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, T? data)
|
||||
{
|
||||
spawnParticleInternal(particle, x, y, z, count, offsetX, offsetY, offsetZ, 0, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the particle (the number of times specified by count)
|
||||
/// at the target location. The position of each particle will be
|
||||
/// randomized positively and negatively by the offset parameters
|
||||
/// on each axis. Only this player will see the particle.
|
||||
/// </summary>
|
||||
/// <param name="particle">The particle to spawn.</param>
|
||||
/// <param name="location">The location to spawn at.</param>
|
||||
/// <param name="count">The number of particles.</param>
|
||||
/// <param name="offsetX">The maximum random offset on the X axis.</param>
|
||||
/// <param name="offsetY">The maximum random offset on the Y axis.</param>
|
||||
/// <param name="offsetZ">The maximum random offset on the Z axis.</param>
|
||||
/// <param name="extra">The extra data for this particle, depends on the particle used (normally speed).</param>
|
||||
public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ, double extra)
|
||||
{
|
||||
spawnParticleInternal(particle, location.X, location.Y, location.Z, count, offsetX, offsetY, offsetZ, extra, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the particle (the number of times specified by count)
|
||||
/// at the target location. The position of each particle will be
|
||||
/// randomized positively and negatively by the offset parameters
|
||||
/// on each axis. Only this player will see the particle.
|
||||
/// </summary>
|
||||
/// <param name="particle">The particle to spawn.</param>
|
||||
/// <param name="x">The position on the x axis to spawn at.</param>
|
||||
/// <param name="y">The position on the y axis to spawn at.</param>
|
||||
/// <param name="z">The position on the z axis to spawn at.</param>
|
||||
/// <param name="count">The number of particles.</param>
|
||||
/// <param name="offsetX">The maximum random offset on the X axis.</param>
|
||||
/// <param name="offsetY">The maximum random offset on the Y axis.</param>
|
||||
/// <param name="offsetZ">The maximum random offset on the Z axis.</param>
|
||||
/// <param name="extra">The extra data for this particle, depends on the particle used (normally speed).</param>
|
||||
public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra)
|
||||
{
|
||||
spawnParticleInternal(particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the particle (the number of times specified by count)
|
||||
/// at the target location. The position of each particle will be
|
||||
/// randomized positively and negatively by the offset parameters
|
||||
/// on each axis. Only this player will see the particle.
|
||||
/// </summary>
|
||||
/// <param name="particle">The particle to spawn.</param>
|
||||
/// <param name="location">The location to spawn at.</param>
|
||||
/// <param name="count">The number of particles.</param>
|
||||
/// <param name="offsetX">The maximum random offset on the X axis.</param>
|
||||
/// <param name="offsetY">The maximum random offset on the Y axis.</param>
|
||||
/// <param name="offsetZ">The maximum random offset on the Z axis.</param>
|
||||
/// <param name="extra">The extra data for this particle, depends on the particle used (normally speed).</param>
|
||||
/// <param name="data">The data to use for the particle or null.</param>
|
||||
/// <typeparam name="T">The type of the particle data.</typeparam>
|
||||
public void spawnParticle<T>(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ, double extra, T? data)
|
||||
{
|
||||
spawnParticleInternal(particle, location.X, location.Y, location.Z, count, offsetX, offsetY, offsetZ, extra, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the particle (the number of times specified by count)
|
||||
/// at the target location. The position of each particle will be
|
||||
/// randomized positively and negatively by the offset parameters
|
||||
/// on each axis. Only this player will see the particle.
|
||||
/// </summary>
|
||||
/// <param name="particle">The particle to spawn.</param>
|
||||
/// <param name="x">The position on the x axis to spawn at.</param>
|
||||
/// <param name="y">The position on the y axis to spawn at.</param>
|
||||
/// <param name="z">The position on the z axis to spawn at.</param>
|
||||
/// <param name="count">The number of particles.</param>
|
||||
/// <param name="offsetX">The maximum random offset on the X axis.</param>
|
||||
/// <param name="offsetY">The maximum random offset on the Y axis.</param>
|
||||
/// <param name="offsetZ">The maximum random offset on the Z axis.</param>
|
||||
/// <param name="extra">The extra data for this particle, depends on the particle used (normally speed).</param>
|
||||
/// <param name="data">The data to use for the particle or null.</param>
|
||||
/// <typeparam name="T">The type of the particle data.</typeparam>
|
||||
public void spawnParticle<T>(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, T? data)
|
||||
{
|
||||
spawnParticleInternal(particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
|
||||
}
|
||||
|
||||
private void spawnParticleInternal(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, object? data)
|
||||
{
|
||||
if (NativeBridge.SpawnParticle == null)
|
||||
return;
|
||||
|
||||
int particleId = (int)particle;
|
||||
if (data is ItemStack itemStack &&
|
||||
(particle == Particle.ITEM_CRACK || particle == Particle.BLOCK_CRACK))
|
||||
{
|
||||
int id = itemStack.getTypeId();
|
||||
int aux = itemStack.getDurability();
|
||||
particleId = (int)particle | ((id & 0x0FFF) << 8) | (aux & 0xFF);
|
||||
}
|
||||
|
||||
NativeBridge.SpawnParticle(getEntityId(), particleId,
|
||||
(float)x, (float)y, (float)z,
|
||||
(float)offsetX, (float)offsetY, (float)offsetZ,
|
||||
(float)extra, count);
|
||||
}
|
||||
|
||||
// INTERNAL
|
||||
internal void SetSaturationInternal(float saturation) => _saturation = saturation;
|
||||
internal void SetWalkSpeedInternal(float walkSpeed) => _walkSpeed = walkSpeed;
|
||||
internal void SetPlayerUniqueIdInternal(Guid id) => _playerUniqueId = id;
|
||||
internal void SetPlayerRawOnlineXUIDInternal(ulong xuid) => _playerRawOnlineXUID = xuid;
|
||||
internal void SetPlayerRawOfflineXUIDInternal(ulong xuid) => _playerRawOfflineXUID = xuid;
|
||||
internal void SetSneakingInternal(bool sneaking) => _sneaking = sneaking;
|
||||
internal void SetSprintingInternal(bool sprinting) => _sprinting = sprinting;
|
||||
internal void SetAllowFlightInternal(bool allowFlight) => _allowFlight = allowFlight;
|
||||
internal void SetSleepingIgnoredInternal(bool ignored) => _sleepingIgnored = ignored;
|
||||
internal void SetLevelInternal(int level) => _level = level;
|
||||
internal void SetExpInternal(float exp) => _exp = exp;
|
||||
internal void SetTotalExperienceInternal(int totalExp) => _totalExperience = totalExp;
|
||||
internal void SetFoodLevelInternal(int foodLevel) => _foodLevel = foodLevel;
|
||||
internal void SetExhaustionInternal(float exhaustion) => _exhaustion = exhaustion;
|
||||
}
|
||||
11
Minecraft.Server.FourKit/Enums/LoginType.cs
Normal file
11
Minecraft.Server.FourKit/Enums/LoginType.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enums;
|
||||
|
||||
public enum LoginType
|
||||
{
|
||||
INITIAL = 1,
|
||||
ACCEPTED = 2,
|
||||
}
|
||||
50
Minecraft.Server.FourKit/Enums/TreeType.cs
Normal file
50
Minecraft.Server.FourKit/Enums/TreeType.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// Tree and organic structure types.
|
||||
/// </summary>
|
||||
public enum TreeType {
|
||||
/// <summary>
|
||||
/// No tree type.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Redwood tree, shaped like a pine tree.
|
||||
/// </summary>
|
||||
SPRUCE = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Birch tree.
|
||||
/// </summary>
|
||||
BIRCH = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Standard jungle tree; 4 blocks wide and tall.
|
||||
/// </summary>
|
||||
JUNGLE = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Regular tree, extra tall with branches.
|
||||
/// </summary>
|
||||
BIG_OAK = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Regular tree, no branches.
|
||||
/// </summary>
|
||||
OAK = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Big brown mushroom; tall and umbrella-like.
|
||||
/// </summary>
|
||||
BROWN_MUSHROOM = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Big red mushroom; short and fat.
|
||||
/// </summary>
|
||||
RED_MUSHROOM = 7,
|
||||
}
|
||||
50
Minecraft.Server.FourKit/Event/Block/BlockBreakEvent.cs
Normal file
50
Minecraft.Server.FourKit/Event/Block/BlockBreakEvent.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
namespace Minecraft.Server.FourKit.Event.Block;
|
||||
|
||||
using Minecraft.Server.FourKit.Block;
|
||||
using Minecraft.Server.FourKit.Entity;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a block is broken by a player.
|
||||
///
|
||||
/// If you wish to have the block drop experience, you must set the experience
|
||||
/// value above 0. By default, experience will be set in the event if:
|
||||
/// <list type="bullet">
|
||||
/// <item><description>The player is not in creative or adventure mode</description></item>
|
||||
/// <item><description>The player can loot the block (ie: does not destroy it completely, by using the correct tool)</description></item>
|
||||
/// <item><description>The player does not have silk touch</description></item>
|
||||
/// <item><description>The block drops experience in vanilla Minecraft</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// Note: Plugins wanting to simulate a traditional block drop should set the
|
||||
/// block to air and utilize their own methods for determining what the default
|
||||
/// drop for the block being broken is and what to do about it, if anything.
|
||||
///
|
||||
/// If a Block Break event is cancelled, the block will not break and experience
|
||||
/// will not drop.
|
||||
/// </summary>
|
||||
public class BlockBreakEvent : BlockExpEvent, Cancellable
|
||||
{
|
||||
private readonly Player _player;
|
||||
private bool _cancel;
|
||||
internal BlockBreakEvent(Block block, Player player, int exp)
|
||||
: base(block, exp)
|
||||
{
|
||||
_player = player;
|
||||
_cancel = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Player that is breaking the block involved in this event.
|
||||
/// </summary>
|
||||
/// <returns>The Player that is breaking the block involved in this event.</returns>
|
||||
public Player getPlayer() => _player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool isCancelled() => _cancel;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void setCancelled(bool cancel)
|
||||
{
|
||||
_cancel = cancel;
|
||||
}
|
||||
}
|
||||
30
Minecraft.Server.FourKit/Event/Block/BlockBurnEvent.cs
Normal file
30
Minecraft.Server.FourKit/Event/Block/BlockBurnEvent.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
namespace Minecraft.Server.FourKit.Event.Block;
|
||||
|
||||
using Minecraft.Server.FourKit.Block;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a block is destroyed as a result of being burnt by fire.
|
||||
///
|
||||
/// <para>If a Block Burn event is cancelled, the block will not be destroyed
|
||||
/// as a result of being burnt by fire.</para>
|
||||
/// </summary>
|
||||
public class BlockBurnEvent : BlockEvent, Cancellable
|
||||
{
|
||||
private bool _cancel;
|
||||
|
||||
internal BlockBurnEvent(Block block) : base(block)
|
||||
{
|
||||
_cancel = false;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool isCancelled() => _cancel;
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public void setCancelled(bool cancel)
|
||||
{
|
||||
_cancel = cancel;
|
||||
}
|
||||
}
|
||||
22
Minecraft.Server.FourKit/Event/Block/BlockEvent.cs
Normal file
22
Minecraft.Server.FourKit/Event/Block/BlockEvent.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
namespace Minecraft.Server.FourKit.Event.Block;
|
||||
|
||||
using Minecraft.Server.FourKit.Block;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Block-related event.
|
||||
/// </summary>
|
||||
public abstract class BlockEvent : Event
|
||||
{
|
||||
private readonly Block _block;
|
||||
|
||||
internal protected BlockEvent(Block block)
|
||||
{
|
||||
_block = block;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the block involved in this event.
|
||||
/// </summary>
|
||||
/// <returns>The Block which is involved in this event.</returns>
|
||||
public Block getBlock() => _block;
|
||||
}
|
||||
31
Minecraft.Server.FourKit/Event/Block/BlockExpEvent.cs
Normal file
31
Minecraft.Server.FourKit/Event/Block/BlockExpEvent.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
namespace Minecraft.Server.FourKit.Event.Block;
|
||||
|
||||
using Minecraft.Server.FourKit.Block;
|
||||
|
||||
/// <summary>
|
||||
/// An event that is called when a block yields experience.
|
||||
/// </summary>
|
||||
public class BlockExpEvent : BlockEvent
|
||||
{
|
||||
private int _exp;
|
||||
internal BlockExpEvent(Block block, int exp)
|
||||
: base(block)
|
||||
{
|
||||
_exp = exp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the experience dropped by the block after the event has processed.
|
||||
/// </summary>
|
||||
/// <returns>The experience to drop.</returns>
|
||||
public int getExpToDrop() => _exp;
|
||||
|
||||
/// <summary>
|
||||
/// Set the amount of experience dropped by the block after the event has processed.
|
||||
/// </summary>
|
||||
/// <param name="exp">1 or higher to drop experience, else nothing will drop.</param>
|
||||
public void setExpToDrop(int exp)
|
||||
{
|
||||
_exp = exp;
|
||||
}
|
||||
}
|
||||
23
Minecraft.Server.FourKit/Event/Block/BlockFormEvent.cs
Normal file
23
Minecraft.Server.FourKit/Event/Block/BlockFormEvent.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
namespace Minecraft.Server.FourKit.Event.Block;
|
||||
|
||||
using Minecraft.Server.FourKit.Block;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a block is formed or spreads based on world conditions.
|
||||
/// Use <see cref="BlockSpreadEvent"/> to catch blocks that actually spread
|
||||
/// and don't just "randomly" form.
|
||||
///
|
||||
/// <para>Examples:</para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>Snow forming due to a snow storm.</description></item>
|
||||
/// <item><description>Ice forming in a snowy Biome like Taiga or Tundra.</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para>If a Block Form event is cancelled, the block will not be formed.</para>
|
||||
/// </summary>
|
||||
public class BlockFormEvent : BlockGrowEvent, Cancellable
|
||||
{
|
||||
internal BlockFormEvent(Block block, BlockState newState) : base(block, newState)
|
||||
{
|
||||
}
|
||||
}
|
||||
59
Minecraft.Server.FourKit/Event/Block/BlockFromToEvent.cs
Normal file
59
Minecraft.Server.FourKit/Event/Block/BlockFromToEvent.cs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
namespace Minecraft.Server.FourKit.Event.Block;
|
||||
|
||||
using Minecraft.Server.FourKit.Block;
|
||||
|
||||
/// <summary>
|
||||
/// Represents events with a source block and a destination block, currently
|
||||
/// only applies to liquid (lava and water) and teleporting dragon eggs.
|
||||
///
|
||||
/// <para>If a Block From To event is cancelled, the block will not move
|
||||
/// (the liquid will not flow).</para>
|
||||
/// </summary>
|
||||
public class BlockFromToEvent : BlockEvent, Cancellable
|
||||
{
|
||||
private readonly Block _to;
|
||||
private readonly BlockFace _face;
|
||||
private bool _cancel;
|
||||
|
||||
internal BlockFromToEvent(Block block, BlockFace face) : base(block)
|
||||
{
|
||||
_face = face;
|
||||
_to = block.getRelative(face);
|
||||
_cancel = false;
|
||||
}
|
||||
|
||||
internal BlockFromToEvent(Block block, Block toBlock) : base(block)
|
||||
{
|
||||
_to = toBlock;
|
||||
_face = BlockFace.SELF;
|
||||
_cancel = false;
|
||||
}
|
||||
|
||||
internal BlockFromToEvent(Block block, Block toBlock, BlockFace face) : base(block)
|
||||
{
|
||||
_to = toBlock;
|
||||
_face = face;
|
||||
_cancel = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the BlockFace that the block is moving to.
|
||||
/// </summary>
|
||||
/// <returns>The BlockFace that the block is moving to.</returns>
|
||||
public BlockFace getFace() => _face;
|
||||
|
||||
/// <summary>
|
||||
/// Convenience method for getting the faced Block.
|
||||
/// </summary>
|
||||
/// <returns>The faced Block.</returns>
|
||||
public Block getToBlock() => _to;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool isCancelled() => _cancel;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void setCancelled(bool cancel)
|
||||
{
|
||||
_cancel = cancel;
|
||||
}
|
||||
}
|
||||
44
Minecraft.Server.FourKit/Event/Block/BlockGrowEvent.cs
Normal file
44
Minecraft.Server.FourKit/Event/Block/BlockGrowEvent.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
namespace Minecraft.Server.FourKit.Event.Block;
|
||||
|
||||
using Minecraft.Server.FourKit.Block;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a block grows naturally in the world.
|
||||
///
|
||||
/// <para>Examples:</para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>Wheat</description></item>
|
||||
/// <item><description>Sugar Cane</description></item>
|
||||
/// <item><description>Cactus</description></item>
|
||||
/// <item><description>Watermelon</description></item>
|
||||
/// <item><description>Pumpkin</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para>If a Block Grow event is cancelled, the block will not grow.</para>
|
||||
/// </summary>
|
||||
public class BlockGrowEvent : BlockEvent, Cancellable
|
||||
{
|
||||
private bool _cancel;
|
||||
private readonly BlockState _newState;
|
||||
|
||||
internal BlockGrowEvent(Block block, BlockState newState) : base(block)
|
||||
{
|
||||
_cancel = false;
|
||||
_newState = newState;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the state of the block where it will form or spread to.
|
||||
/// </summary>
|
||||
/// <returns>The block state for this events block.</returns>
|
||||
public BlockState getNewState() => _newState;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool isCancelled() => _cancel;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void setCancelled(bool cancel)
|
||||
{
|
||||
_cancel = cancel;
|
||||
}
|
||||
}
|
||||
45
Minecraft.Server.FourKit/Event/Block/BlockPistonEvent.cs
Normal file
45
Minecraft.Server.FourKit/Event/Block/BlockPistonEvent.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
namespace Minecraft.Server.FourKit.Event.Block;
|
||||
|
||||
using Minecraft.Server.FourKit.Block;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a piston block is triggered.
|
||||
/// </summary>
|
||||
public abstract class BlockPistonEvent : BlockEvent, Cancellable
|
||||
{
|
||||
private bool _cancel;
|
||||
private readonly BlockFace _direction;
|
||||
|
||||
internal protected BlockPistonEvent(Block block, BlockFace direction) : base(block)
|
||||
{
|
||||
_direction = direction;
|
||||
_cancel = false;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool isCancelled() => _cancel;
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public void setCancelled(bool cancelled)
|
||||
{
|
||||
_cancel = cancelled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the Piston in the event is sticky.
|
||||
/// </summary>
|
||||
/// <returns>Stickiness of the piston.</returns>
|
||||
public bool isSticky()
|
||||
{
|
||||
var type = getBlock().getType();
|
||||
return type == Material.PISTON_STICKY_BASE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the direction in which the piston will operate.
|
||||
/// </summary>
|
||||
/// <returns>Direction of the piston.</returns>
|
||||
public BlockFace getDirection() => _direction;
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
namespace Minecraft.Server.FourKit.Event.Block;
|
||||
|
||||
using Minecraft.Server.FourKit.Block;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a piston extends.
|
||||
/// </summary>
|
||||
public class BlockPistonExtendEvent : BlockPistonEvent
|
||||
{
|
||||
private readonly int _length;
|
||||
|
||||
internal BlockPistonExtendEvent(Block block, int length, BlockFace direction)
|
||||
: base(block, direction)
|
||||
{
|
||||
_length = length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the amount of blocks which will be moved while extending.
|
||||
/// </summary>
|
||||
/// <returns>The amount of moving blocks.</returns>
|
||||
public int getLength() => _length;
|
||||
|
||||
/// <summary>
|
||||
/// Get an immutable list of the blocks which will be moved by the extending.
|
||||
/// </summary>
|
||||
/// <returns>Immutable list of the moved blocks.</returns>
|
||||
public List<Block> getBlocks()
|
||||
{
|
||||
var blocks = new List<Block>();
|
||||
var world = getBlock().getWorld();
|
||||
int x = getBlock().getX();
|
||||
int y = getBlock().getY();
|
||||
int z = getBlock().getZ();
|
||||
var dir = getDirection();
|
||||
|
||||
for (int i = 0; i < _length; i++)
|
||||
{
|
||||
x += dir.getModX();
|
||||
y += dir.getModY();
|
||||
z += dir.getModZ();
|
||||
blocks.Add(new Block(world, x, y, z));
|
||||
}
|
||||
return blocks.AsReadOnly().ToList();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
namespace Minecraft.Server.FourKit.Event.Block;
|
||||
|
||||
using Minecraft.Server.FourKit.Block;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a piston retracts.
|
||||
/// </summary>
|
||||
public class BlockPistonRetractEvent : BlockPistonEvent
|
||||
{
|
||||
internal BlockPistonRetractEvent(Block block, BlockFace direction)
|
||||
: base(block, direction)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the location where the possible moving block might be if the
|
||||
/// retracting piston is sticky.
|
||||
/// </summary>
|
||||
/// <returns>The possible location of the possibly moving block.</returns>
|
||||
public Location getRetractLocation()
|
||||
{
|
||||
var block = getBlock();
|
||||
var dir = getDirection();
|
||||
return new Location(
|
||||
block.getWorld(),
|
||||
block.getX() + dir.getModX() * 2,
|
||||
block.getY() + dir.getModY() * 2,
|
||||
block.getZ() + dir.getModZ() * 2);
|
||||
}
|
||||
}
|
||||
58
Minecraft.Server.FourKit/Event/Block/BlockPlaceEvent.cs
Normal file
58
Minecraft.Server.FourKit/Event/Block/BlockPlaceEvent.cs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
namespace Minecraft.Server.FourKit.Event.Block;
|
||||
|
||||
using Minecraft.Server.FourKit.Block;
|
||||
using Minecraft.Server.FourKit.Entity;
|
||||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a block is placed by a player.
|
||||
/// </summary>
|
||||
public class BlockPlaceEvent : BlockEvent, Cancellable
|
||||
{
|
||||
protected Block placedAgainst;
|
||||
protected ItemStack itemInHand;
|
||||
protected Player player;
|
||||
protected bool canBuild;
|
||||
protected bool cancel;
|
||||
|
||||
internal BlockPlaceEvent(Block placedBlock, Block placedAgainst, ItemStack itemInHand, Player thePlayer, bool canBuild)
|
||||
: base(placedBlock)
|
||||
{
|
||||
this.placedAgainst = placedAgainst;
|
||||
this.itemInHand = itemInHand;
|
||||
this.player = thePlayer;
|
||||
this.canBuild = canBuild;
|
||||
this.cancel = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the player who placed the block involved in this event.
|
||||
/// </summary>
|
||||
/// <returns>The Player who placed the block involved in this event.</returns>
|
||||
public Player getPlayer() => player;
|
||||
|
||||
/// <summary>
|
||||
/// Clarity method for getting the placed block. Not really needed except
|
||||
/// for reasons of clarity.
|
||||
/// </summary>
|
||||
/// <returns>The Block that was placed.</returns>
|
||||
public Block getBlockPlaced() => getBlock();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the block that this block was placed against.
|
||||
/// </summary>
|
||||
/// <returns>Block the block that the new block was placed against.</returns>
|
||||
public Block getBlockAgainst() => placedAgainst;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item in the player's hand when they placed the block.
|
||||
/// </summary>
|
||||
/// <returns>The ItemStack for the item in the player's hand when they placed the block.</returns>
|
||||
public ItemStack getItemInHand() => itemInHand;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool isCancelled() => cancel;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void setCancelled(bool cancel) => this.cancel = cancel;
|
||||
}
|
||||
32
Minecraft.Server.FourKit/Event/Block/BlockSpreadEvent.cs
Normal file
32
Minecraft.Server.FourKit/Event/Block/BlockSpreadEvent.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
namespace Minecraft.Server.FourKit.Event.Block;
|
||||
|
||||
using Minecraft.Server.FourKit.Block;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a block spreads based on world conditions.
|
||||
/// Use <see cref="BlockFormEvent"/> to catch blocks that "randomly" form
|
||||
/// instead of actually spread.
|
||||
///
|
||||
/// <para>Examples:</para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>Mushrooms spreading.</description></item>
|
||||
/// <item><description>Fire spreading.</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para>If a Block Spread event is cancelled, the block will not spread.</para>
|
||||
/// </summary>
|
||||
public class BlockSpreadEvent : BlockFormEvent, Cancellable
|
||||
{
|
||||
private readonly Block _source;
|
||||
|
||||
internal BlockSpreadEvent(Block block, Block source, BlockState newState) : base(block, newState)
|
||||
{
|
||||
_source = source;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the source block involved in this event.
|
||||
/// </summary>
|
||||
/// <returns>The Block for the source block involved in this event.</returns>
|
||||
public Block getSource() => _source;
|
||||
}
|
||||
65
Minecraft.Server.FourKit/Event/Block/SignChangeEvent.cs
Normal file
65
Minecraft.Server.FourKit/Event/Block/SignChangeEvent.cs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
namespace Minecraft.Server.FourKit.Event.Block;
|
||||
|
||||
using Minecraft.Server.FourKit.Block;
|
||||
using Minecraft.Server.FourKit.Entity;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a sign is changed by a player.
|
||||
/// </summary>
|
||||
public class SignChangeEvent : BlockEvent, Cancellable
|
||||
{
|
||||
private readonly Player _player;
|
||||
private readonly string[] _lines;
|
||||
private bool _cancel;
|
||||
internal SignChangeEvent(Block theBlock, Player thePlayer, string[] theLines)
|
||||
: base(theBlock)
|
||||
{
|
||||
_player = thePlayer;
|
||||
_lines = theLines;
|
||||
_cancel = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the player changing the sign involved in this event.
|
||||
/// </summary>
|
||||
/// <returns>The Player involved in this event.</returns>
|
||||
public Player getPlayer() => _player;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all of the lines of text from the sign involved in this event.
|
||||
/// </summary>
|
||||
/// <returns>The String array for the sign's lines new text.</returns>
|
||||
public string[] getLines() => _lines;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a single line of text from the sign involved in this event.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the line to get.</param>
|
||||
/// <returns>The String containing the line of text associated with the provided index.</returns>
|
||||
/// <exception cref="IndexOutOfRangeException">Thrown when the provided index is > 3 or < 0.</exception>
|
||||
public string getLine(int index)
|
||||
{
|
||||
if (index < 0 || index > 3)
|
||||
throw new IndexOutOfRangeException($"Line index must be between 0 and 3, got {index}");
|
||||
return _lines[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a single line for the sign involved in this event.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the line to set.</param>
|
||||
/// <param name="line">Text to set.</param>
|
||||
/// <exception cref="IndexOutOfRangeException">Thrown when the provided index is > 3 or < 0.</exception>
|
||||
public void setLine(int index, string line)
|
||||
{
|
||||
if (index < 0 || index > 3)
|
||||
throw new IndexOutOfRangeException($"Line index must be between 0 and 3, got {index}");
|
||||
_lines[index] = line;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool isCancelled() => _cancel;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void setCancelled(bool cancel) => _cancel = cancel;
|
||||
}
|
||||
14
Minecraft.Server.FourKit/Event/Cancellable.cs
Normal file
14
Minecraft.Server.FourKit/Event/Cancellable.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
namespace Minecraft.Server.FourKit.Event;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for events that can be cancelled by a plugin.
|
||||
/// When cancelled, the server will skip the default action.
|
||||
/// </summary>
|
||||
public interface Cancellable
|
||||
{
|
||||
/// <summary>Gets whether this event is cancelled.</summary>
|
||||
bool isCancelled();
|
||||
|
||||
/// <summary>Sets whether this event is cancelled.</summary>
|
||||
void setCancelled(bool cancel);
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
namespace Minecraft.Server.FourKit.Event.Entity;
|
||||
|
||||
using FourKitEntity = Minecraft.Server.FourKit.Entity;
|
||||
|
||||
/// <summary>
|
||||
/// Called when an entity is damaged by an entity.
|
||||
/// </summary>
|
||||
public class EntityDamageByEntityEvent : EntityDamageEvent
|
||||
{
|
||||
private readonly FourKitEntity.Entity _damager;
|
||||
internal EntityDamageByEntityEvent(FourKitEntity.Entity damager, FourKitEntity.Entity damagee, EntityDamageEvent.DamageCause cause, double damage)
|
||||
: base(damagee, cause, damage)
|
||||
{
|
||||
_damager = damager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the entity that damaged the defender.
|
||||
/// </summary>
|
||||
/// <returns>The Entity that damaged the defender.</returns>
|
||||
public FourKitEntity.Entity getDamager() => _damager;
|
||||
}
|
||||
112
Minecraft.Server.FourKit/Event/Entity/EntityDamageEvent.cs
Normal file
112
Minecraft.Server.FourKit/Event/Entity/EntityDamageEvent.cs
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
namespace Minecraft.Server.FourKit.Event.Entity;
|
||||
|
||||
using FourKitEntity = Minecraft.Server.FourKit.Entity;
|
||||
|
||||
/// <summary>
|
||||
/// Stores data for damage events.
|
||||
/// </summary>
|
||||
public class EntityDamageEvent : EntityEvent, Cancellable
|
||||
{
|
||||
/// <summary>
|
||||
/// An enum to specify the cause of the damage.
|
||||
/// </summary>
|
||||
public enum DamageCause
|
||||
{
|
||||
/// <summary>Damage caused by being in the area when a block explodes.</summary>
|
||||
BLOCK_EXPLOSION,
|
||||
/// <summary>Damage caused when an entity contacts a block such as a Cactus.</summary>
|
||||
CONTACT,
|
||||
/// <summary>Custom damage.</summary>
|
||||
CUSTOM,
|
||||
/// <summary>Damage caused by running out of air while in water.</summary>
|
||||
DROWNING,
|
||||
/// <summary>Damage caused when an entity attacks another entity.</summary>
|
||||
ENTITY_ATTACK,
|
||||
/// <summary>Damage caused by being in the area when an entity, such as a Creeper, explodes.</summary>
|
||||
ENTITY_EXPLOSION,
|
||||
/// <summary>Damage caused when an entity falls a distance greater than 3 blocks.</summary>
|
||||
FALL,
|
||||
/// <summary>Damage caused by being hit by a falling block which deals damage.</summary>
|
||||
FALLING_BLOCK,
|
||||
/// <summary>Damage caused by direct exposure to fire.</summary>
|
||||
FIRE,
|
||||
/// <summary>Damage caused due to burns caused by fire.</summary>
|
||||
FIRE_TICK,
|
||||
/// <summary>Damage caused by direct exposure to lava.</summary>
|
||||
LAVA,
|
||||
/// <summary>Damage caused by being struck by lightning.</summary>
|
||||
LIGHTNING,
|
||||
/// <summary>Damage caused by being hit by a damage potion or spell.</summary>
|
||||
MAGIC,
|
||||
/// <summary>Damage caused due to a snowman melting.</summary>
|
||||
MELTING,
|
||||
/// <summary>Damage caused due to an ongoing poison effect.</summary>
|
||||
POISON,
|
||||
/// <summary>Damage caused when attacked by a projectile.</summary>
|
||||
PROJECTILE,
|
||||
/// <summary>Damage caused by starving due to having an empty hunger bar.</summary>
|
||||
STARVATION,
|
||||
/// <summary>Damage caused by being put in a block.</summary>
|
||||
SUFFOCATION,
|
||||
/// <summary>Damage caused by committing suicide using the command "/kill".</summary>
|
||||
SUICIDE,
|
||||
/// <summary>Damage caused in retaliation to another attack by the Thorns enchantment.</summary>
|
||||
THORNS,
|
||||
/// <summary>Damage caused by falling into the void.</summary>
|
||||
VOID,
|
||||
/// <summary>Damage caused by Wither potion effect.</summary>
|
||||
WITHER,
|
||||
}
|
||||
|
||||
private readonly DamageCause _cause;
|
||||
private double _damage;
|
||||
private readonly double _finalDamage;
|
||||
private bool _cancel;
|
||||
internal EntityDamageEvent(FourKitEntity.Entity damagee, DamageCause cause, double damage)
|
||||
: base(damagee)
|
||||
{
|
||||
_cause = cause;
|
||||
_damage = damage;
|
||||
_finalDamage = damage;
|
||||
_cancel = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cause of the damage.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="DamageCause"/> value detailing the cause of the damage.</returns>
|
||||
public DamageCause getCause() => _cause;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the raw amount of damage caused by the event.
|
||||
/// </summary>
|
||||
/// <returns>The raw amount of damage.</returns>
|
||||
public double getDamage() => _damage;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of damage caused by the event after all damage
|
||||
/// reduction is applied.
|
||||
/// </summary>
|
||||
/// <returns>The amount of damage after reduction.</returns>
|
||||
public double getFinalDamage() => _finalDamage;
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool isCancelled() => _cancel;
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public void setCancelled(bool cancel)
|
||||
{
|
||||
_cancel = cancel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the raw amount of damage caused by the event.
|
||||
/// </summary>
|
||||
/// <param name="damage">The raw amount of damage.</param>
|
||||
public void setDamage(double damage)
|
||||
{
|
||||
_damage = damage;
|
||||
}
|
||||
}
|
||||
53
Minecraft.Server.FourKit/Event/Entity/EntityDeathEvent.cs
Normal file
53
Minecraft.Server.FourKit/Event/Entity/EntityDeathEvent.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
namespace Minecraft.Server.FourKit.Event.Entity;
|
||||
|
||||
using FourKitEntity = Minecraft.Server.FourKit.Entity;
|
||||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
/// <summary>
|
||||
/// Thrown whenever a LivingEntity dies.
|
||||
/// </summary>
|
||||
public class EntityDeathEvent : EntityEvent
|
||||
{
|
||||
private readonly List<ItemStack> _drops;
|
||||
private int _droppedExp;
|
||||
|
||||
internal EntityDeathEvent(FourKitEntity.LivingEntity entity, List<ItemStack> drops)
|
||||
: this(entity, drops, 0)
|
||||
{
|
||||
}
|
||||
|
||||
internal EntityDeathEvent(FourKitEntity.LivingEntity what, List<ItemStack> drops, int droppedExp)
|
||||
: base(what)
|
||||
{
|
||||
_drops = drops;
|
||||
_droppedExp = droppedExp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Entity involved in this event.
|
||||
/// </summary>
|
||||
/// <returns>Entity who is involved in this event.</returns>
|
||||
public new FourKitEntity.LivingEntity getEntity() => (FourKitEntity.LivingEntity)entity;
|
||||
|
||||
/// <summary>
|
||||
/// Gets how much EXP should be dropped from this death.
|
||||
/// This does not indicate how much EXP should be taken from the entity
|
||||
/// in question, merely how much should be created after its death.
|
||||
/// </summary>
|
||||
/// <returns>Amount of EXP to drop.</returns>
|
||||
public int getDroppedExp() => _droppedExp;
|
||||
|
||||
/// <summary>
|
||||
/// Sets how much EXP should be dropped from this death.
|
||||
/// This does not indicate how much EXP should be taken from the entity
|
||||
/// in question, merely how much should be created after its death.
|
||||
/// </summary>
|
||||
/// <param name="exp">Amount of EXP to drop.</param>
|
||||
public void setDroppedExp(int exp) => _droppedExp = exp;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the items which will drop when the entity dies.
|
||||
/// </summary>
|
||||
/// <returns>Items to drop when the entity dies.</returns>
|
||||
public List<ItemStack> getDrops() => _drops;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue