summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--.travis.yml24
-rwxr-xr-xCIbuild.sh4
-rw-r--r--CMakeLists.txt8
-rw-r--r--CONTRIBUTING.md45
-rw-r--r--CONTRIBUTORS9
-rw-r--r--GETTING-STARTED.md17
-rw-r--r--MCServer/Plugins/APIDump/APIDesc.lua363
-rw-r--r--MCServer/Plugins/APIDump/Classes/Plugins.lua207
-rw-r--r--MCServer/Plugins/APIDump/Classes/WebAdmin.lua51
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnChat.lua3
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnEntityChangedWorld.lua28
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnEntityChangingWorld.lua29
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnExecuteCommand.lua22
-rw-r--r--MCServer/Plugins/APIDump/main_APIDump.lua14
m---------MCServer/Plugins/Core0
-rw-r--r--MCServer/Plugins/Debuggers/Debuggers.lua109
-rw-r--r--MCServer/Plugins/Debuggers/Info.lua2
-rw-r--r--MCServer/Plugins/DumpInfo/Init.lua13
-rw-r--r--MCServer/Plugins/HookNotify/HookNotify.lua488
-rw-r--r--MCServer/Plugins/InfoDump.lua28
-rw-r--r--MCServer/Plugins/InfoReg.lua68
-rw-r--r--MCServer/README.txt4
-rw-r--r--MCServer/crafting.txt134
-rw-r--r--MCServer/furnace.txt2
-rw-r--r--MCServer/items.ini2
-rw-r--r--MCServer/webadmin/(original).html375
-rw-r--r--MCServer/webadmin/template.lua26
-rw-r--r--MCServer/webadmin/template_orig.lua137
-rw-r--r--README.md16
-rw-r--r--SetFlags.cmake9
-rw-r--r--Tools/ProtoProxy/CMakeLists.txt2
-rw-r--r--app.yml2
-rwxr-xr-xcompile.sh199
-rwxr-xr-xeasyinstall.sh6
m---------lib/SQLiteCpp0
m---------lib/TCLAP0
m---------lib/libevent0
m---------lib/polarssl0
-rw-r--r--lib/polarssl.cmake11
-rw-r--r--lib/sqlite/CMakeLists.txt2
-rw-r--r--lib/tolua++/CMakeLists.txt4
-rw-r--r--lib/tolua++/src/bin/lua/compat-5.1.lua8
-rw-r--r--src/Bindings/.gitignore4
-rw-r--r--src/Bindings/AllToLua.bat2
-rw-r--r--src/Bindings/AllToLua.sh2
-rw-r--r--src/Bindings/AllToLua_lua.bat2
-rw-r--r--src/Bindings/BindingsProcessor.lua161
-rw-r--r--src/Bindings/CMakeLists.txt16
-rw-r--r--src/Bindings/LuaState.cpp363
-rw-r--r--src/Bindings/LuaState.h100
-rw-r--r--src/Bindings/LuaWindow.cpp6
-rw-r--r--src/Bindings/ManualBindings.cpp1663
-rw-r--r--src/Bindings/ManualBindings.h549
-rw-r--r--src/Bindings/ManualBindings_Network.cpp22
-rw-r--r--src/Bindings/ManualBindings_RankManager.cpp320
-rw-r--r--src/Bindings/ManualBindings_World.cpp588
-rw-r--r--src/Bindings/Plugin.cpp34
-rw-r--r--src/Bindings/Plugin.h99
-rw-r--r--src/Bindings/PluginLua.cpp375
-rw-r--r--src/Bindings/PluginLua.h22
-rw-r--r--src/Bindings/PluginManager.cpp435
-rw-r--r--src/Bindings/PluginManager.h171
-rw-r--r--src/Bindings/WebPlugin.cpp110
-rw-r--r--src/Bindings/WebPlugin.h61
-rw-r--r--src/Bindings/virtual_method_hooks.lua518
-rw-r--r--src/BlockArea.cpp136
-rw-r--r--src/BlockArea.h28
-rw-r--r--src/BlockEntities/BlockEntityWithItems.h2
-rw-r--r--src/BlockEntities/HopperEntity.cpp41
-rw-r--r--src/BlockEntities/HopperEntity.h2
-rw-r--r--src/BlockEntities/MobHeadEntity.cpp2
-rw-r--r--src/BlockEntities/MobHeadEntity.h2
-rw-r--r--src/BlockEntities/NoteEntity.h2
-rw-r--r--src/BlockInServerPluginInterface.h20
-rw-r--r--src/Blocks/BlockBed.cpp4
-rw-r--r--src/Blocks/BlockComparator.h2
-rw-r--r--src/Blocks/BlockDoor.cpp26
-rw-r--r--src/Blocks/BlockDoor.h25
-rw-r--r--src/Blocks/BlockFire.h6
-rw-r--r--src/Blocks/BlockHandler.cpp3
-rw-r--r--src/Blocks/BlockLeaves.h28
-rw-r--r--src/Blocks/BlockLever.h2
-rw-r--r--src/Blocks/BlockOre.h10
-rw-r--r--src/Blocks/BlockPiston.h2
-rw-r--r--src/Blocks/BlockPluginInterface.h17
-rw-r--r--src/Blocks/BlockRail.h16
-rw-r--r--src/Blocks/BlockTorch.h5
-rw-r--r--src/Blocks/BlockTrapdoor.h2
-rw-r--r--src/Blocks/BlockVine.h2
-rw-r--r--src/BoundingBox.h4
-rw-r--r--src/Broadcaster.cpp47
-rw-r--r--src/Broadcaster.h20
-rw-r--r--src/CMakeLists.txt17
-rwxr-xr-x[-rw-r--r--]src/CheckBasicStyle.lua137
-rw-r--r--src/Chunk.cpp26
-rw-r--r--src/Chunk.h10
-rw-r--r--src/ChunkMap.cpp50
-rw-r--r--src/ChunkMap.h16
-rw-r--r--src/ChunkSender.h2
-rw-r--r--src/ClientHandle.cpp36
-rw-r--r--src/ClientHandle.h6
-rw-r--r--src/CommandOutput.cpp19
-rw-r--r--src/CommandOutput.h30
-rw-r--r--src/CraftingRecipes.cpp5
-rw-r--r--src/Defines.h2
-rw-r--r--src/Enchantments.h6
-rw-r--r--src/Endianness.h2
-rw-r--r--src/Entities/ArrowEntity.cpp4
-rw-r--r--src/Entities/Boat.cpp4
-rw-r--r--src/Entities/Entity.cpp39
-rw-r--r--src/Entities/Entity.h14
-rw-r--r--src/Entities/EntityEffect.cpp4
-rw-r--r--src/Entities/FallingBlock.cpp2
-rw-r--r--src/Entities/FireChargeEntity.cpp1
-rw-r--r--src/Entities/FireworkEntity.cpp2
-rw-r--r--src/Entities/Floater.cpp6
-rw-r--r--src/Entities/GhastFireballEntity.cpp1
-rw-r--r--src/Entities/HangingEntity.h7
-rw-r--r--src/Entities/Minecart.cpp8
-rw-r--r--src/Entities/Minecart.h7
-rw-r--r--src/Entities/Pawn.cpp2
-rw-r--r--src/Entities/Pickup.cpp3
-rw-r--r--src/Entities/Player.cpp192
-rw-r--r--src/Entities/Player.h194
-rw-r--r--src/Entities/ProjectileEntity.cpp14
-rw-r--r--src/Entities/ProjectileEntity.h2
-rw-r--r--src/Entities/TNTEntity.cpp4
-rw-r--r--src/Entities/WitherSkullEntity.cpp2
-rw-r--r--src/FastRandom.cpp26
-rw-r--r--src/Generating/Caves.cpp6
-rw-r--r--src/Generating/ChunkGenerator.cpp4
-rw-r--r--src/Generating/CompoGen.cpp11
-rw-r--r--src/Generating/CompoGen.h2
-rw-r--r--src/Generating/ComposableGenerator.cpp6
-rw-r--r--src/Generating/FinishGen.cpp117
-rw-r--r--src/Generating/FinishGen.h22
-rw-r--r--src/Generating/HeiGen.cpp2
-rw-r--r--src/Generating/IntGen.h4
-rw-r--r--src/Generating/Noise3DGenerator.cpp2
-rw-r--r--src/Generating/ProtIntGen.h2
-rw-r--r--src/Generating/RainbowRoadsGen.cpp2
-rw-r--r--src/Generating/RainbowRoadsGen.h2
-rw-r--r--src/Generating/Ravines.cpp4
-rw-r--r--src/Generating/StructGen.cpp2
-rw-r--r--src/Generating/TestRailsGen.cpp2
-rw-r--r--src/Generating/TestRailsGen.h4
-rw-r--r--src/Generating/Trees.cpp4
-rw-r--r--src/Generating/TwoHeights.cpp2
-rw-r--r--src/Generating/UnderwaterBaseGen.cpp2
-rw-r--r--src/Generating/UnderwaterBaseGen.h2
-rw-r--r--src/Generating/VillageGen.cpp6
-rw-r--r--src/Generating/VillageGen.h2
-rw-r--r--src/Globals.h14
-rw-r--r--src/HTTPServer/EnvelopeParser.h2
-rw-r--r--src/HTTPServer/HTTPConnection.cpp4
-rw-r--r--src/HTTPServer/HTTPConnection.h3
-rw-r--r--src/HTTPServer/HTTPMessage.cpp2
-rw-r--r--src/IniFile.cpp66
-rw-r--r--src/IniFile.h62
-rw-r--r--src/Inventory.cpp2
-rw-r--r--src/Inventory.h2
-rw-r--r--src/Items/ItemArmor.h9
-rw-r--r--src/Items/ItemBoat.h7
-rw-r--r--src/Items/ItemBow.h7
-rw-r--r--src/Items/ItemBucket.h92
-rw-r--r--src/Items/ItemChest.h2
-rw-r--r--src/Items/ItemDoor.h2
-rw-r--r--src/Items/ItemDye.h7
-rw-r--r--src/Items/ItemEmptyMap.h13
-rw-r--r--src/Items/ItemFishingRod.h9
-rw-r--r--src/Items/ItemHandler.cpp23
-rw-r--r--src/Items/ItemHandler.h9
-rw-r--r--src/Items/ItemHoe.h9
-rw-r--r--src/Items/ItemItemFrame.h15
-rw-r--r--src/Items/ItemLighter.h9
-rw-r--r--src/Items/ItemLilypad.h8
-rw-r--r--src/Items/ItemMinecart.h7
-rw-r--r--src/Items/ItemPainting.h13
-rw-r--r--src/Items/ItemPotion.h5
-rw-r--r--src/Items/ItemSign.h2
-rw-r--r--src/Items/ItemSlab.h23
-rw-r--r--src/Items/ItemSpawnEgg.h6
-rw-r--r--src/Items/ItemThrowable.h17
-rw-r--r--src/Logger.h8
-rw-r--r--src/Map.cpp2
-rw-r--r--src/Map.h2
-rw-r--r--src/MemorySettingsRepository.cpp312
-rw-r--r--src/MemorySettingsRepository.h80
-rw-r--r--src/MobCensus.cpp4
-rw-r--r--src/MobCensus.h4
-rw-r--r--src/MobProximityCounter.cpp6
-rw-r--r--src/MobProximityCounter.h14
-rw-r--r--src/MobSpawner.cpp4
-rw-r--r--src/MobSpawner.h2
-rw-r--r--src/Mobs/AggressiveMonster.cpp14
-rw-r--r--src/Mobs/Bat.cpp2
-rw-r--r--src/Mobs/Blaze.cpp2
-rw-r--r--src/Mobs/CMakeLists.txt2
-rw-r--r--src/Mobs/Horse.h2
-rw-r--r--src/Mobs/IronGolem.cpp2
-rw-r--r--src/Mobs/MagmaCube.cpp14
-rw-r--r--src/Mobs/MagmaCube.h6
-rw-r--r--src/Mobs/Monster.cpp513
-rw-r--r--src/Mobs/Monster.h100
-rw-r--r--src/Mobs/Path.cpp491
-rw-r--r--src/Mobs/Path.h186
-rw-r--r--src/Mobs/Pig.cpp1
-rw-r--r--src/Mobs/Sheep.cpp2
-rw-r--r--src/Mobs/Skeleton.cpp21
-rw-r--r--src/Mobs/Skeleton.h5
-rw-r--r--src/Mobs/Slime.cpp2
-rw-r--r--src/Mobs/Slime.h2
-rw-r--r--src/Mobs/Villager.cpp2
-rw-r--r--src/Mobs/Wolf.cpp9
-rw-r--r--src/Mobs/Wolf.h4
-rw-r--r--src/Mobs/Zombie.cpp23
-rw-r--r--src/Mobs/Zombie.h8
-rw-r--r--src/MonsterConfig.h2
-rw-r--r--src/Noise/Noise.cpp58
-rw-r--r--src/OSSupport/CriticalSection.h2
-rw-r--r--src/OSSupport/Errors.cpp2
-rw-r--r--src/OSSupport/File.cpp102
-rw-r--r--src/OSSupport/File.h25
-rw-r--r--src/OSSupport/Network.h2
-rw-r--r--src/OSSupport/Semaphore.h2
-rw-r--r--src/OSSupport/TCPLinkImpl.cpp6
-rw-r--r--src/OSSupport/UDPEndpointImpl.cpp2
-rw-r--r--src/OverridesSettingsRepository.cpp273
-rw-r--r--src/OverridesSettingsRepository.h52
-rw-r--r--src/PolarSSL++/BlockingSslClientSocket.cpp6
-rw-r--r--src/PolarSSL++/BlockingSslClientSocket.h5
-rw-r--r--src/PolarSSL++/CMakeLists.txt2
-rw-r--r--src/ProbabDistrib.cpp1
-rw-r--r--src/Protocol/Authenticator.cpp20
-rw-r--r--src/Protocol/Authenticator.h6
-rw-r--r--src/Protocol/ChunkDataSerializer.cpp4
-rw-r--r--src/Protocol/MojangAPI.cpp52
-rw-r--r--src/Protocol/MojangAPI.h4
-rw-r--r--src/Protocol/Protocol.h3
-rw-r--r--src/Protocol/Protocol17x.cpp18
-rw-r--r--src/Protocol/Protocol17x.h1
-rw-r--r--src/Protocol/Protocol18x.cpp58
-rw-r--r--src/Protocol/Protocol18x.h1
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp11
-rw-r--r--src/Protocol/ProtocolRecognizer.h3
-rw-r--r--src/RCONServer.cpp8
-rw-r--r--src/RCONServer.h6
-rw-r--r--src/RankManager.cpp381
-rw-r--r--src/RankManager.h43
-rw-r--r--src/Root.cpp135
-rw-r--r--src/Root.h81
-rw-r--r--src/Scoreboard.cpp8
-rw-r--r--src/Scoreboard.h15
-rw-r--r--src/Server.cpp82
-rw-r--r--src/Server.h4
-rw-r--r--src/SetChunkData.cpp10
-rw-r--r--src/SetChunkData.h10
-rw-r--r--src/SettingsRepositoryInterface.h61
-rw-r--r--src/Simulator/FloodyFluidSimulator.h2
-rw-r--r--src/Simulator/IncrementalRedstoneSimulator.cpp10
-rw-r--r--src/StringUtils.cpp7
-rw-r--r--src/StringUtils.h2
-rw-r--r--src/Tracer.cpp96
-rw-r--r--src/Tracer.h10
-rw-r--r--src/UI/DropSpenserWindow.cpp2
-rw-r--r--src/UI/DropSpenserWindow.h2
-rw-r--r--src/UI/FurnaceWindow.cpp2
-rw-r--r--src/UI/MinecartWithChestWindow.h2
-rw-r--r--src/UI/SlotArea.h4
-rw-r--r--src/UI/Window.cpp8
-rw-r--r--src/UI/Window.h2
-rw-r--r--src/Vector3.h42
-rw-r--r--src/VoronoiMap.h2
-rw-r--r--src/WebAdmin.cpp37
-rw-r--r--src/WebAdmin.h9
-rw-r--r--src/World.cpp55
-rw-r--r--src/World.h21
-rw-r--r--src/WorldStorage/EnchantmentSerializer.cpp4
-rw-r--r--src/WorldStorage/EnchantmentSerializer.h4
-rw-r--r--src/WorldStorage/FastNBT.h2
-rw-r--r--src/WorldStorage/MapSerializer.cpp12
-rw-r--r--src/WorldStorage/MapSerializer.h2
-rw-r--r--src/WorldStorage/ScoreboardSerializer.cpp8
-rw-r--r--src/WorldStorage/ScoreboardSerializer.h4
-rw-r--r--src/WorldStorage/StatSerializer.cpp2
-rw-r--r--src/WorldStorage/StatSerializer.h2
-rwxr-xr-xsrc/WorldStorage/WSSAnvil.cpp12
-rw-r--r--src/XMLParser.h40
-rw-r--r--src/main.cpp154
290 files changed, 8748 insertions, 5349 deletions
diff --git a/.gitmodules b/.gitmodules
index 93fac9d1f..20f8ea103 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -28,3 +28,6 @@
[submodule "lib/libevent"]
path = lib/libevent
url = https://github.com/mc-server/libevent.git
+[submodule "lib/TCLAP"]
+ path = lib/TCLAP
+ url = https://github.com/mc-server/TCLAP.git
diff --git a/.travis.yml b/.travis.yml
index 2fd3bf991..e39cdbc14 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,39 +1,27 @@
language: cpp
-compiler: clang
-before_install:
- - if [ "$TRAVIS_MCSERVER_BUILD_TYPE" == "COVERAGE" ]; then sudo pip install cpp_coveralls; fi
+compiler:
+- clang
+- gcc
- # g++4.8
+before_install:
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
- sudo apt-get update -qq
install:
- # g++4.8 and clang
- sudo apt-get install -qq g++-4.8
+ - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 90
+ - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 90
# lua, needed for style checking and possibly later on for bindings generation
- sudo apt-get install -qq lua5.1
-
- # g++4.8
- - if [ "$CXX" == "g++" ]; then export CXX="g++-4.8"; export CC="gcc-4.8"; fi
-# Build MCServer
script: ./CIbuild.sh
-after_success:
- - ./uploadCoverage.sh
-
env:
- TRAVIS_MCSERVER_BUILD_TYPE=RELEASE MCSERVER_PATH=./MCServer
- TRAVIS_MCSERVER_BUILD_TYPE=DEBUG MCSERVER_PATH=./MCServer_debug
-matrix:
- include:
- - compiler: gcc
- env: TRAVIS_MCSERVER_BUILD_TYPE=COVERAGE MCSERVER_PATH=./MCServer
-
-# Notification Settings
notifications:
email:
on_success: change
diff --git a/CIbuild.sh b/CIbuild.sh
index f5a9005da..60fed73c2 100755
--- a/CIbuild.sh
+++ b/CIbuild.sh
@@ -7,9 +7,13 @@ export MCSERVER_BUILD_ID=$TRAVIS_JOB_NUMBER
export MCSERVER_BUILD_DATETIME=`date`
cmake . -DBUILD_TOOLS=1 -DSELF_TEST=1;
+
+echo "Checking basic style..."
cd src
lua CheckBasicStyle.lua
cd ..
+
+echo "Building..."
make -j 2;
make -j 2 test ARGS="-V";
cd MCServer/;
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5b3fd5e7d..ae83662c9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -52,13 +52,18 @@ if(MSVC OR MSVC_IDE)
endif()
endif()
+set(BUILD_TOOLS OFF CACHE BOOL "")
+set(SELF_TEST OFF CACHE BOOL "")
+
# This has to be done before any flags have been set up.
if(${BUILD_TOOLS})
+ message("Building tools")
add_subdirectory(Tools/MCADefrag/)
add_subdirectory(Tools/ProtoProxy/)
endif()
if(${BUILD_UNSTABLE_TOOLS})
+ message("Building unstable tools")
add_subdirectory(Tools/GeneratorPerformanceTest/)
endif()
@@ -76,6 +81,9 @@ endif()
# The Expat library is linked in statically, make the source files aware of that:
add_definitions(-DXML_STATIC)
+# Let Lua use additional checks on its C API. This is only compiled into Debug builds:
+add_definitions(-DLUA_USE_APICHECK)
+
# Self Test Mode enables extra checks at startup
if(${SELF_TEST})
add_definitions(-DSELF_TEST)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 03481ec48..4e57e6efb 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,7 +1,27 @@
-Code Stuff
+Code Conventions
----------
- * We use C++03 with some C++11 extensions (ask if you think that something would be useful)
+When contributing, you must follow our code conventions. Otherwise, the CI builds will automatically fail and your PR will not be merged until the non-conforming code is fixed. Due to this, we strongly advise you to run `src/CheckBasicStyle.lua` before commiting, it will perform various code style checks and warn you if your code does not conform to our conventions. `CheckBasicStyle.lua` can be configured to run automatically before every commit via a pre-commit hook, **this is highly recommended**. The way to do it is listed at the bottom of this file.
+
+Here are the conventions:
+
+ * We use the subset of C++11 supported by MSVC 2013 (ask if you think that something would be useful)
+ * All new public functions in all classes need documenting comments on what they do and what behavior they follow, use doxy-comments formatted as `/** Description */`. Do not use asterisks on additional lines in multi-line comments.
+ * Use spaces after the comment markers: `// Comment` instead of `//Comment`. A comment must be prefixed with two spaces if it's on the same line with code:
+ - `SomeFunction()<Space><Space>//<Space>Note the two spaces prefixed to me and the space after the slashes.`
+ * All variable names and function names use CamelCase style, with the exception of single letter variables.
+ - `ThisIsAProperFunction()` `This_is_bad()` `this_is_bad` `GoodVariableName` `badVariableName`.
+ * All member variables start with `m_`, all function parameters start with `a_`, all class names start with `c`.
+ - `class cMonster { int m_Health; int DecreaseHealth(int a_Amount); }`
+ * Put spaces after commas. `Vector3d(1, 2, 3)` instead of `Vector3d(1,2,3)`
+ * Put spaces before and after every operator.
+ - `a = b + c;`
+ - `if (a == b)`
+ * Keep individual functions spaced out by 5 empty lines, this enhances readability and makes navigation in the source file easier.
+ * Add those extra parentheses to conditions, especially in C++:
+ - `if ((a == 1) && ((b == 2) || (c == 3)))` instead of ambiguous `if (a == 1 && b == 2 || c == 3)`
+ - This helps prevent mistakes such as `if (a & 1 == 0)`
+ *
* Use the provided wrappers for OS stuff:
- Threading is done by inheriting from `cIsThread`, thread synchronization through `cCriticalSection`, `cSemaphore` and `cEvent`, file access and filesystem operations through the `cFile` class, high-precision timers through `cTimer`, high-precision sleep through `cSleep`
* No magic numbers, use named constants:
@@ -16,10 +36,6 @@ Code Stuff
- `cPlayer:IsGameModeCreative()` instead of` (cPlayer:GetGameMode() == gmCreative)` (the player can also inherit the gamemode from the world, which the value-d condition doesn't catch)
* Please use **tabs for indentation and spaces for alignment**. This means that if it's at line start, it's a tab; if it's in the middle of a line, it's a space
* Alpha-sort stuff that makes sense alpha-sorting - long lists of similar items etc.
- * Keep individual functions spaced out by 5 empty lines, this enhances readability and makes navigation in the source file easier.
- * Add those extra parentheses to conditions, especially in C++
- - `if ((a == 1) && ((b == 2) || (c == 3)))` instead of ambiguous `if (a == 1 && b == 2 || c == 3)`
- - This helps prevent mistakes such as `if (a & 1 == 0)`
* White space is free, so use it freely
- "freely" as in "plentifully", not "arbitrarily"
* All `case` statements inside a `switch` need an extra indent.
@@ -27,9 +43,22 @@ Code Stuff
- The only exception: a `switch` statement with all `case` statements being a single short statement is allowed to use the short brace-less form.
- These two rules really mean that indent is governed by braces
* Add an empty last line in all source files (GCC and GIT can complain otherwise)
- * All new public functions in all classes need documenting comments on what they do and what behavior they follow, use doxy-comments formatted as `/** Description */`. Do not use asterisks on additional lines in multi-line comments.
- * Use spaces after the comment markers: `// Comment` instead of `//Comment`
+Pre-commit hook
+---------
+When contributing, the code conventions above *must* be followed. Otherwise, the CI builds will automatically fail and your PR will not be merged until the non-conforming code is fixed. It is highly recommended to set up a pre-commit hook which will check your code style before every commit. Here is how to do that:
+
+ * Clone the repository as usual.
+ * Go to your `<clone location>/.git/hooks` folder, create a text file named "pre-commit" there with the following contents:
+```
+#!/usr/sh
+src/CheckBasicStyle.lua 1>&2 -g
+```
+ * If on Linux/Unix, you need to give the newly created file an execute permission: `chmod +x .git/hooks/pre-commit`
+ * Lua must be installed.
+ * You're done. Now, `src/CheckBasicStyle.lua` will check the changed files before every commit. If a problem is found, it will point you to that problem and will cancel the commit.
+
+Note that the check script is not smart enough to catch everything, so not having any warnings does not necessarily imply that you followed the conventions fully. The other humans working on this will perform more checks before merging.
Copyright
---------
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 9a0a675e7..4a6850a2f 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -2,11 +2,13 @@ Many people have contributed to MCServer, and this list attempts to broadcast at
BasedDoge (Donated AlchemistVillage prefabs)
bearbin (Alexander Harkness)
+beeduck
derouinw
Diusrex
Duralex
FakeTruth (founder)
Howaner
+jasperarmstrong
keyboard
Lapayo
Luksor
@@ -16,14 +18,17 @@ Masy98
mborland
mgueydan
MikeHunsinger
+missingchar (mathias-github)
mtilden
nesco
p-mcgowan
rs2k
+SafwatHalaby (Safwat Halaby)
SamJBarney
+Seadragon91 (Lukas Pioch)
Sofapriester
SphinxC0re
-STR_Warrior
+NiLSPACE (formerly STR_Warrior)
structinf (xdot)
Sxw1212
Taugeshtu
@@ -32,7 +37,7 @@ tonibm19
UltraCoderRU
WebFreak001
worktycho
-xoft
+xoft (Mattes Dolak/madmaxoft on GH)
Yeeeeezus (Donated AlchemistVillage prefabs)
Please add yourself to this list if you contribute to MCServer.
diff --git a/GETTING-STARTED.md b/GETTING-STARTED.md
index d78b2f84f..2e2568e64 100644
--- a/GETTING-STARTED.md
+++ b/GETTING-STARTED.md
@@ -19,7 +19,7 @@ Useful Resources
* [Minecraft Wiki](http://minecraft.gamepedia.com/Minecraft_Wiki)
* [Minecraft Protocol Wiki](http://wiki.vg)
* [Lua API Documentation](http://mc-server.xoft.cz/LuaAPI)
- * [VS2008 Download](http://stackoverflow.com/questions/15318560/visual-c-2008-express-download-link-dead)
+ * [VS2013 Community Edition Download](https://www.visualstudio.com/products/visual-studio-community-vs)
Setting up a Dev Environment
============================
@@ -39,12 +39,12 @@ You'll also need CMake to generate the makefile to build from.
**Windows:**
-If you use Windows, your best bet is the MSVC2008 (available as a free download in the Express edition from MS) or MSVS2013 (ditto), solution files for which can be generated with cmake. You'll also need cmake to generate the project files.
+If you use Windows, your best bet is the MSVC2013 (available as a free download in the Community edition from MS), solution files for which can be generated with cmake. You'll also need cmake to generate the project files.
Setting up the Repo
-------------------
-Next, you'll need to set up the repo. You can make a fork and work on that then PR in, or I can set you up with membership for the repo so you can work on branches here (still use PRs though, they're great tools and for the first few you'll definitely need some changes). If you want membership to the repo, just create an issue and I can set you up.
+Next, you'll need to set up the repo. You should make a fork and work on that, then create a Pull Request so that we can review and merge your code. After you've "earned" an honorable status, we'll give you write access to the repository, so that you can work on branches in the main repo here (still use PRs though, they're great tools for review and discussion).
Once you've cloned, you need to pull down the submodules:
@@ -71,6 +71,8 @@ Code Styles
Mainly follow the code styles in [CONTRIBUTING.md](https://github.com/mc-server/MCServer/blob/master/CONTRIBUTING.md), which is definitely an important read.
+Note that there is a script file, $/src/CheckBasicStyle.lua, that can check some common violations of the coding style. You should run this file to check your code regularly. This script is run during the integration builds and if it fails, the build will fail. Note that you need Lua installed in order to run this script.
+
How to Build
------------------
@@ -85,12 +87,14 @@ Basically, the process is:
**Windows:**
-You need to first generate a project file with `cmake . -DCMAKE_BUILD_TYPE=DEBUG` then execute the `src/Bindings/AllToLua.bat` script file, then just open the solution file in your MSVC of choice and build.
+You need to first generate a solution file by executing `cmake .` on the commandline at the top-level folder of the repository, then just open the solution file in MSVC and build. Note that the first time after generating the solution, you will need to do extra setup in order to be able to fully debug in MSVC:
+- Set the startup project to MCServer: right-click the MCServer project in the Solution Explorer and choose "Set as Startup Project".
+- Set the debugging folder: right-click the MCServer project in the Solution Explorer, choose "Properties". In the dialog, browse to "Configuration Properties" -> "Debugging" and set "Working Directory" to "../MCServer".
How to Run
----------
-The server can be run (on *nix) by a simple `./MCServer` in the `MCServer` directory. On first run it will generate the world and start a server on the default port (configurable in `settings.ini`) so you can connect in minecraft via `localhost`.
+The server can be run (on *nix) by a simple `./MCServer` in the `MCServer` directory. On first run it will generate the world and start a server on the default port (configurable in `settings.ini`) so you can connect in minecraft via `localhost`. Note that if you build a debug version, the executable will be names `MCServer_debug` instead
Where to Get Started
-------------------------------
@@ -116,6 +120,5 @@ You may also want to write some plugins. They are written in lua, with excellent
Special Things
---------------------
-
- * MCServer uses ToLUA for the Lua API, and you'll really have to ask @madmaxoft for how to export stuff and @worktycho for how to add stuff to the auto generated bindings (he just re-worked it with CMake).
+ * Make yourself familiar with the community. Visit the forums: http://forum.mc-server.org
* Ask questions as much as you like, we're here to help :smiley:
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua
index 9ee818a2c..4af01c0a4 100644
--- a/MCServer/Plugins/APIDump/APIDesc.lua
+++ b/MCServer/Plugins/APIDump/APIDesc.lua
@@ -29,7 +29,7 @@ g_APIDesc =
{
ConstantName = { Notes = "Notes about the constant" },
} ,
-
+
ConstantGroups =
{
GroupName1 = -- GroupName1 is used as the HTML anchor name
@@ -99,6 +99,7 @@ g_APIDesc =
Clear = { Params = "", Return = "", Notes = "Clears the object, resets it to zero size" },
CopyFrom = { Params = "BlockAreaSrc", Return = "", Notes = "Copies contents from BlockAreaSrc into self" },
CopyTo = { Params = "BlockAreaDst", Return = "", Notes = "Copies contents from self into BlockAreaDst." },
+ CountNonAirBlocks = { Params = "", Return = "number", Notes = "Returns the count of blocks that are not air. Returns 0 if blocktypes not available. Block metas are ignored (if present, air with any meta is still considered air)." },
Create = { Params = "SizeX, SizeY, SizeZ, [DataTypes]", Return = "", Notes = "Initializes this BlockArea to an empty area of the specified size and origin of {0, 0, 0}. Any previous contents are lost." },
Crop = { Params = "AddMinX, SubMaxX, AddMinY, SubMaxY, AddMinZ, SubMaxZ", Return = "", Notes = "Crops the specified number of blocks from each border. Modifies the size of this blockarea object." },
DumpToRawFile = { Params = "FileName", Return = "", Notes = "Dumps the raw data into a file. For debugging purposes only." },
@@ -120,6 +121,7 @@ g_APIDesc =
GetOriginX = { Params = "", Return = "number", Notes = "Returns the origin x-coord" },
GetOriginY = { Params = "", Return = "number", Notes = "Returns the origin y-coord" },
GetOriginZ = { Params = "", Return = "number", Notes = "Returns the origin z-coord" },
+ GetNonAirCropRelCoords = { Params = "[IgnoreBlockType]", Return = "MinRelX, MinRelY, MinRelZ, MaxRelX, MaxRelY, MaxRelZ", Notes = "Returns the minimum and maximum coords in each direction for the first non-ignored block in each direction. If there are no non-ignored blocks within the area, or blocktypes are not present, the returned values are reverse-ranges (MinX <- m_RangeX, MaxX <- 0 etc.). IgnoreBlockType defaults to air." },
GetRelBlockLight = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "NIBBLETYPE", Notes = "Returns the blocklight at the specified relative coords" },
GetRelBlockMeta = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "NIBBLETYPE", Notes = "Returns the block meta at the specified relative coords" },
GetRelBlockSkyLight = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "NIBBLETYPE", Notes = "Returns the skylight at the specified relative coords" },
@@ -197,13 +199,14 @@ g_APIDesc =
baMetas = { Notes = "Operations should work on block metas" },
baLight = { Notes = "Operations should work on block (emissive) light" },
baSkyLight = { Notes = "Operations should work on skylight" },
- msDifference = { Notes = "Block becomes air if Src and Dst are the same. Otherwise it becomes the source block." },
- msOverwrite = { Notes = "Src overwrites anything in Dst" },
- msFillAir = { Notes = "Dst is overwritten by Src only where Src has air blocks" },
- msImprint = { Notes = "Src overwrites Dst anywhere where Dst has non-air blocks" },
+ msDifference = { Notes = "Block becomes air if 'self' and src are the same. Otherwise it becomes the src block." },
+ msFillAir = { Notes = "'self' is overwritten by Src only where 'self' has air blocks" },
+ msImprint = { Notes = "Src overwrites 'self' anywhere where 'self' has non-air blocks" },
msLake = { Notes = "Special mode for merging lake images" },
+ msMask = { Notes = "The blocks that are exactly the same are kept in 'self', all differing blocks are replaced by air"},
+ msOverwrite = { Notes = "Src overwrites anything in 'self'" },
+ msSimpleCompare = { Notes = "The blocks that are exactly the same are replaced with air, all differing blocks are replaced by stone"},
msSpongePrint = { Notes = "Similar to msImprint, sponge block doesn't overwrite anything, all other blocks overwrite everything"},
- msMask = { Notes = "The blocks that are exactly the same are kept in Dst, all differing blocks are replaced by air"},
},
ConstantGroups =
{
@@ -267,7 +270,7 @@ g_APIDesc =
<h3>Special strategies</h3>
<p>For each strategy, evaluate the table rows from top downwards, the first match wins.</p>
-
+
<p>
<strong>msDifference</strong> - changes all the blocks which are the same to air. Otherwise the source block gets placed.
</p>
@@ -279,15 +282,15 @@ g_APIDesc =
<td> B </td><td> B </td><td> Air </td><td> The blocks are the same so we get air. </td>
</tr>
</tbody></table>
-
-
+
+
<p>
<strong>msLake</strong> - used for merging areas with lava and water lakes, in the appropriate generator.
</p>
<table><tbody><tr>
<th colspan="2"> area block </th><th> </th><th> Notes </th>
</tr><tr>
- <th> this </th><th> Src </th><th> result </th><th> </th>
+ <th> self </th><th> Src </th><th> result </th><th> </th>
</tr><tr>
<td> A </td><td> sponge </td><td> A </td><td> Sponge is the NOP block </td>
</tr><tr>
@@ -321,7 +324,7 @@ g_APIDesc =
<table><tbody><tr>
<th colspan="2"> area block </th><th> </th><th> Notes </th>
</tr><tr>
- <th> this </th><th> Src </th><th> result </th><th> </th>
+ <th> self </th><th> Src </th><th> result </th><th> </th>
</tr><tr>
<td> A </td><td> sponge </td><td> A </td><td> Sponge is the NOP block </td>
</tr><tr>
@@ -337,13 +340,45 @@ g_APIDesc =
<table><tbody><tr>
<th colspan="2"> area block </th><th> </th><th> Notes </th>
</tr><tr>
- <th> this </th><th> Src </th><th> result </th><th> </th>
+ <th> self </th><th> Src </th><th> result </th><th> </th>
</tr><tr>
<td> A </td><td> A </td><td> A </td><td> Same blocks are kept </td>
</tr><tr>
<td> A </td><td> non-A </td><td> air </td><td> Differing blocks are replaced with air </td>
</tr>
</tbody></table>
+
+ <p>
+ <strong>msDifference</strong> - the blocks that are the same in both areas are replaced with air, all the
+ differing blocks are kept from the first area. Meta is used in the comparison, too, two blocks of the
+ same type but different meta are considered different.
+ </p>
+ <table><tbody><tr>
+ <th colspan="2"> area block </th><th> </th><th> Notes </th>
+ </tr><tr>
+ <th> self </th><th> Src </th><th> result </th><th> </th>
+ </tr><tr>
+ <td> A </td><td> A </td><td> air </td><td> Same blocks are replaced with air </td>
+ </tr><tr>
+ <td> A </td><td> non-A </td><td> A </td><td> Differing blocks are kept from 'self' </td>
+ </tr>
+ </tbody></table>
+
+ <p>
+ <strong>msSimpleCompare</strong> - the blocks that are the same in both areas are replaced with air, all the
+ differing blocks are replaced with stone. Meta is used in the comparison, too, two blocks of the
+ same type but different meta are considered different.
+ </p>
+ <table><tbody><tr>
+ <th colspan="2"> area block </th><th> </th><th> Notes </th>
+ </tr><tr>
+ <th> self </th><th> Src </th><th> result </th><th> </th>
+ </tr><tr>
+ <td> A </td><td> A </td><td> air </td><td> Same blocks are replaced with air </td>
+ </tr><tr>
+ <td> A </td><td> non-A </td><td> stone </td><td> Differing blocks are replaced with stone </td>
+ </tr>
+ </tbody></table>
]],
}, -- Merge strategies
}, -- AdditionalInfo
@@ -500,7 +535,7 @@ g_APIDesc =
function OnChunkGenerated(a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc)
-- Get the topmost block coord:
local Height = a_ChunkDesc:GetHeight(0, 0);
-
+
-- Create a sign there:
a_ChunkDesc:SetBlockTypeMeta(0, Height + 1, 0, E_BLOCK_SIGN_POST, 0);
local BlockEntity = a_ChunkDesc:GetBlockEntity(0, Height + 1, 0);
@@ -631,7 +666,7 @@ end</pre>
},
}, -- AdditionalInfo
}, -- cCompositeChat
-
+
cCraftingGrid =
{
Desc = [[
@@ -703,7 +738,7 @@ local Hash = cCryptoHash.sha1HexString("DataToHash")
</pre></p>
<p>Each cryptographic hash has two variants, one returns the hash as a raw binary string, the other returns the hash as a hex-encoded string twice as long as the binary string.
]],
-
+
Functions =
{
md5 = { Params = "Data", Return = "string", Notes = "(STATIC) Calculates the md5 hash of the data, returns it as a raw (binary) string of 16 characters." },
@@ -712,7 +747,7 @@ local Hash = cCryptoHash.sha1HexString("DataToHash")
sha1HexString = { Params = "Data", Return = "string", Notes = "(STATIC) Calculates the sha1 hash of the data, returns it as a hex-encoded string of 40 characters." },
},
}, -- cCryptoHash
-
+
cEnchantments =
{
Desc = [[
@@ -973,11 +1008,15 @@ cFile:Delete("/usr/bin/virus.exe");
]],
Functions =
{
+ ChangeFileExt = { Params = "FileName, NewExt", Return = "string", Notes = "(STATIC) Returns FileName with its extension changed to NewExt. NewExt may begin with a dot, but needn't, the result is the same in both cases (the first dot, if present, is ignored). FileName may contain path elements, extension is recognized as the last dot after the last path separator in the string." },
Copy = { Params = "SrcFileName, DstFileName", Return = "bool", Notes = "(STATIC) Copies a single file to a new destination. Returns true if successful. Fails if the destination already exists." },
CreateFolder = { Params = "FolderName", Return = "bool", Notes = "(STATIC) Creates a new folder. Returns true if successful." },
Delete = { Params = "FileName", Return = "bool", Notes = "(STATIC) Deletes the specified file. Returns true if successful." },
Exists = { Params = "FileName", Return = "bool", Notes = "(STATIC) Returns true if the specified file exists." },
+ GetExecutableExt = { Params = "", Return = "string", Notes = "(STATIC) Returns the customary executable extension (including the dot) used by the current platform (\".exe\" on Windows, empty string on Linux). " },
GetFolderContents = { Params = "FolderName", Return = "array table of strings", Notes = "(STATIC) Returns the contents of the specified folder, as an array table of strings. Each filesystem object is listed. Use the IsFile() and IsFolder() functions to determine the object type." },
+ GetLastModificationTime = { Params = "Path", Return = "number", Notes = "(STATIC) Returns the last modification time (in current timezone) of the specified file or folder. Returns zero if file not found / not accessible. The returned value is in the same units as values returned by os.time()." },
+ GetPathSeparator = { Params = "", Return = "string", Notes = "(STATIC) Returns the primary path separator used by the current platform. Returns \"\\\" on Windows and \"/\" on Linux. Note that the platform or CRT may support additional path separators, those are not reported." },
GetSize = { Params = "FileName", Return = "number", Notes = "(STATIC) Returns the size of the file, or -1 on failure." },
IsFile = { Params = "Path", Return = "bool", Notes = "(STATIC) Returns true if the specified path points to an existing file." },
IsFolder = { Params = "Path", Return = "bool", Notes = "(STATIC) Returns true if the specified path points to an existing folder." },
@@ -999,7 +1038,7 @@ cFile:Delete("/usr/bin/virus.exe");
},
Inherits = "cEntity",
},
-
+
cIniFile =
{
Desc = [[
@@ -1034,7 +1073,7 @@ ValueName0=SomeOtherValue
{
constructor = { Params = "", Return = "cIniFile", Notes = "Creates a new empty cIniFile object." },
AddHeaderComment = { Params = "Comment", Return = "", Notes = "Adds a comment to be stored in the file header." },
- AddKeyComment =
+ AddKeyComment =
{
{ Params = "KeyID, Comment", Return = "", Notes = "Adds a comment to be stored in the file under the specified key" },
{ Params = "KeyName, Comment", Return = "", Notes = "Adds a comment to be stored in the file under the specified key" },
@@ -1078,12 +1117,12 @@ ValueName0=SomeOtherValue
{ Params = "KeyName", Return = "number", Notes = "Returns the number of comments under the specified key" },
},
GetNumKeys = { Params = "", Return = "number", Notes = "Returns the total number of keys. This is the range for the KeyID (0 .. GetNumKeys() - 1)" },
- GetNumValues =
+ GetNumValues =
{
{ Params = "KeyID", Return = "number", Notes = "Returns the number of values stored under the specified key." },
{ Params = "KeyName", Return = "number", Notes = "Returns the number of values stored under the specified key." },
},
- GetValue =
+ GetValue =
{
{ Params = "KeyName, ValueName", Return = "string", Notes = "Returns the value of the specified name under the specified key. Returns an empty string if the value doesn't exist." },
{ Params = "KeyID, ValueID", Return = "string", Notes = "Returns the value of the specified name under the specified key. Returns an empty string if the value doesn't exist." },
@@ -1091,7 +1130,7 @@ ValueName0=SomeOtherValue
GetValueB = { Params = "KeyName, ValueName", Return = "bool", Notes = "Returns the value of the specified name under the specified key, as a bool. Returns false if the value doesn't exist." },
GetValueF = { Params = "KeyName, ValueName", Return = "number", Notes = "Returns the value of the specified name under the specified key, as a floating-point number. Returns zero if the value doesn't exist." },
GetValueI = { Params = "KeyName, ValueName", Return = "number", Notes = "Returns the value of the specified name under the specified key, as an integer. Returns zero if the value doesn't exist." },
- GetValueName =
+ GetValueName =
{
{ Params = "KeyID, ValueID", Return = "string", Notes = "Returns the name of the specified value Inverse for FindValue()." },
{ Params = "KeyName, ValueID", Return = "string", Notes = "Returns the name of the specified value Inverse for FindValue()." },
@@ -1102,7 +1141,7 @@ ValueName0=SomeOtherValue
GetValueSetI = { Params = "KeyName, ValueName, Default", Return = "number", Notes = "Returns the value of the specified name under the specified key, as an integer. If the value doesn't exist, creates it with the specified default." },
HasValue = { Params = "KeyName, ValueName", Return = "bool", Notes = "Returns true if the specified value is present." },
ReadFile = { Params = "FileName, [AllowExampleFallback]", Return = "bool", Notes = "Reads the values from the specified file. Previous in-memory contents are lost. If the file cannot be opened, and AllowExample is true, another file, \"filename.example.ini\", is loaded and then saved as \"filename.ini\". Returns true if successful, false if not." },
- SetValue =
+ SetValue =
{
{ Params = "KeyID, ValueID, NewValue", Return = "bool", Notes = "Overwrites the specified value with a new value. If the specified value doesn't exist, returns false (doesn't add)." },
{ Params = "KeyName, ValueName, NewValue, [CreateIfNotExists]", Return = "bool", Notes = "Overwrites the specified value with a new value. If CreateIfNotExists is true (default) and the value doesn't exist, it is first created. Returns true if the value was successfully set, false if not (didn't exists, CreateIfNotExists false)." },
@@ -1645,7 +1684,7 @@ a_Player:OpenWindow(Window);
]],
Functions =
{
- DoWithMap = { Params = "ID, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If a map with the specified ID exists, calls the CallbackFunction for that map. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cMap|Map}}, [CallbackData])</pre> Returns true if the map was found and the callback called, false if map not found." },
+ DoWithMap = { Params = "ID, CallbackFunction", Return = "bool", Notes = "If a map with the specified ID exists, calls the CallbackFunction for that map. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cMap|Map}})</pre> Returns true if the map was found and the callback called, false if map not found." },
GetNumMaps = { Params = "", Return = "number", Notes = "Returns the number of registered maps." },
},
@@ -1913,159 +1952,6 @@ a_Player:OpenWindow(Window);
Inherits = "cPawn",
}, -- cPlayer
- cPlugin =
- {
- Desc = [[cPlugin describes a Lua plugin. This page is dedicated to new-style plugins and contain their functions. Each plugin has its own Plugin object.
-]],
- Functions =
- {
- Call = { Params = "Function name, [All the parameters divided with commas]", Notes = "(<b>OBSOLETE</b>) This function allows you to call a function from another plugin. It can only use pass: integers, booleans, strings and usertypes (cPlayer, cEntity, cCuboid, etc.).<br /><br /><b>This function is obsolete and unsafe, use {{cPluginManager}}:CallPlugin() instead!</b>" },
- GetDirectory = { Return = "string", Notes = "Returns the name of the folder where the plugin's files are. (APIDump)" },
- GetLocalDirectory = { Notes = "OBSOLETE use GetLocalFolder instead." },
- GetLocalFolder = { Return = "string", Notes = "Returns the path where the plugin's files are. (Plugins/APIDump)" },
- GetName = { Return = "string", Notes = "Returns the name of the plugin." },
- SetName = { Params = "string", Notes = "Sets the name of the Plugin." },
- GetVersion = { Return = "number", Notes = "Returns the version of the plugin." },
- SetVersion = { Params = "number", Notes = "Sets the version of the plugin." },
- GetFileName = { Return = "string" },
- CreateWebPlugin = { Notes = "{{cWebPlugin|cWebPlugin}}" },
- },
- }, -- cPlugin
-
- cPluginLua =
- {
- Desc = "",
- Functions = {},
- Inherits = "cPlugin",
- }, -- cPluginLua
-
- cPluginManager =
- {
- Desc = [[
- This class is used for generic plugin-related functionality. The plugin manager has a list of all
- plugins, can enable or disable plugins, manages hooks and in-game console commands.</p>
- <p>
- There is one instance of cPluginManager in MCServer, to get it, call either
- {{cRoot|cRoot}}:Get():GetPluginManager() or cPluginManager:Get() function.</p>
- <p>
- Note that some functions are "static", that means that they are called using a dot operator instead
- of the colon operator. For example:
-<pre class="prettyprint lang-lua">
-cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
-</pre></p>
- ]],
- Functions =
- {
- AddHook =
- {
- { Params = "HookType, [HookFunction]", Return = "", Notes = "(STATIC) Informs the plugin manager that it should call the specified function when the specified hook event occurs. If a function is not specified, a default function name is looked up, based on the hook type" },
- { Params = "{{cPlugin|Plugin}}, HookType, [HookFunction]", Return = "", Notes = "(STATIC, <b>DEPRECATED</b>) Informs the plugin manager that it should call the specified function when the specified hook event occurs. If a function is not specified, a default function name is looked up, based on the hook type. NOTE: This format is deprecated and the server outputs a warning if it is used!" },
- },
- BindCommand =
- {
- { Params = "Command, Permission, Callback, HelpString", Return = "[bool]", Notes = "(STATIC) Binds an in-game command with the specified callback function, permission and help string. By common convention, providing an empty string for HelpString will hide the command from the /help display. Returns true if successful, logs to console and returns no value on error. The callback uses the following signature: <pre class=\"prettyprint lang-lua\">function(Split, {{cPlayer|Player}})</pre> The Split parameter contains an array-table of the words that the player has sent, Player is the {{cPlayer}} object representing the player who sent the command. If the callback returns true, the command is assumed to have executed successfully; in all other cases the server sends a warning to the player that the command is unknown (this is so that subcommands can be implemented)." },
- { Params = "Command, Permission, Callback, HelpString", Return = "[bool]", Notes = "Binds an in-game command with the specified callback function, permission and help string. By common convention, providing an empty string for HelpString will hide the command from the /help display. Returns true if successful, logs to console and returns no value on error. The callback uses the following signature: <pre class=\"prettyprint lang-lua\">function(Split, {{cPlayer|Player}})</pre> The Split parameter contains an array-table of the words that the player has sent, Player is the {{cPlayer}} object representing the player who sent the command. If the callback returns true, the command is assumed to have executed successfully; in all other cases the server sends a warning to the player that the command is unknown (this is so that subcommands can be implemented)." },
- },
- BindConsoleCommand =
- {
- { Params = "Command, Callback, HelpString", Return = "[bool]", Notes = "(STATIC) Binds a console command with the specified callback function and help string. By common convention, providing an empty string for HelpString will hide the command from the \"help\" console command. Returns true if successful, logs to console and returns no value on error. The callback uses the following signature: <pre class=\"prettyprint lang-lua\">function(Split)</pre> The Split parameter contains an array-table of the words that the admin has typed. If the callback returns true, the command is assumed to have executed successfully; in all other cases the server issues a warning to the console that the command is unknown (this is so that subcommands can be implemented)." },
- { Params = "Command, Callback, HelpString", Return = "[bool]", Notes = "Binds a console command with the specified callback function and help string. By common convention, providing an empty string for HelpString will hide the command from the \"help\" console command. Returns true if successful, logs to console and returns no value on error. The callback uses the following signature: <pre class=\"prettyprint lang-lua\">function(Split)</pre> The Split parameter contains an array-table of the words that the admin has typed. If the callback returns true, the command is assumed to have executed successfully; in all other cases the server issues a warning to the console that the command is unknown (this is so that subcommands can be implemented)." },
- },
- CallPlugin = { Params = "PluginName, FunctionName, [FunctionArgs...]", Return = "[FunctionRets]", Notes = "(STATIC) Calls the specified function in the specified plugin, passing all the given arguments to it. If it succeeds, it returns all the values returned by that function. If it fails, returns no value at all. Note that only strings, numbers, bools, nils and classes can be used for parameters and return values; tables and functions cannot be copied across plugins." },
- DisablePlugin = { Params = "PluginName", Return = "bool", Notes = "Disables a plugin specified by its name. Returns true if the plugin was disabled, false if it wasn't found or wasn't active." },
- ExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "{{cPluginManager#CommandResult|CommandResult}}", Notes = "Executes the command as if given by the specified Player. Checks permissions." },
- FindPlugins = { Params = "", Return = "", Notes = "Refreshes the list of plugins to include all folders inside the Plugins folder (potentially new disabled plugins)" },
- ForceExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "{{cPluginManager#CommandResult|CommandResult}}", Notes = "Same as ExecuteCommand, but doesn't check permissions" },
- ForEachCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindCommand(). The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function(Command, Permission, HelpString)</pre>. If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." },
- ForEachConsoleCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindConsoleCommand(). The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function (Command, HelpString)</pre>. If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." },
- Get = { Params = "", Return = "cPluginManager", Notes = "(STATIC) Returns the single instance of the plugin manager" },
- GetAllPlugins = { Params = "", Return = "table", Notes = "Returns a table (dictionary) of all plugins, [name => value], where value is a valid {{cPlugin}} if the plugin is loaded, or the bool value false if the plugin is not loaded." },
- GetCommandPermission = { Params = "Command", Return = "Permission", Notes = "Returns the permission needed for executing the specified command" },
- GetCurrentPlugin = { Params = "", Return = "{{cPlugin}}", Notes = "Returns the {{cPlugin}} object for the calling plugin. This is the same object that the Initialize function receives as the argument." },
- GetNumPlugins = { Params = "", Return = "number", Notes = "Returns the number of plugins, including the disabled ones" },
- GetPlugin = { Params = "PluginName", Return = "{{cPlugin}}", Notes = "(<b>DEPRECATED, UNSAFE</b>) Returns a plugin handle of the specified plugin, or nil if such plugin is not loaded. Note thatdue to multithreading the handle is not guaranteed to be safe for use when stored - a single-plugin reload may have been triggered in the mean time for the requested plugin." },
- GetPluginsPath = { Params = "", Return = "string", Notes = "Returns the path where the individual plugin folders are located. Doesn't include the path separator at the end of the returned string." },
- IsCommandBound = { Params = "Command", Return = "bool", Notes = "Returns true if in-game Command is already bound (by any plugin)" },
- IsConsoleCommandBound = { Params = "Command", Return = "bool", Notes = "Returns true if console Command is already bound (by any plugin)" },
- LoadPlugin = { Params = "PluginFolder", Return = "", Notes = "(<b>DEPRECATED</b>) Loads a plugin from the specified folder. NOTE: Loading plugins may be an unsafe operation and may result in a deadlock or a crash. This API is deprecated and might be removed." },
- LogStackTrace = { Params = "", Return = "", Notes = "(STATIC) Logs a current stack trace of the Lua engine to the server console log. Same format as is used when the plugin fails." },
- ReloadPlugins = { Params = "", Return = "", Notes = "Reloads all active plugins" },
- },
- ConstantGroups=
- {
- CommandResult =
- {
- Include = "^cr.*",
- TextBefore = [[
- Results that the (Force)ExecuteCommand return. This gives information if the command is executed or not and the reason.
- ]],
- },
- },
- Constants =
- {
- crBlocked = { Notes = "When a plugin stopped the command using the OnExecuteCommand hook" },
- crError = { Notes = "When the command handler for the given command results in an error" },
- crExecuted = { Notes = "When the command is successfully executed." },
- crNoPermission = { Notes = "When the player doesn't have permission to execute the given command." },
- crUnknownCommand = { Notes = "When the given command doesn't exist." },
- HOOK_BLOCK_SPREAD = { Notes = "Called when a block spreads based on world conditions" },
- HOOK_BLOCK_TO_PICKUPS = { Notes = "Called when a block has been dug and is being converted to pickups. The server has provided the default pickups and the plugins may modify them." },
- HOOK_CHAT = { Notes = "Called when a client sends a chat message that is not a command. The plugin may modify the chat message" },
- HOOK_CHUNK_AVAILABLE = { Notes = "Called when a chunk is loaded or generated and becomes available in the {{cWorld|world}}." },
- HOOK_CHUNK_GENERATED = { Notes = "Called after a chunk is generated. A plugin may do last modifications on the generated chunk before it is handed of to the {{cWorld|world}}." },
- HOOK_CHUNK_GENERATING = { Notes = "Called before a chunk is generated. A plugin may override some parts of the generation algorithm." },
- HOOK_CHUNK_UNLOADED = { Notes = "Called after a chunk has been unloaded from a {{cWorld|world}}." },
- HOOK_CHUNK_UNLOADING = { Notes = "Called before a chunk is unloaded from a {{cWorld|world}}. The chunk has already been saved." },
- HOOK_COLLECTING_PICKUP = { Notes = "Called when a player is about to collect a pickup." },
- HOOK_CRAFTING_NO_RECIPE = { Notes = "Called when a player has items in the crafting slots and the server cannot locate any recipe. Plugin may provide a recipe." },
- HOOK_DISCONNECT = { Notes = "Called after the player has disconnected." },
- HOOK_EXECUTE_COMMAND = { Notes = "Called when a client sends a chat message that is recognized as a command, before handing that command to the regular command handler. A plugin may stop the command from being handled. This hook is called even when the player doesn't have permissions for the command." },
- HOOK_EXPLODED = { Notes = "Called after an explosion has been processed in a {{cWorld|world}}." },
- HOOK_EXPLODING = { Notes = "Called before an explosion is processed in a {{cWorld|world}}. A plugin may alter the explosion parameters or cancel the explosion altogether." },
- HOOK_HANDSHAKE = { Notes = "Called when a Handshake packet is received from a client." },
- HOOK_HOPPER_PULLING_ITEM = { Notes = "Called when a hopper is pulling an item from the container above it." },
- HOOK_HOPPER_PUSHING_ITEM = { Notes = "Called when a hopper is pushing an item into the container it is aimed at." },
- HOOK_KILLING = { Notes = "Called when an entity has just been killed. A plugin may resurrect the entity by setting its health to above zero." },
- HOOK_LOGIN = { Notes = "Called when a Login packet is sent to the client, before the client is queued for authentication." },
- HOOK_MAX = { Notes = "The maximum TypeID of a hook. Used internally by MCS to check hook type for validity." },
- HOOK_NUM_HOOKS = { Notes = "Total number of hook types MCS supports. Used internally by MCS to check hook type for validity." },
- HOOK_PLAYER_ANIMATION = { Notes = "Called when a client send the Animation packet." },
- HOOK_PLAYER_BREAKING_BLOCK = { Notes = "Called when a player is about to break a block. A plugin may cancel the event." },
- HOOK_PLAYER_BROKEN_BLOCK = { Notes = "Called after a player has broken a block." },
- HOOK_PLAYER_EATING = { Notes = "Called when the player starts eating a held item. Plugins may abort the eating." },
- HOOK_PLAYER_FISHED = { Notes = "Called when the player reels the fishing rod back in, after the server decides the player's fishing reward." },
- HOOK_PLAYER_FISHING = { Notes = "Called when the player reels the fishing rod back in, plugins may alter the fishing reward." },
- HOOK_PLAYER_JOINED = { Notes = "Called when the player entity has been created. It has not yet been fully initialized." },
- HOOK_PLAYER_LEFT_CLICK = { Notes = "Called when the client sends the LeftClick packet." },
- HOOK_PLAYER_MOVING = { Notes = "Called when the player has moved and the movement is now being applied." },
- HOOK_PLAYER_PLACED_BLOCK = { Notes = "Called when the player has just placed a block" },
- HOOK_PLAYER_PLACING_BLOCK = { Notes = "Called when the player is about to place a block. A plugin may cancel the event." },
- HOOK_PLAYER_RIGHT_CLICK = { Notes = "Called when the client sends the RightClick packet." },
- HOOK_PLAYER_RIGHT_CLICKING_ENTITY = { Notes = "Called when the client sends the UseEntity packet." },
- HOOK_PLAYER_SHOOTING = { Notes = "Called when the player releases the mouse button to fire their bow." },
- HOOK_PLAYER_SPAWNED = { Notes = "Called after the player entity has been created. The entity is fully initialized and is spawning in the {{cWorld|world}}." },
- HOOK_PLAYER_TOSSING_ITEM = { Notes = "Called when the player is tossing the held item (keypress Q)" },
- HOOK_PLAYER_USED_BLOCK = { Notes = "Called after the player has right-clicked a block" },
- HOOK_PLAYER_USED_ITEM = { Notes = "Called after the player has right-clicked with a usable item in their hand." },
- HOOK_PLAYER_USING_BLOCK = { Notes = "Called when the player is about to use (right-click) a block" },
- HOOK_PLAYER_USING_ITEM = { Notes = "Called when the player is about to right-click with a usable item in their hand." },
- HOOK_POST_CRAFTING = { Notes = "Called after a valid recipe has been chosen for the current contents of the crafting grid. Plugins may modify the recipe." },
- HOOK_PRE_CRAFTING = { Notes = "Called before a recipe is searched for the current contents of the crafting grid. Plugins may provide a recipe and cancel the built-in search." },
- HOOK_SERVER_PING = { Notes = "Called when a client pings the server from the server list. Plugins may change the favicon, server description, players online and maximum players values." },
- HOOK_SPAWNED_ENTITY = { Notes = "Called after an entity is spawned in a {{cWorld|world}}. The entity is already part of the world." },
- HOOK_SPAWNED_MONSTER = { Notes = "Called after a mob is spawned in a {{cWorld|world}}. The mob is already part of the world." },
- HOOK_SPAWNING_ENTITY = { Notes = "Called just before an entity is spawned in a {{cWorld|world}}." },
- HOOK_SPAWNING_MONSTER = { Notes = "Called just before a mob is spawned in a {{cWorld|world}}." },
- HOOK_TAKE_DAMAGE = { Notes = "Called when an entity is taking any kind of damage. Plugins may modify the damage value, effects, source or cancel the damage." },
- HOOK_TICK = { Notes = "Called when the main server thread ticks - 20 times a second." },
- HOOK_UPDATED_SIGN = { Notes = "Called after a {{cSignEntity|sign}} text has been updated, either by a player or by any external means." },
- HOOK_UPDATING_SIGN = { Notes = "Called before a {{cSignEntity|sign}} text is updated, either by a player or by any external means." },
- HOOK_WEATHER_CHANGED = { Notes = "Called after the weather has changed." },
- HOOK_WEATHER_CHANGING = { Notes = "Called just before the weather changes" },
- HOOK_WORLD_TICK = { Notes = "Called in each world's tick thread when the game logic is about to tick (20 times a second)." },
- },
- }, -- cPluginManager
-
cRankManager =
{
Desc = [[
@@ -2160,9 +2046,9 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
BroadcastChatLeave = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtLeave. Use for players leaving the server." },
BroadcastChatSuccess = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtSuccess. Use for success messages." },
BroadcastChatWarning = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtWarning. Use for concerning events, such as plugin reload etc." },
- CreateAndInitializeWorld = { Params = "WorldName", Return = "{{cWorld|cWorld}}", Notes = "Creates a new world and initializes it. If there is a world whith the same name it returns nil.<br/><br/><b>NOTE</b>This function is currently unsafe, do not use!" },
- FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "", Notes = "Calls the given callback function for all players with names partially (or fully) matching the name string provided." },
- DoWithPlayerByUUID = { Params = "PlayerUUID, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is the player with the uuid, calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found." },
+ CreateAndInitializeWorld = { Params = "WorldName", Return = "{{cWorld|cWorld}}", Notes = "Creates a new world and initializes it. If there is a world whith the same name it returns nil.<br><br><b>NOTE</b>This function is currently unsafe, do not use!" },
+ FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "bool", Notes = "Calls the given callback function for the player with the name best matching the name string provided.<br>This function is case-insensitive and will match partial names.<br>Returns false if player not found or there is ambiguity, true otherwise. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}})</pre>" },
+ DoWithPlayerByUUID = { Params = "PlayerUUID, CallbackFunction", Return = "bool", Notes = "If there is the player with the uuid, calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}})</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found." },
ForEachPlayer = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each player. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|cPlayer}})</pre>" },
ForEachWorld = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each world. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cWorld|cWorld}})</pre>" },
Get = { Params = "", Return = "Root object", Notes = "(STATIC)This function returns the cRoot object." },
@@ -2175,6 +2061,7 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
GetPrimaryServerVersion = { Params = "", Return = "number", Notes = "Returns the servers primary server version." },
GetProtocolVersionTextFromInt = { Params = "Protocol Version", Return = "string", Notes = "Returns the Minecraft version from the given Protocol. If there is no version found, it returns 'Unknown protocol(Parameter)'" },
GetServer = { Params = "", Return = "{{cServer|cServer}}", Notes = "Returns the cServer object." },
+ GetServerUpTime = { Params = "", Return = "number", Notes = "Returns the uptime of the server in seconds." },
GetTotalChunkCount = { Params = "", Return = "number", Notes = "Returns the amount of loaded chunks." },
GetVirtualRAMUsage = { Params = "", Return = "number", Notes = "Returns the amount of virtual RAM that the entire MCServer process is using, in KiB. Negative if the OS doesn't support this query." },
GetWebAdmin = { Params = "", Return = "{{cWebAdmin|cWebAdmin}}", Notes = "Returns the cWebAdmin object." },
@@ -2218,8 +2105,8 @@ end
Functions =
{
AddPlayerScore = { Params = "Name, Type, Value", Return = "", Notes = "Adds a value to all player scores of the specified objective type." },
- ForEachObjective = { Params = "CallBackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each objective in the scoreboard. Returns true if all objectives have been processed (including when there are zero objectives), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cObjective|Objective}}, [CallbackData])</pre> The callback should return false or no value to continue with the next objective, or true to abort the enumeration." },
- ForEachTeam = { Params = "CallBackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each team in the scoreboard. Returns true if all teams have been processed (including when there are zero teams), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cObjective|Objective}}, [CallbackData])</pre> The callback should return false or no value to continue with the next team, or true to abort the enumeration." },
+ ForEachObjective = { Params = "CallBackFunction", Return = "bool", Notes = "Calls the specified callback for each objective in the scoreboard. Returns true if all objectives have been processed (including when there are zero objectives), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cObjective|Objective}})</pre> The callback should return false or no value to continue with the next objective, or true to abort the enumeration." },
+ ForEachTeam = { Params = "CallBackFunction", Return = "bool", Notes = "Calls the specified callback for each team in the scoreboard. Returns true if all teams have been processed (including when there are zero teams), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cObjective|Objective}})</pre> The callback should return false or no value to continue with the next team, or true to abort the enumeration." },
GetNumObjectives = { Params = "", Return = "number", Notes = "Returns the nuber of registered objectives." },
GetNumTeams = { Params = "", Return = "number", Notes = "Returns the number of registered teams." },
GetObjective = { Params = "string", Return = "{{cObjective}}", Notes = "Returns the objective with the specified name." },
@@ -2260,7 +2147,7 @@ end
ShouldAuthenticate = { Params = "", Return = "bool", Notes = "Returns true iff the server is set to authenticate players (\"online mode\")." },
},
}, -- cServer
-
+
cStringCompression =
{
Desc = [[
@@ -2271,7 +2158,7 @@ end
local CompressedString = cStringCompression.CompressStringGZIP("DataToCompress")
</pre>
]],
-
+
Functions =
{
CompressStringGZIP = {Params = "string", Return = "string", Notes = "Compress a string using GZIP"},
@@ -2319,15 +2206,6 @@ local CompressedString = cStringCompression.CompressStringGZIP("DataToCompress")
},
Inherits = "cEntity",
},
-
- cWebAdmin =
- {
- Desc = "",
- Functions =
- {
- GetHTMLEscapedString = { Params = "string", Return = "string", Notes = "Gets the HTML escaped representation of a requested string. This is useful for user input and game data that is not guaranteed to be escaped already." },
- },
- }, -- cWebAdmin
cWebPlugin =
{
@@ -2435,33 +2313,33 @@ local CompressedString = cStringCompression.CompressStringGZIP("DataToCompress")
CreateProjectile = { Params = "X, Y, Z, {{cProjectileEntity|ProjectileKind}}, {{cEntity|Creator}}, {{cItem|Originating Item}}, [{{Vector3d|Speed}}]", Return = "", Notes = "Creates a new projectile of the specified kind at the specified coords. The projectile's creator is set to Creator (may be nil). The item that created the projectile entity, commonly the {{cPlayer|player}}'s currently equipped item, is used at present for fireworks to correctly set their entity metadata. It is not used for any other projectile. Optional speed indicates the initial speed for the projectile." },
DigBlock = { Params = "X, Y, Z", Return = "", Notes = "Replaces the specified block with air, without dropping the usual pickups for the block. Wakes up the simulators for the block and its neighbors." },
DoExplosionAt = { Params = "Force, X, Y, Z, CanCauseFire, Source, SourceData", Return = "", Notes = "Creates an explosion of the specified relative force in the specified position. If CanCauseFire is set, the explosion will set blocks on fire, too. The Source parameter specifies the source of the explosion, one of the esXXX constants. The SourceData parameter is specific to each source type, usually it provides more info about the source." },
- DoWithBlockEntityAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a block entity at the specified coords, calls the CallbackFunction with the {{cBlockEntity}} parameter representing the block entity. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}}, [CallbackData])</pre> The function returns false if there is no block entity, or if there is, it returns the bool value that the callback has returned. Use {{tolua}}.cast() to cast the Callback's BlockEntity parameter to the correct {{cBlockEntity}} descendant." },
- DoWithBeaconAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a beacon at the specified coords, calls the CallbackFunction with the {{cBeaconEntity}} parameter representing the beacon. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBeaconEntity|BeaconEntity}}, [CallbackData])</pre> The function returns false if there is no beacon, or if there is, it returns the bool value that the callback has returned." },
- DoWithChestAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a chest at the specified coords, calls the CallbackFunction with the {{cChestEntity}} parameter representing the chest. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cChestEntity|ChestEntity}}, [CallbackData])</pre> The function returns false if there is no chest, or if there is, it returns the bool value that the callback has returned." },
- DoWithCommandBlockAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a command block at the specified coords, calls the CallbackFunction with the {{cCommandBlockEntity}} parameter representing the command block. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cCommandBlockEntity|CommandBlockEntity}}, [CallbackData])</pre> The function returns false if there is no command block, or if there is, it returns the bool value that the callback has returned." },
- DoWithDispenserAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a dispenser at the specified coords, calls the CallbackFunction with the {{cDispenserEntity}} parameter representing the dispenser. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cDispenserEntity|DispenserEntity}}, [CallbackData])</pre> The function returns false if there is no dispenser, or if there is, it returns the bool value that the callback has returned." },
- DoWithDropSpenserAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a dropper or a dispenser at the specified coords, calls the CallbackFunction with the {{cDropSpenserEntity}} parameter representing the dropper or dispenser. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cDropSpenserEntity|DropSpenserEntity}}, [CallbackData])</pre> Note that this can be used to access both dispensers and droppers in a similar way. The function returns false if there is neither dispenser nor dropper, or if there is, it returns the bool value that the callback has returned." },
- DoWithDropperAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a dropper at the specified coords, calls the CallbackFunction with the {{cDropperEntity}} parameter representing the dropper. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cDropperEntity|DropperEntity}}, [CallbackData])</pre> The function returns false if there is no dropper, or if there is, it returns the bool value that the callback has returned." },
- DoWithEntityByID = { Params = "EntityID, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If an entity with the specified ID exists, calls the callback with the {{cEntity}} parameter representing the entity. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}}, [CallbackData])</pre> The function returns false if the entity was not found, and it returns the same bool value that the callback has returned if the entity was found." },
- DoWithFlowerPotAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a flower pot at the specified coords, calls the CallbackFunction with the {{cFlowerPotEntity}} parameter representing the flower pot. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cFlowerPotEntity|FlowerPotEntity}}, [CallbackData])</pre> The function returns false if there is no flower pot, or if there is, it returns the bool value that the callback has returned." },
- DoWithFurnaceAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a furnace at the specified coords, calls the CallbackFunction with the {{cFurnaceEntity}} parameter representing the furnace. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cFurnaceEntity|FurnaceEntity}}, [CallbackData])</pre> The function returns false if there is no furnace, or if there is, it returns the bool value that the callback has returned." },
- DoWithMobHeadAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a mob head at the specified coords, calls the CallbackFunction with the {{cMobHeadEntity}} parameter representing the furnace. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cMobHeadEntity|MobHeadEntity}}, [CallbackData])</pre> The function returns false if there is no mob head, or if there is, it returns the bool value that the callback has returned." },
- DoWithNoteBlockAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a note block at the specified coords, calls the CallbackFunction with the {{cNoteEntity}} parameter representing the note block. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cNoteEntity|NoteEntity}}, [CallbackData])</pre> The function returns false if there is no note block, or if there is, it returns the bool value that the callback has returned." },
- DoWithPlayer = { Params = "PlayerName, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a player of the specified name (exact match), calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found." },
+ DoWithBlockEntityAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction", Return = "bool", Notes = "If there is a block entity at the specified coords, calls the CallbackFunction with the {{cBlockEntity}} parameter representing the block entity. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}})</pre> The function returns false if there is no block entity, or if there is, it returns the bool value that the callback has returned. Use {{tolua}}.cast() to cast the Callback's BlockEntity parameter to the correct {{cBlockEntity}} descendant." },
+ DoWithBeaconAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction", Return = "bool", Notes = "If there is a beacon at the specified coords, calls the CallbackFunction with the {{cBeaconEntity}} parameter representing the beacon. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBeaconEntity|BeaconEntity}})</pre> The function returns false if there is no beacon, or if there is, it returns the bool value that the callback has returned." },
+ DoWithChestAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction", Return = "bool", Notes = "If there is a chest at the specified coords, calls the CallbackFunction with the {{cChestEntity}} parameter representing the chest. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cChestEntity|ChestEntity}})</pre> The function returns false if there is no chest, or if there is, it returns the bool value that the callback has returned." },
+ DoWithCommandBlockAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction", Return = "bool", Notes = "If there is a command block at the specified coords, calls the CallbackFunction with the {{cCommandBlockEntity}} parameter representing the command block. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cCommandBlockEntity|CommandBlockEntity}})</pre> The function returns false if there is no command block, or if there is, it returns the bool value that the callback has returned." },
+ DoWithDispenserAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction", Return = "bool", Notes = "If there is a dispenser at the specified coords, calls the CallbackFunction with the {{cDispenserEntity}} parameter representing the dispenser. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cDispenserEntity|DispenserEntity}})</pre> The function returns false if there is no dispenser, or if there is, it returns the bool value that the callback has returned." },
+ DoWithDropSpenserAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction", Return = "bool", Notes = "If there is a dropper or a dispenser at the specified coords, calls the CallbackFunction with the {{cDropSpenserEntity}} parameter representing the dropper or dispenser. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cDropSpenserEntity|DropSpenserEntity}})</pre> Note that this can be used to access both dispensers and droppers in a similar way. The function returns false if there is neither dispenser nor dropper, or if there is, it returns the bool value that the callback has returned." },
+ DoWithDropperAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction", Return = "bool", Notes = "If there is a dropper at the specified coords, calls the CallbackFunction with the {{cDropperEntity}} parameter representing the dropper. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cDropperEntity|DropperEntity}})</pre> The function returns false if there is no dropper, or if there is, it returns the bool value that the callback has returned." },
+ DoWithEntityByID = { Params = "EntityID, CallbackFunction", Return = "bool", Notes = "If an entity with the specified ID exists, calls the callback with the {{cEntity}} parameter representing the entity. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}})</pre> The function returns false if the entity was not found, and it returns the same bool value that the callback has returned if the entity was found." },
+ DoWithFlowerPotAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction", Return = "bool", Notes = "If there is a flower pot at the specified coords, calls the CallbackFunction with the {{cFlowerPotEntity}} parameter representing the flower pot. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cFlowerPotEntity|FlowerPotEntity}})</pre> The function returns false if there is no flower pot, or if there is, it returns the bool value that the callback has returned." },
+ DoWithFurnaceAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction", Return = "bool", Notes = "If there is a furnace at the specified coords, calls the CallbackFunction with the {{cFurnaceEntity}} parameter representing the furnace. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cFurnaceEntity|FurnaceEntity}})</pre> The function returns false if there is no furnace, or if there is, it returns the bool value that the callback has returned." },
+ DoWithMobHeadAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction", Return = "bool", Notes = "If there is a mob head at the specified coords, calls the CallbackFunction with the {{cMobHeadEntity}} parameter representing the furnace. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cMobHeadEntity|MobHeadEntity}})</pre> The function returns false if there is no mob head, or if there is, it returns the bool value that the callback has returned." },
+ DoWithNoteBlockAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction", Return = "bool", Notes = "If there is a note block at the specified coords, calls the CallbackFunction with the {{cNoteEntity}} parameter representing the note block. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cNoteEntity|NoteEntity}})</pre> The function returns false if there is no note block, or if there is, it returns the bool value that the callback has returned." },
+ DoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "bool", Notes = "If there is a player of the specified name (exact match), calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}})</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found." },
+ DoWithPlayerByUUID = { Params = "PlayerUUID, CallbackFunction", Return = "bool", Notes = "If there is the player with the uuid, calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}})</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found." },
FastSetBlock =
{
- { Params = "X, Y, Z, BlockType, BlockMeta", Return = "", Notes = "Sets the block at the specified coords, without waking up the simulators or replacing the block entities for the previous block type. Do not use if the block being replaced has a block entity tied to it!" },
+ { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta", Return = "", Notes = "Sets the block at the specified coords, without waking up the simulators or replacing the block entities for the previous block type. Do not use if the block being replaced has a block entity tied to it!" },
{ Params = "{{Vector3i|BlockCoords}}, BlockType, BlockMeta", Return = "", Notes = "Sets the block at the specified coords, without waking up the simulators or replacing the block entities for the previous block type. Do not use if the block being replaced has a block entity tied to it!" },
},
- FindAndDoWithPlayer = { Params = "PlayerNameHint, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a player of a name similar to the specified name (weighted-match), calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found. Note that the name matching is very loose, so it is a good idea to check the player name in the callback function." },
- DoWithPlayerByUUID = { Params = "PlayerUUID, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is the player with the uuid, calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found." },
- ForEachBlockEntityInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each block entity in the chunk. Returns true if all block entities in the chunk have been processed (including when there are zero block entities), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next block entity, or true to abort the enumeration. Use {{tolua}}.cast() to cast the Callback's BlockEntity parameter to the correct {{cBlockEntity}} descendant." },
- ForEachChestInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each chest in the chunk. Returns true if all chests in the chunk have been processed (including when there are zero chests), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cChestEntity|ChestEntity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next chest, or true to abort the enumeration." },
- ForEachEntity = { Params = "CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each entity in the loaded world. Returns true if all the entities have been processed (including when there are zero entities), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next entity, or true to abort the enumeration." },
+ FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "bool", Notes = "Calls the given callback function for the player with the name best matching the name string provided.<br>This function is case-insensitive and will match partial names.<br>Returns false if player not found or there is ambiguity, true otherwise. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}})</pre>" },
+ ForEachBlockEntityInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each block entity in the chunk. Returns true if all block entities in the chunk have been processed (including when there are zero block entities), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}})</pre> The callback should return false or no value to continue with the next block entity, or true to abort the enumeration. Use {{tolua}}.cast() to cast the Callback's BlockEntity parameter to the correct {{cBlockEntity}} descendant." },
+ ForEachChestInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each chest in the chunk. Returns true if all chests in the chunk have been processed (including when there are zero chests), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cChestEntity|ChestEntity}})</pre> The callback should return false or no value to continue with the next chest, or true to abort the enumeration." },
+ ForEachEntity = { Params = "CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each entity in the loaded world. Returns true if all the entities have been processed (including when there are zero entities), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}})</pre> The callback should return false or no value to continue with the next entity, or true to abort the enumeration." },
ForEachEntityInBox = { Params = "{{cBoundingBox|Box}}, CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each entity in the specified bounding box. Returns true if all the entities have been processed (including when there are zero entities), or false if the callback function has aborted the enumeration by returning true. If any chunk within the bounding box is not valid, it is silently skipped without any notification. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}})</pre> The callback should return false or no value to continue with the next entity, or true to abort the enumeration." },
- ForEachEntityInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each entity in the specified chunk. Returns true if all the entities have been processed (including when there are zero entities), or false if the chunk is not loaded or the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next entity, or true to abort the enumeration." },
- ForEachFurnaceInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each furnace in the chunk. Returns true if all furnaces in the chunk have been processed (including when there are zero furnaces), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cFurnaceEntity|FurnaceEntity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next furnace, or true to abort the enumeration." },
- ForEachPlayer = { Params = "CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each player in the loaded world. Returns true if all the players have been processed (including when there are zero players), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The callback should return false or no value to continue with the next player, or true to abort the enumeration." },
+ ForEachEntityInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each entity in the specified chunk. Returns true if all the entities have been processed (including when there are zero entities), or false if the chunk is not loaded or the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}})</pre> The callback should return false or no value to continue with the next entity, or true to abort the enumeration." },
+ ForEachFurnaceInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each furnace in the chunk. Returns true if all furnaces in the chunk have been processed (including when there are zero furnaces), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cFurnaceEntity|FurnaceEntity}})</pre> The callback should return false or no value to continue with the next furnace, or true to abort the enumeration." },
+ ForEachPlayer = { Params = "CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each player in the loaded world. Returns true if all the players have been processed (including when there are zero players), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}})</pre> The callback should return false or no value to continue with the next player, or true to abort the enumeration." },
GenerateChunk = { Params = "ChunkX, ChunkZ", Return = "", Notes = "Queues the specified chunk in the chunk generator. Ignored if the chunk is already generated (use RegenerateChunk() to force chunk re-generation)." },
GetBiomeAt = { Params = "BlockX, BlockZ", Return = "eBiome", Notes = "Returns the biome at the specified coords. Reads the biome from the chunk, if it is loaded, otherwise it uses the chunk generator to provide the biome value." },
GetBlock =
@@ -2484,7 +2362,7 @@ local CompressedString = cStringCompression.CompressStringGZIP("DataToCompress")
GetGeneratorQueueLength = { Params = "", Return = "number", Notes = "Returns the number of chunks that are queued in the chunk generator." },
GetHeight = { Params = "BlockX, BlockZ", Return = "number", Notes = "Returns the maximum height of the particula block column in the world. If the chunk is not loaded, it waits for it to load / generate. <b>WARNING</b>: Do not use, Use TryGetHeight() instead for a non-waiting version, otherwise you run the risk of a deadlock!" },
GetIniFileName = { Params = "", Return = "string", Notes = "Returns the name of the world.ini file that the world uses to store the information." },
- GetLightingQueueLength = { Params = "", Return = "number", Notes = "Returns the number of chunks in the lighting thread's queue." },
+ GetLightingQueueLength = { Params = "", Return = "number", Notes = "Returns the number of chunks in the lighting thread's queue." },
GetLinkedEndWorldName = { Params = "", Return = "string", Notes = "Returns the name of the end world this world is linked to." },
GetLinkedNetherWorldName = { Params = "", Return = "string", Notes = "Returns the name of the Netherworld linked to this world." },
GetLinkedOverworldName = { Params = "", Return = "string", Notes = "Returns the name of the world this world is linked to." },
@@ -2637,7 +2515,7 @@ World:ForEachEntity(
if not(a_Entity:IsMob()) then
return;
end
-
+
-- Get the cMonster out of cEntity, now that we know the entity represents one.
local Monster = tolua.cast(a_Entity, "cMonster");
if (Monster:GetMobType() == mtSpider) then
@@ -2651,39 +2529,6 @@ World:ForEachEntity(
}, -- AdditionalInfo
}, -- cWorld
- HTTPFormData =
- {
- Desc = "This class stores data for one form element for a {{HTTPRequest|HTTP request}}.",
- Variables =
- {
- Name = { Type = "string", Notes = "Name of the form element" },
- Type = { Type = "string", Notes = "Type of the data (usually empty)" },
- Value = { Type = "string", Notes = "Value of the form element. Contains the raw data as sent by the browser." },
- },
- }, -- HTTPFormData
-
- HTTPRequest =
- {
- Desc = [[
- This class encapsulates all the data that is sent to the WebAdmin through one HTTP request. Plugins
- receive this class as a parameter to the function handling the web requests, as registered in the
- FIXME: {{cPluginLua}}:AddWebPage().
- ]],
- Constants =
- {
- FormData = { Notes = "Array-table of {{HTTPFormData}}, contains the values of individual form elements submitted by the client" },
- Params = { Notes = "Map-table of parameters given to the request in the URL (?param=value); if a form uses GET method, this is the same as FormData. For each parameter given as \"param=value\", there is an entry in the table with \"param\" as its key and \"value\" as its value." },
- PostParams = { Notes = "Map-table of data posted through a FORM - either a GET or POST method. Logically the same as FormData, but in a map-table format (for each parameter given as \"param=value\", there is an entry in the table with \"param\" as its key and \"value\" as its value)." },
- },
-
- Variables =
- {
- Method = { Type = "string", Notes = "The HTTP method used to make the request. Usually GET or POST." },
- Path = { Type = "string", Notes = "The Path part of the URL (excluding the parameters)" },
- Username = { Type = "string", Notes = "Name of the logged-in user." },
- },
- }, -- HTTPRequest
-
ItemCategory =
{
Desc = [[
@@ -2721,7 +2566,7 @@ end
}
},
}, -- ItemCategory
-
+
lxp =
{
Desc = [[
@@ -2791,7 +2636,7 @@ local Callbacks = {
CharacterData = function(a_Parser, a_String)
LOG(string.rep(" ", Depth) .. "* " .. a_String);
end
-
+
EndElement = function(a_Parser, a_ElementName)
Depth = Depth - 1;
LOG(string.rep(" ", Depth) .. "- " .. a_ElementName);
@@ -2827,12 +2672,12 @@ Parser:close();
},
}, -- AdditionalInfo
}, -- lxp
-
+
sqlite3 =
{
Desc = [[
]],
-
+
Functions =
{
complete = { Params = "string", Return = "bool", Notes = "Returns true if the string sql comprises one or more complete SQL statements and false otherwise." },
@@ -2856,7 +2701,7 @@ myDB:close()
version = { Return = "string", Notes = "Returns a string with SQLite version information, in the form 'x.y[.z]'." },
},
},
-
+
TakeDamageInfo =
{
Desc = [[
@@ -3150,7 +2995,7 @@ end
"WriteHtmlHook",
"WriteStats",
},
-
+
IgnoreConstants =
{
"cChestEntity.__cBlockEntityWindowOwner__",
@@ -3159,12 +3004,12 @@ end
"cHopperEntity.__cBlockEntityWindowOwner__",
"cLuaWindow.__cItemGrid__cListener__",
},
-
+
IgnoreVariables =
{
"__.*__", -- tolua exports multiple inheritance this way
} ,
-
+
ExtraPages =
{
-- No sorting is provided for these, they will be output in the same order as defined here
@@ -3176,7 +3021,3 @@ end
{ FileName = "WebWorldThreads.html", Title = "Webserver vs World threads" },
}
} ;
-
-
-
-
diff --git a/MCServer/Plugins/APIDump/Classes/Plugins.lua b/MCServer/Plugins/APIDump/Classes/Plugins.lua
new file mode 100644
index 000000000..87f864950
--- /dev/null
+++ b/MCServer/Plugins/APIDump/Classes/Plugins.lua
@@ -0,0 +1,207 @@
+return
+{
+ cPlugin =
+ {
+ Desc = [[cPlugin describes a Lua plugin. This page is dedicated to new-style plugins and contain their functions. Each plugin has its own Plugin object.
+]],
+ Functions =
+ {
+ GetDirectory = { Return = "string", Notes = "<b>OBSOLETE</b>, use GetFolderName() instead!" },
+ GetFolderName = { Params = "", Return = "string", Notes = "Returns the name of the folder where the plugin's files are. (APIDump)" },
+ GetLoadError = { Params = "", Return = "string", Notes = "If the plugin failed to load, returns the error message for the failure." },
+ GetLocalDirectory = { Notes = "<b>OBSOLETE</b>, use GetLocalFolder instead." },
+ GetLocalFolder = { Return = "string", Notes = "Returns the path where the plugin's files are. (Plugins/APIDump)" },
+ GetName = { Return = "string", Notes = "Returns the name of the plugin." },
+ GetStatus = { Params = "", Return = "{{cPluginManager#PluginStatus|PluginStatus}}", Notes = "Returns the status of the plugin (loaded, disabled, unloaded, error, not found)" },
+ GetVersion = { Return = "number", Notes = "Returns the version of the plugin." },
+ IsLoaded = { Params = "", Return = "", Notes = "" },
+ SetName = { Params = "string", Notes = "Sets the name of the Plugin." },
+ SetVersion = { Params = "number", Notes = "Sets the version of the plugin." },
+ },
+ }, -- cPlugin
+
+ cPluginLua =
+ {
+ Desc = "",
+ Functions =
+ {
+ AddWebTab = { Params = "", Return = "", Notes = "Adds a new webadmin tab" },
+ },
+ Inherits = "cPlugin",
+ }, -- cPluginLua
+
+ cPluginManager =
+ {
+ Desc = [[
+ This class is used for generic plugin-related functionality. The plugin manager has a list of all
+ plugins, can enable or disable plugins, manages hooks and in-game console commands.</p>
+ <p>
+ Plugins can be identified by either the PluginFolder or PluginName. Note that these two can differ,
+ refer to <a href="http://forum.mc-server.org/showthread.php?tid=1877">the forum</a> for detailed discussion.
+ <p>
+ There is one instance of cPluginManager in MCServer, to get it, call either
+ {{cRoot|cRoot}}:Get():GetPluginManager() or cPluginManager:Get() function.</p>
+ <p>
+ Note that some functions are "static", that means that they are called using a dot operator instead
+ of the colon operator. For example:
+<pre class="prettyprint lang-lua">
+cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
+</pre></p>
+ ]],
+ Functions =
+ {
+ AddHook =
+ {
+ { Params = "{{cPluginManager#Hooks|HookType}}, [HookFunction]", Return = "", Notes = "(STATIC) Informs the plugin manager that it should call the specified function when the specified hook event occurs. If a function is not specified, a default global function name is looked up, based on the hook type" },
+ { Params = "{{cPlugin|Plugin}}, {{cPluginManager#Hooks|HookType}}, [HookFunction]", Return = "", Notes = "(STATIC, <b>DEPRECATED</b>) Informs the plugin manager that it should call the specified function when the specified hook event occurs. If a function is not specified, a default function name is looked up, based on the hook type. NOTE: This format is deprecated and the server outputs a warning if it is used!" },
+ },
+ BindCommand =
+ {
+ { Params = "Command, Permission, Callback, HelpString", Return = "[bool]", Notes = "(STATIC) Binds an in-game command with the specified callback function, permission and help string. By common convention, providing an empty string for HelpString will hide the command from the /help display. Returns true if successful, logs to console and returns no value on error. The callback uses the following signature: <pre class=\"prettyprint lang-lua\">function(Split, {{cPlayer|Player}})</pre> The Split parameter contains an array-table of the words that the player has sent, Player is the {{cPlayer}} object representing the player who sent the command. If the callback returns true, the command is assumed to have executed successfully; in all other cases the server sends a warning to the player that the command is unknown (this is so that subcommands can be implemented)." },
+ { Params = "Command, Permission, Callback, HelpString", Return = "[bool]", Notes = "Binds an in-game command with the specified callback function, permission and help string. By common convention, providing an empty string for HelpString will hide the command from the /help display. Returns true if successful, logs to console and returns no value on error. The callback uses the following signature: <pre class=\"prettyprint lang-lua\">function(Split, {{cPlayer|Player}})</pre> The Split parameter contains an array-table of the words that the player has sent, Player is the {{cPlayer}} object representing the player who sent the command. If the callback returns true, the command is assumed to have executed successfully; in all other cases the server sends a warning to the player that the command is unknown (this is so that subcommands can be implemented)." },
+ },
+ BindConsoleCommand =
+ {
+ { Params = "Command, Callback, HelpString", Return = "[bool]", Notes = "(STATIC) Binds a console command with the specified callback function and help string. By common convention, providing an empty string for HelpString will hide the command from the \"help\" console command. Returns true if successful, logs to console and returns no value on error. The callback uses the following signature: <pre class=\"prettyprint lang-lua\">function(Split)</pre> The Split parameter contains an array-table of the words that the admin has typed. If the callback returns true, the command is assumed to have executed successfully; in all other cases the server issues a warning to the console that the command is unknown (this is so that subcommands can be implemented)." },
+ { Params = "Command, Callback, HelpString", Return = "[bool]", Notes = "Binds a console command with the specified callback function and help string. By common convention, providing an empty string for HelpString will hide the command from the \"help\" console command. Returns true if successful, logs to console and returns no value on error. The callback uses the following signature: <pre class=\"prettyprint lang-lua\">function(Split)</pre> The Split parameter contains an array-table of the words that the admin has typed. If the callback returns true, the command is assumed to have executed successfully; in all other cases the server issues a warning to the console that the command is unknown (this is so that subcommands can be implemented)." },
+ },
+ CallPlugin = { Params = "PluginName, FunctionName, [FunctionArgs...]", Return = "[FunctionRets]", Notes = "(STATIC) Calls the specified function in the specified plugin, passing all the given arguments to it. If it succeeds, it returns all the values returned by that function. If it fails, returns no value at all. Note that only strings, numbers, bools, nils and classes can be used for parameters and return values; tables and functions cannot be copied across plugins." },
+ DoWithPlugin = { Params = "PluginName, CallbackFn", Return = "bool", Notes = "(STATIC) Calls the CallbackFn for the specified plugin, if found. A plugin can be found even if it is currently unloaded, disabled or errored, the callback should check the plugin status. If the plugin is not found, this function returns false, otherwise it returns the bool value that the callback has returned. The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function ({{cPlugin|Plugin}})</pre>" },
+ ExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "{{cPluginManager#CommandResult|CommandResult}}", Notes = "Executes the command as if given by the specified Player. Checks permissions." },
+ ExecuteConsoleCommand = { Params = "CommandStr", Return = "bool, string", Notes = "Executes the console command as if given by the admin on the console. If the command is successfully executed, returns true and the text that would be output to the console normally. On error it returns false and an error message." },
+ FindPlugins = { Params = "", Return = "", Notes = "<b>OBSOLETE</b>, use RefreshPluginList() instead"},
+ ForceExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "{{cPluginManager#CommandResult|CommandResult}}", Notes = "Same as ExecuteCommand, but doesn't check permissions" },
+ ForEachCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindCommand(). The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function(Command, Permission, HelpString)</pre> If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." },
+ ForEachConsoleCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindConsoleCommand(). The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function (Command, HelpString)</pre> If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." },
+ ForEachPlugin = { Params = "CallbackFn", Return = "bool", Notes = "(STATIC) Calls the CallbackFn function for each plugin that is currently discovered by MCServer (including disabled, unloaded and errrored plugins). The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function ({{cPlugin|Plugin}})</pre> If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." },
+ Get = { Params = "", Return = "cPluginManager", Notes = "(STATIC) Returns the single instance of the plugin manager" },
+ GetAllPlugins = { Params = "", Return = "table", Notes = "Returns a table (dictionary) of all plugins, [name => value], where value is a valid {{cPlugin}} if the plugin is loaded, or the bool value false if the plugin is not loaded." },
+ GetCommandPermission = { Params = "Command", Return = "Permission", Notes = "Returns the permission needed for executing the specified command" },
+ GetCurrentPlugin = { Params = "", Return = "{{cPlugin}}", Notes = "Returns the {{cPlugin}} object for the calling plugin. This is the same object that the Initialize function receives as the argument." },
+ GetNumLoadedPlugins = { Params = "", Return = "number", Notes = "Returns the number of loaded plugins (psLoaded only)" },
+ GetNumPlugins = { Params = "", Return = "number", Notes = "Returns the number of plugins, including the disabled, errored, unloaded and not-found ones" },
+ GetPlugin = { Params = "PluginName", Return = "{{cPlugin}}", Notes = "(<b>DEPRECATED, UNSAFE</b>) Returns a plugin handle of the specified plugin, or nil if such plugin is not loaded. Note thatdue to multithreading the handle is not guaranteed to be safe for use when stored - a single-plugin reload may have been triggered in the mean time for the requested plugin." },
+ GetPluginsPath = { Params = "", Return = "string", Notes = "Returns the path where the individual plugin folders are located. Doesn't include the path separator at the end of the returned string." },
+ IsCommandBound = { Params = "Command", Return = "bool", Notes = "Returns true if in-game Command is already bound (by any plugin)" },
+ IsConsoleCommandBound = { Params = "Command", Return = "bool", Notes = "Returns true if console Command is already bound (by any plugin)" },
+ IsPluginLoaded = { Params = "PluginName", Return = "", Notes = "Returns true if the specified plugin is loaded." },
+ LoadPlugin = { Params = "PluginFolder", Return = "", Notes = "(<b>DEPRECATED</b>) Loads a plugin from the specified folder. NOTE: Loading plugins may be an unsafe operation and may result in a deadlock or a crash. This API is deprecated and might be removed." },
+ LogStackTrace = { Params = "", Return = "", Notes = "(STATIC) Logs a current stack trace of the Lua engine to the server console log. Same format as is used when the plugin fails." },
+ RefreshPluginList = { Params = "", Return = "", Notes = "Refreshes the list of plugins to include all folders inside the Plugins folder (potentially new disabled plugins)" },
+ ReloadPlugins = { Params = "", Return = "", Notes = "Reloads all active plugins" },
+ UnloadPlugin = { Params = "PluginName", Return = "", Notes = "Queues the specified plugin to be unloaded. To avoid deadlocks, the unloading happens in the main tick thread asynchronously." },
+ },
+ ConstantGroups=
+ {
+ CommandResult =
+ {
+ Include = "^cr.*",
+ TextBefore = [[
+ Results that the (Force)ExecuteCommand return. This gives information if the command is executed or not and the reason.
+ ]],
+ },
+ },
+ Constants =
+ {
+ crBlocked = { Notes = "When a plugin stopped the command using the OnExecuteCommand hook" },
+ crError = { Notes = "When the command handler for the given command results in an error" },
+ crExecuted = { Notes = "When the command is successfully executed." },
+ crNoPermission = { Notes = "When the player doesn't have permission to execute the given command." },
+ crUnknownCommand = { Notes = "When the given command doesn't exist." },
+ HOOK_BLOCK_SPREAD = { Notes = "Called when a block spreads based on world conditions" },
+ HOOK_BLOCK_TO_PICKUPS = { Notes = "Called when a block has been dug and is being converted to pickups. The server has provided the default pickups and the plugins may modify them." },
+ HOOK_CHAT = { Notes = "Called when a client sends a chat message that is not a command. The plugin may modify the chat message" },
+ HOOK_CHUNK_AVAILABLE = { Notes = "Called when a chunk is loaded or generated and becomes available in the {{cWorld|world}}." },
+ HOOK_CHUNK_GENERATED = { Notes = "Called after a chunk is generated. A plugin may do last modifications on the generated chunk before it is handed of to the {{cWorld|world}}." },
+ HOOK_CHUNK_GENERATING = { Notes = "Called before a chunk is generated. A plugin may override some parts of the generation algorithm." },
+ HOOK_CHUNK_UNLOADED = { Notes = "Called after a chunk has been unloaded from a {{cWorld|world}}." },
+ HOOK_CHUNK_UNLOADING = { Notes = "Called before a chunk is unloaded from a {{cWorld|world}}. The chunk has already been saved." },
+ HOOK_COLLECTING_PICKUP = { Notes = "Called when a player is about to collect a pickup." },
+ HOOK_CRAFTING_NO_RECIPE = { Notes = "Called when a player has items in the crafting slots and the server cannot locate any recipe. Plugin may provide a recipe." },
+ HOOK_DISCONNECT = { Notes = "Called after the player has disconnected." },
+ HOOK_ENTITY_ADD_EFFECT = { Notes = "Called when an effect is being added to an {{cEntity|entity}}. Plugin may refuse the effect." },
+ HOOK_ENTITY_TELEPORT = { Notes = "Called when an {{cEntity|entity}} is being teleported. Plugin may refuse the teleportation." },
+ HOOK_EXECUTE_COMMAND = { Notes = "Called when a client sends a chat message that is recognized as a command, before handing that command to the regular command handler. A plugin may stop the command from being handled. This hook is called even when the player doesn't have permissions for the command." },
+ HOOK_EXPLODED = { Notes = "Called after an explosion has been processed in a {{cWorld|world}}." },
+ HOOK_EXPLODING = { Notes = "Called before an explosion is processed in a {{cWorld|world}}. A plugin may alter the explosion parameters or cancel the explosion altogether." },
+ HOOK_HANDSHAKE = { Notes = "Called when a Handshake packet is received from a client." },
+ HOOK_HOPPER_PULLING_ITEM = { Notes = "Called when a hopper is pulling an item from the container above it." },
+ HOOK_HOPPER_PUSHING_ITEM = { Notes = "Called when a hopper is pushing an item into the container it is aimed at." },
+ HOOK_KILLING = { Notes = "Called when an entity has just been killed. A plugin may resurrect the entity by setting its health to above zero." },
+ HOOK_LOGIN = { Notes = "Called when a Login packet is sent to the client, before the client is queued for authentication." },
+ HOOK_PLAYER_ANIMATION = { Notes = "Called when a client send the Animation packet." },
+ HOOK_PLAYER_BREAKING_BLOCK = { Notes = "Called when a player is about to break a block. A plugin may cancel the event." },
+ HOOK_PLAYER_BROKEN_BLOCK = { Notes = "Called after a player has broken a block." },
+ HOOK_PLAYER_DESTROYED = { Notes = "Called when the {{cPlayer}} object is destroyed - a player has disconnected." },
+ HOOK_PLAYER_EATING = { Notes = "Called when the player starts eating a held item. Plugins may abort the eating." },
+ HOOK_PLAYER_FISHED = { Notes = "Called when the player reels the fishing rod back in, after the server decides the player's fishing reward." },
+ HOOK_PLAYER_FISHING = { Notes = "Called when the player reels the fishing rod back in, plugins may alter the fishing reward." },
+ HOOK_PLAYER_FOOD_LEVEL_CHANGE = { Notes = "Called when the player's food level is changing. Plugins may refuse the change." },
+ HOOK_PLAYER_JOINED = { Notes = "Called when the player entity has been created. It has not yet been fully initialized." },
+ HOOK_PLAYER_LEFT_CLICK = { Notes = "Called when the client sends the LeftClick packet." },
+ HOOK_PLAYER_MOVING = { Notes = "Called when the player has moved and the movement is now being applied." },
+ HOOK_PLAYER_PLACED_BLOCK = { Notes = "Called when the player has just placed a block" },
+ HOOK_PLAYER_PLACING_BLOCK = { Notes = "Called when the player is about to place a block. A plugin may cancel the event." },
+ HOOK_PLAYER_RIGHT_CLICK = { Notes = "Called when the client sends the RightClick packet." },
+ HOOK_PLAYER_RIGHT_CLICKING_ENTITY = { Notes = "Called when the client sends the UseEntity packet." },
+ HOOK_PLAYER_SHOOTING = { Notes = "Called when the player releases the mouse button to fire their bow." },
+ HOOK_PLAYER_SPAWNED = { Notes = "Called after the player entity has been created. The entity is fully initialized and is spawning in the {{cWorld|world}}." },
+ HOOK_PLAYER_TOSSING_ITEM = { Notes = "Called when the player is tossing the held item (keypress Q)" },
+ HOOK_PLAYER_USED_BLOCK = { Notes = "Called after the player has right-clicked a block" },
+ HOOK_PLAYER_USED_ITEM = { Notes = "Called after the player has right-clicked with a usable item in their hand." },
+ HOOK_PLAYER_USING_BLOCK = { Notes = "Called when the player is about to use (right-click) a block" },
+ HOOK_PLAYER_USING_ITEM = { Notes = "Called when the player is about to right-click with a usable item in their hand." },
+ HOOK_PLUGINS_LOADED = { Notes = "Called after all plugins have loaded." },
+ HOOK_PLUGIN_MESSAGE = { Notes = "Called when a PluginMessage packet is received from a client." },
+ HOOK_POST_CRAFTING = { Notes = "Called after a valid recipe has been chosen for the current contents of the crafting grid. Plugins may modify the recipe." },
+ HOOK_PRE_CRAFTING = { Notes = "Called before a recipe is searched for the current contents of the crafting grid. Plugins may provide a recipe and cancel the built-in search." },
+ HOOK_PROJECTILE_HIT_BLOCK = { Notes = "Called when a {{cProjectileEntity|projectile}} hits a block." },
+ HOOK_PROJECTILE_HIT_ENTITY = { Notes = "Called when a {{cProjectileEntity|projectile}} hits an {{cEntity|entity}}." },
+ HOOK_SERVER_PING = { Notes = "Called when a client pings the server from the server list. Plugins may change the favicon, server description, players online and maximum players values." },
+ HOOK_SPAWNED_ENTITY = { Notes = "Called after an entity is spawned in a {{cWorld|world}}. The entity is already part of the world." },
+ HOOK_SPAWNED_MONSTER = { Notes = "Called after a mob is spawned in a {{cWorld|world}}. The mob is already part of the world." },
+ HOOK_SPAWNING_ENTITY = { Notes = "Called just before an entity is spawned in a {{cWorld|world}}." },
+ HOOK_SPAWNING_MONSTER = { Notes = "Called just before a mob is spawned in a {{cWorld|world}}." },
+ HOOK_TAKE_DAMAGE = { Notes = "Called when an entity is taking any kind of damage. Plugins may modify the damage value, effects, source or cancel the damage." },
+ HOOK_TICK = { Notes = "Called when the main server thread ticks - 20 times a second." },
+ HOOK_UPDATED_SIGN = { Notes = "Called after a {{cSignEntity|sign}} text has been updated, either by a player or by any external means." },
+ HOOK_UPDATING_SIGN = { Notes = "Called before a {{cSignEntity|sign}} text is updated, either by a player or by any external means." },
+ HOOK_WEATHER_CHANGED = { Notes = "Called after the weather has changed." },
+ HOOK_WEATHER_CHANGING = { Notes = "Called just before the weather changes" },
+ HOOK_WORLD_STARTED = { Notes = "Called when a world has been started." },
+ HOOK_WORLD_TICK = { Notes = "Called in each world's tick thread when the game logic is about to tick (20 times a second)." },
+
+ psDisabled = { Notes = "The plugin is not enabled in settings.ini" },
+ psError = { Notes = "The plugin is enabled in settings.ini, but it has run into an error while loading. Use {{cPlugin}}:GetLoadError() to identify the error." },
+ psLoaded = { Notes = "The plugin is enabled and loaded." },
+ psNotFound = { Notes = "The plugin has been loaded, but is no longer present on disk." },
+ psUnloaded = { Notes = "The plugin is enabled in settings.ini, but it has been unloaded (by a command)." },
+ }, -- constants
+
+ ConstantGroups =
+ {
+ Hooks =
+ {
+ Include = {"HOOK_.*"},
+ TextBefore = [[
+ These constants identify individual hooks. To register the plugin to receive notifications on hooks, use the
+ cPluginManager:AddHook() function. For detailed description of each hook, see the <a href='index.html#hooks'>
+ hooks reference</a>.]],
+ },
+ PluginStatus =
+ {
+ Include = {"ps.*"},
+ TextBefore = [[
+ These constants are used to report status of individual plugins. Use {{cPlugin}}:GetStatus() to query the
+ status of a plugin; use cPluginManager::ForEachPlugin() to iterate over plugins.]],
+ },
+ CommandResult =
+ {
+ Include = {"cr.*"},
+ TextBefore = [[
+ These constants are returned by the ExecuteCommand() function to identify the exact outcome of the
+ operation.]],
+ },
+ },
+ }, -- cPluginManager
+}
diff --git a/MCServer/Plugins/APIDump/Classes/WebAdmin.lua b/MCServer/Plugins/APIDump/Classes/WebAdmin.lua
new file mode 100644
index 000000000..808335aea
--- /dev/null
+++ b/MCServer/Plugins/APIDump/Classes/WebAdmin.lua
@@ -0,0 +1,51 @@
+return
+{
+ cWebAdmin =
+ {
+ Desc = "",
+ Functions =
+ {
+ GetHTMLEscapedString = { Params = "string", Return = "string", Notes = "(STATIC) Gets the HTML-escaped representation of a requested string. This is useful for user input and game data that is not guaranteed to be escaped already." },
+ },
+ }, -- cWebAdmin
+
+
+ HTTPFormData =
+ {
+ Desc = "This class stores data for one form element for a {{HTTPRequest|HTTP request}}.",
+ Variables =
+ {
+ Name = { Type = "string", Notes = "Name of the form element" },
+ Type = { Type = "string", Notes = "Type of the data (usually empty)" },
+ Value = { Type = "string", Notes = "Value of the form element. Contains the raw data as sent by the browser." },
+ },
+ }, -- HTTPFormData
+
+
+ HTTPRequest =
+ {
+ Desc = [[
+ This class encapsulates all the data that is sent to the WebAdmin through one HTTP request. Plugins
+ receive this class as a parameter to the function handling the web requests, as registered in the
+ {{cPluginLua}}:AddWebPage().
+ ]],
+ Constants =
+ {
+ FormData = { Notes = "Array-table of {{HTTPFormData}}, contains the values of individual form elements submitted by the client" },
+ Params = { Notes = "Map-table of parameters given to the request in the URL (?param=value); if a form uses GET method, this is the same as FormData. For each parameter given as \"param=value\", there is an entry in the table with \"param\" as its key and \"value\" as its value." },
+ PostParams = { Notes = "Map-table of data posted through a FORM - either a GET or POST method. Logically the same as FormData, but in a map-table format (for each parameter given as \"param=value\", there is an entry in the table with \"param\" as its key and \"value\" as its value)." },
+ },
+
+ Variables =
+ {
+ Method = { Type = "string", Notes = "The HTTP method used to make the request. Usually GET or POST." },
+ Path = { Type = "string", Notes = "The Path part of the URL (excluding the parameters)" },
+ URL = { Type = "string", Notes = "The entire URL used for the request." },
+ Username = { Type = "string", Notes = "Name of the logged-in user." },
+ },
+ }, -- HTTPRequest
+}
+
+
+
+
diff --git a/MCServer/Plugins/APIDump/Hooks/OnChat.lua b/MCServer/Plugins/APIDump/Hooks/OnChat.lua
index d98df008a..a15d09cc7 100644
--- a/MCServer/Plugins/APIDump/Hooks/OnChat.lua
+++ b/MCServer/Plugins/APIDump/Hooks/OnChat.lua
@@ -7,7 +7,8 @@ return
Desc = [[
A plugin may implement an OnChat() function and register it as a Hook to process chat messages from
the players. The function is then called for every in-game message sent from any player. Note that
- commands are handled separately using a command framework API.
+ registered in-game commands are not sent through this hook. Use the
+ {{OnExecuteCommand|HOOK_EXECUTE_COMMAND}} to intercept registered in-game commands.
]],
Params = {
{ Name = "Player", Type = "{{cPlayer}}", Notes = "The player who sent the message" },
diff --git a/MCServer/Plugins/APIDump/Hooks/OnEntityChangedWorld.lua b/MCServer/Plugins/APIDump/Hooks/OnEntityChangedWorld.lua
new file mode 100644
index 000000000..6675fdbe0
--- /dev/null
+++ b/MCServer/Plugins/APIDump/Hooks/OnEntityChangedWorld.lua
@@ -0,0 +1,28 @@
+return
+{
+ HOOK_ENTITY_CHANGED_WORLD =
+ {
+ CalledWhen = "After a entity has changed the world.",
+ DefaultFnName = "OnEntityChangedWorld", -- also used as pagename
+ Desc = [[
+ This hook is called after the server has moved the {{cEntity|entity}} to the given world. This is an information-only
+ callback, the entity is already in the new world.<p>
+ See also the {{OnEntityChangingWorld|HOOK_ENTITY_CHANGING_WORLD}} hook for a similar hook called before the
+ entity is moved to the new world.
+ ]],
+ Params =
+ {
+ { Name = "Entity", Type = "{{cEntity}}", Notes = "The entity that has changed the world" },
+ { Name = "World", Type = "{{cWorld}}", Notes = "The world from which the entity has come" },
+ },
+ Returns = [[
+ If the function returns false or no value, the next plugin's callback is called. If the function
+ returns true, no other callback is called for this event.
+ ]],
+ }, -- HOOK_ENTITY_CHANGED_WORLD
+}
+
+
+
+
+
diff --git a/MCServer/Plugins/APIDump/Hooks/OnEntityChangingWorld.lua b/MCServer/Plugins/APIDump/Hooks/OnEntityChangingWorld.lua
new file mode 100644
index 000000000..521f829c7
--- /dev/null
+++ b/MCServer/Plugins/APIDump/Hooks/OnEntityChangingWorld.lua
@@ -0,0 +1,29 @@
+return
+{
+ HOOK_ENTITY_CHANGING_WORLD =
+ {
+ CalledWhen = "Before a entity is changing the world.",
+ DefaultFnName = "OnEntityChangingWorld", -- also used as pagename
+ Desc = [[
+ This hook is called before the server moves the {{cEntity|entity}} to the given world. Plugins may
+ refuse the changing of the entity to the new world.<p>
+ See also the {{OnEntityChangedWorld|HOOK_ENTITY_CHANGED_WORLD}} hook for a similar hook is called after the
+ entity has been moved to the world.
+ ]],
+ Params =
+ {
+ { Name = "Entity", Type = "{{cEntity}}", Notes = "The entity that wants to change the world" },
+ { Name = "World", Type = "{{cWorld}}", Notes = "The world to which the entity wants to change" },
+ },
+ Returns = [[
+ If the function returns false or no value, the next plugin's callback is called. If the function
+ returns true, no other callback is called for this event and the change of the entity to the world is
+ cancelled.
+ ]],
+ }, -- HOOK_ENTITY_CHANGING_WORLD
+}
+
+
+
+
+
diff --git a/MCServer/Plugins/APIDump/Hooks/OnExecuteCommand.lua b/MCServer/Plugins/APIDump/Hooks/OnExecuteCommand.lua
index dadc4e94f..db7eb97d1 100644
--- a/MCServer/Plugins/APIDump/Hooks/OnExecuteCommand.lua
+++ b/MCServer/Plugins/APIDump/Hooks/OnExecuteCommand.lua
@@ -2,7 +2,10 @@ return
{
HOOK_EXECUTE_COMMAND =
{
- CalledWhen = "A player executes an in-game command, or the admin issues a console command. Note that built-in console commands are exempt to this hook - they are always performed and the hook is not called.",
+ CalledWhen = [[
+ A player executes an in-game command, or the admin issues a console command. Note that built-in
+ console commands are exempt to this hook - they are always performed and the hook is not called.
+ ]],
DefaultFnName = "OnExecuteCommand", -- also used as pagename
Desc = [[
A plugin may implement a callback for this hook to intercept both in-game commands executed by the
@@ -11,17 +14,24 @@ return
server.</p>
<p>
If the command is in-game, the first parameter to the hook function is the {{cPlayer|player}} who's
- executing the command. If the command comes from the server console, the first parameter is nil.
+ executing the command. If the command comes from the server console, the first parameter is nil.</p>
+ <p>
+ The server calls this hook even for unregistered (unknown) console commands. It also calls the hook
+ for unknown in-game commands, as long as they begin with a slash ('/'). If a plugin needs to intercept
+ in-game chat messages not beginning with a slash, it should use the {{OnChat|HOOK_CHAT}} hook.
]],
Params =
{
{ Name = "Player", Type = "{{cPlayer}}", Notes = "For in-game commands, the player who has sent the message. For console commands, nil" },
- { Name = "Command", Type = "table of strings", Notes = "The command and its parameters, broken into a table by spaces" },
+ { Name = "CommandSplit", Type = "array-table of strings", Notes = "The command and its parameters, broken into a table by spaces" },
+ { Name = "EntireCommand", Type = "string", Notes = "The entire command as a single string" },
},
Returns = [[
- If the plugin returns true, the command will be blocked and none of the remaining hook handlers will
- be called. If the plugin returns false, MCServer calls all the remaining hook handlers and finally
- the command will be executed.
+ If the plugin returns false, MCServer calls all the remaining hook handlers and finally the command
+ will be executed. If the plugin returns true, the none of the remaining hook handlers will be called.
+ In this case the plugin can return a second value, specifying whether what the command result should
+ be set to, one of the {{cPluginManager#CommandResult|CommandResult}} constants. If not
+ provided, the value defaults to crBlocked.
]],
}, -- HOOK_EXECUTE_COMMAND
}
diff --git a/MCServer/Plugins/APIDump/main_APIDump.lua b/MCServer/Plugins/APIDump/main_APIDump.lua
index 239bec69c..e841922b6 100644
--- a/MCServer/Plugins/APIDump/main_APIDump.lua
+++ b/MCServer/Plugins/APIDump/main_APIDump.lua
@@ -285,7 +285,7 @@ local function WriteHtmlHook(a_Hook, a_HookNav)
for _, param in ipairs(a_Hook.Params) do
f:write("<tr><td>", param.Name, "</td><td>", LinkifyString(param.Type, HookName), "</td><td>", LinkifyString(param.Notes, HookName), "</td></tr>\n");
end
- f:write("</table>\n<p>" .. (a_Hook.Returns or "") .. "</p>\n\n");
+ f:write("</table>\n<p>" .. LinkifyString(a_Hook.Returns or "", HookName) .. "</p>\n\n");
f:write([[<hr /><h1>Code examples</h1><h2>Registering the callback</h2>]]);
f:write("<pre class=\"prettyprint lang-lua\">\n");
f:write([[cPluginManager:AddHook(cPluginManager.]] .. a_Hook.Name .. ", My" .. a_Hook.DefaultFnName .. [[);]]);
@@ -971,7 +971,7 @@ end
--- Writes a list of undocumented objects into a file
local function ListUndocumentedObjects(API, UndocumentedHooks)
- f = io.open("API/_undocumented.lua", "w");
+ local f = io.open("API/_undocumented.lua", "w");
if (f ~= nil) then
f:write("\n-- This is the list of undocumented API objects, automatically generated by APIDump\n\n");
f:write("g_APIDesc =\n{\n\tClasses =\n\t{\n");
@@ -1643,6 +1643,15 @@ end
+local function HandleCmdApiShow(a_Split, a_EntireCmd)
+ os.execute("API" .. cFile:GetPathSeparator() .. "index.html")
+ return true, "Launching the browser to show the API docs..."
+end
+
+
+
+
+
function Initialize(Plugin)
g_Plugin = Plugin;
g_PluginFolder = Plugin:GetLocalFolder();
@@ -1651,6 +1660,7 @@ function Initialize(Plugin)
-- Bind a console command to dump the API:
cPluginManager:BindConsoleCommand("api", HandleCmdApi, "Dumps the Lua API docs into the API/ subfolder")
+ cPluginManager:BindConsoleCommand("apishow", HandleCmdApiShow, "Runs the default browser to show the API docs")
-- Add a WebAdmin tab that has a Dump button
g_Plugin:AddWebTab("APIDump", HandleWebAdminDump)
diff --git a/MCServer/Plugins/Core b/MCServer/Plugins/Core
-Subproject ee3cd9ba917baa94d6b9bfe7c9205609e0722fa
+Subproject 5171b43807ff699a6b239ad4969520730b3748a
diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua
index d0c362ab4..a49f8b5a6 100644
--- a/MCServer/Plugins/Debuggers/Debuggers.lua
+++ b/MCServer/Plugins/Debuggers/Debuggers.lua
@@ -9,7 +9,7 @@ g_ShowFoodStats = false; -- When true, each player's food stats are sent to the
-function Initialize(Plugin)
+function Initialize(a_Plugin)
--[[
-- Test multiple hook handlers:
cPluginManager.AddHook(cPluginManager.HOOK_TICK, OnTick1);
@@ -45,23 +45,27 @@ function Initialize(Plugin)
-- Bind all the console commands:
RegisterPluginInfoConsoleCommands();
- Plugin:AddWebTab("Debuggers", HandleRequest_Debuggers)
- Plugin:AddWebTab("StressTest", HandleRequest_StressTest)
+ a_Plugin:AddWebTab("Debuggers", HandleRequest_Debuggers)
+ a_Plugin:AddWebTab("StressTest", HandleRequest_StressTest)
-- Enable the following line for BlockArea / Generator interface testing:
-- PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHUNK_GENERATED);
- LOG("Initialized " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
-
-- TestBlockAreas()
-- TestSQLiteBindings()
-- TestExpatBindings()
- -- TestPluginCalls()
+ TestPluginCalls()
TestBlockAreasString()
TestStringBase64()
-- TestUUIDFromName()
-- TestRankMgr()
+ TestFileExt()
+ TestFileLastMod()
+ TestPluginInterface()
+
+ local LastSelfMod = cFile:GetLastModificationTime(a_Plugin:GetLocalFolder() .. "/Debuggers.lua")
+ LOG("Debuggers.lua last modified on " .. os.date("%Y-%m-%dT%H:%M:%S", LastSelfMod))
--[[
-- Test cCompositeChat usage in console-logging:
@@ -72,6 +76,18 @@ function Initialize(Plugin)
)
--]]
+ -- Test the crash in #1889:
+ cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY,
+ function (a_CBPlayer, a_CBEntity)
+ a_CBPlayer:GetWorld():DoWithEntityByID( -- This will crash the server in #1889
+ a_CBEntity:GetUniqueID(),
+ function(Entity)
+ LOG("RightClicking an entity, crash #1889 fixed. Entity is a " .. tolua.type(Entity))
+ end
+ )
+ end
+ )
+
return true
end;
@@ -79,6 +95,61 @@ end;
+function TestPluginInterface()
+ cPluginManager:DoWithPlugin("Core",
+ function (a_CBPlugin)
+ if (a_CBPlugin:GetStatus() == cPluginManager.psLoaded) then
+ LOG("Core plugin was found, version " .. a_CBPlugin:GetVersion())
+ else
+ LOG("Core plugin is not loaded")
+ end
+ end
+ )
+
+ cPluginManager:ForEachPlugin(
+ function (a_CBPlugin)
+ LOG("Plugin in " .. a_CBPlugin:GetFolderName() .. " has an API name of " .. a_CBPlugin:GetName() .. " and status " .. a_CBPlugin:GetStatus())
+ end
+ )
+end
+
+
+
+
+function TestFileExt()
+ assert(cFile:ChangeFileExt("fileless_dir/", "new") == "fileless_dir/")
+ assert(cFile:ChangeFileExt("fileless_dir/", ".new") == "fileless_dir/")
+ assert(cFile:ChangeFileExt("pathless_file.ext", "new") == "pathless_file.new")
+ assert(cFile:ChangeFileExt("pathless_file.ext", ".new") == "pathless_file.new")
+ assert(cFile:ChangeFileExt("path/to/file.ext", "new") == "path/to/file.new")
+ assert(cFile:ChangeFileExt("path/to/file.ext", ".new") == "path/to/file.new")
+ assert(cFile:ChangeFileExt("path/to.dir/file", "new") == "path/to.dir/file.new")
+ assert(cFile:ChangeFileExt("path/to.dir/file", ".new") == "path/to.dir/file.new")
+ assert(cFile:ChangeFileExt("path/to.dir/file.ext", "new") == "path/to.dir/file.new")
+ assert(cFile:ChangeFileExt("path/to.dir/file.ext", ".new") == "path/to.dir/file.new")
+ assert(cFile:ChangeFileExt("path/to.dir/file.longext", "new") == "path/to.dir/file.new")
+ assert(cFile:ChangeFileExt("path/to.dir/file.longext", ".new") == "path/to.dir/file.new")
+ assert(cFile:ChangeFileExt("path/to.dir/file.", "new") == "path/to.dir/file.new")
+ assert(cFile:ChangeFileExt("path/to.dir/file.", ".new") == "path/to.dir/file.new")
+end
+
+
+
+
+
+function TestFileLastMod()
+ local f = assert(io.open("test.txt", "w"))
+ f:write("test")
+ f:close()
+ local filetime = cFile:GetLastModificationTime("test.txt")
+ local ostime = os.time()
+ LOG("file time: " .. filetime .. ", OS time: " .. ostime .. ", difference: " .. ostime - filetime)
+end
+
+
+
+
+
function TestPluginCalls()
-- In order to test the inter-plugin communication, we're going to call Core's ReturnColorFromChar() function
-- It is a rather simple function that doesn't need any tables as its params and returns a value, too
@@ -86,26 +157,18 @@ function TestPluginCalls()
-- The Split parameter should be a table, but it is not used in that function anyway,
-- so we can get away with passing nil to it.
- -- Use the old, deprecated and unsafe method:
- local Core = cPluginManager:Get():GetPlugin("Core")
- if (Core ~= nil) then
- LOGINFO("Calling Core::ReturnColorFromChar() the old-fashioned way...")
- local Gray = Core:Call("ReturnColorFromChar", nil, "8")
- if (Gray ~= cChatColor.Gray) then
- LOGWARNING("Call failed, exp " .. cChatColor.Gray .. ", got " .. (Gray or "<nil>"))
- else
- LOGINFO("Call succeeded")
- end
- end
-
- -- Use the new method:
- LOGINFO("Calling Core::ReturnColorFromChar() the recommended way...")
- local Gray = cPluginManager:CallPlugin("Core", "ReturnColorFromChar", nil, "8")
+ LOG("Debuggers: Calling NoSuchPlugin.FnName()...")
+ cPluginManager:CallPlugin("NoSuchPlugin", "FnName", "SomeParam")
+ LOG("Debuggers: Calling Core.NoSuchFunction()...")
+ cPluginManager:CallPlugin("Core", "NoSuchFunction", "SomeParam")
+ LOG("Debuggers: Calling Core.ReturnColorFromChar(..., \"8\")...")
+ local Gray = cPluginManager:CallPlugin("Core", "ReturnColorFromChar", "split", "8")
if (Gray ~= cChatColor.Gray) then
- LOGWARNING("Call failed, exp " .. cChatColor.Gray .. ", got " .. (Gray or "<nil>"))
+ LOGWARNING("Debuggers: Call failed, exp " .. cChatColor.Gray .. ", got " .. (Gray or "<nil>"))
else
- LOGINFO("Call succeeded")
+ LOG("Debuggers: Call succeeded")
end
+ LOG("Debuggers: Inter-plugin calls done.")
end
diff --git a/MCServer/Plugins/Debuggers/Info.lua b/MCServer/Plugins/Debuggers/Info.lua
index 0370145df..2e170487b 100644
--- a/MCServer/Plugins/Debuggers/Info.lua
+++ b/MCServer/Plugins/Debuggers/Info.lua
@@ -28,7 +28,7 @@ g_PluginInfo =
Handler = HandleCompo,
HelpString = "Tests the cCompositeChat bindings"
},
- ["/cs"] =
+ ["/cstay"] =
{
Permission = "debuggers",
Handler = HandleChunkStay,
diff --git a/MCServer/Plugins/DumpInfo/Init.lua b/MCServer/Plugins/DumpInfo/Init.lua
index 5d9c752b0..1faa8d60f 100644
--- a/MCServer/Plugins/DumpInfo/Init.lua
+++ b/MCServer/Plugins/DumpInfo/Init.lua
@@ -28,17 +28,18 @@ function HandleDumpPluginRequest(a_Request)
Content = Content .. [[
<table>
-<th colspan="2">DumpInfo</th>]]
+ <tr>
+ <th colspan="2">DumpInfo</th>
+ </tr>]]
-- Loop through each plugin that is found.
for PluginName, k in pairs(cPluginManager:Get():GetAllPlugins()) do
-
-- Check if there is a file called 'Info.lua' or 'info.lua'
if (cFile:Exists("Plugins/" .. PluginName .. "/Info.lua")) then
- Content = Content .. "<tr>"
- Content = Content .. "<td>" .. PluginName .. "</td>"
- Content = Content .. "<td> <form method='POST'> <input type='hidden' value='" .. PluginName .. "' name='DumpInfo'> <input type='submit' value='DumpInfo'> </form>"
- Content = Content .. "</td>"
+ Content = Content .. "\n<tr>\n"
+ Content = Content .. "\t<td>" .. PluginName .. "</td>\n"
+ Content = Content .. "\t<td><form method='POST'> <input type='hidden' value='" .. PluginName .. "' name='DumpInfo'> <input type='submit' value='DumpInfo'></form></td>\n"
+ Content = Content .. "</tr>\n"
end
end
diff --git a/MCServer/Plugins/HookNotify/HookNotify.lua b/MCServer/Plugins/HookNotify/HookNotify.lua
index 1d3d5088e..411dbebbd 100644
--- a/MCServer/Plugins/HookNotify/HookNotify.lua
+++ b/MCServer/Plugins/HookNotify/HookNotify.lua
@@ -1,456 +1,64 @@
--- Global variables
-PLUGIN = {} -- Reference to own plugin object
+-- HookNotify.lua
+--[[
+Implements the entire plugin
+NOTE: This plugin is not meant for production servers. It is used mainly by developers to verify that things
+are working properly when implementing MCServer features. Do not enable this plugin on production servers!
+This plugin logs a notification for each hook that is being called by the server.
+The TICK and WORLD_TICK hooks are disabled because they produce too much output.
+--]]
-function Initialize(Plugin)
- PLUGIN = Plugin
-
- Plugin:SetName("HookNotify");
- Plugin:SetVersion(1);
-
- PluginManager = cPluginManager:Get();
- cPluginManager.AddHook(cPluginManager.HOOK_BLOCK_TO_PICKUPS, OnBlockToPickups);
- cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChat);
- cPluginManager.AddHook(cPluginManager.HOOK_CHUNK_AVAILABLE, OnChunkAvailable);
- cPluginManager.AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated);
- cPluginManager.AddHook(cPluginManager.HOOK_CHUNK_GENERATING, OnChunkGenerating);
- cPluginManager.AddHook(cPluginManager.HOOK_CHUNK_UNLOADED, OnChunkUnloaded);
- cPluginManager.AddHook(cPluginManager.HOOK_CHUNK_UNLOADING, OnChunkUnloading);
- cPluginManager.AddHook(cPluginManager.HOOK_COLLECTING_PICKUP, OnCollectingPickup);
- cPluginManager.AddHook(cPluginManager.HOOK_CRAFTING_NO_RECIPE, OnCraftingNoRecipe);
- cPluginManager.AddHook(cPluginManager.HOOK_DISCONNECT, OnDisconnect);
- cPluginManager.AddHook(cPluginManager.HOOK_ENTITY_TELEPORT, OnEntityTeleport);
- cPluginManager.AddHook(cPluginManager.HOOK_EXECUTE_COMMAND, OnExecuteCommand);
- cPluginManager.AddHook(cPluginManager.HOOK_HANDSHAKE, OnHandshake);
- cPluginManager.AddHook(cPluginManager.HOOK_KILLING, OnKilling);
- cPluginManager.AddHook(cPluginManager.HOOK_LOGIN, OnLogin);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_BREAKING_BLOCK, OnPlayerBreakingBlock);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_BROKEN_BLOCK, OnPlayerBrokenBlock);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_EATING, OnPlayerEating);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_JOINED, OnPlayerJoined);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_LEFT_CLICK, OnPlayerLeftClick);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_MOVING, OnPlayerMoving);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_PLACED_BLOCK, OnPlayerPlacedBlock);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_PLACING_BLOCK, OnPlayerPlacingBlock);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICK, OnPlayerRightClick);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_SHOOTING, OnPlayerShooting);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_SPAWNED, OnPlayerSpawned);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_TOSSING_ITEM, OnPlayerTossingItem);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_USED_BLOCK, OnPlayerUsedBlock);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_USED_ITEM, OnPlayerUsedItem);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_USING_BLOCK, OnPlayerUsingBlock);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_USING_ITEM, OnPlayerUsingItem);
- cPluginManager.AddHook(cPluginManager.HOOK_POST_CRAFTING, OnPostCrafting);
- cPluginManager.AddHook(cPluginManager.HOOK_PRE_CRAFTING, OnPreCrafting);
- cPluginManager.AddHook(cPluginManager.HOOK_SPAWNED_ENTITY, OnSpawnedEntity);
- cPluginManager.AddHook(cPluginManager.HOOK_SPAWNED_MONSTER, OnSpawnedMonster);
- cPluginManager.AddHook(cPluginManager.HOOK_SPAWNING_ENTITY, OnSpawningEntity);
- cPluginManager.AddHook(cPluginManager.HOOK_SPAWNING_MONSTER, OnSpawningMonster);
- cPluginManager.AddHook(cPluginManager.HOOK_TAKE_DAMAGE, OnTakeDamage);
- cPluginManager.AddHook(cPluginManager.HOOK_UPDATED_SIGN, OnUpdatedSign);
- cPluginManager.AddHook(cPluginManager.HOOK_UPDATING_SIGN, OnUpdatingSign);
- cPluginManager.AddHook(cPluginManager.HOOK_WEATHER_CHANGED, OnWeatherChanged);
- cPluginManager.AddHook(cPluginManager.HOOK_WEATHER_CHANGING, OnWeatherChanging);
-
- LOGINFO("HookNotify plugin is installed, beware, the log output may be quite large!");
- LOGINFO("You want this plugin enabled only when developing another plugin, not for regular gameplay.");
-
- return true
-end
-
-
-
-
-
-function LogHook(FnName, ...)
- LOG(FnName .. "(");
- for i, v in ipairs(arg) do
- local vt = tostring(v);
- local TypeString = type(v);
- if (type(v) == "userdata") then
- TypeString = tolua.type(v);
- end;
- LOG(" " .. tostring(i) .. ": " .. TypeString .. ": " .. tostring(v));
- end
- LOG(")");
-end
+function Initialize(a_Plugin)
+ -- Notify the admin that HookNotify is installed, this is not meant for production servers
+ LOGINFO("HookNotify plugin is installed, beware, the log output may be quite large!");
+ LOGINFO("You want this plugin enabled only when developing another plugin, not for regular gameplay.");
+ -- These hooks will not be notified:
+ local hooksToIgnore =
+ {
+ ["HOOK_TICK"] = true, -- Too much spam
+ ["HOOK_WORLD_TICK"] = true, -- Too much spam
+ ["HOOK_TAKE_DAMAGE"] = true, -- Has a separate handler with more info logged
+ ["HOOK_MAX"] = true, -- No such hook, placeholder only
+ ["HOOK_NUM_HOOKS"] = true, -- No such hook, placeholder only
+ }
+
+ -- Add all hooks:
+ for n, v in pairs(cPluginManager) do
+ if (n:match("HOOK_.*")) then
+ if not(hooksToIgnore[n]) then
+ LOG("Adding notification for hook " .. n .. " (" .. v .. ").")
+ cPluginManager.AddHook(v,
+ function (...)
+ LOG(n .. "(")
+ for i, param in ipairs(arg) do
+ LOG(" " .. i .. ": " .. tolua.type(param) .. ": " .. tostring(param))
+ end
+ LOG(")");
+ end -- hook handler
+ ) -- AddHook
+ end -- not (ignore)
+ end -- n matches "HOOK"
+ end -- for cPluginManager{}
+
+ -- OnTakeDamage has a special handler listing the details of the damage dealt:
+ cPluginManager.AddHook(cPluginManager.HOOK_TAKE_DAMAGE,
+ function (a_Receiver, a_TDI)
+ -- a_Receiver is cPawn
+ -- a_TDI is TakeDamageInfo
-function OnBlockToPickups(...)
- LogHook("OnBlockToPickups", unpack(arg));
- local World, Digger, BlockX, BlockY, BlockZ, BlockType, BlockMeta, Pickups = unpack(arg);
- if (Pickups ~= nil) then
- local Name = "NULL";
- if (Digger ~= nil) then
- Name = Digger:GetName()
+ LOG("OnTakeDamage(): " .. a_Receiver:GetClass() .. " was dealt RawDamage " .. a_TDI.RawDamage .. ", FinalDamage " .. a_TDI.FinalDamage .. " (that is, " .. (a_TDI.RawDamage - a_TDI.FinalDamage) .. " HPs covered by armor)");
end
- LOG("Got cItems from " .. Name .. ", trying to manipulate them.");
- Pickups:Add(cItem:new(E_ITEM_DIAMOND_SHOVEL, 1));
- LOG("Current size: " .. Pickups:Size());
- end;
-end;
-
-
-
-
-
-function OnChat(...)
- LogHook("OnChat", unpack(arg));
-end
-
-
-
-
-
-function OnChunkAvailable(...)
- LogHook("OnChunkAvailable", unpack(arg));
-end
-
-
-
-
-
-function OnChunkGenerated(...)
- LogHook("OnChunkGenerated", unpack(arg));
-end
-
-
-
-
-
-function OnChunkGenerating(...)
- LogHook("OnChunkGenerating", unpack(arg));
-end
-
-
-
-
-
-function OnChunkUnloaded(...)
- LogHook("OnChunkUnloaded", unpack(arg));
-end
-
-
-
-
-
-function OnChunkUnloading(...)
- LogHook("OnChunkUnloading", unpack(arg));
-end
-
-
-
-
-
-function OnPlayerUsingItem(...)
- LogHook("OnPlayerUsingItem", unpack(arg));
-end
-
-
-
-
-
-function OnCollectingPickup(...)
- LogHook("OnCollectingPickup", unpack(arg));
-end
-
-
-
-
-function OnCraftingNoRecipe(...)
- LogHook("OnCraftingNoRecipe", unpack(arg));
-end
-
-
-
-
-
-function OnDisconnect(...)
- LogHook("OnDisconnect", unpack(arg));
-end
-
-
-
-
-
-function OnEntityTeleport(arg1,arg2,arg3)
- if arg1.IsPlayer() then
- -- if it's a player, get his name
- LOG("OnEntityTeleport: Player: " .. arg1.GetName());
- else
- -- if it's a entity, get its type
- LOG("OnEntityTeleport: EntityType: " .. arg1.GetEntityType());
- end
- LOG("OldPos: " .. arg2.x .. " / " .. arg2.y .. " / " .. arg2.z);
- LOG("NewPos: " .. arg3.x .. " / " .. arg3.y .. " / " .. arg3.z);
-end
-
-
-
-
-
-function OnExecuteCommand(...)
- LogHook("OnExecuteCommand", unpack(arg));
+ )
- -- For some reason logging doesn't work for this callback, so list some stuff manually to verify:
- LOG("arg1 type: " .. type(arg[1]));
- if (arg[1] ~= nil) then
- LOG("Player name: " .. arg[1]:GetName());
- end
- LOG("Command: " .. arg[2][1]);
-end
-
-
-
-
-
-function OnHandshake(...)
- LogHook("OnHandshake", unpack(arg));
-end
-
-
-
-
-
-function OnKilling(...)
- LogHook("OnKilling", unpack(arg));
-end
-
-
-
-
-
-function OnLogin(...)
- LogHook("OnLogin", unpack(arg));
-end
-
-
-
-
-
-function OnPlayerBreakingBlock(...)
- LogHook("OnPlayerBreakingBlock", unpack(arg));
-end
-
-
-
-
-
-function OnPlayerBrokenBlock(...)
- LogHook("OnPlayerBrokenBlock", unpack(arg));
-end
-
-
-
-
-
-function OnPlayerEating(...)
- LogHook("OnPlayerEating", unpack(arg));
-end
-
-
-
-
-
-function OnPlayerJoined(...)
- LogHook("OnPlayerJoined", unpack(arg));
-end
-
-
-
-
-
-function OnPlayerLeftClick(...)
- LogHook("OnPlayerLeftClick", unpack(arg));
-end
-
-
-
-
-
-function OnPlayerMoving(...)
- LogHook("OnPlayerMoving", unpack(arg));
-end
-
-
-
-
-
-function OnPlayerPlacedBlock(...)
- LogHook("OnPlayerPlacedBlock", unpack(arg));
-end
-
-
-
-
-
-function OnPlayerPlacingBlock(...)
- LogHook("OnPlayerPlacingBlock", unpack(arg));
-end
-
-
-
-
-
-function OnPlayerRightClick(...)
- LogHook("OnPlayerRightClick", unpack(arg));
-end
-
-
-
-
-
-function OnPlayerShooting(...)
- LogHook("OnPlayerShooting", unpack(arg));
-end
-
-
-
-
-
-function OnPlayerSpawned(...)
- LogHook("OnPlayerSpawned", unpack(arg));
-end
-
-
-
-
-
-function OnPlayerTossingItem(...)
- LogHook("OnPlayerTossingItem", unpack(arg));
-end
-
-
-
-
-
-function OnPlayerUsedBlock(...)
- LogHook("OnPlayerUsedBlock", unpack(arg));
-end
-
-
-
-
-
-function OnPlayerUsedItem(...)
- LogHook("OnPlayerUsedItem", unpack(arg));
-end
-
-
-
-
-
-function OnPlayerUsingBlock(...)
- LogHook("OnPlayerUsingBlock", unpack(arg));
-end
-
-
-
-
-
-function OnPlayerUsingItem(...)
- LogHook("OnPlayerUsingItem", unpack(arg));
-end
-
-
-
-
-
-function OnPostCrafting(...)
- LogHook("OnPostCrafting", unpack(arg));
-end
-
-
-
-
-
-function OnPreCrafting(...)
- LogHook("OnPreCrafting", unpack(arg));
-end
-
-
-
-
-
-function OnSpawnedEntity(...)
- LogHook("OnSpawnedEntity", unpack(arg));
-end
-
-
-
-
-
-function OnSpawnedMonster(...)
- LogHook("OnSpawnedMonster", unpack(arg));
-end
-
-
-
-
-
-function OnSpawningEntity(...)
- LogHook("OnSpawningEntity", unpack(arg));
-end
-
-
-
-
-
-function OnSpawningMonster(...)
- LogHook("OnSpawningMonster", unpack(arg));
-end
-
-
-
-
-
-function OnUpdatedSign(...)
- LogHook("OnUpdatedSign", unpack(arg));
-end
-
-
-
-
-
-function OnUpdatingSign(...)
- LogHook("OnUpdatingSign", unpack(arg));
-end
-
-
-
-
-
-function OnWeatherChanged(...)
- LogHook("OnWeatherChanged", unpack(arg));
-end
-
-
-
-
-
-function OnWeatherChanging(...)
- LogHook("OnWeatherChanging", unpack(arg));
-end
-
-
-
-
-
-------------------------------------------------------------------
--- Special handling for OnTakeDamage to print the contents of TDI:
-
-function OnTakeDamage(Receiver, TDI)
- -- Receiver is cPawn
- -- TDI is TakeDamageInfo
-
- LOG("OnTakeDamage(): " .. Receiver:GetClass() .. " was dealt RawDamage " .. TDI.RawDamage .. ", FinalDamage " .. TDI.FinalDamage .. " (that is, " .. (TDI.RawDamage - TDI.FinalDamage) .. " HPs covered by armor)");
+ return true
end
diff --git a/MCServer/Plugins/InfoDump.lua b/MCServer/Plugins/InfoDump.lua
index 07a534b88..a715c9a18 100644
--- a/MCServer/Plugins/InfoDump.lua
+++ b/MCServer/Plugins/InfoDump.lua
@@ -18,6 +18,13 @@ only that one plugin's documentation. This mode of operation doesn't require Lua
+-- If this file is called using the loadfile function the arg variable isn't filled. We have to do it manualy then.
+local arg = arg or {...}
+
+
+
+
+
-- Check Lua version. We use 5.1-specific construct when loading the plugin info, 5.2 is not compatible!
if (_VERSION ~= "Lua 5.1") then
print("Unsupported Lua version. This script requires Lua version 5.1, this Lua is version " .. (_VERSION or "<nil>"))
@@ -58,8 +65,12 @@ local function ForumizeString(a_Str)
a_Str = a_Str:gsub("{%%p}", "\n\n")
a_Str = a_Str:gsub("{%%b}", "[b]"):gsub("{%%/b}", "[/b]")
a_Str = a_Str:gsub("{%%i}", "[i]"):gsub("{%%/i}", "[/i]")
- a_Str = a_Str:gsub("{%%list}", "[list]"):gsub("{%%/list}", "[/list]")
- a_Str = a_Str:gsub("{%%li}", "[*]"):gsub("{%%/li}", "")
+ a_Str = a_Str:gsub("{%%list}", "\n[list]"):gsub("{%%/list}", "[/list]")
+ a_Str = a_Str:gsub("{%%li}", "\n[*]"):gsub("{%%/li}", "\n")
+
+ -- Process links: {%a LinkDestination}LinkText{%/a}
+ a_Str = a_Str:gsub("{%%a%s([^}]*)}([^{]*){%%/a}", "[url=%1]%2[/url]")
+
-- TODO: Other formatting
return a_Str
@@ -99,8 +110,12 @@ local function GithubizeString(a_Str)
a_Str = a_Str:gsub("{%%p}", "\n\n")
a_Str = a_Str:gsub("{%%b}", "**"):gsub("{%%/b}", "**")
a_Str = a_Str:gsub("{%%i}", "*"):gsub("{%%/i}", "*")
- a_Str = a_Str:gsub("{%%list}", ""):gsub("{%%/list}", "")
- a_Str = a_Str:gsub("{%%li}", " - "):gsub("{%%/li}", "")
+ a_Str = a_Str:gsub("{%%list}", "\n"):gsub("{%%/list}", "\n")
+ a_Str = a_Str:gsub("{%%li}", "\n - "):gsub("{%%/li}", "")
+
+ -- Process links: {%a LinkDestination}LinkText{%/a}
+ a_Str = a_Str:gsub("{%%a%s([^}]*)}([^{]*){%%/a}", "[%2](%1)")
+
-- TODO: Other formatting
return a_Str
@@ -585,7 +600,10 @@ local function DumpPluginInfoForum(a_PluginFolder, a_PluginInfo)
DumpCommandsForum(a_PluginInfo, f)
DumpPermissionsForum(a_PluginInfo, f)
if (a_PluginInfo.SourceLocation ~= nil) then
- f:write("[b][color=blue]Source:[/color] [url=", a_PluginInfo.SourceLocation, "]Link[/url][/b]")
+ f:write("\n[b]Source[/b]: ", a_PluginInfo.SourceLocation, "\n")
+ end
+ if (a_PluginInfo.DownloadLocation ~= nil) then
+ f:write("[b]Download[/b]: ", a_PluginInfo.DownloadLocation)
end
f:close()
return true
diff --git a/MCServer/Plugins/InfoReg.lua b/MCServer/Plugins/InfoReg.lua
index e34b79564..cc075c0b8 100644
--- a/MCServer/Plugins/InfoReg.lua
+++ b/MCServer/Plugins/InfoReg.lua
@@ -43,21 +43,21 @@ end
--- This is a generic command callback used for handling multicommands' parent commands
-- For example, if there are "/gal save" and "/gal load" commands, this callback handles the "/gal" command
-- It is used for both console and in-game commands; the console version has a_Player set to nil
-local function MultiCommandHandler(a_Split, a_Player, a_CmdString, a_CmdInfo, a_Level)
- local Verb = a_Split[a_Level + 1];
+local function MultiCommandHandler(a_Split, a_Player, a_CmdString, a_CmdInfo, a_Level, a_EntireCommand)
+ local Verb = a_Split[a_Level + 1]
if (Verb == nil) then
-- No verb was specified. If there is a handler for the upper level command, call it:
if (a_CmdInfo.Handler ~= nil) then
- return a_CmdInfo.Handler(a_Split, a_Player);
+ return a_CmdInfo.Handler(a_Split, a_Player, a_EntireCommand)
end
-- Let the player know they need to give a subcommand:
assert(type(a_CmdInfo.Subcommands) == "table", "Info.lua error: There is no handler for command \"" .. a_CmdString .. "\" and there are no subcommands defined at level " .. a_Level)
- ListSubcommands(a_Player, a_CmdInfo.Subcommands, a_CmdString);
- return true;
+ ListSubcommands(a_Player, a_CmdInfo.Subcommands, a_CmdString)
+ return true
end
-- A verb was specified, look it up in the subcommands table:
- local Subcommand = a_CmdInfo.Subcommands[Verb];
+ local Subcommand = a_CmdInfo.Subcommands[Verb]
if (Subcommand == nil) then
if (a_Level > 1) then
-- This is a true subcommand, display the message and make MCS think the command was handled
@@ -67,7 +67,7 @@ local function MultiCommandHandler(a_Split, a_Player, a_CmdString, a_CmdInfo, a_
else
a_Player:SendMessage("The " .. a_CmdString .. " command doesn't support verb " .. Verb)
end
- return true;
+ return true
end
-- This is a top-level command, let MCS handle the unknown message
return false;
@@ -76,22 +76,22 @@ local function MultiCommandHandler(a_Split, a_Player, a_CmdString, a_CmdInfo, a_
-- Check the permission:
if (a_Player ~= nil) then
if not(a_Player:HasPermission(Subcommand.Permission or "")) then
- a_Player:SendMessage("You don't have permission to execute this command");
- return true;
+ a_Player:SendMessage("You don't have permission to execute this command")
+ return true
end
end
-- If the handler is not valid, check the next sublevel:
if (Subcommand.Handler == nil) then
if (Subcommand.Subcommands == nil) then
- LOG("Cannot find handler for command " .. a_CmdString .. " " .. Verb);
- return false;
+ LOG("Cannot find handler for command " .. a_CmdString .. " " .. Verb)
+ return false
end
- return MultiCommandHandler(a_Split, a_Player, a_CmdString .. " " .. Verb, Subcommand, a_Level + 1);
+ return MultiCommandHandler(a_Split, a_Player, a_CmdString .. " " .. Verb, Subcommand, a_Level + 1, a_EntireCommand)
end
-- Execute:
- return Subcommand.Handler(a_Split, a_Player);
+ return Subcommand.Handler(a_Split, a_Player, a_EntireCommand)
end
@@ -104,39 +104,39 @@ function RegisterPluginInfoCommands()
-- The a_Prefix param already contains the space after the previous command
-- a_Level is the depth of the subcommands being registered, with 1 being the top level command
local function RegisterSubcommands(a_Prefix, a_Subcommands, a_Level)
- assert(a_Subcommands ~= nil);
+ assert(a_Subcommands ~= nil)
-- A table that will hold aliases to subcommands temporarily, during subcommand iteration
local AliasTable = {}
-- Iterate through the subcommands, register them, and accumulate aliases:
for cmd, info in pairs(a_Subcommands) do
- local CmdName = a_Prefix .. cmd;
- local Handler = info.Handler;
+ local CmdName = a_Prefix .. cmd
+ local Handler = info.Handler
-- Provide a special handler for multicommands:
if (info.Subcommands ~= nil) then
- Handler = function(a_Split, a_Player)
- return MultiCommandHandler(a_Split, a_Player, CmdName, info, a_Level);
+ Handler = function(a_Split, a_Player, a_EntireCommand)
+ return MultiCommandHandler(a_Split, a_Player, CmdName, info, a_Level, a_EntireCommand)
end
end
if (Handler == nil) then
- LOGWARNING(g_PluginInfo.Name .. ": Invalid handler for command " .. CmdName .. ", command will not be registered.");
+ LOGWARNING(g_PluginInfo.Name .. ": Invalid handler for command " .. CmdName .. ", command will not be registered.")
else
- local HelpString;
+ local HelpString
if (info.HelpString ~= nil) then
- HelpString = " - " .. info.HelpString;
+ HelpString = " - " .. info.HelpString
else
- HelpString = "";
+ HelpString = ""
end
- cPluginManager.BindCommand(CmdName, info.Permission or "", Handler, HelpString);
+ cPluginManager.BindCommand(CmdName, info.Permission or "", Handler, HelpString)
-- Register all aliases for the command:
if (info.Alias ~= nil) then
if (type(info.Alias) == "string") then
- info.Alias = {info.Alias};
+ info.Alias = {info.Alias}
end
for idx, alias in ipairs(info.Alias) do
- cPluginManager.BindCommand(a_Prefix .. alias, info.Permission or "", Handler, HelpString);
+ cPluginManager.BindCommand(a_Prefix .. alias, info.Permission or "", Handler, HelpString)
-- Also copy the alias's info table as a separate subcommand,
-- so that MultiCommandHandler() handles it properly. Need to off-load into a separate table
-- than the one we're currently iterating and join after the iterating.
@@ -147,7 +147,7 @@ function RegisterPluginInfoCommands()
-- Recursively register any subcommands:
if (info.Subcommands ~= nil) then
- RegisterSubcommands(a_Prefix .. cmd .. " ", info.Subcommands, a_Level + 1);
+ RegisterSubcommands(a_Prefix .. cmd .. " ", info.Subcommands, a_Level + 1)
end
end -- for cmd, info - a_Subcommands[]
@@ -159,7 +159,7 @@ function RegisterPluginInfoCommands()
end
-- Loop through all commands in the plugin info, register each:
- RegisterSubcommands("", g_PluginInfo.Commands, 1);
+ RegisterSubcommands("", g_PluginInfo.Commands, 1)
end
@@ -171,26 +171,26 @@ function RegisterPluginInfoConsoleCommands()
-- A sub-function that registers all subcommands of a single command, using the command's Subcommands table
-- The a_Prefix param already contains the space after the previous command
local function RegisterSubcommands(a_Prefix, a_Subcommands, a_Level)
- assert(a_Subcommands ~= nil);
+ assert(a_Subcommands ~= nil)
for cmd, info in pairs(a_Subcommands) do
- local CmdName = a_Prefix .. cmd;
+ local CmdName = a_Prefix .. cmd
local Handler = info.Handler
if (Handler == nil) then
- Handler = function(a_Split)
- return MultiCommandHandler(a_Split, nil, CmdName, info, a_Level);
+ Handler = function(a_Split, a_EntireCommand)
+ return MultiCommandHandler(a_Split, nil, CmdName, info, a_Level, a_EntireCommand)
end
end
- cPluginManager.BindConsoleCommand(CmdName, Handler, info.HelpString or "");
+ cPluginManager.BindConsoleCommand(CmdName, Handler, info.HelpString or "")
-- Recursively register any subcommands:
if (info.Subcommands ~= nil) then
- RegisterSubcommands(a_Prefix .. cmd .. " ", info.Subcommands, a_Level + 1);
+ RegisterSubcommands(a_Prefix .. cmd .. " ", info.Subcommands, a_Level + 1)
end
end
end
-- Loop through all commands in the plugin info, register each:
- RegisterSubcommands("", g_PluginInfo.ConsoleCommands, 1);
+ RegisterSubcommands("", g_PluginInfo.ConsoleCommands, 1)
end
diff --git a/MCServer/README.txt b/MCServer/README.txt
index f2611fd04..3cddc37d2 100644
--- a/MCServer/README.txt
+++ b/MCServer/README.txt
@@ -16,5 +16,5 @@
| Mail: faketruth@gmail.com |
\============================/
-Compatible clients: 1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4
-Compatible protocol versions: 29, 39, 47, 49, 51, 60, 61, 73, 74, 77, 78
+Compatible clients: 1.7.x and 1.8.x
+Compatible protocol versions: 4, 5, 47
diff --git a/MCServer/crafting.txt b/MCServer/crafting.txt
index a24fdee0c..e7d11ab92 100644
--- a/MCServer/crafting.txt
+++ b/MCServer/crafting.txt
@@ -14,7 +14,10 @@
#
# <ItemType> can be either a number, or an item name (checked against items.ini)
#
-# ^<DamageValue> is optional, if not present, any damage value is matched for ingredients and zero is produced for the result
+# ^<DamageValue> is optional, if not present, the default damage for the given item is used
+#
+# If the DamageValue in the ingredients list is set to -1, the ingredient matches the specified item with any DamageValue.
+# This is used e. g. for "any planks -> sticks", or beds using any color wool etc.
#
# Ingredients with an asterisk for a coord will not match already matched crafting grid items. This enables simplifying some of the recipes,
# e. g. hoe: "Iron, 2:1, *:1"
@@ -46,10 +49,10 @@ BirchPlanks, 4 = BirchLog, *
JunglePlanks, 4 = JungleLog, *
AcaciaPlanks, 4 = AcaciaLog, *
DarkOakPlanks, 4 = DarkOakLog, *
-Stick, 4 = Planks, 2:2, 2:3
-Torch, 4 = Stick, 1:2 | Coal, 1:1
-Workbench = Planks, 1:1, 1:2, 2:1, 2:2
-Chest = Planks, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3
+Stick, 4 = Planks^-1, 2:2, 2:3
+Torch, 4 = Stick, 1:2 | Coal^-1, 1:1
+Workbench = Planks^-1, 1:1, 1:2, 2:1, 2:2
+Chest = Planks^-1, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3
TrappedChest = TripWireHook, 1:1 | Chest, 2:1
EnderChest = EyeOfEnder, 2:2 | Obsidian, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3
Furnace = Cobblestone, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3
@@ -79,15 +82,15 @@ HayBale = Wheat, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
SnowBlock = SnowBall, 1:1, 1:2, 2:1, 2:2
ClayBlock = Clay, 1:1, 1:2, 2:1, 2:2
BrickBlock = Brick, 1:1, 1:2, 2:1, 2:2
+PolishedGranite, 4 = Granite, 1:1, 1:2, 2:1, 2:2
+PolishedDiorite, 4 = Diorite, 1:1, 1:2, 2:1, 2:2
+PolishedAndesite, 4 = Andesite, 1:1, 1:2, 2:1, 2:2
StoneBrick, 4 = Stone, 1:1, 1:2, 2:1, 2:2
-BookShelf = Planks, 1:1, 2:1, 3:1, 1:3, 2:3, 3:3 | Book, 1:2, 2:2, 3:2
+BookShelf = Planks^-1, 1:1, 2:1, 3:1, 1:3, 2:3, 3:3 | Book, 1:2, 2:2, 3:2
Sandstone, 4 = Sand, 1:1, 1:2, 2:1, 2:2
SmoothSandstone, 4 = Sandstone, 1:1, 1:2, 2:1, 2:2
OrnamentSandstone = SandstoneSlab, 1:1, 1:2
JackOLantern = Pumpkin, 1:1 | Torch, 1:2
-PolishedGranite, 4 = Granite, 1:1, 1:2, 2:1, 2:2
-PolishedDiorite, 4 = Diorite, 1:1, 1:2, 2:1, 2:2
-PolishedAndesite, 4 = Andesite, 1:1, 1:2, 2:1, 2:2
CoarsedDirt, 4 = Dirt, 1:1, 2:2 | Gravel, 1:2, 2:1
CoarsedDirt, 4 = Gravel, 1:1, 2:2 | Dirt, 1:2, 2:1
SlimeBlock = Slimeball, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
@@ -154,8 +157,8 @@ RedSandstoneStairs, 4 = RedSandstone, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
#
# Axes:
-WoodenAxe = Stick, 2:2, 2:3 | Planks, 2:1, 1:1, 1:2
-WoodenAxe = Stick, 2:2, 2:3 | Planks, 2:1, 3:1, 3:2
+WoodenAxe = Stick, 2:2, 2:3 | Planks^-1, 2:1, 1:1, 1:2
+WoodenAxe = Stick, 2:2, 2:3 | Planks^-1, 2:1, 3:1, 3:2
StoneAxe = Stick, 2:2, 2:3 | Cobblestone, 2:1, 1:1, 1:2
StoneAxe = Stick, 2:2, 2:3 | Cobblestone, 2:1, 3:1, 3:2
GoldenAxe = Stick, 2:2, 2:3 | GoldIngot, 2:1, 1:1, 1:2
@@ -166,21 +169,21 @@ DiamondAxe = Stick, 2:2, 2:3 | Diamond, 2:1, 1:1, 1:2
DiamondAxe = Stick, 2:2, 2:3 | Diamond, 2:1, 3:1, 3:2
# Pickaxes:
-WoodenPickaxe = Stick, 2:2, 2:3 | Planks, 1:1, 2:1, 3:1
+WoodenPickaxe = Stick, 2:2, 2:3 | Planks^-1, 1:1, 2:1, 3:1
StonePickaxe = Stick, 2:2, 2:3 | Cobblestone, 1:1, 2:1, 3:1
GoldenPickaxe = Stick, 2:2, 2:3 | GoldIngot, 1:1, 2:1, 3:1
IronPickaxe = Stick, 2:2, 2:3 | IronIngot, 1:1, 2:1, 3:1
DiamondPickaxe = Stick, 2:2, 2:3 | Diamond, 1:1, 2:1, 3:1
# Shovels:
-WoodenShovel = Stick, 2:2, 2:3 | Planks, 2:1
+WoodenShovel = Stick, 2:2, 2:3 | Planks^-1, 2:1
StoneShovel = Stick, 2:2, 2:3 | Cobblestone, 2:1
GoldenShovel = Stick, 2:2, 2:3 | GoldIngot, 2:1
IronShovel = Stick, 2:2, 2:3 | IronIngot, 2:1
DiamondShovel = Stick, 2:2, 2:3 | Diamond, 2:1
# Hoes:
-WoodenHoe = Stick, 2:2, 2:3 | Planks, 2:1, *:1
+WoodenHoe = Stick, 2:2, 2:3 | Planks^-1, 2:1, *:1
StoneHoe = Stick, 2:2, 2:3 | Cobblestone, 2:1, *:1
GoldenHoe = Stick, 2:2, 2:3 | GoldIngot, 2:1, *:1
IronHoe = Stick, 2:2, 2:3 | IronIngot, 2:1, *:1
@@ -206,7 +209,7 @@ Lead = String, 1:1, 1:2, 2:1, 3:3 | Slimeball, 2:2
#******************************************************#
# Weapons
#
-WoodenSword = Stick, 2:3 | Planks, 2:1, 2:2
+WoodenSword = Stick, 2:3 | Planks^-1, 2:1, 2:2
StoneSword = Stick, 2:3 | Cobblestone, 2:1, 2:2
GoldenSword = Stick, 2:3 | GoldIngot, 2:1, 2:2
IronSword = Stick, 2:3 | IronIngot, 2:1, 2:2
@@ -268,7 +271,7 @@ hopperminecart = Minecart, * | Hopper, *
Rails, 16 = IronIngot, 1:1, 3:1, 1:2, 3:2, 1:3, 3:3 | Stick, 2:2
PoweredRail, 6 = GoldIngot, 1:1, 3:1, 1:2, 3:2, 1:3, 3:3 | Stick, 2:2 | RedstoneDust, 2:3
DetectorRail, 6 = IronIngot, 1:1, 3:1, 1:2, 3:2, 1:3, 3:3 | StonePlate, 2:2 | RedstoneDust, 2:3
-Boat = Planks, 1:1, 3:1, 1:2, 2:2, 3:2
+Boat = Planks^-1, 1:1, 3:1, 1:2, 2:2, 3:2
ActivatorRail, 6 = IronIngot, 1:1, 1:2, 1:3, 3:1, 3:2, 3:3 | Stick, 2:1, 2:3 | RedstoneTorchon, 2:2
@@ -277,35 +280,35 @@ ActivatorRail, 6 = IronIngot, 1:1, 1:2, 1:3, 3:1, 3:2, 3:3 | Stick, 2:1, 2:3 | R
#******************************************************#
# Mechanisms
#
-SpruceDoor, 3 = SprucePlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
-BirchDoor, 3 = BirchPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
-JungleDoor, 3 = JunglePlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
-AcaciaDoor, 3 = AcaciaPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
+SpruceDoor, 3 = SprucePlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
+BirchDoor, 3 = BirchPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
+JungleDoor, 3 = JunglePlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
+AcaciaDoor, 3 = AcaciaPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
DarkOakDoor, 3 = DarkOakPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
-WoodenDoor, 3 = OakPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
+WoodenDoor, 3 = OakPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
IronDoor, 3 = IronIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
-TrapDoor, 2 = Planks, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2
+TrapDoor, 2 = Planks^-1, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2
IronTrapDoor = IronIngot, 1:1, 1:2, 2:1, 2:2
-WoodPlate = Planks, 1:1, 2:1
-StonePlate = Stone, 1:1, 2:1
+WoodPlate = Planks^-1, 1:1, 2:1
+StonePlate = Stone, 1:1, 2:1
lightweightedpressureplate = IronIngot, 1:1, 2:1
heavyweightedpressureplate = GoldIngot, 1:1, 2:1
-StoneButton = Stone, 1:1
-WoodenButton = Planks, 1:1
+StoneButton = Stone, 1:1
+WoodenButton = Planks^-1, 1:1
RedstoneTorchOn = Stick, 1:2 | RedstoneDust, 1:1
Lever = Cobblestone, 1:2 | Stick, 1:1
-NoteBlock = Planks, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | RedstoneDust, 2:2
-Jukebox = Planks, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Diamond, 2:2
+NoteBlock = Planks^-1, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | RedstoneDust, 2:2
+Jukebox = Planks^-1, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Diamond, 2:2
Dispenser = Cobblestone, 1:1, 1:2, 1:3, 2:1, 3:1, 3:2, 3:3 | RedstoneDust, 2:3 | Bow, 2:2
Dropper = Cobblestone, 1:1, 2:1, 3:1, 1:2, 1:3, 3:2, 3:3 | Hopper, 2:2 | RedstoneDust, 2:3
Repeater = Stone, 1:2, 2:2, 3:2 | RedstoneTorchOn, 1:1, 3:1 | RedstoneDust, 2:1
Comparator = RedstoneTorchOn, 2:1, 1:2, 3:2 | NetherQuartz, 2:2 | Stone, 1:3, 2:3, 3:3
DaylightSensor = Glass, 1:1, 2:1, 3:1 | NetherQuartz, 1:2, 2:2, 3:2 | Woodslab, 1:3, 2:3, 3:3
-Hopper = Ironbars, 1:1, 3:1, 1:2, 3:2, 2:3 | Chest, 2:2
-Piston = Planks, 1:1, 2:1, 3:1 | RedstoneDust, 2:3 | Cobblestone, 1:2, 3:2, 1:3, 3:3 | IronIngot, 2:2
-StickyPiston = Piston, * | SlimeBall, *
+Hopper = IronIngot, 1:1, 3:1, 1:2, 3:2, 2:3 | Chest, 2:2
+Piston = Planks^-1, 1:1, 2:1, 3:1 | RedstoneDust, 2:3 | Cobblestone, 1:2, 3:2, 1:3, 3:3 | IronIngot, 2:2
+StickyPiston = Piston, * | SlimeBall, *
RedstoneLamp = RedstoneDust, 2:1, 1:2, 3:2, 2:3 | Glowstone, 2:2
-tripwirehook, 2 = planks, 2:3 | stick, 2:2 | ironbar, 2:1
+TripwireHook, 2 = Planks^-1, 2:3 | stick, 2:2 | IronIngot, 2:1
@@ -314,7 +317,7 @@ tripwirehook, 2 = planks, 2:3 | stick, 2:2 | ironbar, 2:1
#******************************************************#
# Food
#
-Bowl, 4 = Planks, 1:1, 2:2, 3:1
+Bowl, 4 = Planks^-1, 1:1, 2:2, 3:1
MushroomStew = Bowl, * | BrownMushroom, * | RedMushroom, *
Bread = Wheat, 1:1, 2:1, 3:1
Sugar = Sugarcane, *
@@ -349,9 +352,9 @@ Coal, 9 = CoalBlock, *
Clay, 4 = ClayBlock, *
SlimeBall, 9 = SlimeBlock, *
-Painting = Stick, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Wool, 2:2
+Painting = Stick, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Wool^-1, 2:2
ItemFrame = Stick, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Leather, 2:2
-Sign, 3 = Planks, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2 | Stick, 2:3
+Sign, 3 = Planks^-1, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2 | Stick, 2:3
Ladder, 3 = Stick, 1:1, 3:1, 1:2, 2:2, 3:2, 1:3, 3:3
GlassPane, 16 = Glass, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2
IronBars, 16 = IronIngot, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2
@@ -373,7 +376,7 @@ JungleFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | JunglePlanks, 2:1, 2:2
DarkOakFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | DarkOakPlanks, 2:1, 2:2
AcaciaFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | AcaciaPlanks, 2:1, 2:2
FenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | OakPlanks, 2:1, 2:2
-Bed = Planks, 1:2, 2:2, 3:2 | Wool, 1:1, 2:1, 3:1
+Bed = Planks^-1, 1:2, 2:2, 3:2 | Wool^-1, 1:1, 2:1, 3:1
GoldIngot = GoldNugget, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
EyeOfEnder = EnderPearl, * | BlazePowder, *
Beacon = Glass, 1:1, 1:2, 2:1, 3:1, 3:2 | Obsidian, 1:3, 2:3, 3:3 | NetherStar, 2:2
@@ -412,7 +415,7 @@ BlackBanner = Stick, 2:3 | BlackWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
WhiteDye, 3 = Bone, *
RedDye, 2 = Rose, *
-YellowDye, 2 = Flower, *
+YellowDye, 2 = Dandelion, *
# Color mixing, duals:
OrangeDye, 2 = YellowDye, * | RedDye, *
@@ -439,24 +442,23 @@ MagentaDye, 4 = BlueDye, * | WhiteDye, * | RedDye, *, *
#******************************************************#
# Colored wool:
#
-WhiteWool = Wool, * | BoneMeal, *
-OrangeWool = Wool, * | OrangeDye, *
-MagentaWool = Wool, * | MagentaDye, *
-LightBlueWool = Wool, * | LightBlueDye, *
-YellowWool = Wool, * | YellowDye, *
-LimeWool = Wool, * | LimeDye, *
-PinkWool = Wool, * | PinkDye, *
-GrayWool = Wool, * | GrayDye, *
-LightGrayWool = Wool, * | LightGrayDye, *
-CyanWool = Wool, * | CyanDye, *
-PurpleWool = Wool, * | PurpleDye, *
-BlueWool = Wool, * | BlueDye, *
-BrownWool = Wool, * | BrownDye, *
-GreenWool = Wool, * | GreenDye, *
-RedWool = Wool, * | RedDye, *
-BlackWool = Wool, * | BlackDye, *
+WhiteWool = Wool^-1, * | BoneMeal, *
+OrangeWool = WhiteWool, * | OrangeDye, *
+MagentaWool = WhiteWool, * | MagentaDye, *
+LightBlueWool = WhiteWool, * | LightBlueDye, *
+YellowWool = WhiteWool, * | YellowDye, *
+LimeWool = WhiteWool, * | LimeDye, *
+PinkWool = WhiteWool, * | PinkDye, *
+GrayWool = WhiteWool, * | GrayDye, *
+LightGrayWool = WhiteWool, * | LightGrayDye, *
+CyanWool = WhiteWool, * | CyanDye, *
+PurpleWool = WhiteWool, * | PurpleDye, *
+BlueWool = WhiteWool, * | BlueDye, *
+BrownWool = WhiteWool, * | BrownDye, *
+GreenWool = WhiteWool, * | GreenDye, *
+RedWool = WhiteWool, * | RedDye, *
+BlackWool = WhiteWool, * | BlackDye, *
-WhiteCarpet, 3 = WhiteWool, 1:1, 2:1
OrangeCarpet, 3 = OrangeWool, 1:1, 2:1
MagentaCarpet, 3 = MagentaWool, 1:1, 2:1
LightBlueCarpet, 3 = LightBlueWool, 1:1, 2:1
@@ -472,11 +474,11 @@ BrownCarpet, 3 = BrownWool, 1:1, 2:1
GreenCarpet, 3 = GreenWool, 1:1, 2:1
RedCarpet, 3 = RedWool, 1:1, 2:2
BlackCarpet, 3 = BlackWool, 1:1, 2:1
+WhiteCarpet, 3 = WhiteWool, 1:1, 2:1
#******************************************************#
# Stained Glass:
#
-WhiteStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BoneMeal, 2:2
OrangeStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | OrangeDye, 2:2
MagentaStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | MagentaDye, 2:2
LightBlueStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | LightBlueDye, 2:2
@@ -492,11 +494,11 @@ BrownStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Brown
GreenStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | GreenDye, 2:2
RedStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | RedDye, 2:2
BlackStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BlackDye, 2:2
+WhiteStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BoneMeal, 2:2
#******************************************************#
# Stained Glass Pane:
#
-WhiteStainedGlassPane, 16 = WhiteStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3
OrangeStainedGlassPane, 16 = OrangeStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3
MagentaStainedGlassPane, 16 = MagentaStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3
LightBlueStainedGlassPane, 16 = LightBlueStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3
@@ -512,7 +514,27 @@ BrownStainedGlassPane, 16 = BrownStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3
GreenStainedGlassPane, 16 = GreenStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3
RedStainedGlassPane, 16 = RedStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3
BlackStainedGlassPane , 16 = BlackStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3
+WhiteStainedGlassPane, 16 = WhiteStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3
+#******************************************************#
+# Stained Clay:
+#
+WhiteStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BoneMeal, 2:2
+OrangeStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | OrangeDye, 2:2
+MagentaStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | MagentaDye, 2:2
+LightBlueStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | LightBlueDye, 2:2
+YellowStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | YellowDye, 2:2
+LimeStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | LimeDye, 2:2
+PinkStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | PinkDye, 2:2
+GrayStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | GrayDye, 2:2
+LightGrayStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | LightGrayDye, 2:2
+CyanStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | CyanDye, 2:2
+VioletStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | VioletDye, 2:2
+BlueStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BlueDye, 2:2
+BrownStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BrownDye, 2:2
+GreenStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | GreenDye, 2:2
+RedStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | RedDye, 2:2
+BlackStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BlackDye, 2:2
#******************************************************#
# Enchantment & Brewing
diff --git a/MCServer/furnace.txt b/MCServer/furnace.txt
index fb8d63677..24bc35bc8 100644
--- a/MCServer/furnace.txt
+++ b/MCServer/furnace.txt
@@ -56,6 +56,7 @@ ClayBlock = HardenedClay
Netherrack = NetherBrickItem
RawFish = CookedFish
Log = CharCoal
+DarkOakLog = CharCoal
Cactus = GreenDye
WetSponge = Sponge
Stonebrick = CrackedStonebrick
@@ -87,6 +88,7 @@ RawMutton = CookedMutton
! Jukebox = 300 # -> 15 sec
! Lavabucket = 20000 # -> 1000 sec
! Log = 300 # -> 15 sec
+! DarkOakLog = 300 # -> 15 sec
! Sapling = 100 # -> 5 sec
! CoalBlock = 16000 # -> 800 sec
! BlazeRod = 2400 # -> 120 sec
diff --git a/MCServer/items.ini b/MCServer/items.ini
index daf366654..5eb04619c 100644
--- a/MCServer/items.ini
+++ b/MCServer/items.ini
@@ -815,7 +815,7 @@ rawrabbit=411
cookedrabbit=412
rabbitstew=413
rabbitsoup=413
-rabbitsfood=414
+rabbitsfoot=414
rabbithide=415
armorstand=416
ironhorsearmor=417
diff --git a/MCServer/webadmin/(original).html b/MCServer/webadmin/(original).html
deleted file mode 100644
index 673a93ada..000000000
--- a/MCServer/webadmin/(original).html
+++ /dev/null
@@ -1,375 +0,0 @@
-<!DOCTYPE html>
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<link rel="icon" href="files/favicon.ico">
-<title>{TITLE}</title>
-
-<style type="text/css" media="screen">
-
- /* reset CSS */
-
- html, body, div, span, applet, object, iframe,
- h1, h2, h3, h4, h5, h6, p, blockquote, pre,
- a, abbr, acronym, address, big, cite, code,
- del, dfn, em, font, img, ins, kbd, q, s, samp,
- small, strike, strong, sub, sup, tt, var,
- b, u, i, center,
- dl, dt, dd, ol, ul, li,
- fieldset, form, label, legend,
- table, caption, tbody, tfoot, thead, tr, th, td {
- margin: 0;
- padding: 0;
- border: 0;
- outline: 0;
- font-size: 100%;
- vertical-align: baseline;
- background: transparent;
- }
- body {
- line-height: 1;
- }
- ol, ul {
- list-style: none;
- }
- blockquote, q {
- quotes: none;
- }
-
- /* remember to define focus styles! */
- :focus {
- outline: 0;
- }
-
- /* remove textarea resize at Safari */
- textarea {
- resize: none;
- }
-
- /* remember to highlight inserts somehow! */
- ins {
- text-decoration: none;
- }
- del {
- text-decoration: line-through;
- }
-
- /* tables still need 'cellspacing="0"' in the markup */
- table {
- border-collapse: collapse;
- border-spacing: 0;
- }
-
-
- /*
- Origional from http://www.perspectived.com/
- Modified by Ben Phelps
- Made for FakeTruth - MCServer
- */
-
- /* Basic ---------------------------------------- */
-
- .clear { clear: both; }
-
- body {
- background: white;
- font-family: Arial, Helvetica, sans-serif;
- font-size: 12px;
- color: #646464;
- text-align: center;
- }
-
- #wrapper {
- text-align: left;
- width: 930px;
- margin: 0 auto;
- }
-
- /* Logo ---------------------------------------- */
-
- h1 {
- margin: 15px 0 10px 5px;
- width: 180px;
- height: 36px;
- background: url(files/logo.png) no-repeat left top;
- }
-
- h1 a {
- display: block;
- width: 225px;
- height: 28px;
- }
-
- h1 span { display: none; }
-
- a {
- color: #646464;
- }
-
- /* Container ---------------------------------------- */
-
- #containerHolder {
- background: #eee;
- padding: 5px;
- }
-
-
- #container {
- background: #fff url(files/background.gif) repeat-y left top;
- border: 1px solid #ddd;
- width: 918px;
-
- }
-
- #connectHolder {
- background: #eee;
- padding: 5px;
- margin-bottom:8px;
- }
-
-
- #connect {
- border: 1px solid #ddd;
- background-color: #fff;
- padding:5px;
- width: 908px;
- }
-
- .pics {
- height: 375px;
- width: 600px;
- }
-
- .pics img {
- padding: 5px;
- border: 1px solid #ddd;
- background-color: #eee;
- width: 600px;
- height: 375px;
- margin-left: 15px;
- }
-
- /* Login -------------------------------------- */
-
- #loginLogo {
- margin: 0 auto;
- margin-top:100px;
- width: 180px;
- height: 36px;
- background-image: url(files/logo.png);
- }
-
- #loginHolder {
- background: #eee;
- padding: 5px;
- width: 310px;
- margin: 0 auto;
- height: 90px;
- margin-top:20px;
- }
-
- #login {
- padding:10px;
- width: 288px;
- height: 68px;
- border: 1px solid #ddd;
- background:#fff;
- text-align: left;
- }
-
-
- /* Sidebar ---------------------------------------- */
-
- #sidebar {
- width: 179px;
- float: left;
- }
-
- #sidebar .sideNav { width: 179px; }
-
- #sidebar .sideNav li { border-bottom: 1px solid #ddd; width: 179px; }
-
- #sidebar .sideNav li a {
- display: block;
- color: #646464;
- background: #f6f6f6;
- text-decoration: none;
- height: 29px;
- line-height: 29px;
- padding: 0 19px;
- width: 141px;
- }
-
- #sidebar .sideNav li a:hover { background: #fdfcf6; }
-
- #sidebar .sideNav li a.active, #sidebar .sideNav li a.active:hover {
- background: #f0f7fa;
- color: #c66653;
- }
-
- /* Breadcrumb ---------------------------------------- */
-
- h2 {
- width: 718px;
- float: right;
- color: #646464;
- font-size: 16px;
- line-height: 16px;
- font-weight: bold;
- margin: 20px 0 0 0;
- padding: 0 0 10px 0;
- border-bottom: 1px solid #ddd;
- }
-
- h2 a {
- color: #646464;
- text-decoration: none;
- }
-
- h2 a.active { color: #c66653; }
-
- h2 a:hover { text-decoration: underline; }
-
- /* Content ---------------------------------------- */
-
- #main {
- width: 700px;
- float: right;
- padding: 0 19px 0 0;
- }
-
- #main p {
-
- padding: 10px;
-
- }
-
- h3 {
- font-size: 14px;
- line-height: 14px;
- font-weight: bold;
- color: #5494af;
- padding: 0 0 0 10px;
- margin: 20px 0 10px;
- }
-
- h4 {
- padding: 0 0 0 10px;
- margin: 20px 0 10px;
- }
-
- #main ul {
- padding: 0 0 0 10px;
- list-style-type: circle;
- list-style-position: inside;
- }
-
- #main table {
- border-top: 1px solid #ddd;
- width: 700px;
- }
-
- #main table tr th {
- text-align: left;
- background: #f6f6f6;
- padding: 0px 20px;
- height: 20px;
- line-height: 20px;
- border-bottom: 1px solid #ddd;
- }
-
- #main table tr td {
- background: #f6f6f6;
- padding: 0px 20px;
- height: 29px;
- line-height: 29px;
- border-bottom: 1px solid #ddd;
- }
-
- #main table tr.odd td {
- background: #fbfbfb;
- }
-
- #main table tr:hover td { background: #fdfcf6; }
-
- #main table .action {
- text-align: right;
- padding: 0 20px 0 10px;
- }
-
- #main table tr .action a { margin: 0 0 0 10px; text-decoration: none; color: #9b9b9b; }
- #main table tr:hover .action .edit { color: #c5a059; }
- #main table tr:hover .action .delete { color: #a02b2b; }
- #main table tr:hover .action .view { color: #55a34a; }
-
- #main table tr:hover .action a:hover { text-decoration: underline; }
-
- fieldset {
- border: 1px solid #ddd;
- padding: 19px;
- margin: 0 0 20px 0;
- background: #fbfbfb;
- }
-
- form p { margin: 0 0 14px 0; float: left; width: 100%; }
-
- label {
- display: block;
- width: 100%;
- margin: 0 0 7px 0;
- line-height: 12px;
- }
-
- /* Footer ---------------------------------------- */
-
- #footer {
- margin: 10px 0 30px 0;
- font-size: 11px;
- line-height: 11px;
- color: #9B9B9B;
- padding: 0 0 0 5px;
- }
-
- #footer a { color: #9B9B9B; }
-
- #footer a:hover { text-decoration: none; }
-</style>
-
-</head>
-
-<body>
- <div id="wrapper">
- <!-- h1 tag stays for the logo, you can use the a tag for linking the index page -->
- <h1><a href="./"><span>{TITLE}</span></a></h1>
-
- <div id="containerHolder">
- <div id="container">
- <div id="sidebar">
- <ul class="sideNav">
- {MENU}
- </ul>
- <!-- // .sideNav -->
- </div>
- <!-- // #sidebar -->
-
- <!-- h2 stays for breadcrumbs -->
- <h2>Welcome {USERNAME}</h2>
-
- <div id="main">
- <h3>{PLUGIN_NAME}</h3>
-
- {CONTENT}
-
- </div>
- <!-- // #main -->
-
- <div class="clear"></div>
- </div>
- <!-- // #container -->
- </div>
- <!-- // #containerHolder -->
-
- <p id="footer">MCServer is using: {MEM}MB of memory; Current chunk count: {NUMCHUNKS} </p>
- </div>
- <!-- // #wrapper -->
-</body>
-</html>
diff --git a/MCServer/webadmin/template.lua b/MCServer/webadmin/template.lua
index 6ea7b69bc..f210a2ce8 100644
--- a/MCServer/webadmin/template.lua
+++ b/MCServer/webadmin/template.lua
@@ -26,24 +26,23 @@ function GetDefaultPage()
local SubTitle = "Current Game"
local Content = ""
- Content = Content .. "<h4>Server Name:</h4>"
- Content = Content .. "<p>" .. cRoot:Get():GetServer():GetServerID() .. "</p>"
-
Content = Content .. "<h4>Plugins:</h4><ul>"
- local AllPlugins = PM:GetAllPlugins()
- for key,value in pairs(AllPlugins) do
- if( value ~= nil and value ~= false ) then
- Content = Content .. "<li>" .. key .. " (version " .. value:GetVersion() .. ")</li>"
+ PM:ForEachPlugin(
+ function (a_CBPlugin)
+ if (a_CBPlugin:IsLoaded()) then
+ Content = Content .. "<li>" .. a_CBPlugin:GetName() .. " (version " .. a_CBPlugin:GetVersion() .. ")</li>"
+ end
end
- end
+ )
Content = Content .. "</ul>"
Content = Content .. "<h4>Players:</h4><ul>"
- local AddPlayerToTable = function( Player )
- Content = Content .. "<li>" .. Player:GetName() .. "</li>"
- end
- cRoot:Get():ForEachPlayer( AddPlayerToTable )
+ cRoot:Get():ForEachPlayer(
+ function(a_CBPlayer)
+ Content = Content .. "<li>" .. a_CBPlayer:GetName() .. "</li>"
+ end
+ )
Content = Content .. "</ul><br>";
@@ -102,9 +101,8 @@ function ShowPage(WebAdmin, TemplateRequest)
<div class="upper">
<div class="wrapper">
<ul class="menu top_links">
- <li><a>Server Name: <strong>]] .. cRoot:Get():GetServer():GetServerID() .. [[</strong></a></li>
<li><a>Players online: <strong>]] .. NumPlayers .. [[</strong></a></li>
- <li><a>Memory: <strong>]] .. MemoryUsageKiB / 1024 .. [[MB</strong></a></li>
+ <li><a>Memory: <strong>]] .. string.format("%.2f", MemoryUsageKiB / 1024) .. [[MB</strong></a></li>
<li><a>Chunks: <strong>]] .. NumChunks .. [[</strong></a></li>
</ul>
<div class="welcome"><strong>Welcome back, ]] .. TemplateRequest.Request.Username .. [[</strong>&nbsp;&nbsp;&nbsp;<a href=".././"><img src="/log_out.png" style="vertical-align:bottom;"> Log Out</a></div>
diff --git a/MCServer/webadmin/template_orig.lua b/MCServer/webadmin/template_orig.lua
deleted file mode 100644
index a7480f83e..000000000
--- a/MCServer/webadmin/template_orig.lua
+++ /dev/null
@@ -1,137 +0,0 @@
--- Use a table for fast concatenation of strings
-local SiteContent = {}
-function Output(String)
- table.insert(SiteContent, String)
-end
-
-
-
-
-
-function GetTableSize(Table)
- local Size = 0
- for key,value in pairs(Table) do
- Size = Size + 1
- end
- return Size
-end
-
-
-
-
-
-function GetDefaultPage()
- local PM = cRoot:Get():GetPluginManager()
-
- local SubTitle = "Current Game"
- local Content = ""
-
- Content = Content .. "<h4>Server Name:</h4>"
- Content = Content .. "<p>" .. cRoot:Get():GetServer():GetServerID() .. "</p>"
-
- Content = Content .. "<h4>Plugins:</h4><ul>"
- local AllPlugins = PM:GetAllPlugins()
- for key,value in pairs(AllPlugins) do
- if( value ~= nil and value ~= false ) then
- Content = Content .. "<li>" .. key .. " V." .. value:GetVersion() .. "</li>"
- end
- end
-
- Content = Content .. "</ul>"
- Content = Content .. "<h4>Players:</h4><ul>"
-
- local AddPlayerToTable = function( Player )
- Content = Content .. "<li>" .. Player:GetName() .. "</li>"
- end
- cRoot:Get():ForEachPlayer( AddPlayerToTable )
-
- Content = Content .. "</ul><br>";
-
- return Content, SubTitle
-end
-
-
-
-
-
-function ShowPage(WebAdmin, TemplateRequest)
- SiteContent = {}
- local BaseURL = WebAdmin:GetBaseURL(TemplateRequest.Request.Path)
- local Title = "MCServer WebAdmin"
- local MemoryUsageKiB = cRoot:GetPhysicalRAMUsage()
- local NumChunks = cRoot:Get():GetTotalChunkCount()
- local PluginPage = WebAdmin:GetPage(TemplateRequest.Request)
- local PageContent = PluginPage.Content
- local SubTitle = PluginPage.PluginName
- if (PluginPage.TabName ~= "") then
- SubTitle = PluginPage.PluginName .. " - " .. PluginPage.TabName
- end
- if (PageContent == "") then
- PageContent, SubTitle = GetDefaultPage()
- end
-
- Output([[
-<!DOCTYPE html>
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<link rel="icon" href="/favicon.ico">
-<title>]] .. Title .. [[</title>
-<link rel="stylesheet" type="text/css" media="screen" href="/style.css">
-</head>
-
-<body>
- <div id="wrapper">
- <!-- h1 tag stays for the logo, you can use the a tag for linking the index page -->
- <h1>
- <a href="]] .. BaseURL .. [["><span>MCServer</span></a>
- </h1>
- <div id="containerHolder">
- <div id="container">
- <div id="sidebar">
- <ul class="sideNav">
- ]])
-
-
- local AllPlugins = WebAdmin:GetPlugins()
- for key,value in pairs(AllPlugins) do
- local PluginWebTitle = value:GetWebTitle()
- local TabNames = value:GetTabNames()
- if (GetTableSize(TabNames) > 0) then
- Output("<li>"..PluginWebTitle.."</li>\n");
-
- for webname,prettyname in pairs(TabNames) do
- Output("<li><a href='" .. BaseURL .. PluginWebTitle .. "/" .. webname .. "'>" .. prettyname .. "</a></li>\n")
- end
- end
- end
-
-
- Output([[
- </ul>
- <!-- // .sideNav -->
- </div>
- <!-- // #sidebar -->
- <!-- h2 stays for breadcrumbs -->
- <h2>Welcome ]] .. TemplateRequest.Request.Username .. [[</h2>
- <div id="main">
- <h3>]] .. SubTitle .. [[</h3>
- ]] .. PageContent .. [[
- </div>
- <!-- // #main -->
-
- <div class="clear"></div>
-
- </div>
- <!-- // #container -->
- </div>
- <!-- // #containerHolder -->
-
- <p id="footer">MCServer is using: ]] .. MemoryUsageKiB / 1024 .. [[ MiB of memory; Current chunk count: ]] .. NumChunks .. [[ </p>
- </div>
- <!-- // #wrapper -->
-</body>
-</html>
- ]])
-
- return table.concat(SiteContent)
-end
diff --git a/README.md b/README.md
index 87c4ee93e..8731b0896 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,14 @@
-MCServer [![Build Status](http://img.shields.io/travis/mc-server/MCServer/master.svg?style=flat)](https://travis-ci.org/mc-server/MCServer) [![Coverity Scan Build Status](https://scan.coverity.com/projects/1930/badge.svg)](https://scan.coverity.com/projects/1930) [![weekly tips](http://img.shields.io/gratipay/cuberite_team.svg?style=flat)](http://gratipay.com/cuberite_team) [![tip for next commit](http://tip4commit.com/projects/74.svg)](http://tip4commit.com/projects/74)
+MCServer [![Build Status](http://img.shields.io/travis/mc-server/MCServer/master.svg?style=flat)](https://travis-ci.org/mc-server/MCServer) [![Coverity Scan Build Status](https://img.shields.io/coverity/scan/1930.svg)](https://scan.coverity.com/projects/1930) [![weekly tips](http://img.shields.io/gratipay/cuberite_team.svg?style=flat)](http://gratipay.com/cuberite_team)
========
-MCServer is a Minecraft server that is written in C++ and designed to be efficient with memory and CPU, as well as having a flexible Lua Plugin API.
+MCServer is a Minecraft-compatible multiplayer game server that is written in C++ and designed to be efficient with memory and CPU, as well as having a flexible Lua Plugin API. MCServer is compatible with the vanilla Minecraft client.
MCServer can run on Windows, *nix and Android operating systems. This includes Android phones and tablets as well as Raspberry Pis.
We currently support Release 1.7 and 1.8 (not beta) Minecraft protocol versions.
+Subscribe to [the newsletter](http://newsletter.cuberite.org/subscribe.htm) for important updates and project news.
+
Installation
------------
Hosted MCServer is available DIY on DigitalOcean: [![Install on DigitalOcean](http://doinstall.bearbin.net/button.svg)](http://doinstall.bearbin.net/install?url=https://github.com/mc-server/MCServer) and [Gamososm](https://gamocosm.com) also offers MCServer support.
@@ -20,7 +22,11 @@ For Windows, you just need to download a file and extract it:
- [Windows 32 bit](http://builds.cuberite.org/job/MCServer%20Windows%20x86/lastSuccessfulBuild/artifact/Install/MCServer.zip)
- [Windows 64 bit](http://builds.cuberite.org/job/MCServer%20Windows%20x64/lastSuccessfulBuild/artifact/Install/MCServer.zip)
-For other operating systems you need to download and compile yourself. There is also an archive of binary builds on the buildserver: http://builds.cuberite.org
+For other operating systems you need to download and compile yourself. This can be done either manually, or with this automatic script:
+
+ bash -c "$(wget -O - https://raw.githubusercontent.com/mc-server/MCServer/master/compile.sh)"
+
+There is also an archive of binary builds on the buildserver: http://builds.cuberite.org
Compiling the server yourself has other benefits: you may get better performance performance (1.5-3x as fast) and it supports more operating systems. See the [COMPILING.md](https://github.com/mc-server/MCServer/blob/master/COMPILING.md) file for more details.
@@ -34,9 +40,7 @@ Check out the [CONTRIBUTING.md](https://github.com/mc-server/MCServer/blob/maste
Other Stuff
-----------
-For other stuff, including plugins and discussion, check the [forums](http://forum.mc-server.org) and [Plugin API](http://mc-server.xoft.cz/LuaAPI/).
-
-Earn bitcoins for commits or donate to reward the MCServer developers: [![tip for next commit](http://tip4commit.com/projects/74.svg)](http://tip4commit.com/projects/74)
+For other stuff, including plugins and discussion, check out the [forums](http://forum.mc-server.org) and [Plugin API](http://mc-server.xoft.cz/LuaAPI/).
Support Us on Gratipay: [![Support via Gratipay](http://img.shields.io/gittip/cuberite_team.svg)](https://www.gratipay.com/cuberite_team)
diff --git a/SetFlags.cmake b/SetFlags.cmake
index 0d1bd99b3..92046b688 100644
--- a/SetFlags.cmake
+++ b/SetFlags.cmake
@@ -240,6 +240,11 @@ macro(set_exe_flags)
# we support non-IEEE 754 fpus so can make no guarentees about error
add_flags_cxx("-ffast-math")
+ if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
+ # backtrace() and friends are in libexecinfo
+ add_flags_lnk("-lexecinfo")
+ endif()
+
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
get_clang_version()
if ("${CLANG_VERSION}" VERSION_LESS 3.0)
@@ -263,6 +268,10 @@ macro(set_exe_flags)
# flags introduced in 3.2
add_flags_cxx("-Wno-documentation")
endif()
+ if ("${CLANG_VERSION}" VERSION_GREATER 3.5)
+ # Use this flag to ignore error for a reserved macro problem in sqlite 3
+ add_flags_cxx("-Wno-reserved-id-macro")
+ endif()
endif()
endif()
diff --git a/Tools/ProtoProxy/CMakeLists.txt b/Tools/ProtoProxy/CMakeLists.txt
index 132a14f78..ce64db38d 100644
--- a/Tools/ProtoProxy/CMakeLists.txt
+++ b/Tools/ProtoProxy/CMakeLists.txt
@@ -105,5 +105,5 @@ add_executable(ProtoProxy
${SHARED_OSS_HDR}
)
-target_link_libraries(ProtoProxy zlib polarssl)
+target_link_libraries(ProtoProxy zlib mbedtls)
diff --git a/app.yml b/app.yml
index 51f9b0f58..f72089f51 100644
--- a/app.yml
+++ b/app.yml
@@ -1,5 +1,5 @@
name: MCServer
-image: ubuntu-14-04-x64
+image: ubuntu-15-04-x64
config:
#cloud-config
packages:
diff --git a/compile.sh b/compile.sh
new file mode 100755
index 000000000..329d79d81
--- /dev/null
+++ b/compile.sh
@@ -0,0 +1,199 @@
+#|| goto :windows_detected
+
+# Do we already have a repo?
+if [[ -d .git && -f easyinstall.sh && -f MakeLuaAPI.cmd ]]; then # A good enough indicator that we're in the MCServer git repo.
+cd ../
+echo "MCServer repository detected. This should make the process faster, especially if you compiled before."
+fi
+
+# Error functions.
+function error
+{
+ echo
+ echo "-----------------"
+ echo "Script aborted, reason:"
+ echo $1
+ exit -1
+}
+
+function missingDepsExit
+{
+ echo
+ echo "Please install the dependencies, then come back."
+ echo
+ exit -2
+}
+
+
+# Echo: Greetings.
+echo
+echo "Hello, this script will download and compile Cuberite/MCServer."
+echo "On subsequent runs, it will update your MCServer."
+echo "The compilation and download will occur in the current directory."
+echo "If you're updating, you should run <Path to MCServer>/MCServer/compile.sh"
+echo "Compiling from source takes time, but it usually generates better executables."
+echo "If you prefer ready-to-use binaries or if you want more info, please visit:"
+echo "http://cuberite.org/"
+echo "http://mc-server.org/"
+
+MISSING_PROGRAMS=""
+
+# Compiler check.
+GCC_EXISTS=0
+CLANG_EXISTS=0
+g++ --help > /dev/null 2> /dev/null && GCC_EXISTS=1
+clang --help > /dev/null 2> /dev/null && CLANG_EXISTS=1
+if [[ $GCC_EXISTS == 0 && $CLANG_EXISTS == 0 ]]; then
+MISSING_PROGRAMS="gcc g++"
+fi
+
+# Depdendency check.
+while read program; do
+$program --help > /dev/null 2> /dev/null || MISSING_PROGRAMS="$MISSING_PROGRAMS $program"
+done <<"EOF"
+git
+make
+cmake
+EOF
+if [[ $MISSING_PROGRAMS != "" ]]; then
+ echo
+ echo "-----------------"
+ echo "You have missing compilation dependencies:"
+ echo $MISSING_PROGRAMS
+ echo
+
+ # apt-get guide.
+ apt-get --help > /dev/null 2> /dev/null && \
+ echo "You can install the missing depndencies via:" && \
+ echo -n "sudo apt-get install " && echo $MISSING_PROGRAMS && missingDepsExit
+
+ # yum guide.
+ yum --help > /dev/null 2> /dev/null && \
+ echo "You can install the missing depndencies via:" && \
+ echo -n "sudo yum install " && echo $MISSING_PROGRAMS && missingDepsExit
+
+ # rpm guide.
+ rpm --help > /dev/null 2> /dev/null && \
+ echo "You can install the missing depndencies via:" && \
+ echo -n "sudo rpm -i " && echo $MISSING_PROGRAMS && missingDepsExit
+
+ # pacman guide.
+ pacman --help > /dev/null 2> /dev/null && \
+ echo "You can install the missing depndencies via:" && \
+ echo -n "sudo pacman -S " && echo $MISSING_PROGRAMS && missingDepsExit
+
+ missingDepsExit
+fi
+
+exit
+# Echo: Branch choice.
+echo
+echo "You can choose between 2 branches:"
+echo "* (S)Stable: (Coming soon) Choose the stable branch if you want the most reliable server."
+echo " As of now, Stable is not yet available, please use testing instead."
+echo
+echo "* (T)Testing: The testing branch is less stable,"
+echo " but using it and finding and reporting bugs helps us a lot!"
+echo
+echo "* (D)Dev: The least stable of the three. (Master branch)"
+echo " Choose the development branch if you are feeling adventurous and"
+echo " want to try new, bleeding edge features."
+echo
+
+
+# Input: Branch choice.
+echo -n "Choose the branch (s/t/d): "
+read BRANCH
+if [[ ($BRANCH == "s") || ($BRANCH == "S" ) ]]; then
+ #BRANCH="stable"
+ error "We don't have a stable branch yet, please use testing, sorry."
+elif [[ ($BRANCH == "t") || ($BRANCH == "T" ) ]]; then
+ BRANCH="testing"
+elif [[ ($BRANCH == "d") || ($BRANCH == "D" ) ]]; then
+ BRANCH="master"
+else
+ error "Unrecognized user input."
+fi
+
+# Echo: Compile mode choice.
+echo
+echo "Choose compile mode:"
+echo "* (N)Normal: Compiles normally."
+echo
+echo "* (D)Debug: Compiles in debug mode. Makes your console and crashes much more verbose."
+echo " But it costs performance."
+echo
+echo "Note that the script will connect to the internet in order to fetch code after this step."
+echo "It will then compile your program."
+echo
+
+# Input: Compile mode choice.
+echo -n "Choose compile mode: (n/d): "
+read BUILDTYPE
+if [[ ($BUILDTYPE == "d") || ($BUILDTYPE == "D") ]]; then
+ BUILDTYPE="Debug"
+elif [[ ($BUILDTYPE == "n") || ($BUILDTYPE == "N") ]]; then
+ BUILDTYPE="Release"
+else
+ error "Unrecognized user input."
+fi
+
+
+# Echo: Downloading began.
+echo
+echo " --- Downloading MCServer's source code from the $BRANCH branch..."
+
+
+# Git: Clone.
+if [ ! -d MCServer ]; then
+ echo " --- Looks like your first run, cloning the whole code..."
+ git clone https://github.com/mc-server/MCServer.git
+fi
+
+
+# Git: Fetch.
+pushd MCServer
+echo " --- Updating the $BRANCH branch..."
+git fetch origin $BRANCH || error "git fetch failed"
+git checkout $BRANCH || error "git checkout failed"
+git merge origin/$BRANCH || error "git merge failed"
+
+
+# Git: Submodules.
+echo " --- Updating submodules..."
+git submodule init
+git submodule update
+
+
+# Cmake.
+echo " --- Running cmake..."
+popd
+if [ ! -d build-mcserver ]; then mkdir build-mcserver; fi
+pushd build-mcserver
+cmake ../MCServer/ -DCMAKE_BUILD_TYPE=$BUILDTYPE || error "cmake failed"
+
+
+# Make.
+echo " --- Compiling..."
+make -j`nproc` || error "Compiling failed"
+echo
+
+
+# Echo: Compilation complete.
+popd
+pushd MCServer/MCServer
+echo
+echo "-----------------"
+echo "Compilation done!"
+echo
+echo "Cuberite awaits you at:"
+echo "`pwd`/MCServer"
+echo
+echo "Enjoy :)"
+popd
+exit 0
+
+:windows_detected
+echo "This script is not available for Windows yet, sorry."
+echo "You can still download the Windows binaries from: http://mc-server.org"
+
diff --git a/easyinstall.sh b/easyinstall.sh
index 40fa85cfe..15ca1d358 100755
--- a/easyinstall.sh
+++ b/easyinstall.sh
@@ -4,10 +4,10 @@ PLATFORM=$(uname -m)
echo "Identifying platform: $PLATFORM"
case $PLATFORM in
- "i686") DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20x86/lastSuccessfulBuild/artifact/MCServer.tar" ;;
- "x86_64") DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20x64/lastSuccessfulBuild/artifact/MCServer.tar" ;;
+ "i686") DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20x86/lastSuccessfulBuild/artifact/MCServer.tar.gz" ;;
+ "x86_64") DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20x64/lastSuccessfulBuild/artifact/MCServer.tar.gz" ;;
# Assume that all arm devices are a raspi for now.
- arm*) DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20armhf/lastSuccessfulBuild/artifact/MCServer/MCServer.tar"
+ arm*) DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20armhf/lastSuccessfulBuild/artifact/MCServer/MCServer.tar.gz"
esac
echo "Downloading precompiled binaries."
diff --git a/lib/SQLiteCpp b/lib/SQLiteCpp
-Subproject 55edadd56d0d6f506954ad00c3b9a5d425814a2
+Subproject b17195b8d03e8908807c51f4d6ce610b148fc1b
diff --git a/lib/TCLAP b/lib/TCLAP
new file mode 160000
+Subproject 12cee38782897cfe60a1611615c200c45cd99ea
diff --git a/lib/libevent b/lib/libevent
-Subproject 62eaa889cc1996a7c58a389bf2dfa5d8ce784bd
+Subproject de2bb6568c930f76a5bc41ef6e0bf35a8a826e6
diff --git a/lib/polarssl b/lib/polarssl
-Subproject 38f47a8546b55e2b593bba27f03070e1e82d3c8
+Subproject 4f4c5b7450631e46a94cb89adf4a7737fbb178b
diff --git a/lib/polarssl.cmake b/lib/polarssl.cmake
index ced70b94e..3506d0fb4 100644
--- a/lib/polarssl.cmake
+++ b/lib/polarssl.cmake
@@ -1,11 +1,10 @@
-if(NOT TARGET polarssl)
+# This script includes PolarSSL, if not already included.
+# It is needed for when multiple projects reference PolarSSL.
+
+if(NOT TARGET mbedtls)
message("including polarssl")
set(ENABLE_TESTING OFF CACHE BOOL "Disable tests")
set(ENABLE_PROGRAMS OFF CACHE BOOL "Disable programs")
- if (SELF_TEST)
- add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/polarssl/ ${CMAKE_CURRENT_BINARY_DIR}/lib/polarssl)
- else()
- add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/polarssl/ ${CMAKE_CURRENT_BINARY_DIR}/lib/polarssl EXCLUDE_FROM_ALL)
- endif()
+ add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/polarssl/ ${CMAKE_CURRENT_BINARY_DIR}/lib/polarssl EXCLUDE_FROM_ALL)
endif()
diff --git a/lib/sqlite/CMakeLists.txt b/lib/sqlite/CMakeLists.txt
index 993dac146..b9471c1f2 100644
--- a/lib/sqlite/CMakeLists.txt
+++ b/lib/sqlite/CMakeLists.txt
@@ -25,7 +25,7 @@ endif()
# FreeBSD requires us to define this to get POSIX 2001 standard
if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
- add_flags_cxx(-D__POSIX_VISIBLE=200112)
+ add_flags_cxx("-D_XOPEN_SOURCE=600")
endif()
add_library(sqlite ${SOURCE})
diff --git a/lib/tolua++/CMakeLists.txt b/lib/tolua++/CMakeLists.txt
index 12054323b..02883397e 100644
--- a/lib/tolua++/CMakeLists.txt
+++ b/lib/tolua++/CMakeLists.txt
@@ -53,4 +53,8 @@ if(UNIX)
target_link_libraries(tolua m ${DYNAMIC_LOADER})
endif()
+if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
+ add_flags_lnk(-L/usr/local/lib)
+endif()
+
target_link_libraries(tolua tolualib lua)
diff --git a/lib/tolua++/src/bin/lua/compat-5.1.lua b/lib/tolua++/src/bin/lua/compat-5.1.lua
index c591592a6..c0a3ec388 100644
--- a/lib/tolua++/src/bin/lua/compat-5.1.lua
+++ b/lib/tolua++/src/bin/lua/compat-5.1.lua
@@ -54,3 +54,11 @@ string.repl = ogsub
+-- Lua 5.2+ and LuaJit don't have string.gfind(). Use string.gmatch() instead:
+if not(string.gfind) then
+ string.gfind = string.gmatch
+end
+
+
+
+
diff --git a/src/Bindings/.gitignore b/src/Bindings/.gitignore
index 0d00dd578..711ae9c3a 100644
--- a/src/Bindings/.gitignore
+++ b/src/Bindings/.gitignore
@@ -1,2 +1,4 @@
lua51.dll
-LuaState_Call.inc
+LuaState_Declaration.inc
+LuaState_Implementation.cpp
+LuaState_Typedefs.inc
diff --git a/src/Bindings/AllToLua.bat b/src/Bindings/AllToLua.bat
index f085af9e9..44675a886 100644
--- a/src/Bindings/AllToLua.bat
+++ b/src/Bindings/AllToLua.bat
@@ -12,7 +12,7 @@
:: Regenerate the files:
echo Regenerating LUA bindings . . .
-"tolua++.exe" -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
+"tolua++.exe" -L BindingsProcessor.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
diff --git a/src/Bindings/AllToLua.sh b/src/Bindings/AllToLua.sh
index 887c2490c..625ae4300 100644
--- a/src/Bindings/AllToLua.sh
+++ b/src/Bindings/AllToLua.sh
@@ -1,2 +1,2 @@
#!/bin/bash
-/usr/bin/tolua++ -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
+/usr/bin/tolua++ -L BindingsProcessor.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
diff --git a/src/Bindings/AllToLua_lua.bat b/src/Bindings/AllToLua_lua.bat
index 81c738f32..2d52c022d 100644
--- a/src/Bindings/AllToLua_lua.bat
+++ b/src/Bindings/AllToLua_lua.bat
@@ -13,7 +13,7 @@
:: Regenerate the files:
echo Regenerating LUA bindings . . .
-lua ..\..\lib\tolua++\src\bin\lua\_driver.lua -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
+lua ..\..\lib\tolua++\src\bin\lua\_driver.lua -L BindingsProcessor.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
diff --git a/src/Bindings/BindingsProcessor.lua b/src/Bindings/BindingsProcessor.lua
new file mode 100644
index 000000000..f86be6c6d
--- /dev/null
+++ b/src/Bindings/BindingsProcessor.lua
@@ -0,0 +1,161 @@
+
+-- BindingsProcessor.lua
+
+-- Implements additional processing that is done while generating the Lua bindings
+
+
+
+
+
+local access = {public = 0, protected = 1, private = 2}
+
+
+
+
+
+--- Defines classes that have a custom manual Push() implementation and should not generate the automatic one
+-- Map of classname -> true
+local g_HasCustomPushImplementation =
+{
+ cEntity = true
+}
+
+
+
+
+
+function parser_hook(s)
+ local container = classContainer.curr -- get the current container
+
+ -- process access-specifying labels (public, private, etc)
+ do
+ local b, e, label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type'
+ if b then
+
+ -- found a label, get the new access value from the global 'access' table
+ if access[label] then
+ container.curr_member_access = access[label]
+ end -- else ?
+
+ return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:]
+ end
+ end
+end
+
+
+
+
+
+--- Outputs the helper files supplementing the cLuaState class
+-- Writes:
+-- LuaState_Declaration.inc
+-- LuaState_Implementation.cpp
+-- LuaState_Typedefs.inc
+local function OutputLuaStateHelpers(a_Package)
+ -- Collect all class types from ToLua:
+ local types = {}
+ for idx, item in ipairs(a_Package) do
+ local mt = getmetatable(item) or {}
+ if (mt.classtype == "class") then
+ table.insert(types, {name = item.name, lname = item.lname})
+ end
+ end
+ table.sort(types,
+ function(a_Item1, a_Item2)
+ return (a_Item1.name:lower() < a_Item2.name:lower())
+ end
+ )
+
+ -- Output the typedefs:
+ do
+ local f = assert(io.open("LuaState_Typedefs.inc", "w"))
+ f:write("\n// LuaState_Typedefs.inc\n\n// This file is generated along with the Lua bindings by ToLua. Do not edit manually, do not commit to repo.\n")
+ f:write("// Provides a forward declaration and a typedef for a pointer to each class exported to the Lua API.\n")
+ f:write("\n\n\n\n\n")
+ for _, item in ipairs(types) do
+ if not(item.name:match(".*<.*")) then -- Skip templates altogether
+ -- Classes start with a "c", everything else is a struct:
+ if (item.name:sub(1, 1) == "c") then
+ f:write("class " .. item.name .. ";\n")
+ else
+ f:write("struct " .. item.name .. ";\n")
+ end
+ end
+ end
+ f:write("\n\n\n\n\n")
+ for _, item in ipairs(types) do
+ f:write("typedef " .. item.name .. " * Ptr" .. item.lname .. ";\n")
+ end
+ f:write("\n\n\n\n\n")
+ f:close()
+ end
+
+ -- Output the Push() and GetStackValue() function declarations:
+ do
+ local f = assert(io.open("LuaState_Declaration.inc", "w"))
+ f:write("\n// LuaState_Declaration.inc\n\n// This file is generated along with the Lua bindings by ToLua. Do not edit manually, do not commit to repo.\n")
+ f:write("// Implements a Push() and GetStackValue() function for each class exported to the Lua API.\n")
+ f:write("// This file expects to be included form inside the cLuaState class definition\n")
+ f:write("\n\n\n\n\n")
+ for _, item in ipairs(types) do
+ f:write("void Push(" .. item.name .. " * a_Value);\n")
+ end
+ for _, item in ipairs(types) do
+ f:write("void GetStackValue(int a_StackPos, Ptr" .. item.lname .. " & a_ReturnedVal);\n")
+ end
+ f:write("\n\n\n\n\n")
+ f:close()
+ end
+
+ -- Output the Push() and GetStackValue() function implementations:
+ do
+ local f = assert(io.open("LuaState_Implementation.cpp", "w"))
+ f:write("\n// LuaState_Implementation.cpp\n\n// This file is generated along with the Lua bindings by ToLua. Do not edit manually, do not commit to repo.\n")
+ f:write("// Implements a Push() and GetStackValue() function for each class exported to the Lua API.\n")
+ f:write("// This file expects to be compiled as a separate translation unit\n")
+ f:write("\n\n\n\n\n")
+ f:write("#include \"Globals.h\"\n#include \"LuaState.h\"\n#include \"tolua++/include/tolua++.h\"\n")
+ f:write("\n\n\n\n\n")
+ for _, item in ipairs(types) do
+ if not(g_HasCustomPushImplementation[item.name]) then
+ f:write("void cLuaState::Push(" .. item.name .. " * a_Value)\n{\n\tASSERT(IsValid());\n")
+ f:write("\ttolua_pushusertype(m_LuaState, a_Value, \"" .. item.name .. "\");\n");
+ f:write("\tm_NumCurrentFunctionArgs += 1;\n")
+ f:write("}\n\n\n\n\n\n")
+ end
+ end
+ for _, item in ipairs(types) do
+ f:write("void cLuaState::GetStackValue(int a_StackPos, Ptr" .. item.lname .. " & a_ReturnedVal)\n{\n\tASSERT(IsValid());\n")
+ f:write("\tif (lua_isnil(m_LuaState, a_StackPos))\n\t{\n")
+ f:write("\t a_ReturnedVal = nullptr;\n")
+ f:write("\t return;\n\t}\n")
+ f:write("\ttolua_Error err;\n")
+ f:write("\tif (tolua_isusertype(m_LuaState, a_StackPos, \"" .. item.name .. "\", false, &err))\n")
+ f:write("\t{\n")
+ f:write("\t a_ReturnedVal = *(reinterpret_cast<" .. item.name .. " **>(lua_touserdata(m_LuaState, a_StackPos)));\n")
+ f:write("\t}\n")
+ f:write("}\n\n\n\n\n\n")
+ end
+ f:close()
+ end
+end
+
+
+
+
+
+function pre_output_hook(a_Package)
+ OutputLuaStateHelpers(a_Package)
+end
+
+
+
+
+
+function post_output_hook()
+ print("Bindings have been generated.")
+end
+
+
+
+
diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt
index 4cc73b350..133c2224d 100644
--- a/src/Bindings/CMakeLists.txt
+++ b/src/Bindings/CMakeLists.txt
@@ -11,12 +11,14 @@ SET (SRCS
LuaNameLookup.cpp
LuaServerHandle.cpp
LuaState.cpp
+ LuaState_Implementation.cpp
LuaTCPLink.cpp
LuaUDPEndpoint.cpp
LuaWindow.cpp
ManualBindings.cpp
ManualBindings_Network.cpp
ManualBindings_RankManager.cpp
+ ManualBindings_World.cpp
Plugin.cpp
PluginLua.cpp
PluginManager.cpp
@@ -31,6 +33,8 @@ SET (HDRS
LuaNameLookup.h
LuaServerHandle.h
LuaState.h
+ LuaState_Declaration.inc
+ LuaState_Typedefs.inc
LuaTCPLink.h
LuaUDPEndpoint.h
LuaWindow.h
@@ -46,12 +50,15 @@ SET (HDRS
set (BINDING_OUTPUTS
${CMAKE_CURRENT_SOURCE_DIR}/Bindings.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Bindings.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/LuaState_Declaration.inc
+ ${CMAKE_CURRENT_SOURCE_DIR}/LuaState_Implementation.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/LuaState_Typedefs.inc
)
set(BINDING_DEPENDENCIES
tolua
- ../Bindings/virtual_method_hooks.lua
../Bindings/AllToLua.pkg
+ ../Bindings/BindingsProcessor.lua
../Bindings/LuaFunctions.h
../Bindings/LuaWindow.h
../Bindings/Plugin.h
@@ -126,7 +133,7 @@ if (NOT MSVC)
OUTPUT ${BINDING_OUTPUTS}
# Regenerate bindings:
- COMMAND tolua -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
+ COMMAND tolua -L BindingsProcessor.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
# add any new generation dependencies here
@@ -134,13 +141,12 @@ if (NOT MSVC)
)
endif ()
-set_source_files_properties(${CMAKE_SOURCE_DIR}/src/Bindings/Bindings.cpp PROPERTIES GENERATED TRUE)
-set_source_files_properties(${CMAKE_SOURCE_DIR}/src/Bindings/Bindings.h PROPERTIES GENERATED TRUE)
+set_source_files_properties(${BINDING_OUTPUTS} PROPERTIES GENERATED TRUE)
set_source_files_properties(${CMAKE_SOURCE_DIR}/src/Bindings/Bindings.cpp PROPERTIES COMPILE_FLAGS -Wno-error)
if(NOT MSVC)
add_library(Bindings ${SRCS} ${HDRS})
- target_link_libraries(Bindings lua sqlite tolualib polarssl)
+ target_link_libraries(Bindings lua sqlite tolualib mbedtls)
endif()
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index 25c77a652..08c7e19d7 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -19,13 +19,13 @@ extern "C"
#include "../Entities/Entity.h"
#include "../BlockEntities/BlockEntity.h"
-// fwd: SQLite/lsqlite3.c
+// fwd: "SQLite/lsqlite3.c"
extern "C"
{
int luaopen_lsqlite3(lua_State * L);
}
-// fwd: LuaExpat/lxplib.c:
+// fwd: "LuaExpat/lxplib.c":
extern "C"
{
int luaopen_lxp(lua_State * L);
@@ -107,7 +107,7 @@ void cLuaState::Create(void)
void cLuaState::RegisterAPILibs(void)
{
tolua_AllToLua_open(m_LuaState);
- ManualBindings::Bind(m_LuaState);
+ cManualBindings::Bind(m_LuaState);
DeprecatedBindings::Bind(m_LuaState);
luaopen_lsqlite3(m_LuaState);
luaopen_lxp(m_LuaState);
@@ -530,42 +530,6 @@ void cLuaState::Push(bool a_Value)
-void cLuaState::Push(cBlockEntity * a_BlockEntity)
-{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_BlockEntity, (a_BlockEntity == nullptr) ? "cBlockEntity" : a_BlockEntity->GetClass());
- m_NumCurrentFunctionArgs += 1;
-}
-
-
-
-
-
-void cLuaState::Push(cChunkDesc * a_ChunkDesc)
-{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_ChunkDesc, "cChunkDesc");
- m_NumCurrentFunctionArgs += 1;
-}
-
-
-
-
-
-void cLuaState::Push(cClientHandle * a_Client)
-{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_Client, "cClientHandle");
- m_NumCurrentFunctionArgs += 1;
-}
-
-
-
-
-
void cLuaState::Push(cEntity * a_Entity)
{
ASSERT(IsValid());
@@ -632,42 +596,6 @@ void cLuaState::Push(cEntity * a_Entity)
-void cLuaState::Push(cHopperEntity * a_Hopper)
-{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_Hopper, "cHopperEntity");
- m_NumCurrentFunctionArgs += 1;
-}
-
-
-
-
-
-void cLuaState::Push(cItem * a_Item)
-{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_Item, "cItem");
- m_NumCurrentFunctionArgs += 1;
-}
-
-
-
-
-
-void cLuaState::Push(cItems * a_Items)
-{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_Items, "cItems");
- m_NumCurrentFunctionArgs += 1;
-}
-
-
-
-
-
void cLuaState::Push(cLuaServerHandle * a_ServerHandle)
{
ASSERT(IsValid());
@@ -704,114 +632,6 @@ void cLuaState::Push(cLuaUDPEndpoint * a_UDPEndpoint)
-void cLuaState::Push(cMonster * a_Monster)
-{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_Monster, "cMonster");
- m_NumCurrentFunctionArgs += 1;
-}
-
-
-
-
-
-void cLuaState::Push(cPickup * a_Pickup)
-{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_Pickup, "cPickup");
- m_NumCurrentFunctionArgs += 1;
-}
-
-
-
-
-
-void cLuaState::Push(cPlayer * a_Player)
-{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_Player, "cPlayer");
- m_NumCurrentFunctionArgs += 1;
-}
-
-
-
-
-
-void cLuaState::Push(cPluginLua * a_Plugin)
-{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_Plugin, "cPluginLua");
- m_NumCurrentFunctionArgs += 1;
-}
-
-
-
-
-
-void cLuaState::Push(cProjectileEntity * a_ProjectileEntity)
-{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_ProjectileEntity, "cProjectileEntity");
- m_NumCurrentFunctionArgs += 1;
-}
-
-
-
-
-
-void cLuaState::Push(cTNTEntity * a_TNTEntity)
-{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_TNTEntity, "cTNTEntity");
- m_NumCurrentFunctionArgs += 1;
-}
-
-
-
-
-
-void cLuaState::Push(cWebAdmin * a_WebAdmin)
-{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_WebAdmin, "cWebAdmin");
- m_NumCurrentFunctionArgs += 1;
-}
-
-
-
-
-
-void cLuaState::Push(cWindow * a_Window)
-{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_Window, "cWindow");
- m_NumCurrentFunctionArgs += 1;
-}
-
-
-
-
-
-void cLuaState::Push(cWorld * a_World)
-{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_World, "cWorld");
- m_NumCurrentFunctionArgs += 1;
-}
-
-
-
-
-
void cLuaState::Push(double a_Value)
{
ASSERT(IsValid());
@@ -836,11 +656,18 @@ void cLuaState::Push(int a_Value)
-void cLuaState::Push(TakeDamageInfo * a_TDI)
+void cLuaState::Push(void * a_Ptr)
{
+ UNUSED(a_Ptr);
ASSERT(IsValid());
- tolua_pushusertype(m_LuaState, a_TDI, "TakeDamageInfo");
+ // Investigate the cause of this - what is the callstack?
+ // One code path leading here is the OnHookExploding / OnHookExploded with exotic parameters. Need to decide what to do with them
+ LOGWARNING("Lua engine: attempting to push a plain pointer, pushing nil instead.");
+ LOGWARNING("This indicates an unimplemented part of MCS bindings");
+ LogStackTrace();
+
+ lua_pushnil(m_LuaState);
m_NumCurrentFunctionArgs += 1;
}
@@ -848,11 +675,11 @@ void cLuaState::Push(TakeDamageInfo * a_TDI)
-void cLuaState::Push(Vector3d * a_Vector)
+void cLuaState::Push(std::chrono::milliseconds a_Value)
{
ASSERT(IsValid());
- tolua_pushusertype(m_LuaState, a_Vector, "Vector3<double>");
+ tolua_pushnumber(m_LuaState, static_cast<lua_Number>(a_Value.count()));
m_NumCurrentFunctionArgs += 1;
}
@@ -860,51 +687,40 @@ void cLuaState::Push(Vector3d * a_Vector)
-void cLuaState::Push(Vector3i * a_Vector)
+/*
+void cLuaState::PushUserType(void * a_Object, const char * a_Type)
{
ASSERT(IsValid());
- tolua_pushusertype(m_LuaState, a_Vector, "Vector3<int>");
+ tolua_pushusertype(m_LuaState, a_Object, a_Type);
m_NumCurrentFunctionArgs += 1;
}
+*/
-void cLuaState::Push(void * a_Ptr)
-{
- UNUSED(a_Ptr);
- ASSERT(IsValid());
-
- // Investigate the cause of this - what is the callstack?
- // One code path leading here is the OnHookExploding / OnHookExploded with exotic parameters. Need to decide what to do with them
- LOGWARNING("Lua engine: attempting to push a plain pointer, pushing nil instead.");
- LOGWARNING("This indicates an unimplemented part of MCS bindings");
- LogStackTrace();
-
- lua_pushnil(m_LuaState);
- m_NumCurrentFunctionArgs += 1;
-}
-
-void cLuaState::Push(std::chrono::milliseconds a_Value)
+void cLuaState::GetStackValue(int a_StackPos, AString & a_Value)
{
- ASSERT(IsValid());
-
- tolua_pushnumber(m_LuaState, static_cast<lua_Number>(a_Value.count()));
- m_NumCurrentFunctionArgs += 1;
+ size_t len = 0;
+ const char * data = lua_tolstring(m_LuaState, a_StackPos, &len);
+ if (data != nullptr)
+ {
+ a_Value.assign(data, len);
+ }
}
-void cLuaState::PushUserType(void * a_Object, const char * a_Type)
+void cLuaState::GetStackValue(int a_StackPos, BLOCKTYPE & a_ReturnedVal)
{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_Object, a_Type);
- m_NumCurrentFunctionArgs += 1;
+ if (lua_isnumber(m_LuaState, a_StackPos))
+ {
+ a_ReturnedVal = static_cast<BLOCKTYPE>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal));
+ }
}
@@ -920,13 +736,11 @@ void cLuaState::GetStackValue(int a_StackPos, bool & a_ReturnedVal)
-void cLuaState::GetStackValue(int a_StackPos, AString & a_Value)
+void cLuaState::GetStackValue(int a_StackPos, cPluginManager::CommandResult & a_Result)
{
- size_t len = 0;
- const char * data = lua_tolstring(m_LuaState, a_StackPos, &len);
- if (data != nullptr)
+ if (lua_isnumber(m_LuaState, a_StackPos))
{
- a_Value.assign(data, len);
+ a_Result = static_cast<cPluginManager::CommandResult>(static_cast<int>((tolua_tonumber(m_LuaState, a_StackPos, a_Result))));
}
}
@@ -934,12 +748,9 @@ void cLuaState::GetStackValue(int a_StackPos, AString & a_Value)
-void cLuaState::GetStackValue(int a_StackPos, int & a_ReturnedVal)
+void cLuaState::GetStackValue(int a_StackPos, cRef & a_Ref)
{
- if (lua_isnumber(m_LuaState, a_StackPos))
- {
- a_ReturnedVal = (int)tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal);
- }
+ a_Ref.RefStack(*this, a_StackPos);
}
@@ -958,11 +769,11 @@ void cLuaState::GetStackValue(int a_StackPos, double & a_ReturnedVal)
-void cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal)
+void cLuaState::GetStackValue(int a_StackPos, float & a_ReturnedVal)
{
if (lua_isnumber(m_LuaState, a_StackPos))
{
- a_ReturnedVal = (eWeather)Clamp((int)tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal), (int)wSunny, (int)wThunderstorm);
+ a_ReturnedVal = static_cast<float>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal));
}
}
@@ -970,35 +781,27 @@ void cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal)
-void cLuaState::GetStackValue(int a_StackPos, pBoundingBox & a_ReturnedVal)
+void cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal)
{
- if (lua_isnil(m_LuaState, a_StackPos))
+ if (!lua_isnumber(m_LuaState, a_StackPos))
{
- a_ReturnedVal = nullptr;
return;
}
- tolua_Error err;
- if (tolua_isusertype(m_LuaState, a_StackPos, "cBoundingBox", false, &err))
- {
- a_ReturnedVal = *((cBoundingBox **)lua_touserdata(m_LuaState, a_StackPos));
- }
+ a_ReturnedVal = static_cast<eWeather>(Clamp(
+ static_cast<int>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal)),
+ static_cast<int>(wSunny), static_cast<int>(wThunderstorm))
+ );
}
-void cLuaState::GetStackValue(int a_StackPos, pWorld & a_ReturnedVal)
+void cLuaState::GetStackValue(int a_StackPos, int & a_ReturnedVal)
{
- if (lua_isnil(m_LuaState, a_StackPos))
- {
- a_ReturnedVal = nullptr;
- return;
- }
- tolua_Error err;
- if (tolua_isusertype(m_LuaState, a_StackPos, "cWorld", false, &err))
+ if (lua_isnumber(m_LuaState, a_StackPos))
{
- a_ReturnedVal = *((cWorld **)lua_touserdata(m_LuaState, a_StackPos));
+ a_ReturnedVal = static_cast<int>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal));
}
}
@@ -1006,15 +809,6 @@ void cLuaState::GetStackValue(int a_StackPos, pWorld & a_ReturnedVal)
-void cLuaState::GetStackValue(int a_StackPos, cRef & a_Ref)
-{
- a_Ref.RefStack(*this, a_StackPos);
-}
-
-
-
-
-
bool cLuaState::CallFunction(int a_NumResults)
{
ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
@@ -1133,6 +927,9 @@ bool cLuaState::CheckParamTable(int a_StartParam, int a_EndParam)
VERIFY(lua_getstack(m_LuaState, 0, &entry));
VERIFY(lua_getinfo (m_LuaState, "n", &entry));
AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?");
+
+ BreakIntoDebugger(m_LuaState);
+
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
return false;
} // for i - Param
@@ -1280,7 +1077,7 @@ bool cLuaState::CheckParamFunctionOrNil(int a_StartParam, int a_EndParam)
bool cLuaState::CheckParamEnd(int a_Param)
{
tolua_Error tolua_err;
- if (tolua_isnoobj(m_LuaState, a_Param, &tolua_err))
+ if (tolua_isnoobj(m_LuaState, a_Param, &tolua_err) == 1)
{
return true;
}
@@ -1297,6 +1094,30 @@ bool cLuaState::CheckParamEnd(int a_Param)
+bool cLuaState::IsParamUserType(int a_Param, AString a_UserType)
+{
+ ASSERT(IsValid());
+
+ tolua_Error tolua_err;
+ return (tolua_isusertype(m_LuaState, a_Param, a_UserType.c_str(), 0, &tolua_err) == 1);
+}
+
+
+
+
+
+bool cLuaState::IsParamNumber(int a_Param)
+{
+ ASSERT(IsValid());
+
+ tolua_Error tolua_err;
+ return (tolua_isnumber(m_LuaState, a_Param, 0, &tolua_err) == 1);
+}
+
+
+
+
+
bool cLuaState::ReportErrors(int a_Status)
{
return ReportErrors(m_LuaState, a_Status);
@@ -1376,7 +1197,7 @@ int cLuaState::CallFunctionWithForeignParams(
if (!PushFunction(a_FunctionName.c_str()))
{
LOGWARNING("Function '%s' not found", a_FunctionName.c_str());
- lua_pop(m_LuaState, 2);
+ lua_settop(m_LuaState, OldTop);
return -1;
}
@@ -1384,7 +1205,7 @@ int cLuaState::CallFunctionWithForeignParams(
if (CopyStackFrom(a_SrcLuaState, a_SrcParamStart, a_SrcParamEnd) < 0)
{
// Something went wrong, fix the stack and exit
- lua_pop(m_LuaState, 2);
+ lua_settop(m_LuaState, OldTop);
m_NumCurrentFunctionArgs = -1;
m_CurrentFunctionName.clear();
return -1;
@@ -1395,13 +1216,8 @@ int cLuaState::CallFunctionWithForeignParams(
if (ReportErrors(s))
{
LOGWARN("Error while calling function '%s' in '%s'", a_FunctionName.c_str(), m_SubsystemName.c_str());
- // Fix the stack.
- // We don't know how many values have been pushed, so just get rid of any that weren't there initially
- int CurTop = lua_gettop(m_LuaState);
- if (CurTop > OldTop)
- {
- lua_pop(m_LuaState, CurTop - OldTop);
- }
+ // Reset the stack:
+ lua_settop(m_LuaState, OldTop);
// Reset the internal checking mechanisms:
m_NumCurrentFunctionArgs = -1;
@@ -1553,6 +1369,7 @@ int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
{
LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1));
LogStackTrace(a_LuaState, 1);
+ BreakIntoDebugger(a_LuaState);
return 1; // We left the error message on the stack as the return value
}
@@ -1560,6 +1377,28 @@ int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
+int cLuaState::BreakIntoDebugger(lua_State * a_LuaState)
+{
+ // Call the BreakIntoDebugger function, if available:
+ lua_getglobal(a_LuaState, "BreakIntoDebugger");
+ if (!lua_isfunction(a_LuaState, -1))
+ {
+ LOGD("LUA: BreakIntoDebugger() not found / not a function");
+ lua_pop(a_LuaState, 1);
+ return 1;
+ }
+ lua_insert(a_LuaState, -2); // Copy the string that has been passed to us
+ LOGD("Calling BreakIntoDebugger()...");
+ lua_call(a_LuaState, 1, 0);
+ LOGD("Returned from BreakIntoDebugger().");
+
+ return 0;
+}
+
+
+
+
+
////////////////////////////////////////////////////////////////////////////////
// cLuaState::cRef:
diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h
index 159483634..5b4ec3ae4 100644
--- a/src/Bindings/LuaState.h
+++ b/src/Bindings/LuaState.h
@@ -32,40 +32,14 @@ extern "C"
#include "../Vector3.h"
#include "../Defines.h"
+#include "PluginManager.h"
+#include "LuaState_Typedefs.inc"
-
-
-
-
-class cWorld;
-class cPlayer;
-class cEntity;
-class cProjectileEntity;
-class cMonster;
-class cItem;
-class cItems;
-class cClientHandle;
-class cPickup;
-class cChunkDesc;
-class cCraftingGrid;
-class cCraftingRecipe;
-struct TakeDamageInfo;
-class cWindow;
-class cPluginLua;
-struct HTTPRequest;
-class cWebAdmin;
-struct HTTPTemplateRequest;
-class cTNTEntity;
-class cHopperEntity;
-class cBlockEntity;
-class cBoundingBox;
-class cLuaTCPLink;
+// fwd:
class cLuaServerHandle;
+class cLuaTCPLink;
class cLuaUDPEndpoint;
-typedef cBoundingBox * pBoundingBox;
-typedef cWorld * pWorld;
-
@@ -202,59 +176,30 @@ public:
void Push(const Vector3i & a_Vector);
void Push(const Vector3i * a_Vector);
- // Push a value onto the stack (keep alpha-sorted):
+ // Push a simple value onto the stack (keep alpha-sorted):
void Push(bool a_Value);
- void Push(cBlockEntity * a_BlockEntity);
- void Push(cChunkDesc * a_ChunkDesc);
- void Push(cClientHandle * a_ClientHandle);
- void Push(cEntity * a_Entity);
- void Push(cHopperEntity * a_Hopper);
- void Push(cItem * a_Item);
- void Push(cItems * a_Items);
- void Push(cLuaServerHandle * a_ServerHandle);
- void Push(cLuaTCPLink * a_TCPLink);
- void Push(cLuaUDPEndpoint * a_UDPEndpoint);
- void Push(cMonster * a_Monster);
- void Push(cPickup * a_Pickup);
- void Push(cPlayer * a_Player);
- void Push(cPluginLua * a_Plugin);
- void Push(cProjectileEntity * a_ProjectileEntity);
- void Push(cTNTEntity * a_TNTEntity);
- void Push(cWebAdmin * a_WebAdmin);
- void Push(cWindow * a_Window);
- void Push(cWorld * a_World);
void Push(double a_Value);
void Push(int a_Value);
- void Push(TakeDamageInfo * a_TDI);
- void Push(Vector3d * a_Vector);
- void Push(Vector3i * a_Vector);
void Push(void * a_Ptr);
void Push(std::chrono::milliseconds a_time);
+ void Push(cLuaServerHandle * a_ServerHandle);
+ void Push(cLuaTCPLink * a_TCPLink);
+ void Push(cLuaUDPEndpoint * a_UDPEndpoint);
- /** Retrieve value at a_StackPos, if it is a valid bool. If not, a_Value is unchanged */
- void GetStackValue(int a_StackPos, bool & a_Value);
-
- /** Retrieve value at a_StackPos, if it is a valid string. If not, a_Value is unchanged */
+ // GetStackValue() retrieves the value at a_StackPos, if it is a valid type. If not, a_Value is unchanged.
+ // Enum values are clamped to their allowed range.
void GetStackValue(int a_StackPos, AString & a_Value);
-
- /** Retrieve value at a_StackPos, if it is a valid number. If not, a_Value is unchanged */
- void GetStackValue(int a_StackPos, int & a_Value);
-
- /** Retrieve value at a_StackPos, if it is a valid number. If not, a_Value is unchanged */
+ void GetStackValue(int a_StackPos, BLOCKTYPE & a_Value);
+ void GetStackValue(int a_StackPos, bool & a_Value);
+ void GetStackValue(int a_StackPos, cPluginManager::CommandResult & a_Result);
+ void GetStackValue(int a_StackPos, cRef & a_Ref);
void GetStackValue(int a_StackPos, double & a_Value);
-
- /** Retrieve value at a_StackPos, if it is a valid number, converting and clamping it to eWeather.
- If not, a_Value is unchanged. */
void GetStackValue(int a_StackPos, eWeather & a_Value);
+ void GetStackValue(int a_StackPos, float & a_ReturnedVal);
+ void GetStackValue(int a_StackPos, int & a_Value);
- /** Retrieve value at a_StackPos, if it is a valid cBoundingBox class. If not, a_Value is unchanged */
- void GetStackValue(int a_StackPos, pBoundingBox & a_Value);
-
- /** Retrieve value at a_StackPos, if it is a valid cWorld class. If not, a_Value is unchanged */
- void GetStackValue(int a_StackPos, pWorld & a_Value);
-
- /** Store the value at a_StackPos as a reference. */
- void GetStackValue(int a_StackPos, cRef & a_Ref);
+ // Include the auto-generated Push and GetStackValue() functions:
+ #include "LuaState_Declaration.inc"
/** Call the specified Lua function.
Returns true if call succeeded, false if there was an error.
@@ -303,6 +248,10 @@ public:
/** Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters) */
bool CheckParamEnd(int a_Param);
+ bool IsParamUserType(int a_Param, AString a_UserType);
+
+ bool IsParamNumber(int a_Param);
+
/** If the status is nonzero, prints the text on the top of Lua stack and returns true */
bool ReportErrors(int status);
@@ -429,7 +378,7 @@ protected:
bool PushFunction(const cTableRef & a_TableRef);
/** Pushes a usertype of the specified class type onto the stack */
- void PushUserType(void * a_Object, const char * a_Type);
+ // void PushUserType(void * a_Object, const char * a_Type);
/**
Calls the function that has been pushed onto the stack by PushFunction(),
@@ -440,6 +389,9 @@ protected:
/** Used as the error reporting function for function calls */
static int ReportFnCallErrors(lua_State * a_LuaState);
+
+ /** Tries to break into the MobDebug debugger, if it is installed. */
+ static int BreakIntoDebugger(lua_State * a_LuaState);
} ;
diff --git a/src/Bindings/LuaWindow.cpp b/src/Bindings/LuaWindow.cpp
index d4014059b..5f7832ef5 100644
--- a/src/Bindings/LuaWindow.cpp
+++ b/src/Bindings/LuaWindow.cpp
@@ -159,7 +159,7 @@ void cLuaWindow::Destroy(void)
m_Plugin->Unreference(m_LuaRef);
}
- // Lua will take care of this object, it will garbage-collect it, so we *must not* delete it!
+ // Lua will take care of this object, it will garbage-collect it, so we must not delete it!
m_IsDestroyed = false;
}
@@ -167,10 +167,10 @@ void cLuaWindow::Destroy(void)
-void cLuaWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer& a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply)
+void cLuaWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply)
{
cSlotAreas Areas;
- for (auto Area : m_SlotAreas)
+ for (auto && Area : m_SlotAreas)
{
if (Area != a_ClickedArea)
{
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index 6e579b364..ff904d74a 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -5,6 +5,7 @@
#undef TOLUA_TEMPLATE_BIND
#include <sstream>
#include <iomanip>
+#include <array>
#include "tolua++/include/tolua++.h"
#include "polarssl/md5.h"
#include "polarssl/sha1.h"
@@ -32,13 +33,14 @@
#include "../WorldStorage/SchematicFileSerializer.h"
#include "../CompositeChat.h"
#include "../StringCompression.h"
+#include "../CommandOutput.h"
// Better error reporting for Lua
-static int tolua_do_error(lua_State* L, const char * a_pMsg, tolua_Error * a_pToLuaError)
+int cManualBindings::tolua_do_error(lua_State * L, const char * a_pMsg, tolua_Error * a_pToLuaError)
{
// Retrieve current function name
lua_Debug entry;
@@ -58,7 +60,7 @@ static int tolua_do_error(lua_State* L, const char * a_pMsg, tolua_Error * a_pTo
-static int lua_do_error(lua_State* L, const char * a_pFormat, ...)
+int cManualBindings::lua_do_error(lua_State * L, const char * a_pFormat, ...)
{
// Retrieve current function name
lua_Debug entry;
@@ -67,7 +69,7 @@ static int lua_do_error(lua_State* L, const char * a_pFormat, ...)
// Insert function name into error msg
AString msg(a_pFormat);
- ReplaceString(msg, "#funcname#", entry.name?entry.name:"?");
+ ReplaceString(msg, "#funcname#", (entry.name != nullptr) ? entry.name : "?");
// Copied from luaL_error and modified
va_list argp;
@@ -90,12 +92,12 @@ static int tolua_Clamp(lua_State * tolua_S)
int NumArgs = lua_gettop(LuaState);
if (NumArgs != 3)
{
- return lua_do_error(LuaState, "Error in function call '#funcname#': Requires 3 arguments, got %i", NumArgs);
+ return cManualBindings::lua_do_error(LuaState, "Error in function call '#funcname#': Requires 3 arguments, got %i", NumArgs);
}
if (!lua_isnumber(LuaState, 1) || !lua_isnumber(LuaState, 2) || !lua_isnumber(LuaState, 3))
{
- return lua_do_error(LuaState, "Error in function call '#funcname#': Expected a number for parameters #1, #2 and #3");
+ return cManualBindings::lua_do_error(LuaState, "Error in function call '#funcname#': Expected a number for parameters #1, #2 and #3");
}
lua_Number Number = tolua_tonumber(LuaState, 1, 0);
@@ -253,12 +255,13 @@ static int tolua_InflateString(lua_State * tolua_S)
static int tolua_StringSplit(lua_State * tolua_S)
{
+ // Get the params:
cLuaState LuaState(tolua_S);
- std::string str = (std::string)tolua_tocppstring(LuaState, 1, 0);
- std::string delim = (std::string)tolua_tocppstring(LuaState, 2, 0);
+ AString str, delim;
+ LuaState.GetStackValues(1, str, delim);
- AStringVector Split = StringSplit(str, delim);
- LuaState.Push(Split);
+ // Execute and push the result:
+ LuaState.Push(StringSplit(str, delim));
return 1;
}
@@ -286,12 +289,20 @@ static int tolua_StringSplitWithQuotes(lua_State * tolua_S)
static int tolua_StringSplitAndTrim(lua_State * tolua_S)
{
- cLuaState LuaState(tolua_S);
- std::string str = (std::string)tolua_tocppstring(LuaState, 1, 0);
- std::string delim = (std::string)tolua_tocppstring(LuaState, 2, 0);
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamString(1, 2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
- AStringVector Split = StringSplitAndTrim(str, delim);
- LuaState.Push(Split);
+ // Process:
+ AString str, delim;
+ L.GetStackValues(1, str, delim);
+ L.Push(StringSplitAndTrim(str, delim));
return 1;
}
@@ -450,7 +461,7 @@ static int tolua_Base64Decode(lua_State * tolua_S)
-cPluginLua * GetLuaPlugin(lua_State * L)
+cPluginLua * cManualBindings::GetLuaPlugin(lua_State * L)
{
// Get the plugin identification out of LuaState:
lua_getglobal(L, LUA_PLUGIN_INSTANCE_VAR_NAME);
@@ -472,6 +483,7 @@ cPluginLua * GetLuaPlugin(lua_State * L)
static int tolua_cFile_GetFolderContents(lua_State * tolua_S)
{
+ // Check params:
cLuaState LuaState(tolua_S);
if (
!LuaState.CheckParamUserTable(1, "cFile") ||
@@ -482,10 +494,12 @@ static int tolua_cFile_GetFolderContents(lua_State * tolua_S)
return 0;
}
- AString Folder = (AString)tolua_tocppstring(LuaState, 2, 0);
+ // Get params:
+ AString Folder;
+ LuaState.GetStackValues(2, Folder);
- AStringVector Contents = cFile::GetFolderContents(Folder);
- LuaState.Push(Contents);
+ // Execute and push result:
+ LuaState.Push(cFile::GetFolderContents(Folder));
return 1;
}
@@ -493,447 +507,25 @@ static int tolua_cFile_GetFolderContents(lua_State * tolua_S)
-template <
- class Ty1,
- class Ty2,
- bool (Ty1::*Func1)(const AString &, cItemCallback<Ty2> &)
->
-static int tolua_DoWith(lua_State* tolua_S)
-{
- int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
- if ((NumArgs != 2) && (NumArgs != 3))
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 2 or 3 arguments, got %i", NumArgs);
- }
-
- Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, nullptr);
-
- const char * ItemName = tolua_tocppstring(tolua_S, 2, "");
- if ((ItemName == nullptr) || (ItemName[0] == 0))
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a non-empty string for parameter #1", NumArgs);
- }
- if (!lua_isfunction( tolua_S, 3))
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #2", NumArgs);
- }
-
- /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */
- int TableRef = LUA_REFNIL;
- if (NumArgs == 3)
- {
- TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
- if (TableRef == LUA_REFNIL)
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #3", NumArgs);
- }
- }
-
- /* table value is popped, and now function is on top of the stack */
- int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
- if (FuncRef == LUA_REFNIL)
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #2", NumArgs);
- }
-
- class cLuaCallback : public cItemCallback<Ty2>
- {
- public:
- cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef)
- : LuaState( a_LuaState)
- , FuncRef( a_FuncRef)
- , TableRef( a_TableRef)
- {}
-
- private:
- virtual bool Item(Ty2 * a_Item) override
- {
- lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */
- tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic());
- if (TableRef != LUA_REFNIL)
- {
- lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */
- }
-
- int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0);
- if (cLuaState::ReportErrors(LuaState, s))
- {
- return true; // Abort enumeration
- }
- if (lua_isboolean(LuaState, -1))
- {
- return (tolua_toboolean(LuaState, -1, 0) > 0);
- }
- return false; /* Continue enumeration */
- }
- lua_State * LuaState;
- int FuncRef;
- int TableRef;
- } Callback(tolua_S, FuncRef, TableRef);
-
-
- bool bRetVal = (self->*Func1)(ItemName, Callback);
-
- /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */
- luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef);
- luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef);
-
- /* Push return value on stack */
- tolua_pushboolean(tolua_S, bRetVal);
- return 1;
-}
-
-
-
-
-
-template <
- class Ty1,
- class Ty2,
- bool (Ty1::*Func1)(UInt32, cItemCallback<Ty2> &)
->
-static int tolua_DoWithID(lua_State* tolua_S)
-{
- int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
- if ((NumArgs != 2) && (NumArgs != 3))
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 2 or 3 arguments, got %i", NumArgs);
- }
-
- Ty1 * self = (Ty1 *)tolua_tousertype(tolua_S, 1, nullptr);
-
- int ItemID = (int)tolua_tonumber(tolua_S, 2, 0);
- if (!lua_isfunction(tolua_S, 3))
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #2", NumArgs);
- }
-
- /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */
- int TableRef = LUA_REFNIL;
- if (NumArgs == 3)
- {
- TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
- if (TableRef == LUA_REFNIL)
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #3", NumArgs);
- }
- }
-
- /* table value is popped, and now function is on top of the stack */
- int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
- if (FuncRef == LUA_REFNIL)
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #2", NumArgs);
- }
-
- class cLuaCallback : public cItemCallback<Ty2>
- {
- public:
- cLuaCallback(lua_State * a_LuaState, int a_FuncRef, int a_TableRef) :
- LuaState(a_LuaState),
- FuncRef(a_FuncRef),
- TableRef(a_TableRef)
- {}
-
- private:
- virtual bool Item(Ty2 * a_Item) override
- {
- lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); // Push function to call
- tolua_pushusertype(LuaState, a_Item, a_Item->GetClass()); // Push the item
- if (TableRef != LUA_REFNIL)
- {
- lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); // Push the optional callbackdata param
- }
-
- int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0);
- if (cLuaState::ReportErrors(LuaState, s))
- {
- return true; // Abort enumeration
- }
- if (lua_isboolean(LuaState, -1))
- {
- return (tolua_toboolean(LuaState, -1, 0) > 0);
- }
- return false; /* Continue enumeration */
- }
- lua_State * LuaState;
- int FuncRef;
- int TableRef;
- } Callback(tolua_S, FuncRef, TableRef);
-
-
- bool bRetVal = (self->*Func1)(ItemID, Callback);
-
- /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */
- luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef);
- luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef);
-
- /* Push return value on stack */
- tolua_pushboolean(tolua_S, bRetVal);
- return 1;
-}
-
-
-
-
-
-template <
- class Ty1,
- class Ty2,
- bool (Ty1::*Func1)(int, int, int, cItemCallback<Ty2> &)
->
-static int tolua_DoWithXYZ(lua_State* tolua_S)
-{
- int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
- if ((NumArgs != 4) && (NumArgs != 5))
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 4 or 5 arguments, got %i", NumArgs);
- }
-
- Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, nullptr);
- if (!lua_isnumber(tolua_S, 2) || !lua_isnumber(tolua_S, 3) || !lua_isnumber(tolua_S, 4))
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a number for parameters #1, #2 and #3");
- }
-
- int ItemX = ((int)tolua_tonumber(tolua_S, 2, 0));
- int ItemY = ((int)tolua_tonumber(tolua_S, 3, 0));
- int ItemZ = ((int)tolua_tonumber(tolua_S, 4, 0));
- if (!lua_isfunction( tolua_S, 5))
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #4");
- }
-
- /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */
- int TableRef = LUA_REFNIL;
- if (NumArgs == 5)
- {
- TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
- if (TableRef == LUA_REFNIL)
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #5");
- }
- }
-
- /* table value is popped, and now function is on top of the stack */
- int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
- if (FuncRef == LUA_REFNIL)
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #4");
- }
-
- class cLuaCallback : public cItemCallback<Ty2>
- {
- public:
- cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef)
- : LuaState( a_LuaState)
- , FuncRef( a_FuncRef)
- , TableRef( a_TableRef)
- {}
-
- private:
- virtual bool Item(Ty2 * a_Item) override
- {
- lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */
- tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic());
- if (TableRef != LUA_REFNIL)
- {
- lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */
- }
-
- int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0);
- if (cLuaState::ReportErrors(LuaState, s))
- {
- return true; // Abort enumeration
- }
- if (lua_isboolean(LuaState, -1))
- {
- return (tolua_toboolean(LuaState, -1, 0) > 0);
- }
- return false; /* Continue enumeration */
- }
- lua_State * LuaState;
- int FuncRef;
- int TableRef;
- } Callback(tolua_S, FuncRef, TableRef);
-
- bool bRetVal = (self->*Func1)(ItemX, ItemY, ItemZ, Callback);
-
- /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */
- luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef);
- luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef);
-
- /* Push return value on stack */
- tolua_pushboolean(tolua_S, bRetVal);
- return 1;
-}
-
-
-
-
-
-template <
- class Ty1,
- class Ty2,
- bool (Ty1::*Func1)(int, int, cItemCallback<Ty2> &)
->
-static int tolua_ForEachInChunk(lua_State * tolua_S)
-{
- int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
- if ((NumArgs != 3) && (NumArgs != 4))
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 3 or 4 arguments, got %i", NumArgs);
- }
-
- Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, nullptr);
- if (!lua_isnumber(tolua_S, 2) || !lua_isnumber(tolua_S, 3))
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a number for parameters #1 and #2");
- }
-
- int ChunkX = ((int)tolua_tonumber(tolua_S, 2, 0));
- int ChunkZ = ((int)tolua_tonumber(tolua_S, 3, 0));
-
- if (!lua_isfunction( tolua_S, 4))
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #3");
- }
-
- /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */
- int TableRef = LUA_REFNIL;
- if (NumArgs == 4)
- {
- TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
- if (TableRef == LUA_REFNIL)
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #4");
- }
- }
-
- /* table value is popped, and now function is on top of the stack */
- int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
- if (FuncRef == LUA_REFNIL)
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #3");
- }
-
- class cLuaCallback : public cItemCallback<Ty2>
- {
- public:
- cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef)
- : LuaState( a_LuaState)
- , FuncRef( a_FuncRef)
- , TableRef( a_TableRef)
- {}
-
- private:
- virtual bool Item(Ty2 * a_Item) override
- {
- lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */
- tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic());
- if (TableRef != LUA_REFNIL)
- {
- lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */
- }
-
- int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0);
- if (cLuaState::ReportErrors(LuaState, s))
- {
- return true; /* Abort enumeration */
- }
-
- if (lua_isboolean(LuaState, -1))
- {
- return (tolua_toboolean(LuaState, -1, 0) > 0);
- }
- return false; /* Continue enumeration */
- }
- lua_State * LuaState;
- int FuncRef;
- int TableRef;
- } Callback(tolua_S, FuncRef, TableRef);
-
- bool bRetVal = (self->*Func1)(ChunkX, ChunkZ, Callback);
-
- /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */
- luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef);
- luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef);
-
- /* Push return value on stack */
- tolua_pushboolean(tolua_S, bRetVal);
- return 1;
-}
-
-
-
-
-
-template <
- class Ty1,
- class Ty2,
- bool (Ty1::*Func1)(const cBoundingBox &, cItemCallback<Ty2> &)
->
-static int tolua_ForEachInBox(lua_State * tolua_S)
+static int tolua_cFile_ReadWholeFile(lua_State * tolua_S)
{
// Check params:
- cLuaState L(tolua_S);
+ cLuaState LuaState(tolua_S);
if (
- !L.CheckParamUserType(1, "cWorld") ||
- !L.CheckParamUserType(2, "cBoundingBox") ||
- !L.CheckParamFunction(3) ||
- !L.CheckParamEnd(4)
+ !LuaState.CheckParamUserTable(1, "cFile") ||
+ !LuaState.CheckParamString (2) ||
+ !LuaState.CheckParamEnd (3)
)
{
return 0;
}
-
- // Get the params:
- Ty1 * Self = nullptr;
- cBoundingBox * Box = nullptr;
- L.GetStackValues(1, Self, Box);
- if ((Self == nullptr) || (Box == nullptr))
- {
- LOGWARNING("Invalid world (%p) or boundingbox (%p)", Self, Box);
- L.LogStackTrace();
- return 0;
- }
- // Create a reference for the function:
- cLuaState::cRef FnRef(L, 3);
+ // Get params:
+ AString FileName;
+ LuaState.GetStackValues(2, FileName);
- // Callback wrapper for the Lua function:
- class cLuaCallback : public cItemCallback<Ty2>
- {
- public:
- cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FuncRef) :
- m_LuaState(a_LuaState),
- m_FnRef(a_FuncRef)
- {}
-
- private:
- // cItemCallback<Ty2> overrides:
- virtual bool Item(Ty2 * a_Item) override
- {
- bool res = false;
- if (!m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, res))
- {
- LOGWARNING("Failed to call Lua callback");
- m_LuaState.LogStackTrace();
- return true; // Abort enumeration
- }
-
- return res;
- }
- cLuaState & m_LuaState;
- cLuaState::cRef & m_FnRef;
- } Callback(L, FnRef);
-
- bool bRetVal = (Self->*Func1)(*Box, Callback);
-
- FnRef.UnRef();
-
- /* Push return value on stack */
- tolua_pushboolean(tolua_S, bRetVal);
+ // Execute and push result:
+ LuaState.Push(cFile::ReadWholeFile(FileName));
return 1;
}
@@ -941,535 +533,30 @@ static int tolua_ForEachInBox(lua_State * tolua_S)
-template <
- class Ty1,
- class Ty2,
- bool (Ty1::*Func1)(cItemCallback<Ty2> &)
->
-static int tolua_ForEach(lua_State * tolua_S)
-{
- int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
- if ((NumArgs != 1) && (NumArgs != 2))
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 1 or 2 arguments, got %i", NumArgs);
- }
-
- Ty1 * self = (Ty1 *)tolua_tousertype(tolua_S, 1, nullptr);
- if (self == nullptr)
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance");
- }
-
- if (!lua_isfunction( tolua_S, 2))
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1");
- }
-
- /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */
- int TableRef = LUA_REFNIL;
- if (NumArgs == 2)
- {
- TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
- if (TableRef == LUA_REFNIL)
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #2");
- }
- }
-
- /* table value is popped, and now function is on top of the stack */
- int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
- if (FuncRef == LUA_REFNIL)
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1");
- }
-
- class cLuaCallback : public cItemCallback<Ty2>
- {
- public:
- cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef)
- : LuaState( a_LuaState)
- , FuncRef( a_FuncRef)
- , TableRef( a_TableRef)
- {}
-
- private:
- virtual bool Item(Ty2 * a_Item) override
- {
- lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */
- tolua_pushusertype( LuaState, a_Item, Ty2::GetClassStatic());
- if (TableRef != LUA_REFNIL)
- {
- lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */
- }
-
- int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0);
- if (cLuaState::ReportErrors(LuaState, s))
- {
- return true; /* Abort enumeration */
- }
-
- if (lua_isboolean(LuaState, -1))
- {
- return (tolua_toboolean( LuaState, -1, 0) > 0);
- }
- return false; /* Continue enumeration */
- }
- lua_State * LuaState;
- int FuncRef;
- int TableRef;
- } Callback(tolua_S, FuncRef, TableRef);
-
- bool bRetVal = (self->*Func1)(Callback);
-
- /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */
- luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef);
- luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef);
-
- /* Push return value on stack */
- tolua_pushboolean(tolua_S, bRetVal);
- return 1;
-}
-
-
-
-
-
-static int tolua_cWorld_GetBlockInfo(lua_State * tolua_S)
-{
- // Exported manually, because tolua would generate useless additional parameters (a_BlockType .. a_BlockSkyLight)
- // Function signature: GetBlockInfo(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta, BlockSkyLight, BlockBlockLight]
- #ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) ||
- !tolua_isnumber (tolua_S, 2, 0, &tolua_err) ||
- !tolua_isnumber (tolua_S, 3, 0, &tolua_err) ||
- !tolua_isnumber (tolua_S, 4, 0, &tolua_err) ||
- !tolua_isnoobj (tolua_S, 5, &tolua_err)
- )
- goto tolua_lerror;
- else
- #endif
- {
- cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, nullptr);
- int BlockX = (int) tolua_tonumber (tolua_S, 2, 0);
- int BlockY = (int) tolua_tonumber (tolua_S, 3, 0);
- int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0);
- #ifndef TOLUA_RELEASE
- if (self == nullptr)
- {
- tolua_error(tolua_S, "invalid 'self' in function 'GetBlockInfo'", nullptr);
- }
- #endif
- {
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta, BlockSkyLight, BlockBlockLight;
- bool res = self->GetBlockInfo(BlockX, BlockY, BlockZ, BlockType, BlockMeta, BlockSkyLight, BlockBlockLight);
- tolua_pushboolean(tolua_S, res ? 1 : 0);
- if (res)
- {
- tolua_pushnumber(tolua_S, BlockType);
- tolua_pushnumber(tolua_S, BlockMeta);
- tolua_pushnumber(tolua_S, BlockSkyLight);
- tolua_pushnumber(tolua_S, BlockBlockLight);
- return 5;
- }
- }
- }
- return 1;
-
- #ifndef TOLUA_RELEASE
-tolua_lerror:
- tolua_error(tolua_S, "#ferror in function 'GetBlockInfo'.", &tolua_err);
- return 0;
- #endif
-}
-
-
-
-
-
-static int tolua_cWorld_GetBlockTypeMeta(lua_State * tolua_S)
-{
- // Exported manually, because tolua would generate useless additional parameters (a_BlockType, a_BlockMeta)
- // Function signature: GetBlockTypeMeta(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta]
- #ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) ||
- !tolua_isnumber (tolua_S, 2, 0, &tolua_err) ||
- !tolua_isnumber (tolua_S, 3, 0, &tolua_err) ||
- !tolua_isnumber (tolua_S, 4, 0, &tolua_err) ||
- !tolua_isnoobj (tolua_S, 5, &tolua_err)
- )
- goto tolua_lerror;
- else
- #endif
- {
- cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, nullptr);
- int BlockX = (int) tolua_tonumber (tolua_S, 2, 0);
- int BlockY = (int) tolua_tonumber (tolua_S, 3, 0);
- int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0);
- #ifndef TOLUA_RELEASE
- if (self == nullptr)
- {
- tolua_error(tolua_S, "invalid 'self' in function 'GetBlockTypeMeta'", nullptr);
- }
- #endif
- {
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- bool res = self->GetBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta);
- tolua_pushboolean(tolua_S, res ? 1 : 0);
- if (res)
- {
- tolua_pushnumber(tolua_S, BlockType);
- tolua_pushnumber(tolua_S, BlockMeta);
- return 3;
- }
- }
- }
- return 1;
-
- #ifndef TOLUA_RELEASE
-tolua_lerror:
- tolua_error(tolua_S, "#ferror in function 'GetBlockTypeMeta'.", &tolua_err);
- return 0;
- #endif
-}
-
-
-
-
-
-static int tolua_cWorld_GetSignLines(lua_State * tolua_S)
-{
- // Exported manually, because tolua would generate useless additional parameters (a_Line1 .. a_Line4)
- #ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) ||
- !tolua_isnumber (tolua_S, 2, 0, &tolua_err) ||
- !tolua_isnumber (tolua_S, 3, 0, &tolua_err) ||
- !tolua_isnumber (tolua_S, 4, 0, &tolua_err) ||
- !tolua_isnoobj (tolua_S, 10, &tolua_err)
- )
- goto tolua_lerror;
- else
- #endif
- {
- cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, nullptr);
- int BlockX = (int) tolua_tonumber (tolua_S, 2, 0);
- int BlockY = (int) tolua_tonumber (tolua_S, 3, 0);
- int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0);
- #ifndef TOLUA_RELEASE
- if (self == nullptr)
- {
- tolua_error(tolua_S, "invalid 'self' in function 'GetSignLines'", nullptr);
- }
- #endif
- {
- AString Line1, Line2, Line3, Line4;
- bool res = self->GetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4);
- tolua_pushboolean(tolua_S, res ? 1 : 0);
- if (res)
- {
- tolua_pushstring(tolua_S, Line1.c_str());
- tolua_pushstring(tolua_S, Line2.c_str());
- tolua_pushstring(tolua_S, Line3.c_str());
- tolua_pushstring(tolua_S, Line4.c_str());
- return 5;
- }
- }
- }
- return 1;
-
- #ifndef TOLUA_RELEASE
-tolua_lerror:
- tolua_error(tolua_S, "#ferror in function 'GetSignLines'.", &tolua_err);
- return 0;
- #endif
-}
-
-
-
-
-
-static int tolua_cWorld_SetSignLines(lua_State * tolua_S)
+static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S)
{
- // Exported manually, because tolua would generate useless additional return values (a_Line1 .. a_Line4)
- #ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) ||
- !tolua_isnumber (tolua_S, 2, 0, &tolua_err) ||
- !tolua_isnumber (tolua_S, 3, 0, &tolua_err) ||
- !tolua_isnumber (tolua_S, 4, 0, &tolua_err) ||
- !tolua_iscppstring(tolua_S, 5, 0, &tolua_err) ||
- !tolua_iscppstring(tolua_S, 6, 0, &tolua_err) ||
- !tolua_iscppstring(tolua_S, 7, 0, &tolua_err) ||
- !tolua_iscppstring(tolua_S, 8, 0, &tolua_err) ||
- !tolua_isusertype (tolua_S, 9, "cPlayer", 1, &tolua_err) ||
- !tolua_isnoobj (tolua_S, 10, &tolua_err)
- )
- goto tolua_lerror;
- else
- #endif
- {
- cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, nullptr);
- int BlockX = (int) tolua_tonumber (tolua_S, 2, 0);
- int BlockY = (int) tolua_tonumber (tolua_S, 3, 0);
- int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0);
- const AString Line1 = tolua_tocppstring(tolua_S, 5, 0);
- const AString Line2 = tolua_tocppstring(tolua_S, 6, 0);
- const AString Line3 = tolua_tocppstring(tolua_S, 7, 0);
- const AString Line4 = tolua_tocppstring(tolua_S, 8, 0);
- cPlayer * Player = (cPlayer *)tolua_tousertype (tolua_S, 9, nullptr);
- #ifndef TOLUA_RELEASE
- if (self == nullptr)
- {
- tolua_error(tolua_S, "invalid 'self' in function 'SetSignLines'", nullptr);
- }
- #endif
- {
- bool res = self->SetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4, Player);
- tolua_pushboolean(tolua_S, res ? 1 : 0);
- }
- }
- return 1;
-
- #ifndef TOLUA_RELEASE
-tolua_lerror:
- tolua_error(tolua_S, "#ferror in function 'SetSignLines'.", &tolua_err);
- return 0;
- #endif
-}
-
-
-
+ // API function no longer available:
+ LOGWARNING("cPluginManager:GetAllPlugins() is no longer available, use cPluginManager:ForEachPlugin() instead");
+ cLuaState::LogStackTrace(tolua_S);
-static int tolua_cWorld_TryGetHeight(lua_State * tolua_S)
-{
- // Exported manually, because tolua would require the out-only param a_Height to be used when calling
- // Takes a_World, a_BlockX, a_BlockZ
- // Returns Height, IsValid
- #ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) ||
- !tolua_isnumber (tolua_S, 2, 0, &tolua_err) ||
- !tolua_isnumber (tolua_S, 3, 0, &tolua_err) ||
- !tolua_isnoobj (tolua_S, 4, &tolua_err)
- )
- goto tolua_lerror;
- else
- #endif
- {
- cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, nullptr);
- int BlockX = (int) tolua_tonumber (tolua_S, 2, 0);
- int BlockZ = (int) tolua_tonumber (tolua_S, 3, 0);
- #ifndef TOLUA_RELEASE
- if (self == nullptr)
- {
- tolua_error(tolua_S, "Invalid 'self' in function 'TryGetHeight'", nullptr);
- }
- #endif
- {
- int Height = 0;
- bool res = self->TryGetHeight(BlockX, BlockZ, Height);
- tolua_pushboolean(tolua_S, res ? 1 : 0);
- if (res)
- {
- tolua_pushnumber(tolua_S, Height);
- return 2;
- }
- }
- }
+ // Return an empty table:
+ lua_newtable(tolua_S);
return 1;
-
- #ifndef TOLUA_RELEASE
-tolua_lerror:
- tolua_error(tolua_S, "#ferror in function 'TryGetHeight'.", &tolua_err);
- return 0;
- #endif
}
-class cLuaWorldTask :
- public cWorld::cTask,
- public cPluginLua::cResettable
-{
-public:
- cLuaWorldTask(cPluginLua & a_Plugin, int a_FnRef) :
- cPluginLua::cResettable(a_Plugin),
- m_FnRef(a_FnRef)
- {
- }
-
-protected:
- int m_FnRef;
-
- // cWorld::cTask overrides:
- virtual void Run(cWorld & a_World) override
- {
- cCSLock Lock(m_CSPlugin);
- if (m_Plugin != nullptr)
- {
- m_Plugin->Call(m_FnRef, &a_World);
- }
- }
-} ;
-
-
-
-
-
-static int tolua_cWorld_QueueTask(lua_State * tolua_S)
-{
- // Binding for cWorld::QueueTask
- // Params: function
-
- // Retrieve the cPlugin from the LuaState:
- cPluginLua * Plugin = GetLuaPlugin(tolua_S);
- if (Plugin == nullptr)
- {
- // An error message has been already printed in GetLuaPlugin()
- return 0;
- }
-
- // Retrieve the args:
- cWorld * self = (cWorld *)tolua_tousertype(tolua_S, 1, nullptr);
- if (self == nullptr)
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance");
- }
- if (!lua_isfunction(tolua_S, 2))
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1");
- }
-
- // Create a reference to the function:
- int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
- if (FnRef == LUA_REFNIL)
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1");
- }
-
- auto task = std::make_shared<cLuaWorldTask>(*Plugin, FnRef);
- Plugin->AddResettable(task);
- self->QueueTask(task);
- return 0;
-}
-
-
-
-
-
-class cLuaScheduledWorldTask :
- public cWorld::cTask,
- public cPluginLua::cResettable
-{
-public:
- cLuaScheduledWorldTask(cPluginLua & a_Plugin, int a_FnRef) :
- cPluginLua::cResettable(a_Plugin),
- m_FnRef(a_FnRef)
- {
- }
-
-protected:
- int m_FnRef;
-
- // cWorld::cTask overrides:
- virtual void Run(cWorld & a_World) override
- {
- cCSLock Lock(m_CSPlugin);
- if (m_Plugin != nullptr)
- {
- m_Plugin->Call(m_FnRef, &a_World);
- }
- }
-};
-
-
-
-
-
-static int tolua_cWorld_ScheduleTask(lua_State * tolua_S)
+static int tolua_cPluginManager_GetCurrentPlugin(lua_State * S)
{
- // Binding for cWorld::ScheduleTask
- // Params: function, Ticks
-
- // Retrieve the cPlugin from the LuaState:
- cPluginLua * Plugin = GetLuaPlugin(tolua_S);
+ cPluginLua * Plugin = cManualBindings::GetLuaPlugin(S);
if (Plugin == nullptr)
{
- // An error message has been already printed in GetLuaPlugin()
- return 0;
- }
-
- // Retrieve the args:
- cLuaState L(tolua_S);
- if (
- !L.CheckParamUserType(1, "cWorld") ||
- !L.CheckParamNumber (2) ||
- !L.CheckParamFunction(3)
- )
- {
+ // An error message has already been printed in GetLuaPlugin()
return 0;
}
- cWorld * World = (cWorld *)tolua_tousertype(tolua_S, 1, nullptr);
- if (World == nullptr)
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance");
- }
-
- // Create a reference to the function:
- int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
- if (FnRef == LUA_REFNIL)
- {
- return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1");
- }
-
- int DelayTicks = (int)tolua_tonumber(tolua_S, 2, 0);
-
- auto task = std::make_shared<cLuaScheduledWorldTask>(*Plugin, FnRef);
- Plugin->AddResettable(task);
- World->ScheduleTask(DelayTicks, task);
- return 0;
-}
-
-
-
-
-
-static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S)
-{
- cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, nullptr);
-
- const cPluginManager::PluginMap & AllPlugins = self->GetAllPlugins();
-
- lua_newtable(tolua_S);
- int index = 1;
- cPluginManager::PluginMap::const_iterator iter = AllPlugins.begin();
- while (iter != AllPlugins.end())
- {
- const cPlugin* Plugin = iter->second;
- tolua_pushstring(tolua_S, iter->first.c_str());
- if (Plugin != nullptr)
- {
- tolua_pushusertype(tolua_S, (void *)Plugin, "const cPlugin");
- }
- else
- {
- tolua_pushboolean(tolua_S, 0);
- }
- lua_rawset(tolua_S, -3);
- ++iter;
- ++index;
- }
+ tolua_pushusertype(S, Plugin, "cPluginLua");
return 1;
}
@@ -1477,16 +564,12 @@ static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S)
-static int tolua_cPluginManager_GetCurrentPlugin(lua_State * S)
+static int tolua_cPluginManager_GetPlugin(lua_State * tolua_S)
{
- cPluginLua * Plugin = GetLuaPlugin(S);
- if (Plugin == nullptr)
- {
- // An error message has already been printed in GetLuaPlugin()
- return 0;
- }
- tolua_pushusertype(S, Plugin, "cPluginLua");
- return 1;
+ // API function no longer available:
+ LOGWARNING("cPluginManager:GetPlugin() is no longer available. Use cPluginManager:DoWithPlugin() or cPluginManager:CallPlugin() instead.");
+ cLuaState::LogStackTrace(tolua_S);
+ return 0;
}
@@ -1510,7 +593,7 @@ static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager,
// The arg types have already been checked
// Retrieve the cPlugin from the LuaState:
- cPluginLua * Plugin = GetLuaPlugin(S);
+ cPluginLua * Plugin = cManualBindings::GetLuaPlugin(S);
if (Plugin == nullptr)
{
// An error message has been already printed in GetLuaPlugin()
@@ -1557,7 +640,7 @@ static int tolua_cPluginManager_AddHook_DefFn(cPluginManager * a_PluginManager,
S.LogStackTrace();
return 0;
}
- if (Plugin != GetLuaPlugin(S))
+ if (Plugin != cManualBindings::GetLuaPlugin(S))
{
// The plugin parameter passed to us is not our stored plugin. Disallow this!
LOGWARNING("cPluginManager.AddHook(): Invalid Plugin parameter, cannot add hook to foreign plugins. Hook not added.");
@@ -1664,74 +747,55 @@ static int tolua_cPluginManager_AddHook(lua_State * tolua_S)
static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S)
{
- int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
- if (NumArgs != 1)
- {
- LOGWARN("Error in function call 'ForEachCommand': Requires 1 argument, got %i", NumArgs);
- return 0;
- }
-
- cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, nullptr);
- if (self == nullptr)
- {
- LOGWARN("Error in function call 'ForEachCommand': Not called on an object instance");
- return 0;
- }
+ /*
+ Function signature:
+ cPluginManager:Get():ForEachCommand(a_CallbackFn) -> bool
+ */
- if (!lua_isfunction(tolua_S, 2))
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cPluginManager") ||
+ !L.CheckParamFunction(2) ||
+ !L.CheckParamEnd(3)
+ )
{
- LOGWARN("Error in function call 'ForEachCommand': Expected a function for parameter #1");
return 0;
}
- int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
- if (FuncRef == LUA_REFNIL)
+ // Get the params:
+ cLuaState::cRef FnRef;
+ L.GetStackValues(2, FnRef);
+ if (!FnRef.IsValid())
{
LOGWARN("Error in function call 'ForEachCommand': Could not get function reference of parameter #1");
return 0;
}
+ // Callback for enumerating all commands:
class cLuaCallback : public cPluginManager::cCommandEnumCallback
{
public:
- cLuaCallback(lua_State * a_LuaState, int a_FuncRef)
- : LuaState( a_LuaState)
- , FuncRef( a_FuncRef)
- {}
+ cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
+ m_LuaState(a_LuaState),
+ m_FnRef(a_FnRef)
+ {
+ }
private:
virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override
{
UNUSED(a_Plugin);
-
- lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */
- tolua_pushcppstring(LuaState, a_Command);
- tolua_pushcppstring(LuaState, a_Permission);
- tolua_pushcppstring(LuaState, a_HelpString);
-
- int s = lua_pcall(LuaState, 3, 1, 0);
- if (cLuaState::ReportErrors(LuaState, s))
- {
- return true; /* Abort enumeration */
- }
-
- if (lua_isboolean(LuaState, -1))
- {
- return (tolua_toboolean( LuaState, -1, 0) > 0);
- }
- return false; /* Continue enumeration */
+ bool ret = false;
+ m_LuaState.Call(m_FnRef, a_Command, a_Permission, a_HelpString, cLuaState::Return, ret);
+ return ret;
}
- lua_State * LuaState;
- int FuncRef;
- } Callback(tolua_S, FuncRef);
-
- bool bRetVal = self->ForEachCommand(Callback);
-
- /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */
- luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef);
+ cLuaState & m_LuaState;
+ cLuaState::cRef & m_FnRef;
+ } Callback(L, FnRef);
- /* Push return value on stack */
- tolua_pushboolean(tolua_S, bRetVal);
+ // Execute and push the returned value:
+ L.Push(cPluginManager::Get()->ForEachCommand(Callback));
return 1;
}
@@ -1741,74 +805,56 @@ static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S)
static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S)
{
- int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
- if (NumArgs != 1)
- {
- LOGWARN("Error in function call 'ForEachConsoleCommand': Requires 1 argument, got %i", NumArgs);
- return 0;
- }
-
- cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, nullptr);
- if (self == nullptr)
- {
- LOGWARN("Error in function call 'ForEachConsoleCommand': Not called on an object instance");
- return 0;
- }
+ /*
+ Function signature:
+ cPluginManager:Get():ForEachConsoleCommand(a_CallbackFn) -> bool
+ */
- if (!lua_isfunction(tolua_S, 2))
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cPluginManager") ||
+ !L.CheckParamFunction(2) ||
+ !L.CheckParamEnd(3)
+ )
{
- LOGWARN("Error in function call 'ForEachConsoleCommand': Expected a function for parameter #1");
return 0;
}
- int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
- if (FuncRef == LUA_REFNIL)
+ // Get the params:
+ cLuaState::cRef FnRef;
+ L.GetStackValues(2, FnRef);
+ if (!FnRef.IsValid())
{
LOGWARN("Error in function call 'ForEachConsoleCommand': Could not get function reference of parameter #1");
return 0;
}
+ // Callback for enumerating all commands:
class cLuaCallback : public cPluginManager::cCommandEnumCallback
{
public:
- cLuaCallback(lua_State * a_LuaState, int a_FuncRef)
- : LuaState( a_LuaState)
- , FuncRef( a_FuncRef)
- {}
+ cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
+ m_LuaState(a_LuaState),
+ m_FnRef(a_FnRef)
+ {
+ }
private:
virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override
{
UNUSED(a_Plugin);
UNUSED(a_Permission);
-
- lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */
- tolua_pushcppstring(LuaState, a_Command);
- tolua_pushcppstring(LuaState, a_HelpString);
-
- int s = lua_pcall(LuaState, 2, 1, 0);
- if (cLuaState::ReportErrors(LuaState, s))
- {
- return true; /* Abort enumeration */
- }
-
- if (lua_isboolean(LuaState, -1))
- {
- return (tolua_toboolean( LuaState, -1, 0) > 0);
- }
- return false; /* Continue enumeration */
+ bool ret = false;
+ m_LuaState.Call(m_FnRef, a_Command, a_HelpString, cLuaState::Return, ret);
+ return ret;
}
- lua_State * LuaState;
- int FuncRef;
- } Callback(tolua_S, FuncRef);
-
- bool bRetVal = self->ForEachConsoleCommand(Callback);
-
- /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */
- luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef);
+ cLuaState & m_LuaState;
+ cLuaState::cRef & m_FnRef;
+ } Callback(L, FnRef);
- /* Push return value on stack */
- tolua_pushboolean(tolua_S, bRetVal);
+ // Execute and push the returned value:
+ L.Push(cPluginManager::Get()->ForEachConsoleCommand(Callback));
return 1;
}
@@ -1822,7 +868,7 @@ static int tolua_cPluginManager_BindCommand(lua_State * L)
cPluginManager:BindCommand(Command, Permission, Function, HelpString)
cPluginManager.BindCommand(Command, Permission, Function, HelpString) -- without the "self" param
*/
- cPluginLua * Plugin = GetLuaPlugin(L);
+ cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L);
if (Plugin == nullptr)
{
return 0;
@@ -1892,7 +938,7 @@ static int tolua_cPluginManager_BindConsoleCommand(lua_State * L)
*/
// Get the plugin identification out of LuaState:
- cPluginLua * Plugin = GetLuaPlugin(L);
+ cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L);
if (Plugin == nullptr)
{
return 0;
@@ -1980,7 +1026,7 @@ static int tolua_cPluginManager_CallPlugin(lua_State * tolua_S)
}
// If requesting calling the current plugin, refuse:
- cPluginLua * ThisPlugin = GetLuaPlugin(L);
+ cPluginLua * ThisPlugin = cManualBindings::GetLuaPlugin(L);
if (ThisPlugin == nullptr)
{
return 0;
@@ -2011,7 +1057,11 @@ static int tolua_cPluginManager_CallPlugin(lua_State * tolua_S)
virtual bool Item(cPlugin * a_Plugin) override
{
- m_NumReturns = ((cPluginLua *)a_Plugin)->CallFunctionFromForeignState(
+ if (!a_Plugin->IsLoaded())
+ {
+ return false;
+ }
+ m_NumReturns = static_cast<cPluginLua *>(a_Plugin)->CallFunctionFromForeignState(
m_FunctionName, m_SrcLuaState, 4, lua_gettop(m_SrcLuaState)
);
return true;
@@ -2019,9 +1069,11 @@ static int tolua_cPluginManager_CallPlugin(lua_State * tolua_S)
} Callback(FunctionName, L);
if (!cPluginManager::Get()->DoWithPlugin(PluginName, Callback))
{
- // TODO 2014_01_20 _X: This might be too much logging, plugins cannot know if other plugins are loaded (async)
- LOGWARNING("cPluginManager::CallPlugin: No such plugin name (\"%s\")", PluginName.c_str());
- L.LogStackTrace();
+ return 0;
+ }
+ if (Callback.m_NumReturns < 0)
+ {
+ // The call has failed, there are zero return values. Do NOT return negative number (Lua considers that a "yield")
return 0;
}
return Callback.m_NumReturns;
@@ -2031,48 +1083,48 @@ static int tolua_cPluginManager_CallPlugin(lua_State * tolua_S)
-static int tolua_cWorld_ChunkStay(lua_State * tolua_S)
+static int tolua_cPluginManager_ExecuteConsoleCommand(lua_State * tolua_S)
{
- /* Function signature:
- World:ChunkStay(ChunkCoordTable, OnChunkAvailable, OnAllChunksAvailable)
- ChunkCoordTable == { {Chunk1x, Chunk1z}, {Chunk2x, Chunk2z}, ... }
+ /*
+ Function signature:
+ cPluginManager:ExecuteConsoleCommand(EntireCommandStr) -> OutputString
*/
-
+
+ // Check params:
cLuaState L(tolua_S);
if (
- !L.CheckParamUserType (1, "cWorld") ||
- !L.CheckParamTable (2) ||
- !L.CheckParamFunctionOrNil(3, 4)
+ !L.CheckParamUserTable(1, "cPluginManager") ||
+ !L.CheckParamString(2) ||
+ !L.CheckParamEnd(3)
)
{
return 0;
}
-
- cPluginLua * Plugin = GetLuaPlugin(tolua_S);
- if (Plugin == nullptr)
- {
- return 0;
- }
-
- // Read the params:
- cWorld * World = (cWorld *)tolua_tousertype(tolua_S, 1, nullptr);
- if (World == nullptr)
- {
- LOGWARNING("World:ChunkStay(): invalid world parameter");
- L.LogStackTrace();
- return 0;
- }
- cLuaChunkStay * ChunkStay = new cLuaChunkStay(*Plugin);
+ // Get the params:
+ AString Command;
+ L.GetStackValues(2, Command);
+ auto Split = StringSplit(Command, " ");
+
+ // Store the command output in a string:
+ cStringAccumCommandOutputCallback CommandOutput;
+ L.Push(cPluginManager::Get()->ExecuteConsoleCommand(Split, CommandOutput, Command));
+ L.Push(CommandOutput.GetAccum());
+ return 2;
+}
- if (!ChunkStay->AddChunks(2))
- {
- delete ChunkStay;
- ChunkStay = nullptr;
- return 0;
- }
- ChunkStay->Enable(*World->GetChunkMap(), 3, 4);
+
+
+
+static int tolua_cPluginManager_FindPlugins(lua_State * tolua_S)
+{
+ // API function no longer exists:
+ LOGWARNING("cPluginManager:FindPlugins() is obsolete, use cPluginManager:RefreshPluginList() instead!");
+ cLuaState::LogStackTrace(tolua_S);
+
+ // Still, do the actual work performed by the API function when it existed:
+ cPluginManager::Get()->RefreshPluginList();
return 0;
}
@@ -2080,75 +1132,40 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S)
-static int tolua_cWorld_PrepareChunk(lua_State * tolua_S)
+static int tolua_cPlayer_GetPermissions(lua_State * tolua_S)
{
- /* Function signature:
- World:PrepareChunk(ChunkX, ChunkZ, Callback)
- */
-
- // Check the param types:
+ // Function signature: cPlayer:GetPermissions() -> {permissions-array}
+
+ // Check the params:
cLuaState L(tolua_S);
if (
- !L.CheckParamUserType (1, "cWorld") ||
- !L.CheckParamNumber (2, 3) ||
- !L.CheckParamFunctionOrNil(4)
+ !L.CheckParamUserType(1, "cPlayer") ||
+ !L.CheckParamEnd (2)
)
{
return 0;
}
-
- // Read the params:
- cWorld * world = nullptr;
- int chunkX = 0, chunkZ = 0;
- L.GetStackValues(1, world, chunkX, chunkZ);
- if (world == nullptr)
+
+ // Get the params:
+ cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, nullptr);
+ if (self == nullptr)
{
- LOGWARNING("World:PrepareChunk(): invalid world parameter");
- L.LogStackTrace();
+ LOGWARNING("%s: invalid self (%p)", __FUNCTION__, self);
return 0;
}
-
- // Wrap the Lua callback inside a C++ callback class:
- class cCallback:
- public cChunkCoordCallback
- {
- public:
- cCallback(lua_State * a_LuaState):
- m_LuaState(a_LuaState),
- m_Callback(m_LuaState, 4)
- {
- }
-
- // cChunkCoordCallback override:
- virtual void Call(int a_CBChunkX, int a_CBChunkZ) override
- {
- if (m_Callback.IsValid())
- {
- m_LuaState.Call(m_Callback, a_CBChunkX, a_CBChunkZ);
- }
-
- // This is the last reference of this object, we must delete it so that it doesn't leak:
- delete this;
- }
-
- protected:
- cLuaState m_LuaState;
- cLuaState::cRef m_Callback;
- };
- cCallback * callback = new cCallback(tolua_S);
-
- // Call the chunk preparation:
- world->PrepareChunk(chunkX, chunkZ, callback);
- return 0;
+
+ // Push the permissions:
+ L.Push(self->GetPermissions());
+ return 1;
}
-static int tolua_cPlayer_GetPermissions(lua_State * tolua_S)
+static int tolua_cPlayer_GetRestrictions(lua_State * tolua_S)
{
- // Function signature: cPlayer:GetPermissions() -> {permissions-array}
+ // Function signature: cPlayer:GetRestrictions() -> {restrictions-array}
// Check the params:
cLuaState L(tolua_S);
@@ -2169,7 +1186,7 @@ static int tolua_cPlayer_GetPermissions(lua_State * tolua_S)
}
// Push the permissions:
- L.Push(self->GetPermissions());
+ L.Push(self->GetRestrictions());
return 1;
}
@@ -2182,7 +1199,7 @@ static int tolua_cPlayer_OpenWindow(lua_State * tolua_S)
// Function signature: cPlayer:OpenWindow(Window)
// Retrieve the plugin instance from the Lua state
- cPluginLua * Plugin = GetLuaPlugin(tolua_S);
+ cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S);
if (Plugin == nullptr)
{
return 0;
@@ -2263,7 +1280,7 @@ static int tolua_SetObjectCallback(lua_State * tolua_S)
// Function signature: OBJTYPE:SetWhateverCallback(CallbackFunction)
// Retrieve the plugin instance from the Lua state
- cPluginLua * Plugin = GetLuaPlugin(tolua_S);
+ cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S);
if (Plugin == nullptr)
{
// Warning message has already been printed by GetLuaPlugin(), bail out silently
@@ -2315,7 +1332,7 @@ static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S)
}
else
{
- return tolua_do_error(tolua_S, "#ferror calling function '#funcname#'", &tolua_err);
+ return cManualBindings::tolua_do_error(tolua_S, "#ferror calling function '#funcname#'", &tolua_err);
}
if (Reference != LUA_REFNIL)
@@ -2337,40 +1354,40 @@ static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S)
-static int tolua_cPluginLua_AddTab(lua_State* tolua_S)
+static int tolua_cPlugin_GetDirectory(lua_State * tolua_S)
{
- cPluginLua * self = (cPluginLua *) tolua_tousertype(tolua_S, 1, nullptr);
- LOGWARN("WARNING: Using deprecated function AddTab()! Use AddWebTab() instead. (plugin \"%s\" in folder \"%s\")",
- self->GetName().c_str(), self->GetDirectory().c_str()
- );
- return tolua_cPluginLua_AddWebTab( tolua_S);
+ cLuaState L(tolua_S);
+
+ // Log the obsoletion warning:
+ LOGWARNING("cPlugin:GetDirectory() is obsolete, use cPlugin:GetFolderName() instead.");
+ L.LogStackTrace();
+
+ // Retrieve the params:
+ cPlugin * Plugin = static_cast<cPluginLua *>(tolua_tousertype(tolua_S, 1, nullptr));
+
+ // Get the folder name:
+ L.Push(Plugin->GetFolderName());
+ return 1;
}
-static int tolua_cPlugin_Call(lua_State * tolua_S)
+static int tolua_cPlugin_GetLocalDirectory(lua_State * tolua_S)
{
cLuaState L(tolua_S);
// Log the obsoletion warning:
- LOGWARNING("cPlugin:Call() is obsolete and unsafe, use cPluginManager:CallPlugin() instead.");
+ LOGWARNING("cPlugin:GetLocalDirectory() is obsolete, use cPlugin:GetLocalFolder() instead.");
L.LogStackTrace();
- // Retrieve the params: plugin and the function name to call
- cPluginLua * TargetPlugin = (cPluginLua *) tolua_tousertype(tolua_S, 1, nullptr);
- AString FunctionName = tolua_tostring(tolua_S, 2, "");
+ // Retrieve the params:
+ cPlugin * Plugin = static_cast<cPluginLua *>(tolua_tousertype(tolua_S, 1, nullptr));
- // Call the function:
- int NumReturns = TargetPlugin->CallFunctionFromForeignState(FunctionName, L, 3, lua_gettop(L));
- if (NumReturns < 0)
- {
- LOGWARNING("cPlugin::Call() failed to call destination function");
- L.LogStackTrace();
- return 0;
- }
- return NumReturns;
+ // Get the folder:
+ L.Push(Plugin->GetLocalFolder());
+ return 1;
}
@@ -2488,8 +1505,8 @@ static int tolua_push_StringStringMap(lua_State* tolua_S, std::map< std::string,
for (std::map<std::string, std::string>::iterator it = a_StringStringMap.begin(); it != a_StringStringMap.end(); ++it)
{
- const char* key = it->first.c_str();
- const char* value = it->second.c_str();
+ const char * key = it->first.c_str();
+ const char * value = it->second.c_str();
lua_pushstring(tolua_S, key);
lua_pushstring(tolua_S, value);
lua_settable(tolua_S, top);
@@ -2504,7 +1521,7 @@ static int tolua_push_StringStringMap(lua_State* tolua_S, std::map< std::string,
static int tolua_get_HTTPRequest_Params(lua_State* tolua_S)
{
- HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S, 1, nullptr);
+ HTTPRequest * self = reinterpret_cast<HTTPRequest *>(tolua_tousertype(tolua_S, 1, nullptr));
return tolua_push_StringStringMap(tolua_S, self->Params);
}
@@ -2514,7 +1531,7 @@ static int tolua_get_HTTPRequest_Params(lua_State* tolua_S)
static int tolua_get_HTTPRequest_PostParams(lua_State* tolua_S)
{
- HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S, 1, nullptr);
+ HTTPRequest * self = reinterpret_cast<HTTPRequest *>(tolua_tousertype(tolua_S, 1, nullptr));
return tolua_push_StringStringMap(tolua_S, self->PostParams);
}
@@ -2524,8 +1541,8 @@ static int tolua_get_HTTPRequest_PostParams(lua_State* tolua_S)
static int tolua_get_HTTPRequest_FormData(lua_State* tolua_S)
{
- HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S, 1, nullptr);
- std::map< std::string, HTTPFormData >& FormData = self->FormData;
+ HTTPRequest * self = reinterpret_cast<HTTPRequest *>(tolua_tousertype(tolua_S, 1, nullptr));
+ std::map<std::string, HTTPFormData> & FormData = self->FormData;
lua_newtable(tolua_S);
int top = lua_gettop(tolua_S);
@@ -2628,22 +1645,16 @@ static int tolua_AllToLua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S)
static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S)
{
- cWebPlugin* self = (cWebPlugin*) tolua_tousertype(tolua_S, 1, nullptr);
-
- const cWebPlugin::TabNameList & TabNames = self->GetTabNames();
-
+ // Returns a map of (SafeTitle -> Title) for the plugin's web tabs.
+ auto self = reinterpret_cast<cWebPlugin *>(tolua_tousertype(tolua_S, 1, nullptr));
+ auto TabNames = self->GetTabNames();
lua_newtable(tolua_S);
int index = 1;
- cWebPlugin::TabNameList::const_iterator iter = TabNames.begin();
- while (iter != TabNames.end())
- {
- const AString & FancyName = iter->first;
- const AString & WebName = iter->second;
- tolua_pushstring( tolua_S, WebName.c_str()); // Because the WebName is supposed to be unique, use it as key
- tolua_pushstring( tolua_S, FancyName.c_str());
- //
+ for (auto itr = TabNames.cbegin(), end = TabNames.cend(); itr != end; ++itr)
+ {
+ tolua_pushstring(tolua_S, itr->second.c_str()); // Because the SafeTitle is supposed to be unique, use it as key
+ tolua_pushstring(tolua_S, itr->first.c_str());
lua_rawset(tolua_S, -3);
- ++iter;
++index;
}
return 1;
@@ -3214,6 +2225,44 @@ static int tolua_cBlockArea_GetOrigin(lua_State * tolua_S)
+static int tolua_cBlockArea_GetNonAirCropRelCoords(lua_State * tolua_S)
+{
+ // function cBlockArea::GetNonAirCropRelCoords()
+ // Exported manually because tolua would generate extra input params for the outputs
+
+ cLuaState L(tolua_S);
+ if (!L.CheckParamUserType(1, "cBlockArea"))
+ {
+ return 0;
+ }
+
+ cBlockArea * self = nullptr;
+ BLOCKTYPE IgnoreBlockType = E_BLOCK_AIR;
+ L.GetStackValues(1, self, IgnoreBlockType);
+ if (self == nullptr)
+ {
+ tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea:GetNonAirCropRelCoords'", nullptr);
+ return 0;
+ }
+
+ // Calculate the crop coords:
+ int MinRelX, MinRelY, MinRelZ, MaxRelX, MaxRelY, MaxRelZ;
+ self->GetNonAirCropRelCoords(MinRelX, MinRelY, MinRelZ, MaxRelX, MaxRelY, MaxRelZ, IgnoreBlockType);
+
+ // Push the six crop coords:
+ L.Push(MinRelX);
+ L.Push(MinRelY);
+ L.Push(MinRelZ);
+ L.Push(MaxRelX);
+ L.Push(MaxRelY);
+ L.Push(MaxRelZ);
+ return 6;
+}
+
+
+
+
+
static int tolua_cBlockArea_GetRelBlockTypeMeta(lua_State * tolua_S)
{
// function cBlockArea::GetRelBlockTypeMeta()
@@ -3680,17 +2729,17 @@ static int tolua_cCompositeChat_UnderlineUrls(lua_State * tolua_S)
-void ManualBindings::Bind(lua_State * tolua_S)
+void cManualBindings::Bind(lua_State * tolua_S)
{
tolua_beginmodule(tolua_S, nullptr);
// Create the new classes:
tolua_usertype(tolua_S, "cCryptoHash");
- tolua_cclass(tolua_S, "cCryptoHash", "cCryptoHash", "", nullptr);
+ tolua_usertype(tolua_S, "cLineBlockTracer");
tolua_usertype(tolua_S, "cStringCompression");
+ tolua_cclass(tolua_S, "cCryptoHash", "cCryptoHash", "", nullptr);
+ tolua_cclass(tolua_S, "cLineBlockTracer", "cLineBlockTracer", "", nullptr);
tolua_cclass(tolua_S, "cStringCompression", "cStringCompression", "", nullptr);
- tolua_usertype(tolua_S, "cLineBlockTracer");
- tolua_cclass(tolua_S, "cLineBlockTracer", "cLineBlockTracer", "", nullptr);
// Globals:
tolua_function(tolua_S, "Clamp", tolua_Clamp);
@@ -3705,15 +2754,12 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "Base64Encode", tolua_Base64Encode);
tolua_function(tolua_S, "Base64Decode", tolua_Base64Decode);
tolua_function(tolua_S, "md5", tolua_md5_obsolete); // OBSOLETE, use cCryptoHash.md5() instead
-
- tolua_beginmodule(tolua_S, "cFile");
- tolua_function(tolua_S, "GetFolderContents", tolua_cFile_GetFolderContents);
- tolua_endmodule(tolua_S);
-
+
tolua_beginmodule(tolua_S, "cBlockArea");
tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cBlockArea_GetBlockTypeMeta);
tolua_function(tolua_S, "GetCoordRange", tolua_cBlockArea_GetCoordRange);
tolua_function(tolua_S, "GetOrigin", tolua_cBlockArea_GetOrigin);
+ tolua_function(tolua_S, "GetNonAirCropRelCoords", tolua_cBlockArea_GetNonAirCropRelCoords);
tolua_function(tolua_S, "GetRelBlockTypeMeta", tolua_cBlockArea_GetRelBlockTypeMeta);
tolua_function(tolua_S, "GetSize", tolua_cBlockArea_GetSize);
tolua_function(tolua_S, "LoadFromSchematicFile", tolua_cBlockArea_LoadFromSchematicFile);
@@ -3721,7 +2767,13 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "SaveToSchematicFile", tolua_cBlockArea_SaveToSchematicFile);
tolua_function(tolua_S, "SaveToSchematicString", tolua_cBlockArea_SaveToSchematicString);
tolua_endmodule(tolua_S);
-
+
+ tolua_beginmodule(tolua_S, "cClientHandle");
+ tolua_constant(tolua_S, "MAX_VIEW_DISTANCE", cClientHandle::MAX_VIEW_DISTANCE);
+ tolua_constant(tolua_S, "MIN_VIEW_DISTANCE", cClientHandle::MIN_VIEW_DISTANCE);
+ tolua_function(tolua_S, "SendPluginMessage", tolua_cClientHandle_SendPluginMessage);
+ tolua_endmodule(tolua_S);
+
tolua_beginmodule(tolua_S, "cCompositeChat");
tolua_function(tolua_S, "AddRunCommandPart", tolua_cCompositeChat_AddRunCommandPart);
tolua_function(tolua_S, "AddSuggestCommandPart", tolua_cCompositeChat_AddSuggestCommandPart);
@@ -3731,105 +2783,105 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "SetMessageType", tolua_cCompositeChat_SetMessageType);
tolua_function(tolua_S, "UnderlineUrls", tolua_cCompositeChat_UnderlineUrls);
tolua_endmodule(tolua_S);
-
+
+ tolua_beginmodule(tolua_S, "cCryptoHash");
+ tolua_function(tolua_S, "md5", tolua_md5);
+ tolua_function(tolua_S, "md5HexString", tolua_md5HexString);
+ tolua_function(tolua_S, "sha1", tolua_sha1);
+ tolua_function(tolua_S, "sha1HexString", tolua_sha1HexString);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cEntity");
+ tolua_constant(tolua_S, "INVALID_ID", cEntity::INVALID_ID);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cFile");
+ tolua_function(tolua_S, "GetFolderContents", tolua_cFile_GetFolderContents);
+ tolua_function(tolua_S, "ReadWholeFile", tolua_cFile_ReadWholeFile);
+ tolua_endmodule(tolua_S);
+
tolua_beginmodule(tolua_S, "cHopperEntity");
tolua_function(tolua_S, "GetOutputBlockPos", tolua_cHopperEntity_GetOutputBlockPos);
tolua_endmodule(tolua_S);
-
+
+ tolua_beginmodule(tolua_S, "cItemGrid");
+ tolua_function(tolua_S, "GetSlotCoords", Lua_ItemGrid_GetSlotCoords);
+ tolua_endmodule(tolua_S);
+
tolua_beginmodule(tolua_S, "cLineBlockTracer");
tolua_function(tolua_S, "Trace", tolua_cLineBlockTracer_Trace);
tolua_endmodule(tolua_S);
-
- tolua_beginmodule(tolua_S, "cRoot");
- tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith <cRoot, cPlayer, &cRoot::FindAndDoWithPlayer>);
- tolua_function(tolua_S, "DoWithPlayerByUUID", tolua_DoWith <cRoot, cPlayer, &cRoot::DoWithPlayerByUUID>);
- tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach<cRoot, cPlayer, &cRoot::ForEachPlayer>);
- tolua_function(tolua_S, "ForEachWorld", tolua_ForEach<cRoot, cWorld, &cRoot::ForEachWorld>);
- tolua_function(tolua_S, "GetFurnaceRecipe", tolua_cRoot_GetFurnaceRecipe);
- tolua_endmodule(tolua_S);
-
- tolua_beginmodule(tolua_S, "cWorld");
- tolua_function(tolua_S, "ChunkStay", tolua_cWorld_ChunkStay);
- tolua_function(tolua_S, "DoWithBlockEntityAt", tolua_DoWithXYZ<cWorld, cBlockEntity, &cWorld::DoWithBlockEntityAt>);
- tolua_function(tolua_S, "DoWithBeaconAt", tolua_DoWithXYZ<cWorld, cBeaconEntity, &cWorld::DoWithBeaconAt>);
- tolua_function(tolua_S, "DoWithChestAt", tolua_DoWithXYZ<cWorld, cChestEntity, &cWorld::DoWithChestAt>);
- tolua_function(tolua_S, "DoWithDispenserAt", tolua_DoWithXYZ<cWorld, cDispenserEntity, &cWorld::DoWithDispenserAt>);
- tolua_function(tolua_S, "DoWithDropSpenserAt", tolua_DoWithXYZ<cWorld, cDropSpenserEntity, &cWorld::DoWithDropSpenserAt>);
- tolua_function(tolua_S, "DoWithDropperAt", tolua_DoWithXYZ<cWorld, cDropperEntity, &cWorld::DoWithDropperAt>);
- tolua_function(tolua_S, "DoWithEntityByID", tolua_DoWithID< cWorld, cEntity, &cWorld::DoWithEntityByID>);
- tolua_function(tolua_S, "DoWithFurnaceAt", tolua_DoWithXYZ<cWorld, cFurnaceEntity, &cWorld::DoWithFurnaceAt>);
- tolua_function(tolua_S, "DoWithNoteBlockAt", tolua_DoWithXYZ<cWorld, cNoteEntity, &cWorld::DoWithNoteBlockAt>);
- tolua_function(tolua_S, "DoWithCommandBlockAt", tolua_DoWithXYZ<cWorld, cCommandBlockEntity, &cWorld::DoWithCommandBlockAt>);
- tolua_function(tolua_S, "DoWithMobHeadAt", tolua_DoWithXYZ<cWorld, cMobHeadEntity, &cWorld::DoWithMobHeadAt>);
- tolua_function(tolua_S, "DoWithFlowerPotAt", tolua_DoWithXYZ<cWorld, cFlowerPotEntity, &cWorld::DoWithFlowerPotAt>);
- tolua_function(tolua_S, "DoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>);
- tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>);
- tolua_function(tolua_S, "DoWithPlayerByUUID", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayerByUUID>);
- tolua_function(tolua_S, "ForEachBlockEntityInChunk", tolua_ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>);
- tolua_function(tolua_S, "ForEachChestInChunk", tolua_ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>);
- tolua_function(tolua_S, "ForEachEntity", tolua_ForEach< cWorld, cEntity, &cWorld::ForEachEntity>);
- tolua_function(tolua_S, "ForEachEntityInBox", tolua_ForEachInBox< cWorld, cEntity, &cWorld::ForEachEntityInBox>);
- tolua_function(tolua_S, "ForEachEntityInChunk", tolua_ForEachInChunk<cWorld, cEntity, &cWorld::ForEachEntityInChunk>);
- tolua_function(tolua_S, "ForEachFurnaceInChunk", tolua_ForEachInChunk<cWorld, cFurnaceEntity, &cWorld::ForEachFurnaceInChunk>);
- tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach< cWorld, cPlayer, &cWorld::ForEachPlayer>);
- tolua_function(tolua_S, "GetBlockInfo", tolua_cWorld_GetBlockInfo);
- tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cWorld_GetBlockTypeMeta);
- tolua_function(tolua_S, "GetSignLines", tolua_cWorld_GetSignLines);
- tolua_function(tolua_S, "PrepareChunk", tolua_cWorld_PrepareChunk);
- tolua_function(tolua_S, "QueueTask", tolua_cWorld_QueueTask);
- tolua_function(tolua_S, "ScheduleTask", tolua_cWorld_ScheduleTask);
- tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines);
- tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight);
+
+ tolua_beginmodule(tolua_S, "cLuaWindow");
+ tolua_function(tolua_S, "SetOnClosing", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnClosing>);
+ tolua_function(tolua_S, "SetOnSlotChanged", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnSlotChanged>);
tolua_endmodule(tolua_S);
-
+
tolua_beginmodule(tolua_S, "cMapManager");
- tolua_function(tolua_S, "DoWithMap", tolua_DoWithID<cMapManager, cMap, &cMapManager::DoWithMap>);
+ tolua_function(tolua_S, "DoWithMap", DoWithID<cMapManager, cMap, &cMapManager::DoWithMap>);
tolua_endmodule(tolua_S);
- tolua_beginmodule(tolua_S, "cScoreboard");
- tolua_function(tolua_S, "ForEachObjective", tolua_ForEach<cScoreboard, cObjective, &cScoreboard::ForEachObjective>);
- tolua_function(tolua_S, "ForEachTeam", tolua_ForEach<cScoreboard, cTeam, &cScoreboard::ForEachTeam>);
+ tolua_beginmodule(tolua_S, "cMojangAPI");
+ tolua_function(tolua_S, "AddPlayerNameToUUIDMapping", tolua_cMojangAPI_AddPlayerNameToUUIDMapping);
+ tolua_function(tolua_S, "GetPlayerNameFromUUID", tolua_cMojangAPI_GetPlayerNameFromUUID);
+ tolua_function(tolua_S, "GetUUIDFromPlayerName", tolua_cMojangAPI_GetUUIDFromPlayerName);
+ tolua_function(tolua_S, "GetUUIDsFromPlayerNames", tolua_cMojangAPI_GetUUIDsFromPlayerNames);
+ tolua_function(tolua_S, "MakeUUIDDashed", tolua_cMojangAPI_MakeUUIDDashed);
+ tolua_function(tolua_S, "MakeUUIDShort", tolua_cMojangAPI_MakeUUIDShort);
tolua_endmodule(tolua_S);
-
+
+ tolua_beginmodule(tolua_S, "cPlayer");
+ tolua_function(tolua_S, "GetPermissions", tolua_cPlayer_GetPermissions);
+ tolua_function(tolua_S, "GetRestrictions", tolua_cPlayer_GetRestrictions);
+ tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow);
+ tolua_function(tolua_S, "PermissionMatches", tolua_cPlayer_PermissionMatches);
+ tolua_endmodule(tolua_S);
+
tolua_beginmodule(tolua_S, "cPlugin");
- tolua_function(tolua_S, "Call", tolua_cPlugin_Call);
+ tolua_function(tolua_S, "GetDirectory", tolua_cPlugin_GetDirectory);
+ tolua_function(tolua_S, "GetLocalDirectory", tolua_cPlugin_GetLocalDirectory);
tolua_endmodule(tolua_S);
+ tolua_beginmodule(tolua_S, "cPluginLua");
+ tolua_function(tolua_S, "AddWebTab", tolua_cPluginLua_AddWebTab);
+ tolua_endmodule(tolua_S);
+
tolua_beginmodule(tolua_S, "cPluginManager");
tolua_function(tolua_S, "AddHook", tolua_cPluginManager_AddHook);
tolua_function(tolua_S, "BindCommand", tolua_cPluginManager_BindCommand);
tolua_function(tolua_S, "BindConsoleCommand", tolua_cPluginManager_BindConsoleCommand);
tolua_function(tolua_S, "CallPlugin", tolua_cPluginManager_CallPlugin);
+ tolua_function(tolua_S, "DoWithPlugin", StaticDoWith<cPluginManager, cPlugin, &cPluginManager::DoWithPlugin>);
+ tolua_function(tolua_S, "ExecuteConsoleCommand", tolua_cPluginManager_ExecuteConsoleCommand);
+ tolua_function(tolua_S, "FindPlugins", tolua_cPluginManager_FindPlugins);
tolua_function(tolua_S, "ForEachCommand", tolua_cPluginManager_ForEachCommand);
tolua_function(tolua_S, "ForEachConsoleCommand", tolua_cPluginManager_ForEachConsoleCommand);
+ tolua_function(tolua_S, "ForEachPlugin", StaticForEach<cPluginManager, cPlugin, &cPluginManager::ForEachPlugin>);
tolua_function(tolua_S, "GetAllPlugins", tolua_cPluginManager_GetAllPlugins);
tolua_function(tolua_S, "GetCurrentPlugin", tolua_cPluginManager_GetCurrentPlugin);
+ tolua_function(tolua_S, "GetPlugin", tolua_cPluginManager_GetPlugin);
tolua_function(tolua_S, "LogStackTrace", tolua_cPluginManager_LogStackTrace);
tolua_endmodule(tolua_S);
-
- tolua_beginmodule(tolua_S, "cPlayer");
- tolua_function(tolua_S, "GetPermissions", tolua_cPlayer_GetPermissions);
- tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow);
- tolua_function(tolua_S, "PermissionMatches", tolua_cPlayer_PermissionMatches);
- tolua_endmodule(tolua_S);
-
- tolua_beginmodule(tolua_S, "cLuaWindow");
- tolua_function(tolua_S, "SetOnClosing", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnClosing>);
- tolua_function(tolua_S, "SetOnSlotChanged", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnSlotChanged>);
+
+ tolua_beginmodule(tolua_S, "cRoot");
+ tolua_function(tolua_S, "FindAndDoWithPlayer", DoWith <cRoot, cPlayer, &cRoot::FindAndDoWithPlayer>);
+ tolua_function(tolua_S, "DoWithPlayerByUUID", DoWith <cRoot, cPlayer, &cRoot::DoWithPlayerByUUID>);
+ tolua_function(tolua_S, "ForEachPlayer", ForEach<cRoot, cPlayer, &cRoot::ForEachPlayer>);
+ tolua_function(tolua_S, "ForEachWorld", ForEach<cRoot, cWorld, &cRoot::ForEachWorld>);
+ tolua_function(tolua_S, "GetFurnaceRecipe", tolua_cRoot_GetFurnaceRecipe);
tolua_endmodule(tolua_S);
- tolua_beginmodule(tolua_S, "cPluginLua");
- tolua_function(tolua_S, "AddTab", tolua_cPluginLua_AddTab);
- tolua_function(tolua_S, "AddWebTab", tolua_cPluginLua_AddWebTab);
+ tolua_beginmodule(tolua_S, "cScoreboard");
+ tolua_function(tolua_S, "ForEachObjective", ForEach<cScoreboard, cObjective, &cScoreboard::ForEachObjective>);
+ tolua_function(tolua_S, "ForEachTeam", ForEach<cScoreboard, cTeam, &cScoreboard::ForEachTeam>);
tolua_endmodule(tolua_S);
- tolua_cclass(tolua_S, "HTTPRequest", "HTTPRequest", "", nullptr);
- tolua_beginmodule(tolua_S, "HTTPRequest");
- // tolua_variable(tolua_S, "Method", tolua_get_HTTPRequest_Method, tolua_set_HTTPRequest_Method);
- // tolua_variable(tolua_S, "Path", tolua_get_HTTPRequest_Path, tolua_set_HTTPRequest_Path);
- tolua_variable(tolua_S, "FormData", tolua_get_HTTPRequest_FormData, 0);
- tolua_variable(tolua_S, "Params", tolua_get_HTTPRequest_Params, 0);
- tolua_variable(tolua_S, "PostParams", tolua_get_HTTPRequest_PostParams, 0);
+ tolua_beginmodule(tolua_S, "cStringCompression");
+ tolua_function(tolua_S, "CompressStringZLIB", tolua_CompressStringZLIB);
+ tolua_function(tolua_S, "UncompressStringZLIB", tolua_UncompressStringZLIB);
+ tolua_function(tolua_S, "CompressStringGZIP", tolua_CompressStringGZIP);
+ tolua_function(tolua_S, "UncompressStringGZIP", tolua_UncompressStringGZIP);
+ tolua_function(tolua_S, "InflateString", tolua_InflateString);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cWebAdmin");
@@ -3841,47 +2893,16 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_beginmodule(tolua_S, "cWebPlugin");
tolua_function(tolua_S, "GetTabNames", tolua_cWebPlugin_GetTabNames);
tolua_endmodule(tolua_S);
-
- tolua_beginmodule(tolua_S, "cClientHandle");
- tolua_constant(tolua_S, "MAX_VIEW_DISTANCE", cClientHandle::MAX_VIEW_DISTANCE);
- tolua_constant(tolua_S, "MIN_VIEW_DISTANCE", cClientHandle::MIN_VIEW_DISTANCE);
- tolua_function(tolua_S, "SendPluginMessage", tolua_cClientHandle_SendPluginMessage);
- tolua_endmodule(tolua_S);
- tolua_beginmodule(tolua_S, "cMojangAPI");
- tolua_function(tolua_S, "AddPlayerNameToUUIDMapping", tolua_cMojangAPI_AddPlayerNameToUUIDMapping);
- tolua_function(tolua_S, "GetPlayerNameFromUUID", tolua_cMojangAPI_GetPlayerNameFromUUID);
- tolua_function(tolua_S, "GetUUIDFromPlayerName", tolua_cMojangAPI_GetUUIDFromPlayerName);
- tolua_function(tolua_S, "GetUUIDsFromPlayerNames", tolua_cMojangAPI_GetUUIDsFromPlayerNames);
- tolua_function(tolua_S, "MakeUUIDDashed", tolua_cMojangAPI_MakeUUIDDashed);
- tolua_function(tolua_S, "MakeUUIDShort", tolua_cMojangAPI_MakeUUIDShort);
- tolua_endmodule(tolua_S);
-
- tolua_beginmodule(tolua_S, "cItemGrid");
- tolua_function(tolua_S, "GetSlotCoords", Lua_ItemGrid_GetSlotCoords);
+ tolua_beginmodule(tolua_S, "HTTPRequest");
+ tolua_variable(tolua_S, "FormData", tolua_get_HTTPRequest_FormData, nullptr);
+ tolua_variable(tolua_S, "Params", tolua_get_HTTPRequest_Params, nullptr);
+ tolua_variable(tolua_S, "PostParams", tolua_get_HTTPRequest_PostParams, nullptr);
tolua_endmodule(tolua_S);
- tolua_beginmodule(tolua_S, "cCryptoHash");
- tolua_function(tolua_S, "md5", tolua_md5);
- tolua_function(tolua_S, "md5HexString", tolua_md5HexString);
- tolua_function(tolua_S, "sha1", tolua_sha1);
- tolua_function(tolua_S, "sha1HexString", tolua_sha1HexString);
- tolua_endmodule(tolua_S);
-
- tolua_beginmodule(tolua_S, "cStringCompression");
- tolua_function(tolua_S, "CompressStringZLIB", tolua_CompressStringZLIB);
- tolua_function(tolua_S, "UncompressStringZLIB", tolua_UncompressStringZLIB);
- tolua_function(tolua_S, "CompressStringGZIP", tolua_CompressStringGZIP);
- tolua_function(tolua_S, "UncompressStringGZIP", tolua_UncompressStringGZIP);
- tolua_function(tolua_S, "InflateString", tolua_InflateString);
- tolua_endmodule(tolua_S);
-
- BindRankManager(tolua_S);
BindNetwork(tolua_S);
-
- tolua_beginmodule(tolua_S, "cEntity");
- tolua_constant(tolua_S, "INVALID_ID", cEntity::INVALID_ID);
- tolua_endmodule(tolua_S);
+ BindRankManager(tolua_S);
+ BindWorld(tolua_S);
tolua_endmodule(tolua_S);
}
diff --git a/src/Bindings/ManualBindings.h b/src/Bindings/ManualBindings.h
index 74d24d5f5..e7a576588 100644
--- a/src/Bindings/ManualBindings.h
+++ b/src/Bindings/ManualBindings.h
@@ -1,33 +1,566 @@
+
+// ManualBindings.h
+
+// Declares the cManualBindings class used as a namespace for functions exported to the Lua API manually
+
+
+
+
+
#pragma once
-struct lua_State;
-class cPluginLua;
+#include "LuaState.h"
+
+
+
+
+// fwd:
+struct tolua_Error;
/** Provides namespace for the bindings. */
-class ManualBindings
+class cManualBindings
{
public:
/** Binds all the manually implemented functions to tolua_S. */
static void Bind(lua_State * tolua_S);
protected:
+ /** Binds the manually implemented cNetwork-related API to tolua_S.
+ Implemented in ManualBindings_Network.cpp. */
+ static void BindNetwork(lua_State * tolua_S);
+
/** Binds the manually implemented cRankManager glue code to tolua_S.
Implemented in ManualBindings_RankManager.cpp. */
static void BindRankManager(lua_State * tolua_S);
- /** Binds the manually implemented cNetwork-related API to tolua_S.
- Implemented in ManualBindings_Network.cpp. */
- static void BindNetwork(lua_State * tolua_S);
-};
+ /** Binds the manually implemented cWorld API functions to tolua_S.
+ Implemented in ManualBindings_World.cpp. */
+ static void BindWorld(lua_State * tolua_S);
+
+
+public:
+ // Helper functions:
+ static cPluginLua * GetLuaPlugin(lua_State * L);
+ static int tolua_do_error(lua_State * L, const char * a_pMsg, tolua_Error * a_pToLuaError);
+ static int lua_do_error(lua_State * L, const char * a_pFormat, ...);
+
+
+ /** Binds the DoWith(ItemName) functions of regular classes. */
+ template <
+ class Ty1,
+ class Ty2,
+ bool (Ty1::*DoWithFn)(const AString &, cItemCallback<Ty2> &)
+ >
+ static int DoWith(lua_State * tolua_S)
+ {
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamString(2) ||
+ !L.CheckParamFunction(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get parameters:
+ Ty1 * Self;
+ AString ItemName;
+ cLuaState::cRef FnRef;
+ L.GetStackValues(1, Self, ItemName, FnRef);
+ if (Self == nullptr)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
+ }
+ if (ItemName.empty() || (ItemName[0] == 0))
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a non-empty string for parameter #1");
+ }
+ if (!FnRef.IsValid())
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #2");
+ }
+
+ class cLuaCallback : public cItemCallback<Ty2>
+ {
+ public:
+ cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
+ m_LuaState(a_LuaState),
+ m_FnRef(a_FnRef)
+ {
+ }
+
+ private:
+ virtual bool Item(Ty2 * a_Item) override
+ {
+ bool ret = false;
+ m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret);
+ return ret;
+ }
+ cLuaState & m_LuaState;
+ cLuaState::cRef & m_FnRef;
+ } Callback(L, FnRef);
+
+ // Call the DoWith function:
+ bool res = (Self->*DoWithFn)(ItemName, Callback);
+
+ // Push the result as the return value:
+ L.Push(res);
+ return 1;
+ }
+
+
+
+
+
+ /** Template for static functions DoWith(ItemName), on a type that has a static ::Get() function. */
+ template <
+ class Ty1,
+ class Ty2,
+ bool (Ty1::*DoWithFn)(const AString &, cItemCallback<Ty2> &)
+ >
+ static int StaticDoWith(lua_State * tolua_S)
+ {
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamString(2) ||
+ !L.CheckParamFunction(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get parameters:
+ AString ItemName;
+ cLuaState::cRef FnRef;
+ L.GetStackValues(2, ItemName, FnRef);
+ if (ItemName.empty() || (ItemName[0] == 0))
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a non-empty string for parameter #1");
+ }
+ if (!FnRef.IsValid())
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #2");
+ }
+
+ class cLuaCallback : public cItemCallback<Ty2>
+ {
+ public:
+ cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
+ m_LuaState(a_LuaState),
+ m_FnRef(a_FnRef)
+ {
+ }
+
+ private:
+ virtual bool Item(Ty2 * a_Item) override
+ {
+ bool ret = false;
+ m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret);
+ return ret;
+ }
+ cLuaState & m_LuaState;
+ cLuaState::cRef & m_FnRef;
+ } Callback(L, FnRef);
+
+ // Call the DoWith function:
+ bool res = (Ty1::Get()->*DoWithFn)(ItemName, Callback);
+
+ // Push the result as the return value:
+ L.Push(res);
+ return 1;
+ }
+
+
+
+
+
+ template <
+ class Ty1,
+ class Ty2,
+ bool (Ty1::*DoWithFn)(UInt32, cItemCallback<Ty2> &)
+ >
+ static int DoWithID(lua_State * tolua_S)
+ {
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamNumber(2) ||
+ !L.CheckParamFunction(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get parameters:
+ Ty1 * Self = nullptr;
+ int ItemID;
+ cLuaState::cRef FnRef;
+ L.GetStackValues(1, Self, ItemID, FnRef);
+ if (Self == nullptr)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
+ }
+ if (!FnRef.IsValid())
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #2");
+ }
+
+ class cLuaCallback : public cItemCallback<Ty2>
+ {
+ public:
+ cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
+ m_LuaState(a_LuaState),
+ m_FnRef(a_FnRef)
+ {
+ }
+
+ private:
+ virtual bool Item(Ty2 * a_Item) override
+ {
+ bool ret = false;
+ m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret);
+ return ret;
+ }
+ cLuaState & m_LuaState;
+ cLuaState::cRef & m_FnRef;
+ } Callback(L, FnRef);
+
+ // Call the DoWith function:
+ bool res = (Self->*DoWithFn)(ItemID, Callback);
+
+ // Push the result as the return value:
+ L.Push(res);
+ return 1;
+ }
+
-extern cPluginLua * GetLuaPlugin(lua_State * L);
+ template <
+ class Ty1,
+ class Ty2,
+ bool (Ty1::*DoWithFn)(int, int, int, cItemCallback<Ty2> &)
+ >
+ static int DoWithXYZ(lua_State * tolua_S)
+ {
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamNumber(2, 5) ||
+ !L.CheckParamFunction(6)
+ )
+ {
+ return 0;
+ }
+
+ // Get parameters:
+ Ty1 * Self = nullptr;
+ int BlockX, BlockY, BlockZ;
+ cLuaState::cRef FnRef;
+ L.GetStackValues(1, Self, BlockX, BlockY, BlockZ, FnRef);
+ if (Self == nullptr)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
+ }
+ if (!FnRef.IsValid())
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #5");
+ }
+
+ class cLuaCallback : public cItemCallback<Ty2>
+ {
+ public:
+ cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
+ m_LuaState(a_LuaState),
+ m_FnRef(a_FnRef)
+ {
+ }
+
+ private:
+ virtual bool Item(Ty2 * a_Item) override
+ {
+ bool ret = false;
+ m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret);
+ return ret;
+ }
+ cLuaState & m_LuaState;
+ cLuaState::cRef & m_FnRef;
+ } Callback(L, FnRef);
+
+ // Call the DoWith function:
+ bool res = (Self->*DoWithFn)(BlockX, BlockY, BlockZ, Callback);
+
+ // Push the result as the return value:
+ L.Push(res);
+ return 1;
+ }
+
+
+
+
+
+ template <
+ class Ty1,
+ class Ty2,
+ bool (Ty1::*ForEachFn)(int, int, cItemCallback<Ty2> &)
+ >
+ static int ForEachInChunk(lua_State * tolua_S)
+ {
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamNumber(2, 4) ||
+ !L.CheckParamFunction(5)
+ )
+ {
+ return 0;
+ }
+
+ // Get parameters:
+ Ty1 * Self = nullptr;
+ int ChunkX, ChunkZ;
+ cLuaState::cRef FnRef;
+ L.GetStackValues(1, Self, ChunkX, ChunkZ, FnRef);
+ if (Self == nullptr)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
+ }
+ if (!FnRef.IsValid())
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #4");
+ }
+
+ class cLuaCallback : public cItemCallback<Ty2>
+ {
+ public:
+ cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
+ m_LuaState(a_LuaState),
+ m_FnRef(a_FnRef)
+ {
+ }
+
+ private:
+ virtual bool Item(Ty2 * a_Item) override
+ {
+ bool ret = false;
+ m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret);
+ return ret;
+ }
+ cLuaState & m_LuaState;
+ cLuaState::cRef & m_FnRef;
+ } Callback(L, FnRef);
+
+ // Call the DoWith function:
+ bool res = (Self->*ForEachFn)(ChunkX, ChunkZ, Callback);
+
+ // Push the result as the return value:
+ L.Push(res);
+ return 1;
+ }
+
+
+
+
+
+ template <
+ class Ty1,
+ class Ty2,
+ bool (Ty1::*ForEachFn)(const cBoundingBox &, cItemCallback<Ty2> &)
+ >
+ static int ForEachInBox(lua_State * tolua_S)
+ {
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cWorld") ||
+ !L.CheckParamUserType(2, "cBoundingBox") ||
+ !L.CheckParamFunction(3) ||
+ !L.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ Ty1 * Self = nullptr;
+ cBoundingBox * Box = nullptr;
+ cLuaState::cRef FnRef;
+ L.GetStackValues(1, Self, Box, FnRef);
+ if ((Self == nullptr) || (Box == nullptr))
+ {
+ LOGWARNING("Invalid world (%p) or boundingbox (%p)", Self, Box);
+ L.LogStackTrace();
+ return 0;
+ }
+ if (!FnRef.IsValid())
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #2");
+ }
+
+ // Callback wrapper for the Lua function:
+ class cLuaCallback : public cItemCallback<Ty2>
+ {
+ public:
+ cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FuncRef) :
+ m_LuaState(a_LuaState),
+ m_FnRef(a_FuncRef)
+ {
+ }
+
+ private:
+ cLuaState & m_LuaState;
+ cLuaState::cRef & m_FnRef;
+
+ // cItemCallback<Ty2> overrides:
+ virtual bool Item(Ty2 * a_Item) override
+ {
+ bool res = false;
+ if (!m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, res))
+ {
+ LOGWARNING("Failed to call Lua callback");
+ m_LuaState.LogStackTrace();
+ return true; // Abort enumeration
+ }
+
+ return res;
+ }
+ } Callback(L, FnRef);
+
+ bool res = (Self->*ForEachFn)(*Box, Callback);
+
+ // Push the result as the return value:
+ L.Push(res);
+ return 1;
+ }
+
+
+
+
+
+ template <
+ class Ty1,
+ class Ty2,
+ bool (Ty1::*ForEachFn)(cItemCallback<Ty2> &)
+ >
+ static int ForEach(lua_State * tolua_S)
+ {
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamFunction(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ Ty1 * Self = nullptr;
+ cLuaState::cRef FnRef;
+ L.GetStackValues(1, Self, FnRef);
+ if (Self == nullptr)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'.");
+ }
+ if (!FnRef.IsValid())
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #1");
+ }
+
+ class cLuaCallback : public cItemCallback<Ty2>
+ {
+ public:
+ cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
+ m_LuaState(a_LuaState),
+ m_FnRef(a_FnRef)
+ {
+ }
+
+ private:
+ cLuaState & m_LuaState;
+ cLuaState::cRef & m_FnRef;
+
+ virtual bool Item(Ty2 * a_Item) override
+ {
+ bool res = false; // By default continue the enumeration
+ m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, res);
+ return res;
+ }
+ } Callback(L, FnRef);
+
+ // Call the enumeration:
+ bool res = (Self->*ForEachFn)(Callback);
+
+ // Push the return value:
+ L.Push(res);
+ return 1;
+ }
+
+
+
+
+
+ /** Implements bindings for ForEach() functions in a class that is static (has a ::Get() static function). */
+ template <
+ class Ty1,
+ class Ty2,
+ bool (Ty1::*ForEachFn)(cItemCallback<Ty2> &)
+ >
+ static int StaticForEach(lua_State * tolua_S)
+ {
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamFunction(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ cLuaState::cRef FnRef(L, 2);
+ if (!FnRef.IsValid())
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #1");
+ }
+
+ class cLuaCallback : public cItemCallback<Ty2>
+ {
+ public:
+ cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
+ m_LuaState(a_LuaState),
+ m_FnRef(a_FnRef)
+ {
+ }
+
+ private:
+ cLuaState & m_LuaState;
+ cLuaState::cRef & m_FnRef;
+
+ virtual bool Item(Ty2 * a_Item) override
+ {
+ bool res = false; // By default continue the enumeration
+ m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, res);
+ return res;
+ }
+ } Callback(L, FnRef);
+
+ // Call the enumeration:
+ bool res = (Ty1::Get()->*ForEachFn)(Callback);
+
+ // Push the return value:
+ L.Push(res);
+ return 1;
+ }
+};
diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp
index 628cda7f0..df97d60b3 100644
--- a/src/Bindings/ManualBindings_Network.cpp
+++ b/src/Bindings/ManualBindings_Network.cpp
@@ -39,7 +39,7 @@ static int tolua_cNetwork_Connect(lua_State * L)
}
// Get the plugin instance:
- cPluginLua * Plugin = GetLuaPlugin(L);
+ cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L);
if (Plugin == nullptr)
{
// An error message has been already printed in GetLuaPlugin()
@@ -92,7 +92,7 @@ static int tolua_cNetwork_CreateUDPEndpoint(lua_State * L)
}
// Get the plugin instance:
- cPluginLua * Plugin = GetLuaPlugin(L);
+ cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L);
if (Plugin == nullptr)
{
// An error message has been already printed in GetLuaPlugin()
@@ -171,7 +171,7 @@ static int tolua_cNetwork_HostnameToIP(lua_State * L)
}
// Get the plugin instance:
- cPluginLua * Plugin = GetLuaPlugin(L);
+ cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L);
if (Plugin == nullptr)
{
// An error message has been already printed in GetLuaPlugin()
@@ -212,7 +212,7 @@ static int tolua_cNetwork_IPToHostname(lua_State * L)
}
// Get the plugin instance:
- cPluginLua * Plugin = GetLuaPlugin(L);
+ cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L);
if (Plugin == nullptr)
{
// An error message has been already printed in GetLuaPlugin()
@@ -253,7 +253,7 @@ static int tolua_cNetwork_Listen(lua_State * L)
}
// Get the plugin instance:
- cPluginLua * Plugin = GetLuaPlugin(L);
+ cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L);
if (Plugin == nullptr)
{
// An error message has been already printed in GetLuaPlugin()
@@ -913,17 +913,17 @@ static int tolua_cUDPEndpoint_Send(lua_State * L)
////////////////////////////////////////////////////////////////////////////////
// Register the bindings:
-void ManualBindings::BindNetwork(lua_State * tolua_S)
+void cManualBindings::BindNetwork(lua_State * tolua_S)
{
// Create the cNetwork API classes:
tolua_usertype(tolua_S, "cNetwork");
- tolua_cclass(tolua_S, "cNetwork", "cNetwork", "", nullptr);
- tolua_usertype(tolua_S, "cTCPLink");
- tolua_cclass(tolua_S, "cTCPLink", "cTCPLink", "", nullptr);
tolua_usertype(tolua_S, "cServerHandle");
- tolua_cclass(tolua_S, "cServerHandle", "cServerHandle", "", tolua_collect_cServerHandle);
+ tolua_usertype(tolua_S, "cTCPLink");
tolua_usertype(tolua_S, "cUDPEndpoint");
- tolua_cclass(tolua_S, "cUDPEndpoint", "cUDPEndpoint", "", tolua_collect_cUDPEndpoint);
+ tolua_cclass(tolua_S, "cNetwork", "cNetwork", "", nullptr);
+ tolua_cclass(tolua_S, "cServerHandle", "cServerHandle", "", tolua_collect_cServerHandle);
+ tolua_cclass(tolua_S, "cTCPLink", "cTCPLink", "", nullptr);
+ tolua_cclass(tolua_S, "cUDPEndpoint", "cUDPEndpoint", "", tolua_collect_cUDPEndpoint);
// Fill in the functions (alpha-sorted):
tolua_beginmodule(tolua_S, "cNetwork");
diff --git a/src/Bindings/ManualBindings_RankManager.cpp b/src/Bindings/ManualBindings_RankManager.cpp
index fa1b88b6a..7797d1887 100644
--- a/src/Bindings/ManualBindings_RankManager.cpp
+++ b/src/Bindings/ManualBindings_RankManager.cpp
@@ -100,6 +100,35 @@ static int tolua_cRankManager_AddPermissionToGroup(lua_State * L)
+/** Binds cRankManager::AddRestrictionToGroup */
+static int tolua_cRankManager_AddRestrictionToGroup(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:AddRestrictionToGroup(Permission, GroupName) -> bool
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2, 3) ||
+ !S.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Read the params:
+ AString GroupName, Permission;
+ S.GetStackValues(2, Permission, GroupName);
+
+ // Add the group to the rank:
+ S.Push(cRoot::Get()->GetRankManager()->AddRestrictionToGroup(Permission, GroupName));
+ return 1;
+}
+
+
+
+
+
/** Binds cRankManager::AddRank */
static int tolua_cRankManager_AddRank(lua_State * L)
{
@@ -204,6 +233,60 @@ static int tolua_cRankManager_GetAllPermissions(lua_State * L)
+/** Binds cRankManager::GetAllPermissions */
+static int tolua_cRankManager_GetAllRestrictions(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GetAllRestrictions() -> arraytable of Permissions
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+
+ // Get the permissions:
+ AStringVector Permissions = cRoot::Get()->GetRankManager()->GetAllRestrictions();
+
+ // Push the results:
+ S.Push(Permissions);
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::GetAllPermissionsRestrictions */
+static int tolua_cRankManager_GetAllPermissionsRestrictions(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GetAllPermissionsRestrictions() -> arraytable of Permissions and Restrictions
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+
+ // Get the permissions:
+ AStringVector Permissions = cRoot::Get()->GetRankManager()->GetAllPermissionsRestrictions();
+
+ // Push the results:
+ S.Push(Permissions);
+ return 1;
+}
+
+
+
+
+
/** Binds cRankManager::GetAllPlayerUUIDs */
static int tolua_cRankManager_GetAllPlayerUUIDs(lua_State * L)
{
@@ -314,6 +397,38 @@ static int tolua_cRankManager_GetGroupPermissions(lua_State * L)
+/** Binds cRankManager::GetGroupRestrictions */
+static int tolua_cRankManager_GetGroupRestrictions(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GetGroupRestrictions(GroupName) -> arraytable of restrictions
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString GroupName;
+ S.GetStackValue(2, GroupName);
+
+ // Get the restrictions:
+ AStringVector Restrictions = cRoot::Get()->GetRankManager()->GetGroupRestrictions(GroupName);
+
+ // Push the results:
+ S.Push(Restrictions);
+ return 1;
+}
+
+
+
+
+
/** Binds cRankManager::GetPlayerGroups */
static int tolua_cRankManager_GetPlayerGroups(lua_State * L)
{
@@ -416,6 +531,38 @@ static int tolua_cRankManager_GetPlayerPermissions(lua_State * L)
+/** Binds cRankManager::GetPlayerRestrictions */
+static int tolua_cRankManager_GetPlayerRestrictions(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GetPlayerRestrictions(PlayerUUID) -> arraytable of restrictions
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString PlayerUUID;
+ S.GetStackValue(2, PlayerUUID);
+
+ // Get the permissions:
+ AStringVector Restrictions = cRoot::Get()->GetRankManager()->GetPlayerRestrictions(PlayerUUID);
+
+ // Push the results:
+ S.Push(Restrictions);
+ return 1;
+}
+
+
+
+
+
/** Binds cRankManager::GetPlayerRankName */
static int tolua_cRankManager_GetPlayerRankName(lua_State * L)
{
@@ -544,6 +691,38 @@ static int tolua_cRankManager_GetRankPermissions(lua_State * L)
+/** Binds cRankManager::GetRankRestrictions */
+static int tolua_cRankManager_GetRankRestrictions(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GetRankRestrictions(RankName) -> arraytable of restrictions
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString RankName;
+ S.GetStackValue(2, RankName);
+
+ // Get the permissions:
+ AStringVector Restrictions = cRoot::Get()->GetRankManager()->GetRankRestrictions(RankName);
+
+ // Push the results:
+ S.Push(Restrictions);
+ return 1;
+}
+
+
+
+
+
/** Binds cRankManager::GetRankVisuals */
static int tolua_cRankManager_GetRankVisuals(lua_State * L)
{
@@ -679,6 +858,38 @@ static int tolua_cRankManager_IsPermissionInGroup(lua_State * L)
+/** Binds cRankManager::IsRestrictionInGroup */
+static int tolua_cRankManager_IsRestrictionInGroup(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:IsRestrictionInGroup(Restriction, GroupName) -> bool
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2, 3) ||
+ !S.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString GroupName, Restriction;
+ S.GetStackValues(2, Restriction, GroupName);
+
+ // Get the response:
+ bool res = cRoot::Get()->GetRankManager()->IsRestrictionInGroup(Restriction, GroupName);
+
+ // Push the result:
+ S.Push(res);
+ return 1;
+}
+
+
+
+
+
/** Binds cRankManager::IsPlayerRankSet */
static int tolua_cRankManager_IsPlayerRankSet(lua_State * L)
{
@@ -821,7 +1032,7 @@ static int tolua_cRankManager_RemovePermissionFromGroup(lua_State * L)
AString GroupName, Permission;
S.GetStackValues(2, Permission, GroupName);
- // Remove the group:
+ // Remove the permission:
cRoot::Get()->GetRankManager()->RemovePermissionFromGroup(Permission, GroupName);
return 0;
}
@@ -830,6 +1041,35 @@ static int tolua_cRankManager_RemovePermissionFromGroup(lua_State * L)
+/** Binds cRankManager::RemoveRestrictionFromGroup */
+static int tolua_cRankManager_RemoveRestrictionFromGroup(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:RemoveRestrictionFromGroup(Restriction, GroupName)
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2, 3) ||
+ !S.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString GroupName, Restriction;
+ S.GetStackValues(2, Restriction, GroupName);
+
+ // Remove the restriction:
+ cRoot::Get()->GetRankManager()->RemoveRestrictionFromGroup(Restriction, GroupName);
+ return 0;
+}
+
+
+
+
+
/** Binds cRankManager::RemovePlayerRank */
static int tolua_cRankManager_RemovePlayerRank(lua_State * L)
{
@@ -1040,7 +1280,7 @@ static int tolua_cRankManager_SetRankVisuals(lua_State * L)
-void ManualBindings::BindRankManager(lua_State * tolua_S)
+void cManualBindings::BindRankManager(lua_State * tolua_S)
{
// Create the cRankManager class in the API:
tolua_usertype(tolua_S, "cRankManager");
@@ -1048,40 +1288,48 @@ void ManualBindings::BindRankManager(lua_State * tolua_S)
// Fill in the functions (alpha-sorted):
tolua_beginmodule(tolua_S, "cRankManager");
- tolua_function(tolua_S, "AddGroup", tolua_cRankManager_AddGroup);
- tolua_function(tolua_S, "AddGroupToRank", tolua_cRankManager_AddGroupToRank);
- tolua_function(tolua_S, "AddPermissionToGroup", tolua_cRankManager_AddPermissionToGroup);
- tolua_function(tolua_S, "AddRank", tolua_cRankManager_AddRank);
- tolua_function(tolua_S, "ClearPlayerRanks", tolua_cRankManager_ClearPlayerRanks);
- tolua_function(tolua_S, "GetAllGroups", tolua_cRankManager_GetAllGroups);
- tolua_function(tolua_S, "GetAllPermissions", tolua_cRankManager_GetAllPermissions);
- tolua_function(tolua_S, "GetAllPlayerUUIDs", tolua_cRankManager_GetAllPlayerUUIDs);
- tolua_function(tolua_S, "GetAllRanks", tolua_cRankManager_GetAllRanks);
- tolua_function(tolua_S, "GetDefaultRank", tolua_cRankManager_GetDefaultRank);
- tolua_function(tolua_S, "GetGroupPermissions", tolua_cRankManager_GetGroupPermissions);
- tolua_function(tolua_S, "GetPlayerGroups", tolua_cRankManager_GetPlayerGroups);
- tolua_function(tolua_S, "GetPlayerMsgVisuals", tolua_cRankManager_GetPlayerMsgVisuals);
- tolua_function(tolua_S, "GetPlayerPermissions", tolua_cRankManager_GetPlayerPermissions);
- tolua_function(tolua_S, "GetPlayerRankName", tolua_cRankManager_GetPlayerRankName);
- tolua_function(tolua_S, "GetPlayerName", tolua_cRankManager_GetPlayerName);
- tolua_function(tolua_S, "GetRankGroups", tolua_cRankManager_GetRankGroups);
- tolua_function(tolua_S, "GetRankPermissions", tolua_cRankManager_GetRankPermissions);
- tolua_function(tolua_S, "GetRankVisuals", tolua_cRankManager_GetRankVisuals);
- tolua_function(tolua_S, "GroupExists", tolua_cRankManager_GroupExists);
- tolua_function(tolua_S, "IsGroupInRank", tolua_cRankManager_IsGroupInRank);
- tolua_function(tolua_S, "IsPermissionInGroup", tolua_cRankManager_IsPermissionInGroup);
- tolua_function(tolua_S, "IsPlayerRankSet", tolua_cRankManager_IsPlayerRankSet);
- tolua_function(tolua_S, "RankExists", tolua_cRankManager_RankExists);
- tolua_function(tolua_S, "RemoveGroup", tolua_cRankManager_RemoveGroup);
- tolua_function(tolua_S, "RemoveGroupFromRank", tolua_cRankManager_RemoveGroupFromRank);
- tolua_function(tolua_S, "RemovePermissionFromGroup", tolua_cRankManager_RemovePermissionFromGroup);
- tolua_function(tolua_S, "RemovePlayerRank", tolua_cRankManager_RemovePlayerRank);
- tolua_function(tolua_S, "RemoveRank", tolua_cRankManager_RemoveRank);
- tolua_function(tolua_S, "RenameGroup", tolua_cRankManager_RenameGroup);
- tolua_function(tolua_S, "RenameRank", tolua_cRankManager_RenameRank);
- tolua_function(tolua_S, "SetDefaultRank", tolua_cRankManager_SetDefaultRank);
- tolua_function(tolua_S, "SetPlayerRank", tolua_cRankManager_SetPlayerRank);
- tolua_function(tolua_S, "SetRankVisuals", tolua_cRankManager_SetRankVisuals);
+ tolua_function(tolua_S, "AddGroup", tolua_cRankManager_AddGroup);
+ tolua_function(tolua_S, "AddGroupToRank", tolua_cRankManager_AddGroupToRank);
+ tolua_function(tolua_S, "AddPermissionToGroup", tolua_cRankManager_AddPermissionToGroup);
+ tolua_function(tolua_S, "AddRestrictionToGroup", tolua_cRankManager_AddRestrictionToGroup);
+ tolua_function(tolua_S, "AddRank", tolua_cRankManager_AddRank);
+ tolua_function(tolua_S, "ClearPlayerRanks", tolua_cRankManager_ClearPlayerRanks);
+ tolua_function(tolua_S, "GetAllGroups", tolua_cRankManager_GetAllGroups);
+ tolua_function(tolua_S, "GetAllPermissions", tolua_cRankManager_GetAllPermissions);
+ tolua_function(tolua_S, "GetAllRestrictions", tolua_cRankManager_GetAllRestrictions);
+ tolua_function(tolua_S, "GetAllPermissionsRestrictions", tolua_cRankManager_GetAllPermissionsRestrictions);
+ tolua_function(tolua_S, "GetAllPlayerUUIDs", tolua_cRankManager_GetAllPlayerUUIDs);
+ tolua_function(tolua_S, "GetAllRanks", tolua_cRankManager_GetAllRanks);
+ tolua_function(tolua_S, "GetDefaultRank", tolua_cRankManager_GetDefaultRank);
+ tolua_function(tolua_S, "GetGroupPermissions", tolua_cRankManager_GetGroupPermissions);
+ tolua_function(tolua_S, "GetGroupRestrictions", tolua_cRankManager_GetGroupRestrictions);
+ tolua_function(tolua_S, "GetPlayerGroups", tolua_cRankManager_GetPlayerGroups);
+ tolua_function(tolua_S, "GetPlayerMsgVisuals", tolua_cRankManager_GetPlayerMsgVisuals);
+ tolua_function(tolua_S, "GetPlayerPermissions", tolua_cRankManager_GetPlayerPermissions);
+ tolua_function(tolua_S, "GetPlayerPermissions", tolua_cRankManager_GetPlayerRestrictions);
+ tolua_function(tolua_S, "GetPlayerRankName", tolua_cRankManager_GetPlayerRankName);
+ tolua_function(tolua_S, "GetPlayerName", tolua_cRankManager_GetPlayerName);
+ tolua_function(tolua_S, "GetRankGroups", tolua_cRankManager_GetRankGroups);
+ tolua_function(tolua_S, "GetRankPermissions", tolua_cRankManager_GetRankPermissions);
+ tolua_function(tolua_S, "GetRankRestrictions", tolua_cRankManager_GetRankRestrictions);
+ tolua_function(tolua_S, "GetRankVisuals", tolua_cRankManager_GetRankVisuals);
+ tolua_function(tolua_S, "GroupExists", tolua_cRankManager_GroupExists);
+ tolua_function(tolua_S, "IsGroupInRank", tolua_cRankManager_IsGroupInRank);
+ tolua_function(tolua_S, "IsPermissionInGroup", tolua_cRankManager_IsPermissionInGroup);
+ tolua_function(tolua_S, "IsRestrictionInGroup", tolua_cRankManager_IsRestrictionInGroup);
+ tolua_function(tolua_S, "IsPlayerRankSet", tolua_cRankManager_IsPlayerRankSet);
+ tolua_function(tolua_S, "RankExists", tolua_cRankManager_RankExists);
+ tolua_function(tolua_S, "RemoveGroup", tolua_cRankManager_RemoveGroup);
+ tolua_function(tolua_S, "RemoveGroupFromRank", tolua_cRankManager_RemoveGroupFromRank);
+ tolua_function(tolua_S, "RemovePermissionFromGroup", tolua_cRankManager_RemovePermissionFromGroup);
+ tolua_function(tolua_S, "RemoveRestrictionFromGroup", tolua_cRankManager_RemoveRestrictionFromGroup);
+ tolua_function(tolua_S, "RemovePlayerRank", tolua_cRankManager_RemovePlayerRank);
+ tolua_function(tolua_S, "RemoveRank", tolua_cRankManager_RemoveRank);
+ tolua_function(tolua_S, "RenameGroup", tolua_cRankManager_RenameGroup);
+ tolua_function(tolua_S, "RenameRank", tolua_cRankManager_RenameRank);
+ tolua_function(tolua_S, "SetDefaultRank", tolua_cRankManager_SetDefaultRank);
+ tolua_function(tolua_S, "SetPlayerRank", tolua_cRankManager_SetPlayerRank);
+ tolua_function(tolua_S, "SetRankVisuals", tolua_cRankManager_SetRankVisuals);
tolua_endmodule(tolua_S);
}
diff --git a/src/Bindings/ManualBindings_World.cpp b/src/Bindings/ManualBindings_World.cpp
new file mode 100644
index 000000000..5becbba92
--- /dev/null
+++ b/src/Bindings/ManualBindings_World.cpp
@@ -0,0 +1,588 @@
+
+// ManualBindings_World.cpp
+
+// Implements the manual Lua API bindings for the cWorld class
+
+#include "Globals.h"
+#include "tolua++/include/tolua++.h"
+#include "../World.h"
+#include "../Broadcaster.h"
+#include "ManualBindings.h"
+#include "LuaState.h"
+#include "PluginLua.h"
+#include "LuaChunkStay.h"
+
+
+
+
+
+static int tolua_cWorld_BroadcastParticleEffect(lua_State * tolua_S)
+{
+ /* Function signature:
+ World:BroadcastParticleEffect("Name", PosX, PosY, PosZ, OffX, OffY, OffZ, ParticleData, ParticleAmount, [ExcludeClient], [OptionalParam1], [OptionalParam2]
+ */
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cWorld") ||
+ !L.CheckParamString (2) ||
+ !L.CheckParamNumber (3, 10)
+ )
+ {
+ return 0;
+ }
+
+ // Read the params:
+ cWorld * World = nullptr;
+ AString Name;
+ float PosX, PosY, PosZ, OffX, OffY, OffZ;
+ float ParticleData;
+ int ParticleAmmount;
+ cClientHandle * ExcludeClient = nullptr;
+ L.GetStackValues(1, World, Name, PosX, PosY, PosZ, OffX, OffY, OffZ, ParticleData, ParticleAmmount, ExcludeClient);
+ if (World == nullptr)
+ {
+ LOGWARNING("World:BroadcastParticleEffect(): invalid world parameter");
+ L.LogStackTrace();
+ return 0;
+ }
+
+ // Read up to 2 more optional data params:
+ std::array<int, 2> data;
+ for (int i = 0; (i < 2) && L.IsParamNumber(11 + i); i++)
+ {
+ L.GetStackValue(11 + i, data[i]);
+ }
+
+ World->GetBroadcaster().BroadcastParticleEffect(Name, Vector3f(PosX, PosY, PosZ), Vector3f(OffX, OffY, OffZ), ParticleData, ParticleAmmount, ExcludeClient);
+
+ return 0;
+}
+
+
+
+
+
+static int tolua_cWorld_ChunkStay(lua_State * tolua_S)
+{
+ /* Function signature:
+ World:ChunkStay(ChunkCoordTable, OnChunkAvailable, OnAllChunksAvailable)
+ ChunkCoordTable == { {Chunk1x, Chunk1z}, {Chunk2x, Chunk2z}, ... }
+ */
+
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType (1, "cWorld") ||
+ !L.CheckParamTable (2) ||
+ !L.CheckParamFunctionOrNil(3, 4)
+ )
+ {
+ return 0;
+ }
+
+ cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S);
+ if (Plugin == nullptr)
+ {
+ return 0;
+ }
+
+ // Read the params:
+ cWorld * World = (cWorld *)tolua_tousertype(tolua_S, 1, nullptr);
+ if (World == nullptr)
+ {
+ LOGWARNING("World:ChunkStay(): invalid world parameter");
+ L.LogStackTrace();
+ return 0;
+ }
+
+ cLuaChunkStay * ChunkStay = new cLuaChunkStay(*Plugin);
+
+ if (!ChunkStay->AddChunks(2))
+ {
+ delete ChunkStay;
+ ChunkStay = nullptr;
+ return 0;
+ }
+
+ ChunkStay->Enable(*World->GetChunkMap(), 3, 4);
+ return 0;
+}
+
+
+
+
+
+static int tolua_cWorld_GetBlockInfo(lua_State * tolua_S)
+{
+ // Exported manually, because tolua would generate useless additional parameters (a_BlockType .. a_BlockSkyLight)
+ // Function signature: GetBlockInfo(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta, BlockSkyLight, BlockBlockLight]
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cWorld") ||
+ !L.CheckParamNumber(2, 4) ||
+ !L.CheckParamEnd(5)
+ )
+ {
+ return 0;
+ }
+
+ // Get params:
+ cWorld * Self = nullptr;
+ int BlockX, BlockY, BlockZ;
+ L.GetStackValues(1, Self, BlockX, BlockY, BlockZ);
+ if (Self == nullptr)
+ {
+ return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
+ }
+
+ // Call the function:
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta, BlockSkyLight, BlockBlockLight;
+ bool res = Self->GetBlockInfo(BlockX, BlockY, BlockZ, BlockType, BlockMeta, BlockSkyLight, BlockBlockLight);
+
+ // Push the returned values:
+ L.Push(res);
+ if (res)
+ {
+ L.Push(BlockType);
+ L.Push(BlockMeta);
+ L.Push(BlockSkyLight);
+ L.Push(BlockBlockLight);
+ return 5;
+ }
+ return 1;
+}
+
+
+
+
+
+static int tolua_cWorld_GetBlockTypeMeta(lua_State * tolua_S)
+{
+ // Exported manually, because tolua would generate useless additional parameters (a_BlockType, a_BlockMeta)
+ // Function signature: GetBlockTypeMeta(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta]
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cWorld") ||
+ !L.CheckParamNumber(2, 4) ||
+ !L.CheckParamEnd(5)
+ )
+ {
+ return 0;
+ }
+
+ // Get params:
+ cWorld * Self = nullptr;
+ int BlockX, BlockY, BlockZ;
+ L.GetStackValues(1, Self, BlockX, BlockY, BlockZ);
+ if (Self == nullptr)
+ {
+ return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
+ }
+
+ // Call the function:
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ bool res = Self->GetBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta);
+
+ // Push the returned values:
+ L.Push(res);
+ if (res)
+ {
+ L.Push(BlockType);
+ L.Push(BlockMeta);
+ return 3;
+ }
+ return 1;
+}
+
+
+
+
+
+static int tolua_cWorld_GetSignLines(lua_State * tolua_S)
+{
+ // Exported manually, because tolua would generate useless additional parameters (a_Line1 .. a_Line4)
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cWorld") ||
+ !L.CheckParamNumber(2, 4) ||
+ !L.CheckParamEnd(5)
+ )
+ {
+ return 0;
+ }
+
+ // Get params:
+ cWorld * Self = nullptr;
+ int BlockX, BlockY, BlockZ;
+ L.GetStackValues(1, Self, BlockX, BlockY, BlockZ);
+ if (Self == nullptr)
+ {
+ return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
+ }
+
+ // Call the function:
+ AString Line1, Line2, Line3, Line4;
+ bool res = Self->GetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4);
+
+ // Push the returned values:
+ L.Push(res);
+ if (res)
+ {
+ L.Push(Line1);
+ L.Push(Line2);
+ L.Push(Line3);
+ L.Push(Line4);
+ return 5;
+ }
+ return 1;
+}
+
+
+
+
+
+static int tolua_cWorld_PrepareChunk(lua_State * tolua_S)
+{
+ /* Function signature:
+ World:PrepareChunk(ChunkX, ChunkZ, Callback)
+ */
+
+ // Check the param types:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType (1, "cWorld") ||
+ !L.CheckParamNumber (2, 3) ||
+ !L.CheckParamFunctionOrNil(4)
+ )
+ {
+ return 0;
+ }
+
+ // Read the params:
+ cWorld * world = nullptr;
+ int chunkX = 0, chunkZ = 0;
+ L.GetStackValues(1, world, chunkX, chunkZ);
+ if (world == nullptr)
+ {
+ LOGWARNING("World:PrepareChunk(): invalid world parameter");
+ L.LogStackTrace();
+ return 0;
+ }
+
+ // Wrap the Lua callback inside a C++ callback class:
+ class cCallback:
+ public cChunkCoordCallback
+ {
+ public:
+ cCallback(lua_State * a_LuaState):
+ m_LuaState(a_LuaState),
+ m_Callback(m_LuaState, 4)
+ {
+ }
+
+ // cChunkCoordCallback override:
+ virtual void Call(int a_CBChunkX, int a_CBChunkZ) override
+ {
+ if (m_Callback.IsValid())
+ {
+ m_LuaState.Call(m_Callback, a_CBChunkX, a_CBChunkZ);
+ }
+
+ // This is the last reference of this object, we must delete it so that it doesn't leak:
+ delete this;
+ }
+
+ protected:
+ cLuaState m_LuaState;
+ cLuaState::cRef m_Callback;
+ };
+ cCallback * callback = new cCallback(tolua_S);
+
+ // Call the chunk preparation:
+ world->PrepareChunk(chunkX, chunkZ, callback);
+ return 0;
+}
+
+
+
+
+
+class cLuaWorldTask :
+ public cWorld::cTask,
+ public cPluginLua::cResettable
+{
+public:
+ cLuaWorldTask(cPluginLua & a_Plugin, int a_FnRef) :
+ cPluginLua::cResettable(a_Plugin),
+ m_FnRef(a_FnRef)
+ {
+ }
+
+protected:
+ int m_FnRef;
+
+ // cWorld::cTask overrides:
+ virtual void Run(cWorld & a_World) override
+ {
+ cCSLock Lock(m_CSPlugin);
+ if (m_Plugin != nullptr)
+ {
+ m_Plugin->Call(m_FnRef, &a_World);
+ }
+ }
+} ;
+
+
+
+
+
+static int tolua_cWorld_QueueTask(lua_State * tolua_S)
+{
+ // Binding for cWorld::QueueTask
+ // Params: function
+
+ // Retrieve the cPlugin from the LuaState:
+ cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S);
+ if (Plugin == nullptr)
+ {
+ // An error message has been already printed in GetLuaPlugin()
+ return 0;
+ }
+
+ // Retrieve the args:
+ cWorld * self = (cWorld *)tolua_tousertype(tolua_S, 1, nullptr);
+ if (self == nullptr)
+ {
+ return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance");
+ }
+ if (!lua_isfunction(tolua_S, 2))
+ {
+ return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1");
+ }
+
+ // Create a reference to the function:
+ int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ if (FnRef == LUA_REFNIL)
+ {
+ return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1");
+ }
+
+ auto task = std::make_shared<cLuaWorldTask>(*Plugin, FnRef);
+ Plugin->AddResettable(task);
+ self->QueueTask(task);
+ return 0;
+}
+
+
+
+
+
+static int tolua_cWorld_SetSignLines(lua_State * tolua_S)
+{
+ // Exported manually, because tolua would generate useless additional return values (a_Line1 .. a_Line4)
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cWorld") ||
+ !L.CheckParamNumber(2, 4) ||
+ !L.CheckParamString(5, 8) ||
+ !L.CheckParamEnd(9)
+ )
+ {
+ return 0;
+ }
+
+ // Get params:
+ cWorld * Self = nullptr;
+ int BlockX, BlockY, BlockZ;
+ AString Line1, Line2, Line3, Line4;
+ L.GetStackValues(1, Self, BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4);
+ if (Self == nullptr)
+ {
+ return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
+ }
+
+ // Call the function:
+ bool res = Self->SetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4);
+
+ // Push the returned values:
+ L.Push(res);
+ return 1;
+}
+
+
+
+
+
+class cLuaScheduledWorldTask :
+ public cWorld::cTask,
+ public cPluginLua::cResettable
+{
+public:
+ cLuaScheduledWorldTask(cPluginLua & a_Plugin, int a_FnRef) :
+ cPluginLua::cResettable(a_Plugin),
+ m_FnRef(a_FnRef)
+ {
+ }
+
+protected:
+ int m_FnRef;
+
+ // cWorld::cTask overrides:
+ virtual void Run(cWorld & a_World) override
+ {
+ cCSLock Lock(m_CSPlugin);
+ if (m_Plugin != nullptr)
+ {
+ m_Plugin->Call(m_FnRef, &a_World);
+ }
+ }
+};
+
+
+
+
+
+static int tolua_cWorld_ScheduleTask(lua_State * tolua_S)
+{
+ // Binding for cWorld::ScheduleTask
+ // Params: function, Ticks
+
+ // Retrieve the cPlugin from the LuaState:
+ cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S);
+ if (Plugin == nullptr)
+ {
+ // An error message has been already printed in GetLuaPlugin()
+ return 0;
+ }
+
+ // Retrieve the args:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cWorld") ||
+ !L.CheckParamNumber (2) ||
+ !L.CheckParamFunction(3)
+ )
+ {
+ return 0;
+ }
+ cWorld * World = (cWorld *)tolua_tousertype(tolua_S, 1, nullptr);
+ if (World == nullptr)
+ {
+ return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance");
+ }
+
+ // Create a reference to the function:
+ int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ if (FnRef == LUA_REFNIL)
+ {
+ return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1");
+ }
+
+ int DelayTicks = (int)tolua_tonumber(tolua_S, 2, 0);
+
+ auto task = std::make_shared<cLuaScheduledWorldTask>(*Plugin, FnRef);
+ Plugin->AddResettable(task);
+ World->ScheduleTask(DelayTicks, task);
+ return 0;
+}
+
+
+
+
+
+
+static int tolua_cWorld_TryGetHeight(lua_State * tolua_S)
+{
+ /* Exported manually, because tolua would require the out-only param a_Height to be used when calling
+ Function signature: world:TryGetHeight(a_World, a_BlockX, a_BlockZ) -> IsValid, Height
+ */
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cWorld") ||
+ !L.CheckParamNumber(2, 3) ||
+ !L.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Get params:
+ cWorld * self = nullptr;
+ int BlockX, BlockZ;
+ L.GetStackValues(1, self, BlockX, BlockZ);
+ if (self == nullptr)
+ {
+ tolua_error(tolua_S, "Invalid 'self' in function 'TryGetHeight'", nullptr);
+ return 0;
+ }
+
+ // Call the implementation:
+ int Height = 0;
+ bool res = self->TryGetHeight(BlockX, BlockZ, Height);
+ L.Push(res);
+ if (res)
+ {
+ L.Push(Height);
+ return 2;
+ }
+ return 1;
+}
+
+
+
+
+
+void cManualBindings::BindWorld(lua_State * tolua_S)
+{
+ tolua_beginmodule(tolua_S, nullptr);
+ tolua_beginmodule(tolua_S, "cWorld");
+ tolua_function(tolua_S, "BroadcastParticleEffect", tolua_cWorld_BroadcastParticleEffect);
+ tolua_function(tolua_S, "ChunkStay", tolua_cWorld_ChunkStay);
+ tolua_function(tolua_S, "DoWithBlockEntityAt", DoWithXYZ<cWorld, cBlockEntity, &cWorld::DoWithBlockEntityAt>);
+ tolua_function(tolua_S, "DoWithBeaconAt", DoWithXYZ<cWorld, cBeaconEntity, &cWorld::DoWithBeaconAt>);
+ tolua_function(tolua_S, "DoWithChestAt", DoWithXYZ<cWorld, cChestEntity, &cWorld::DoWithChestAt>);
+ tolua_function(tolua_S, "DoWithDispenserAt", DoWithXYZ<cWorld, cDispenserEntity, &cWorld::DoWithDispenserAt>);
+ tolua_function(tolua_S, "DoWithDropSpenserAt", DoWithXYZ<cWorld, cDropSpenserEntity, &cWorld::DoWithDropSpenserAt>);
+ tolua_function(tolua_S, "DoWithDropperAt", DoWithXYZ<cWorld, cDropperEntity, &cWorld::DoWithDropperAt>);
+ tolua_function(tolua_S, "DoWithEntityByID", DoWithID< cWorld, cEntity, &cWorld::DoWithEntityByID>);
+ tolua_function(tolua_S, "DoWithFurnaceAt", DoWithXYZ<cWorld, cFurnaceEntity, &cWorld::DoWithFurnaceAt>);
+ tolua_function(tolua_S, "DoWithNoteBlockAt", DoWithXYZ<cWorld, cNoteEntity, &cWorld::DoWithNoteBlockAt>);
+ tolua_function(tolua_S, "DoWithCommandBlockAt", DoWithXYZ<cWorld, cCommandBlockEntity, &cWorld::DoWithCommandBlockAt>);
+ tolua_function(tolua_S, "DoWithMobHeadAt", DoWithXYZ<cWorld, cMobHeadEntity, &cWorld::DoWithMobHeadAt>);
+ tolua_function(tolua_S, "DoWithFlowerPotAt", DoWithXYZ<cWorld, cFlowerPotEntity, &cWorld::DoWithFlowerPotAt>);
+ tolua_function(tolua_S, "DoWithPlayer", DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>);
+ tolua_function(tolua_S, "FindAndDoWithPlayer", DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>);
+ tolua_function(tolua_S, "DoWithPlayerByUUID", DoWith< cWorld, cPlayer, &cWorld::DoWithPlayerByUUID>);
+ tolua_function(tolua_S, "ForEachBlockEntityInChunk", ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>);
+ tolua_function(tolua_S, "ForEachChestInChunk", ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>);
+ tolua_function(tolua_S, "ForEachEntity", ForEach< cWorld, cEntity, &cWorld::ForEachEntity>);
+ tolua_function(tolua_S, "ForEachEntityInBox", ForEachInBox< cWorld, cEntity, &cWorld::ForEachEntityInBox>);
+ tolua_function(tolua_S, "ForEachEntityInChunk", ForEachInChunk<cWorld, cEntity, &cWorld::ForEachEntityInChunk>);
+ tolua_function(tolua_S, "ForEachFurnaceInChunk", ForEachInChunk<cWorld, cFurnaceEntity, &cWorld::ForEachFurnaceInChunk>);
+ tolua_function(tolua_S, "ForEachPlayer", ForEach< cWorld, cPlayer, &cWorld::ForEachPlayer>);
+ tolua_function(tolua_S, "GetBlockInfo", tolua_cWorld_GetBlockInfo);
+ tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cWorld_GetBlockTypeMeta);
+ tolua_function(tolua_S, "GetSignLines", tolua_cWorld_GetSignLines);
+ tolua_function(tolua_S, "PrepareChunk", tolua_cWorld_PrepareChunk);
+ tolua_function(tolua_S, "QueueTask", tolua_cWorld_QueueTask);
+ tolua_function(tolua_S, "ScheduleTask", tolua_cWorld_ScheduleTask);
+ tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines);
+ tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight);
+ tolua_endmodule(tolua_S);
+ tolua_endmodule(tolua_S);
+}
+
+
+
+
diff --git a/src/Bindings/Plugin.cpp b/src/Bindings/Plugin.cpp
index 98ccfb88c..2f2771e38 100644
--- a/src/Bindings/Plugin.cpp
+++ b/src/Bindings/Plugin.cpp
@@ -7,11 +7,11 @@
-cPlugin::cPlugin(const AString & a_PluginDirectory) :
- m_Language(E_CPP),
- m_Name(a_PluginDirectory),
+cPlugin::cPlugin(const AString & a_FolderName) :
+ m_Status(cPluginManager::psDisabled),
+ m_Name(a_FolderName),
m_Version(0),
- m_Directory(a_PluginDirectory)
+ m_FolderName(a_FolderName)
{
}
@@ -28,9 +28,33 @@ cPlugin::~cPlugin()
+void cPlugin::Unload(void)
+{
+ auto pm = cPluginManager::Get();
+ pm->RemovePluginCommands(this);
+ pm->RemovePluginConsoleCommands(this);
+ pm->RemoveHooks(this);
+ OnDisable();
+ m_Status = cPluginManager::psUnloaded;
+ m_LoadError.clear();
+}
+
+
+
+
+
AString cPlugin::GetLocalFolder(void) const
{
- return std::string("Plugins/") + m_Directory;
+ return std::string("Plugins/") + m_FolderName;
+}
+
+
+
+
+void cPlugin::SetLoadError(const AString & a_LoadError)
+{
+ m_Status = cPluginManager::psError;
+ m_LoadError = a_LoadError;
}
diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h
index 3f9fa7655..1330bca0d 100644
--- a/src/Bindings/Plugin.h
+++ b/src/Bindings/Plugin.h
@@ -1,30 +1,16 @@
-#pragma once
-
-#include "Defines.h"
+// Plugin.h
-class cCommandOutputCallback;
-class cItems;
-class cHopperEntity;
+// Declares the cPlugin class representing an interface that a plugin implementation needs to expose, with some helping functions
-class cBlockEntityWithItems;
-class cClientHandle;
-class cPickup;
-class cPlayer;
-class cProjectileEntity;
-class cEntity;
-class cMonster;
-class cWorld;
-class cChunkDesc;
-struct TakeDamageInfo;
-// fwd: CraftingRecipes.h
-class cCraftingGrid;
-class cCraftingRecipe;
+#pragma once
+#include "Defines.h"
+#include "PluginManager.h"
@@ -35,11 +21,23 @@ class cPlugin
public:
// tolua_end
- cPlugin( const AString & a_PluginDirectory);
+ /** Creates a new instance.
+ a_FolderName is the name of the folder (in the Plugins folder) from which the plugin is loaded.
+ The plugin's name defaults to the folder name. */
+ cPlugin(const AString & a_FolderName);
+
virtual ~cPlugin();
+ /** Called as the last call into the plugin before it is unloaded. */
virtual void OnDisable(void) {}
- virtual bool Initialize(void) = 0;
+
+ /** Loads and initializes the plugin. Sets m_Status to psLoaded or psError accordingly.
+ Returns true if the initialization succeeded, false otherwise. */
+ virtual bool Load(void) = 0;
+
+ /** Unloads the plugin. Sets m_Status to psDisabled.
+ The default implementation removes the plugin's associations with cPluginManager, descendants should call it as well. */
+ virtual void Unload(void);
// Called each tick
virtual void Tick(float a_Dt) = 0;
@@ -58,7 +56,9 @@ public:
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0;
virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) = 0;
virtual bool OnEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0;
- virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0;
+ virtual bool OnEntityChangingWorld (cEntity & a_Entity, cWorld & a_World) = 0;
+ virtual bool OnEntityChangedWorld (cEntity & a_Entity, cWorld & a_World) = 0;
+ virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result) = 0;
virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;
virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;
virtual bool OnHandshake (cClientHandle & a_Client, const AString & a_Username) = 0;
@@ -109,19 +109,17 @@ public:
/** Handles the command split into a_Split, issued by player a_Player.
Command permissions have already been checked.
- Returns true if command handled successfully
- */
+ Returns true if command handled successfully. */
virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) = 0;
/** Handles the console command split into a_Split.
- Returns true if command handled successfully. Output is to be sent to the a_Output callback.
- */
+ Returns true if command handled successfully. Output is to be sent to the a_Output callback. */
virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) = 0;
- /// All bound commands are to be removed, do any language-dependent cleanup here
+ /** All bound commands are to be removed, do any language-dependent cleanup here */
virtual void ClearCommands(void) {}
- /// All bound console commands are to be removed, do any language-dependent cleanup here
+ /** All bound console commands are to be removed, do any language-dependent cleanup here */
virtual void ClearConsoleCommands(void) {}
// tolua_begin
@@ -131,28 +129,43 @@ public:
int GetVersion(void) const { return m_Version; }
void SetVersion(int a_Version) { m_Version = a_Version; }
- const AString & GetDirectory(void) const {return m_Directory; }
- AString GetLocalDirectory(void) const {return GetLocalFolder(); } // OBSOLETE, use GetLocalFolder() instead
+ /** Returns the name of the folder (in the Plugins folder) from which the plugin is loaded. */
+ const AString & GetFolderName(void) const {return m_FolderName; }
+
+ /** Returns the folder relative to the MCS Executable, from which the plugin is loaded. */
AString GetLocalFolder(void) const;
+
+ /** Returns the error encountered while loading the plugin. Only valid if m_Status == psError. */
+ const AString & GetLoadError(void) const { return m_LoadError; }
+
+ cPluginManager::ePluginStatus GetStatus(void) const { return m_Status; }
+
+ bool IsLoaded(void) const { return (m_Status == cPluginManager::psLoaded); }
// tolua_end
+ // Needed for ManualBindings' tolua_ForEach<>
+ static const char * GetClassStatic(void) { return "cPlugin"; }
+
+protected:
+ friend class cPluginManager;
+
+ cPluginManager::ePluginStatus m_Status;
- /* This should not be exposed to scripting languages */
- enum PluginLanguage
- {
- E_CPP,
- E_LUA,
- E_SQUIRREL, // OBSOLETE, but kept in place to remind us of the horrors lurking in the history
- };
- PluginLanguage GetLanguage() { return m_Language; }
- void SetLanguage( PluginLanguage a_Language) { m_Language = a_Language; }
-
-private:
- PluginLanguage m_Language;
+ /** The name of the plugin, used to identify the plugin in the system and for inter-plugin calls. */
AString m_Name;
+
int m_Version;
- AString m_Directory;
+ /** Name of the folder (in the Plugins folder) from which the plugin is loaded. */
+ AString m_FolderName;
+
+ /** The error encountered while loading the plugin.
+ Only valid if m_Status == psError. */
+ AString m_LoadError;
+
+
+ /** Sets m_LoadError to the specified string and m_Status to psError. */
+ void SetLoadError(const AString & a_LoadError);
}; // tolua_export
diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp
index 0a2a8411d..234bf579b 100644
--- a/src/Bindings/PluginLua.cpp
+++ b/src/Bindings/PluginLua.cpp
@@ -63,6 +63,11 @@ void cPluginLua::Close(void)
return;
}
+ // Remove the command bindings and web tabs:
+ ClearCommands();
+ ClearConsoleCommands();
+ ClearTabs();
+
// Notify and remove all m_Resettables (unlock the m_CriticalSection while resetting them):
cResettablePtrs resettables;
std::swap(m_Resettables, resettables);
@@ -93,7 +98,7 @@ void cPluginLua::Close(void)
-bool cPluginLua::Initialize(void)
+bool cPluginLua::Load(void)
{
cCSLock Lock(m_CriticalSection);
if (!m_LuaState.IsValid())
@@ -144,6 +149,7 @@ bool cPluginLua::Initialize(void)
// Warn if there are no Lua files in the plugin folder:
if (LuaFiles.empty())
{
+ SetLoadError("No lua files found, plugin is probably missing.");
LOGWARNING("No lua files found: plugin %s is missing.", GetName().c_str());
Close();
return false;
@@ -155,6 +161,7 @@ bool cPluginLua::Initialize(void)
AString Path = PluginPath + *itr;
if (!m_LuaState.LoadFile(Path))
{
+ SetLoadError(Printf("Failed to load file %s.", itr->c_str()));
Close();
return false;
}
@@ -164,6 +171,8 @@ bool cPluginLua::Initialize(void)
AString Path = PluginPath + "Info.lua";
if (!m_LuaState.LoadFile(Path))
{
+ SetLoadError("Failed to load file Info.lua.");
+ m_Status = cPluginManager::psError;
Close();
return false;
}
@@ -173,17 +182,20 @@ bool cPluginLua::Initialize(void)
bool res = false;
if (!m_LuaState.Call("Initialize", this, cLuaState::Return, res))
{
+ SetLoadError("Cannot call the Initialize() function.");
LOGWARNING("Error in plugin %s: Cannot call the Initialize() function. Plugin is temporarily disabled.", GetName().c_str());
Close();
return false;
}
if (!res)
{
+ SetLoadError("The Initialize() function failed.");
LOGINFO("Plugin %s: Initialize() call failed, plugin is temporarily disabled.", GetName().c_str());
Close();
return false;
}
+ m_Status = cPluginManager::psLoaded;
return true;
}
@@ -191,6 +203,17 @@ bool cPluginLua::Initialize(void)
+void cPluginLua::Unload(void)
+{
+ ClearTabs();
+ super::Unload();
+ Close();
+}
+
+
+
+
+
void cPluginLua::OnDisable(void)
{
cCSLock Lock(m_CriticalSection);
@@ -208,6 +231,10 @@ void cPluginLua::OnDisable(void)
void cPluginLua::Tick(float a_Dt)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return;
+ }
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TICK];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
{
@@ -222,6 +249,10 @@ void cPluginLua::Tick(float a_Dt)
bool cPluginLua::OnBlockSpread(cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BLOCK_SPREAD];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -242,6 +273,10 @@ bool cPluginLua::OnBlockSpread(cWorld & a_World, int a_BlockX, int a_BlockY, int
bool cPluginLua::OnBlockToPickups(cWorld & a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BLOCK_TO_PICKUPS];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -262,6 +297,10 @@ bool cPluginLua::OnBlockToPickups(cWorld & a_World, cEntity * a_Digger, int a_Bl
bool cPluginLua::OnChat(cPlayer & a_Player, AString & a_Message)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHAT];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -282,6 +321,10 @@ bool cPluginLua::OnChat(cPlayer & a_Player, AString & a_Message)
bool cPluginLua::OnChunkAvailable(cWorld & a_World, int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_AVAILABLE];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -302,6 +345,10 @@ bool cPluginLua::OnChunkAvailable(cWorld & a_World, int a_ChunkX, int a_ChunkZ)
bool cPluginLua::OnChunkGenerated(cWorld & a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATED];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -322,6 +369,10 @@ bool cPluginLua::OnChunkGenerated(cWorld & a_World, int a_ChunkX, int a_ChunkZ,
bool cPluginLua::OnChunkGenerating(cWorld & a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATING];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -342,6 +393,10 @@ bool cPluginLua::OnChunkGenerating(cWorld & a_World, int a_ChunkX, int a_ChunkZ,
bool cPluginLua::OnChunkUnloaded(cWorld & a_World, int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADED];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -362,6 +417,10 @@ bool cPluginLua::OnChunkUnloaded(cWorld & a_World, int a_ChunkX, int a_ChunkZ)
bool cPluginLua::OnChunkUnloading(cWorld & a_World, int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADING];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -382,6 +441,10 @@ bool cPluginLua::OnChunkUnloading(cWorld & a_World, int a_ChunkX, int a_ChunkZ)
bool cPluginLua::OnCollectingPickup(cPlayer & a_Player, cPickup & a_Pickup)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_COLLECTING_PICKUP];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -402,6 +465,10 @@ bool cPluginLua::OnCollectingPickup(cPlayer & a_Player, cPickup & a_Pickup)
bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CRAFTING_NO_RECIPE];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -422,6 +489,10 @@ bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid,
bool cPluginLua::OnDisconnect(cClientHandle & a_Client, const AString & a_Reason)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_DISCONNECT];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -442,6 +513,10 @@ bool cPluginLua::OnDisconnect(cClientHandle & a_Client, const AString & a_Reason
bool cPluginLua::OnEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_ADD_EFFECT];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -459,14 +534,66 @@ bool cPluginLua::OnEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_E
-bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split)
+bool cPluginLua::OnEntityChangingWorld(cEntity & a_Entity, cWorld & a_World)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGING_WORLD];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Entity, &a_World, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnEntityChangedWorld(cEntity & a_Entity, cWorld & a_World)
+{
+ cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGED_WORLD];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Entity, &a_World, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result)
+{
+ cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXECUTE_COMMAND];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
{
- m_LuaState.Call((int)(**itr), a_Player, a_Split, cLuaState::Return, res);
+ m_LuaState.Call((int)(**itr), a_Player, a_Split, a_EntireCommand, cLuaState::Return, res, a_Result);
if (res)
{
return true;
@@ -482,6 +609,10 @@ bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Sp
bool cPluginLua::OnExploded(cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODED];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -519,6 +650,10 @@ bool cPluginLua::OnExploded(cWorld & a_World, double a_ExplosionSize, bool a_Can
bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODING];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -556,6 +691,10 @@ bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool &
bool cPluginLua::OnHandshake(cClientHandle & a_Client, const AString & a_Username)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HANDSHAKE];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -576,8 +715,11 @@ bool cPluginLua::OnHandshake(cClientHandle & a_Client, const AString & a_Usernam
bool cPluginLua::OnHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
-
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PULLING_ITEM];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
{
@@ -597,6 +739,10 @@ bool cPluginLua::OnHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper,
bool cPluginLua::OnHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PUSHING_ITEM];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -617,6 +763,10 @@ bool cPluginLua::OnHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper,
bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_KILLING];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -637,6 +787,10 @@ bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer, TakeDamageInf
bool cPluginLua::OnLogin(cClientHandle & a_Client, int a_ProtocolVersion, const AString & a_Username)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_LOGIN];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -657,6 +811,10 @@ bool cPluginLua::OnLogin(cClientHandle & a_Client, int a_ProtocolVersion, const
bool cPluginLua::OnPlayerAnimation(cPlayer & a_Player, int a_Animation)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_ANIMATION];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -677,6 +835,10 @@ bool cPluginLua::OnPlayerAnimation(cPlayer & a_Player, int a_Animation)
bool cPluginLua::OnPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BREAKING_BLOCK];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -697,6 +859,10 @@ bool cPluginLua::OnPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_B
bool cPluginLua::OnPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BROKEN_BLOCK];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -717,6 +883,10 @@ bool cPluginLua::OnPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_Blo
bool cPluginLua::OnPlayerDestroyed(cPlayer & a_Player)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_DESTROYED];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -737,6 +907,10 @@ bool cPluginLua::OnPlayerDestroyed(cPlayer & a_Player)
bool cPluginLua::OnPlayerEating(cPlayer & a_Player)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_EATING];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -757,6 +931,10 @@ bool cPluginLua::OnPlayerEating(cPlayer & a_Player)
bool cPluginLua::OnPlayerFoodLevelChange(cPlayer & a_Player, int a_NewFoodLevel)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FOOD_LEVEL_CHANGE];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -777,6 +955,10 @@ bool cPluginLua::OnPlayerFoodLevelChange(cPlayer & a_Player, int a_NewFoodLevel)
bool cPluginLua::OnPlayerFished(cPlayer & a_Player, const cItems & a_Reward)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FISHED];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -797,6 +979,10 @@ bool cPluginLua::OnPlayerFished(cPlayer & a_Player, const cItems & a_Reward)
bool cPluginLua::OnPlayerFishing(cPlayer & a_Player, cItems & a_Reward)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FISHING];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -817,6 +1003,10 @@ bool cPluginLua::OnPlayerFishing(cPlayer & a_Player, cItems & a_Reward)
bool cPluginLua::OnPlayerJoined(cPlayer & a_Player)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_JOINED];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -837,6 +1027,10 @@ bool cPluginLua::OnPlayerJoined(cPlayer & a_Player)
bool cPluginLua::OnPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_LEFT_CLICK];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -857,6 +1051,10 @@ bool cPluginLua::OnPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_Block
bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_MOVING];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -877,6 +1075,10 @@ bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPositi
bool cPluginLua::OnEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_TELEPORT];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -897,6 +1099,10 @@ bool cPluginLua::OnEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosi
bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACED_BLOCK];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -922,6 +1128,10 @@ bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_Blo
bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACING_BLOCK];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -947,6 +1157,10 @@ bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_Bl
bool cPluginLua::OnPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICK];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -967,6 +1181,10 @@ bool cPluginLua::OnPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_Bloc
bool cPluginLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -987,6 +1205,10 @@ bool cPluginLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Ent
bool cPluginLua::OnPlayerShooting(cPlayer & a_Player)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SHOOTING];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1007,6 +1229,10 @@ bool cPluginLua::OnPlayerShooting(cPlayer & a_Player)
bool cPluginLua::OnPlayerSpawned(cPlayer & a_Player)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SPAWNED];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1027,6 +1253,10 @@ bool cPluginLua::OnPlayerSpawned(cPlayer & a_Player)
bool cPluginLua::OnPlayerTossingItem(cPlayer & a_Player)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_TOSSING_ITEM];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1047,6 +1277,10 @@ bool cPluginLua::OnPlayerTossingItem(cPlayer & a_Player)
bool cPluginLua::OnPlayerUsedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_BLOCK];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1067,6 +1301,10 @@ bool cPluginLua::OnPlayerUsedBlock(cPlayer & a_Player, int a_BlockX, int a_Block
bool cPluginLua::OnPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_ITEM];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1087,6 +1325,10 @@ bool cPluginLua::OnPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY
bool cPluginLua::OnPlayerUsingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_BLOCK];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1107,6 +1349,10 @@ bool cPluginLua::OnPlayerUsingBlock(cPlayer & a_Player, int a_BlockX, int a_Bloc
bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_ITEM];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1127,6 +1373,10 @@ bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_Block
bool cPluginLua::OnPluginMessage(cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLUGIN_MESSAGE];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1147,6 +1397,10 @@ bool cPluginLua::OnPluginMessage(cClientHandle & a_Client, const AString & a_Cha
bool cPluginLua::OnPluginsLoaded(void)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLUGINS_LOADED];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1165,6 +1419,10 @@ bool cPluginLua::OnPluginsLoaded(void)
bool cPluginLua::OnPostCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_POST_CRAFTING];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1185,6 +1443,10 @@ bool cPluginLua::OnPostCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCra
bool cPluginLua::OnPreCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PRE_CRAFTING];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1205,6 +1467,10 @@ bool cPluginLua::OnPreCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraf
bool cPluginLua::OnProjectileHitBlock(cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PROJECTILE_HIT_BLOCK];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1225,6 +1491,10 @@ bool cPluginLua::OnProjectileHitBlock(cProjectileEntity & a_Projectile, int a_Bl
bool cPluginLua::OnProjectileHitEntity(cProjectileEntity & a_Projectile, cEntity & a_HitEntity)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PROJECTILE_HIT_ENTITY];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1245,6 +1515,10 @@ bool cPluginLua::OnProjectileHitEntity(cProjectileEntity & a_Projectile, cEntity
bool cPluginLua::OnServerPing(cClientHandle & a_ClientHandle, AString & a_ServerDescription, int & a_OnlinePlayersCount, int & a_MaxPlayersCount, AString & a_Favicon)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SERVER_PING];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1265,6 +1539,10 @@ bool cPluginLua::OnServerPing(cClientHandle & a_ClientHandle, AString & a_Server
bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_ENTITY];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1285,6 +1563,10 @@ bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity)
bool cPluginLua::OnSpawnedMonster(cWorld & a_World, cMonster & a_Monster)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_MONSTER];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1305,6 +1587,10 @@ bool cPluginLua::OnSpawnedMonster(cWorld & a_World, cMonster & a_Monster)
bool cPluginLua::OnSpawningEntity(cWorld & a_World, cEntity & a_Entity)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_ENTITY];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1325,6 +1611,10 @@ bool cPluginLua::OnSpawningEntity(cWorld & a_World, cEntity & a_Entity)
bool cPluginLua::OnSpawningMonster(cWorld & a_World, cMonster & a_Monster)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_MONSTER];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1345,6 +1635,10 @@ bool cPluginLua::OnSpawningMonster(cWorld & a_World, cMonster & a_Monster)
bool cPluginLua::OnTakeDamage(cEntity & a_Receiver, TakeDamageInfo & a_TDI)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TAKE_DAMAGE];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1370,6 +1664,10 @@ bool cPluginLua::OnUpdatedSign(
)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATED_SIGN];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1395,6 +1693,10 @@ bool cPluginLua::OnUpdatingSign(
)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATING_SIGN];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1415,6 +1717,10 @@ bool cPluginLua::OnUpdatingSign(
bool cPluginLua::OnWeatherChanged(cWorld & a_World)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGED];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1435,6 +1741,10 @@ bool cPluginLua::OnWeatherChanged(cWorld & a_World)
bool cPluginLua::OnWeatherChanging(cWorld & a_World, eWeather & a_NewWeather)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGING];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
@@ -1455,6 +1765,10 @@ bool cPluginLua::OnWeatherChanging(cWorld & a_World, eWeather & a_NewWeather)
bool cPluginLua::OnWorldStarted(cWorld & a_World)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WORLD_STARTED];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
{
@@ -1470,6 +1784,10 @@ bool cPluginLua::OnWorldStarted(cWorld & a_World)
bool cPluginLua::OnWorldTick(cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec)
{
cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WORLD_TICK];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
{
@@ -1614,6 +1932,8 @@ const char * cPluginLua::GetHookFnName(int a_HookType)
case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect";
case cPluginManager::HOOK_PLAYER_ANIMATION: return "OnPlayerAnimation";
case cPluginManager::HOOK_ENTITY_ADD_EFFECT: return "OnEntityAddEffect";
+ case cPluginManager::HOOK_ENTITY_CHANGING_WORLD: return "OnEntityChangingWorld";
+ case cPluginManager::HOOK_ENTITY_CHANGED_WORLD: return "OnEntityChangedWorld";
case cPluginManager::HOOK_ENTITY_TELEPORT: return "OnEntityTeleport";
case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand";
case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake";
@@ -1736,40 +2056,29 @@ void cPluginLua::AddResettable(cPluginLua::cResettablePtr a_Resettable)
-AString cPluginLua::HandleWebRequest(const HTTPRequest * a_Request)
+AString cPluginLua::HandleWebRequest(const HTTPRequest & a_Request)
{
- cCSLock Lock(m_CriticalSection);
- std::string RetVal = "";
-
- std::pair< std::string, std::string > TabName = GetTabNameForRequest(a_Request);
- std::string SafeTabName = TabName.second;
- if (SafeTabName.empty())
+ // Find the tab to use for the request:
+ auto TabName = GetTabNameForRequest(a_Request);
+ AString SafeTabTitle = TabName.second;
+ if (SafeTabTitle.empty())
{
return "";
}
-
- sWebPluginTab * Tab = 0;
- for (TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr)
+ auto Tab = GetTabBySafeTitle(SafeTabTitle);
+ if (Tab == nullptr)
{
- if ((*itr)->SafeTitle.compare(SafeTabName) == 0) // This is the one! Rawr
- {
- Tab = *itr;
- break;
- }
+ return "";
}
- if (Tab != nullptr)
+ // Get the page content from the plugin:
+ cCSLock Lock(m_CriticalSection);
+ AString Contents = Printf("WARNING: WebPlugin tab '%s' did not return a string!", Tab->m_Title.c_str());
+ if (!m_LuaState.Call(Tab->m_UserData, &a_Request, cLuaState::Return, Contents))
{
- AString Contents = Printf("WARNING: WebPlugin tab '%s' did not return a string!", Tab->Title.c_str());
- if (!m_LuaState.Call(Tab->UserData, a_Request, cLuaState::Return, Contents))
- {
- return "Lua encountered error while processing the page request";
- }
-
- RetVal += Contents;
+ return "Lua encountered error while processing the page request";
}
-
- return RetVal;
+ return Contents;
}
@@ -1784,13 +2093,7 @@ bool cPluginLua::AddWebTab(const AString & a_Title, lua_State * a_LuaState, int
LOGERROR("Only allowed to add a tab to a WebPlugin of your own Plugin!");
return false;
}
- sWebPluginTab * Tab = new sWebPluginTab();
- Tab->Title = a_Title;
- Tab->SafeTitle = SafeString(a_Title);
-
- Tab->UserData = a_FunctionReference;
-
- GetTabs().push_back(Tab);
+ AddNewWebTab(a_Title, a_FunctionReference);
return true;
}
diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h
index c14b02687..a763cdfdf 100644
--- a/src/Bindings/PluginLua.h
+++ b/src/Bindings/PluginLua.h
@@ -20,7 +20,7 @@
-// fwd: UI/Window.h
+// fwd: "UI/Window.h"
class cWindow;
@@ -32,6 +32,8 @@ class cPluginLua :
public cPlugin,
public cWebPlugin
{
+ typedef cPlugin super;
+
public:
// tolua_end
@@ -96,7 +98,8 @@ public:
~cPluginLua();
virtual void OnDisable(void) override;
- virtual bool Initialize(void) override;
+ virtual bool Load(void) override;
+ virtual void Unload(void) override;
virtual void Tick(float a_Dt) override;
@@ -112,7 +115,9 @@ public:
virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) override;
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) override;
virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) override;
- virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override;
+ virtual bool OnEntityChangingWorld (cEntity & a_Entity, cWorld & a_World) override;
+ virtual bool OnEntityChangedWorld (cEntity & a_Entity, cWorld & a_World) override;
+ virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result) override;
virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override;
virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override;
virtual bool OnHandshake (cClientHandle & a_Client, const AString & a_Username) override;
@@ -173,12 +178,13 @@ public:
/** Returns true if the plugin contains the function for the specified hook type, using the old-style registration (#121) */
bool CanAddOldStyleHook(int a_HookType);
- // cWebPlugin override
- virtual const AString GetWebTitle(void) const {return GetName(); }
+ // cWebPlugin overrides
+ virtual const AString GetWebTitle(void) const override {return GetName(); }
+ virtual AString HandleWebRequest(const HTTPRequest & a_Request) override;
- // cWebPlugin and WebAdmin stuff
- virtual AString HandleWebRequest(const HTTPRequest * a_Request) override;
- bool AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference); // >> EXPORTED IN MANUALBINDINGS <<
+ /** Adds a new web tab to webadmin.
+ Displaying the tab calls the referenced function. */
+ bool AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference); // Exported in ManualBindings.cpp
/** Binds the command to call the function specified by a Lua function reference. Simply adds to CommandMap. */
void BindCommand(const AString & a_Command, int a_FnRef);
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index 8935f7dd3..5b6bec728 100644
--- a/src/Bindings/PluginManager.cpp
+++ b/src/Bindings/PluginManager.cpp
@@ -59,39 +59,48 @@ void cPluginManager::ReloadPlugins(void)
-void cPluginManager::FindPlugins(void)
+void cPluginManager::RefreshPluginList(void)
{
+ // Get a list of currently available folders:
AString PluginsPath = GetPluginsPath() + "/";
-
- // First get a clean list of only the currently running plugins, we don't want to mess those up
- for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end();)
+ AStringVector Contents = cFile::GetFolderContents(PluginsPath.c_str());
+ AStringVector Folders;
+ for (auto & item: Contents)
{
- if (itr->second == nullptr)
+ if ((item == ".") || (item == "..") || (!cFile::IsFolder(PluginsPath + item)))
{
- PluginMap::iterator thiz = itr;
- ++thiz;
- m_Plugins.erase( itr);
- itr = thiz;
+ // We only want folders, and don't want "." or ".."
continue;
}
- ++itr;
- }
+ Folders.push_back(item);
+ } // for item - Contents[]
- AStringVector Files = cFile::GetFolderContents(PluginsPath.c_str());
- for (AStringVector::const_iterator itr = Files.begin(); itr != Files.end(); ++itr)
+ // Set all plugins with invalid folders as psNotFound:
+ for (auto & plugin: m_Plugins)
{
- if ((*itr == ".") || (*itr == "..") || (!cFile::IsFolder(PluginsPath + *itr)))
+ if (std::find(Folders.cbegin(), Folders.cend(), plugin->GetFolderName()) == Folders.end())
{
- // We only want folders, and don't want "." or ".."
- continue;
+ plugin->m_Status = psNotFound;
}
+ } // for plugin - m_Plugins[]
- // Add plugin name/directory to the list
- if (m_Plugins.find(*itr) == m_Plugins.end())
+ // Add all newly discovered plugins:
+ for (auto & folder: Folders)
+ {
+ bool hasFound = false;
+ for (auto & plugin: m_Plugins)
{
- m_Plugins[*itr] = nullptr;
+ if (plugin->GetFolderName() == folder)
+ {
+ hasFound = true;
+ break;
+ }
+ } // for plugin - m_Plugins[]
+ if (!hasFound)
+ {
+ m_Plugins.push_back(std::make_shared<cPluginLua>(folder));
}
- }
+ } // for folder - Folders[]
}
@@ -109,60 +118,26 @@ void cPluginManager::ReloadPluginsNow(void)
-void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni)
+void cPluginManager::ReloadPluginsNow(cSettingsRepositoryInterface & a_Settings)
{
LOG("-- Loading Plugins --");
+
+ // Unload any existing plugins:
m_bReloadPlugins = false;
UnloadPluginsNow();
- FindPlugins();
-
- cServer::BindBuiltInConsoleCommands();
-
- // Check if the Plugins section exists.
- int KeyNum = a_SettingsIni.FindKey("Plugins");
-
- if (KeyNum == -1)
- {
- InsertDefaultPlugins(a_SettingsIni);
- KeyNum = a_SettingsIni.FindKey("Plugins");
- }
-
- // How many plugins are there?
- int NumPlugins = a_SettingsIni.GetNumValues(KeyNum);
-
- for (int i = 0; i < NumPlugins; i++)
- {
- AString ValueName = a_SettingsIni.GetValueName(KeyNum, i);
- if (ValueName.compare("Plugin") == 0)
- {
- AString PluginFile = a_SettingsIni.GetValue(KeyNum, i);
- if (!PluginFile.empty())
- {
- if (m_Plugins.find(PluginFile) != m_Plugins.end())
- {
- LoadPlugin(PluginFile);
- }
- }
- }
- }
-
+ // Refresh the list of plugins to load new ones from disk / remove the deleted ones:
+ RefreshPluginList();
- // Remove invalid plugins from the PluginMap.
- for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end();)
+ // Load the plugins:
+ AStringVector ToLoad = GetFoldersToLoad(a_Settings);
+ for (auto & pluginFolder: ToLoad)
{
- if (itr->second == nullptr)
- {
- PluginMap::iterator thiz = itr;
- ++thiz;
- m_Plugins.erase(itr);
- itr = thiz;
- continue;
- }
- ++itr;
- }
+ LoadPlugin(pluginFolder);
+ } // for pluginFolder - ToLoad[]
- size_t NumLoadedPlugins = GetNumPlugins();
+ // Log a report of the loading process
+ size_t NumLoadedPlugins = GetNumLoadedPlugins();
if (NumLoadedPlugins == 0)
{
LOG("-- No Plugins Loaded --");
@@ -173,7 +148,7 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni)
}
else
{
- LOG("-- Loaded %i Plugins --", (int)NumLoadedPlugins);
+ LOG("-- Loaded %u Plugins --", static_cast<unsigned>(NumLoadedPlugins));
}
CallHookPluginsLoaded();
}
@@ -182,16 +157,16 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni)
-void cPluginManager::InsertDefaultPlugins(cIniFile & a_SettingsIni)
+void cPluginManager::InsertDefaultPlugins(cSettingsRepositoryInterface & a_Settings)
{
- a_SettingsIni.AddKeyName("Plugins");
- a_SettingsIni.AddKeyComment("Plugins", " Plugin=Debuggers");
- a_SettingsIni.AddKeyComment("Plugins", " Plugin=HookNotify");
- a_SettingsIni.AddKeyComment("Plugins", " Plugin=ChunkWorx");
- a_SettingsIni.AddKeyComment("Plugins", " Plugin=APIDump");
- a_SettingsIni.AddValue("Plugins", "Plugin", "Core");
- a_SettingsIni.AddValue("Plugins", "Plugin", "TransAPI");
- a_SettingsIni.AddValue("Plugins", "Plugin", "ChatLog");
+ a_Settings.AddKeyName("Plugins");
+ a_Settings.AddKeyComment("Plugins", " Plugin=Debuggers");
+ a_Settings.AddKeyComment("Plugins", " Plugin=HookNotify");
+ a_Settings.AddKeyComment("Plugins", " Plugin=ChunkWorx");
+ a_Settings.AddKeyComment("Plugins", " Plugin=APIDump");
+ a_Settings.AddValue("Plugins", "Plugin", "Core");
+ a_Settings.AddValue("Plugins", "Plugin", "TransAPI");
+ a_Settings.AddValue("Plugins", "Plugin", "ChatLog");
}
@@ -200,12 +175,39 @@ void cPluginManager::InsertDefaultPlugins(cIniFile & a_SettingsIni)
void cPluginManager::Tick(float a_Dt)
{
- while (!m_DisablePluginList.empty())
+ // Unload plugins that have been scheduled for unloading:
+ AStringVector PluginsToUnload;
{
- RemovePlugin(m_DisablePluginList.front());
- m_DisablePluginList.pop_front();
+ cCSLock Lock(m_CSPluginsToUnload);
+ std::swap(m_PluginsToUnload, PluginsToUnload);
}
+ for (auto & folder: PluginsToUnload)
+ {
+ bool HasUnloaded = false;
+ bool HasFound = false;
+ for (auto & plugin: m_Plugins)
+ {
+ if (plugin->GetFolderName() == folder)
+ {
+ HasFound = true;
+ if (plugin->IsLoaded())
+ {
+ plugin->Unload();
+ HasUnloaded = true;
+ }
+ }
+ }
+ if (!HasFound)
+ {
+ LOG("Cannot unload plugin in folder \"%s\", there's no such plugin folder", folder.c_str());
+ }
+ else if (!HasUnloaded)
+ {
+ LOG("Cannot unload plugin in folder \"%s\", it has not been loaded.", folder.c_str());
+ }
+ } // for plugin - m_Plugins[]
+ // If a plugin reload has been scheduled, reload now:
if (m_bReloadPlugins)
{
ReloadPluginsNow();
@@ -523,14 +525,50 @@ bool cPluginManager::CallHookEntityTeleport(cEntity & a_Entity, const Vector3d &
-bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split)
+bool cPluginManager::CallHookEntityChangingWorld(cEntity & a_Entity, cWorld & a_World)
+{
+ FIND_HOOK(HOOK_ENTITY_CHANGING_WORLD);
+ VERIFY_HOOK;
+
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnEntityChangingWorld(a_Entity, a_World))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+bool cPluginManager::CallHookEntityChangedWorld(cEntity & a_Entity, cWorld & a_World)
+{
+ FIND_HOOK(HOOK_ENTITY_CHANGED_WORLD);
+ VERIFY_HOOK;
+
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnEntityChangedWorld(a_Entity, a_World))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, CommandResult & a_Result)
{
FIND_HOOK(HOOK_EXECUTE_COMMAND);
VERIFY_HOOK;
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
{
- if ((*itr)->OnExecuteCommand(a_Player, a_Split))
+ if ((*itr)->OnExecuteCommand(a_Player, a_Split, a_EntireCommand, a_Result))
{
return true;
}
@@ -1443,14 +1481,25 @@ cPluginManager::CommandResult cPluginManager::HandleCommand(cPlayer & a_Player,
if (cmd == m_Commands.end())
{
// Command not found
+ // If it started with a slash, ask the plugins if they still want to handle it:
+ if (!a_Command.empty() && (a_Command[0] == '/'))
+ {
+ CommandResult Result = crUnknownCommand;
+ CallHookExecuteCommand(&a_Player, Split, a_Command, Result);
+ return Result;
+ }
return crUnknownCommand;
}
// Ask plugins first if a command is okay to execute the command:
- if (CallHookExecuteCommand(&a_Player, Split))
+ CommandResult Result = crBlocked;
+ if (CallHookExecuteCommand(&a_Player, Split, a_Command, Result))
{
- LOGINFO("Player %s tried executing command \"%s\" that was stopped by the HOOK_EXECUTE_COMMAND hook", a_Player.GetName().c_str(), Split[0].c_str());
- return crBlocked;
+ if (Result == crBlocked)
+ {
+ LOGINFO("Player %s tried executing command \"%s\" that was stopped by the HOOK_EXECUTE_COMMAND hook", a_Player.GetName().c_str(), Split[0].c_str());
+ }
+ return Result;
}
if (
@@ -1477,68 +1526,56 @@ cPluginManager::CommandResult cPluginManager::HandleCommand(cPlayer & a_Player,
-cPlugin * cPluginManager::GetPlugin(const AString & a_Plugin) const
+void cPluginManager::UnloadPluginsNow()
{
- for (PluginMap::const_iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr)
- {
- if (itr->second == nullptr)
- {
- // The plugin is currently unloaded
- continue;
- }
+ // Remove all bindings:
+ m_Hooks.clear();
+ m_Commands.clear();
+ m_ConsoleCommands.clear();
- if (itr->second->GetName().compare(a_Plugin) == 0)
+ // Re-bind built-in console commands:
+ cServer::BindBuiltInConsoleCommands();
+
+ // Unload all loaded plugins:
+ for (auto & plugin: m_Plugins)
+ {
+ if (plugin->IsLoaded())
{
- return itr->second;
+ plugin->Unload();
}
}
- return 0;
}
-const cPluginManager::PluginMap & cPluginManager::GetAllPlugins() const
+void cPluginManager::UnloadPlugin(const AString & a_PluginFolder)
{
- return m_Plugins;
+ cCSLock Lock(m_CSPluginsToUnload);
+ m_PluginsToUnload.push_back(a_PluginFolder);
}
-void cPluginManager::UnloadPluginsNow()
+bool cPluginManager::LoadPlugin(const AString & a_FolderName)
{
- m_Hooks.clear();
-
- while (!m_Plugins.empty())
+ for (auto & plugin: m_Plugins)
{
- RemovePlugin(m_Plugins.begin()->second);
- }
-
- m_Commands.clear();
- m_ConsoleCommands.clear();
-}
-
-
-
-
-
-bool cPluginManager::DisablePlugin(const AString & a_PluginName)
-{
- PluginMap::iterator itr = m_Plugins.find(a_PluginName);
- if (itr == m_Plugins.end())
- {
- return false;
- }
+ if (plugin->GetFolderName() == a_FolderName)
+ {
+ if (!plugin->IsLoaded())
+ {
+ return plugin->Load();
+ }
+ return true;
+ }
+ } // for plugin - m_Plugins[]
- if (itr->first.compare(a_PluginName) == 0) // _X 2013_02_01: wtf? Isn't this supposed to be what find() does?
- {
- m_DisablePluginList.push_back(itr->second);
- itr->second = nullptr; // Get rid of this thing right away
- return true;
- }
+ // Plugin not found
+ LOG("Cannot load plugin, folder \"%s\" not found.", a_FolderName.c_str());
return false;
}
@@ -1546,15 +1583,6 @@ bool cPluginManager::DisablePlugin(const AString & a_PluginName)
-bool cPluginManager::LoadPlugin(const AString & a_PluginName)
-{
- return AddPlugin(new cPluginLua(a_PluginName.c_str()));
-}
-
-
-
-
-
void cPluginManager::RemoveHooks(cPlugin * a_Plugin)
{
for (HookMap::iterator itr = m_Hooks.begin(), end = m_Hooks.end(); itr != end; ++itr)
@@ -1567,32 +1595,6 @@ void cPluginManager::RemoveHooks(cPlugin * a_Plugin)
-void cPluginManager::RemovePlugin(cPlugin * a_Plugin)
-{
- for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr)
- {
- if (itr->second == a_Plugin)
- {
- m_Plugins.erase(itr);
- break;
- }
- }
-
- RemovePluginCommands(a_Plugin);
- RemovePluginConsoleCommands(a_Plugin);
- RemoveHooks(a_Plugin);
- if (a_Plugin != nullptr)
- {
- a_Plugin->OnDisable();
- }
- delete a_Plugin;
- a_Plugin = nullptr;
-}
-
-
-
-
-
void cPluginManager::RemovePluginCommands(cPlugin * a_Plugin)
{
if (a_Plugin != nullptr)
@@ -1619,6 +1621,22 @@ void cPluginManager::RemovePluginCommands(cPlugin * a_Plugin)
+bool cPluginManager::IsPluginLoaded(const AString & a_PluginName)
+{
+ for (auto & plugin: m_Plugins)
+ {
+ if (plugin->GetName() == a_PluginName)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
bool cPluginManager::BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString)
{
CommandMap::iterator cmd = m_Commands.find(a_Command);
@@ -1779,7 +1797,10 @@ bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cComma
if (cmd == m_ConsoleCommands.end())
{
// Command not found
- return false;
+ // Still notify the plugins (so that plugins such as Aliases can intercept unknown commands).
+ CommandResult res = crBlocked;
+ CallHookExecuteCommand(nullptr, a_Split, a_Command, res);
+ return (res == crExecuted);
}
if (cmd->second.m_Plugin == nullptr)
@@ -1789,10 +1810,10 @@ bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cComma
}
// Ask plugins first if a command is okay to execute the console command:
- if (CallHookExecuteCommand(nullptr, a_Split))
+ CommandResult res = crBlocked;
+ if (CallHookExecuteCommand(nullptr, a_Split, a_Command, res))
{
- a_Output.Out("Command \"%s\" was stopped by the HOOK_EXECUTE_COMMAND hook", a_Split[0].c_str());
- return false;
+ return (res == crExecuted);
}
return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output, a_Command);
@@ -1836,31 +1857,31 @@ bool cPluginManager::IsValidHookType(int a_HookType)
bool cPluginManager::DoWithPlugin(const AString & a_PluginName, cPluginCallback & a_Callback)
{
// TODO: Implement locking for plugins
- PluginMap::iterator itr = m_Plugins.find(a_PluginName);
- if ((itr == m_Plugins.end()) || (itr->second == nullptr))
+ for (auto & plugin: m_Plugins)
{
- return false;
+ if (plugin->GetName() == a_PluginName)
+ {
+ return a_Callback.Item(plugin.get());
+ }
}
- return a_Callback.Item(itr->second);
+ return false;
}
-bool cPluginManager::AddPlugin(cPlugin * a_Plugin)
+bool cPluginManager::ForEachPlugin(cPluginCallback & a_Callback)
{
- m_Plugins[a_Plugin->GetDirectory()] = a_Plugin;
-
- if (a_Plugin->Initialize())
+ // TODO: Implement locking for plugins
+ for (auto & plugin: m_Plugins)
{
- // Initialization OK
- return true;
+ if (a_Callback.Item(plugin.get()))
+ {
+ return false;
+ }
}
-
- // Initialization failed
- RemovePlugin(a_Plugin); // Also undoes any registrations that Initialize() might have made
- return false;
+ return true;
}
@@ -1869,21 +1890,23 @@ bool cPluginManager::AddPlugin(cPlugin * a_Plugin)
void cPluginManager::AddHook(cPlugin * a_Plugin, int a_Hook)
{
- if (!a_Plugin)
+ if (a_Plugin == nullptr)
{
LOGWARN("Called cPluginManager::AddHook() with a_Plugin == nullptr");
return;
}
PluginList & Plugins = m_Hooks[a_Hook];
- Plugins.remove(a_Plugin);
- Plugins.push_back(a_Plugin);
+ if (std::find(Plugins.cbegin(), Plugins.cend(), a_Plugin) == Plugins.cend())
+ {
+ Plugins.push_back(a_Plugin);
+ }
}
-size_t cPluginManager::GetNumPlugins() const
+size_t cPluginManager::GetNumPlugins(void) const
{
return m_Plugins.size();
}
@@ -1891,3 +1914,51 @@ size_t cPluginManager::GetNumPlugins() const
+
+size_t cPluginManager::GetNumLoadedPlugins(void) const
+{
+ size_t res = 0;
+ for (auto & plugin: m_Plugins)
+ {
+ if (plugin->IsLoaded())
+ {
+ res += 1;
+ }
+ }
+ return res;
+}
+
+
+
+
+
+AStringVector cPluginManager::GetFoldersToLoad(cSettingsRepositoryInterface & a_Settings)
+{
+ // Check if the Plugins section exists.
+ if (!a_Settings.KeyExists("Plugins"))
+ {
+ InsertDefaultPlugins(a_Settings);
+ }
+
+ // Get the list of plugins to load:
+ AStringVector res;
+ auto Values = a_Settings.GetValues("Plugins");
+ for (auto NameValue : Values)
+ {
+ AString ValueName = NameValue.first;
+ if (ValueName.compare("Plugin") == 0)
+ {
+ AString PluginFile = NameValue.second;
+ if (!PluginFile.empty())
+ {
+ res.push_back(PluginFile);
+ }
+ }
+ } // for i - ini values
+
+ return res;
+}
+
+
+
+
diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h
index 4efcbb6f3..6bcef87bf 100644
--- a/src/Bindings/PluginManager.h
+++ b/src/Bindings/PluginManager.h
@@ -6,48 +6,31 @@
-class cPlugin;
-// fwd: World.h
-class cWorld;
-// fwd: ChunkDesc.h
+// fwd:
+class cBlockEntityWithItems;
class cChunkDesc;
-
-// fwd: Entities/Entity.h
-class cEntity;
-
-// fwd: Entities/ProjectileEntity.h
-class cProjectileEntity;
-
-// fwd: Mobs/Monster.h
-class cMonster;
-
-// fwd: Player.h
-class cPlayer;
-
-// fwd: CraftingRecipes.h
+class cClientHandle;
+class cCommandOutputCallback;
class cCraftingGrid;
class cCraftingRecipe;
-
-// fwd: Pickup.h
+class cEntity;
+class cHopperEntity;
+class cItems;
+class cMonster;
class cPickup;
-
-// fwd: Pawn.h
+class cPlayer;
+class cPlugin;
+class cProjectileEntity;
+class cWorld;
+class cSettingsRepositoryInterface;
struct TakeDamageInfo;
-// fwd: CommandOutput.h
-class cCommandOutputCallback;
-
-// fwd: BlockEntities/HopperEntity.h
-class cHopperEntity;
-
-// fwd: BlockEntities/BlockEntityWithItems.h
-class cBlockEntityWithItems;
-
+typedef SharedPtr<cPlugin> cPluginPtr;
+typedef std::vector<cPluginPtr> cPluginPtrs;
-class cItems;
@@ -55,12 +38,7 @@ class cItems;
class cPluginManager
{
public:
- // tolua_end
-
- // Called each tick
- virtual void Tick(float a_Dt);
-
- // tolua_begin
+
enum CommandResult
{
crExecuted,
@@ -70,6 +48,29 @@ public:
crNoPermission,
} ;
+
+ /** Defines the status of a single plugin - whether it is loaded, disabled or errored. */
+ enum ePluginStatus
+ {
+ /** The plugin has been loaded successfully. */
+ psLoaded,
+
+ /** The plugin is disabled in settings.ini. */
+ psDisabled,
+
+ /** The plugin is enabled in settings.ini but has been unloaded (by a command). */
+ psUnloaded,
+
+ /** The plugin is enabled in settings.ini but has failed to load.
+ m_LoadError is the description of the error. */
+ psError,
+
+ /** The plugin has been loaded before, but after a folder refresh it is no longer present.
+ The plugin will be unloaded in the next call to ReloadPlugins(). */
+ psNotFound,
+ };
+
+
enum PluginHook
{
HOOK_BLOCK_SPREAD,
@@ -85,6 +86,8 @@ public:
HOOK_DISCONNECT,
HOOK_PLAYER_ANIMATION,
HOOK_ENTITY_ADD_EFFECT,
+ HOOK_ENTITY_CHANGING_WORLD,
+ HOOK_ENTITY_CHANGED_WORLD,
HOOK_EXECUTE_COMMAND,
HOOK_EXPLODED,
HOOK_EXPLODING,
@@ -134,6 +137,8 @@ public:
HOOK_WEATHER_CHANGING,
HOOK_WORLD_STARTED,
HOOK_WORLD_TICK,
+
+ // tolua_end
// Note that if a hook type is added, it may need processing in cPlugin::CanAddHook() descendants,
// and it definitely needs adding in cPluginLua::GetHookFnName() !
@@ -141,8 +146,7 @@ public:
// Keep these two as the last items, they are used for validity checking and get their values automagically
HOOK_NUM_HOOKS,
HOOK_MAX = HOOK_NUM_HOOKS - 1,
- } ;
- // tolua_end
+ } ; // tolua_export
/** Used as a callback for enumerating bound commands */
class cCommandEnumCallback
@@ -159,24 +163,31 @@ public:
/** The interface used for enumerating and extern-calling plugins */
typedef cItemCallback<cPlugin> cPluginCallback;
+ typedef std::list<cPlugin *> PluginList;
+
+
+ /** Called each tick, calls the plugins' OnTick hook, as well as processes plugin events (addition, removal) */
+ void Tick(float a_Dt);
/** Returns the instance of the Plugin Manager (there is only ever one) */
static cPluginManager * Get(void); // tolua_export
- typedef std::map< AString, cPlugin * > PluginMap;
- typedef std::list< cPlugin * > PluginList;
- cPlugin * GetPlugin( const AString & a_Plugin) const; // tolua_export
- const PluginMap & GetAllPlugins() const; // >> EXPORTED IN MANUALBINDINGS <<
+ /** Refreshes the m_Plugins list based on the current contents of the Plugins folder.
+ If an active plugin's folder is not found anymore, the plugin is set as psNotFound, but not yet unloaded. */
+ void RefreshPluginList(); // tolua_export
- // tolua_begin
- void FindPlugins();
- void ReloadPlugins();
- // tolua_end
+ /** Schedules a reload of the plugins to happen within the next call to Tick(). */
+ void ReloadPlugins(); // tolua_export
- /** Adds the plugin to the list of plugins called for the specified hook type. Handles multiple adds as a single add */
+ /** Adds the plugin to the list of plugins called for the specified hook type.
+ If a plugin adds multiple handlers for a single hook, it is added only once (ignore-duplicates). */
void AddHook(cPlugin * a_Plugin, int a_HookType);
+ /** Returns the number of all plugins in m_Plugins (includes disabled, unloaded and errored plugins). */
size_t GetNumPlugins() const; // tolua_export
+
+ /** Returns the number of plugins that are psLoaded. */
+ size_t GetNumLoadedPlugins(void) const; // tolua_export
// Calls for individual hooks. Each returns false if the action is to continue or true if the plugin wants to abort
bool CallHookBlockSpread (cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source);
@@ -192,7 +203,9 @@ public:
bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason);
bool CallHookEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier);
bool CallHookEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition);
- bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == nullptr, it is a console cmd
+ bool CallHookEntityChangingWorld (cEntity & a_Entity, cWorld & a_World);
+ bool CallHookEntityChangedWorld (cEntity & a_Entity, cWorld & a_World);
+ bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, CommandResult & a_Result); // If a_Player == nullptr, it is a console cmd
bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
bool CallHookHandshake (cClientHandle & a_ClientHandle, const AString & a_Username);
@@ -241,18 +254,26 @@ public:
bool CallHookWorldStarted (cWorld & a_World);
bool CallHookWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec);
- bool DisablePlugin(const AString & a_PluginName); // tolua_export
- bool LoadPlugin (const AString & a_PluginName); // tolua_export
+ /** Queues the specified plugin to be unloaded in the next call to Tick().
+ Note that this function returns before the plugin is unloaded, to avoid deadlocks. */
+ void UnloadPlugin(const AString & a_PluginFolder); // tolua_export
+
+ /** Loads the plugin from the specified plugin folder.
+ Returns true if the plugin was loaded successfully or was already loaded before, false otherwise. */
+ bool LoadPlugin(const AString & a_PluginFolder); // tolua_export
/** Removes all hooks the specified plugin has registered */
void RemoveHooks(cPlugin * a_Plugin);
- /** Removes the plugin from the internal structures and deletes its object. */
- void RemovePlugin(cPlugin * a_Plugin);
+ /** Removes the plugin of the specified name from the internal structures and deletes its object. */
+ void RemovePlugin(const AString & a_PluginName);
/** Removes all command bindings that the specified plugin has made */
void RemovePluginCommands(cPlugin * a_Plugin);
-
+
+ /** Returns true if the specified plugin is loaded. */
+ bool IsPluginLoaded(const AString & a_PluginName); // tolua_export
+
/** Binds a command to the specified plugin. Returns true if successful, false if command already bound. */
bool BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param
@@ -283,7 +304,9 @@ public:
/** Returns true if the console command is in the command map */
bool IsConsoleCommandBound(const AString & a_Command); // tolua_export
- /** Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback */
+ /** Executes the command split into a_Split, as if it was given on the console.
+ Returns true if executed. Output is sent to the a_Output callback
+ Exported in ManualBindings.cpp with a different signature. */
bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_Command);
/** Appends all commands beginning with a_Text (case-insensitive) into a_Results.
@@ -295,8 +318,12 @@ public:
static bool IsValidHookType(int a_HookType);
/** Calls the specified callback with the plugin object of the specified plugin.
- Returns false if plugin not found, and the value that the callback has returned otherwise. */
+ Returns false if plugin not found, otherwise returns the value that the callback has returned. */
bool DoWithPlugin(const AString & a_PluginName, cPluginCallback & a_Callback);
+
+ /** Calls the specified callback for each plugin in m_Plugins.
+ Returns true if all plugins have been reported, false if the callback has aborted the enumeration by returning true. */
+ bool ForEachPlugin(cPluginCallback & a_Callback);
/** Returns the path where individual plugins' folders are expected.
The path doesn't end in a slash. */
@@ -316,34 +343,46 @@ private:
typedef std::map<int, cPluginManager::PluginList> HookMap;
typedef std::map<AString, cCommandReg> CommandMap;
- PluginList m_DisablePluginList;
- PluginMap m_Plugins;
+
+ /** FolderNames of plugins that should be unloaded.
+ The plugins will be unloaded within the next call to Tick(), to avoid multithreading issues.
+ Protected against multithreaded access by m_CSPluginsToUnload. */
+ AStringVector m_PluginsToUnload;
+
+ /** Protects m_PluginsToUnload against multithreaded access. */
+ mutable cCriticalSection m_CSPluginsToUnload;
+
+ /** All plugins that have been found in the Plugins folder. */
+ cPluginPtrs m_Plugins;
+
HookMap m_Hooks;
CommandMap m_Commands;
CommandMap m_ConsoleCommands;
+ /** If set to true, all the plugins will be reloaded within the next call to Tick(). */
bool m_bReloadPlugins;
+
cPluginManager();
virtual ~cPluginManager();
/** Reloads all plugins, defaulting to settings.ini for settings location */
void ReloadPluginsNow(void);
- /** Reloads all plugins with a cIniFile object expected to be initialised to settings.ini */
- void ReloadPluginsNow(cIniFile & a_SettingsIni);
+ /** Reloads all plugins with a settings repo expected to be initialised to settings.ini */
+ void ReloadPluginsNow(cSettingsRepositoryInterface & a_Settings);
/** Unloads all plugins */
void UnloadPluginsNow(void);
- /** Handles writing default plugins if 'Plugins' key not found using a cIniFile object expected to be intialised to settings.ini */
- void InsertDefaultPlugins(cIniFile & a_SettingsIni);
-
- /** Adds the plugin into the internal list of plugins and initializes it. If initialization fails, the plugin is removed again. */
- bool AddPlugin(cPlugin * a_Plugin);
+ /** Handles writing default plugins if 'Plugins' key not found using a settings repo expected to be intialised to settings.ini */
+ void InsertDefaultPlugins(cSettingsRepositoryInterface & a_Settings);
/** Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns crExecuted if the command is executed. */
CommandResult HandleCommand(cPlayer & a_Player, const AString & a_Command, bool a_ShouldCheckPermissions);
+
+ /** Returns the folders that are specified in the settings ini to load plugins from. */
+ AStringVector GetFoldersToLoad(cSettingsRepositoryInterface & a_Settings);
} ; // tolua_export
diff --git a/src/Bindings/WebPlugin.cpp b/src/Bindings/WebPlugin.cpp
index 5759b20e7..1eca7de93 100644
--- a/src/Bindings/WebPlugin.cpp
+++ b/src/Bindings/WebPlugin.cpp
@@ -24,75 +24,82 @@ cWebPlugin::cWebPlugin()
cWebPlugin::~cWebPlugin()
{
+ ASSERT(m_Tabs.empty()); // Has ClearTabs() been called?
+
+ // Remove from WebAdmin:
cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin();
if (WebAdmin != nullptr)
{
WebAdmin->RemovePlugin(this);
}
+}
+
+
+
- for (TabList::iterator itr = m_Tabs.begin(); itr != m_Tabs.end(); ++itr)
+
+cWebPlugin::cTabNames cWebPlugin::GetTabNames(void) const
+{
+ std::list< std::pair<AString, AString>> NameList;
+ for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr)
{
- delete *itr;
+ NameList.push_back(std::make_pair((*itr)->m_Title, (*itr)->m_SafeTitle));
}
- m_Tabs.clear();
+ return NameList;
}
-std::list<std::pair<AString, AString> > cWebPlugin::GetTabNames(void)
+cWebPlugin::cTabPtr cWebPlugin::GetTabBySafeTitle(const AString & a_SafeTitle) const
{
- std::list< std::pair< AString, AString > > NameList;
- for (TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr)
+ cCSLock Lock(m_CSTabs);
+ for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr)
{
- std::pair< AString, AString > StringPair;
- StringPair.first = (*itr)->Title;
- StringPair.second = (*itr)->SafeTitle;
- NameList.push_back( StringPair);
+ if ((*itr)->m_SafeTitle == a_SafeTitle)
+ {
+ return *itr;
+ }
}
- return NameList;
+ return nullptr;
}
-std::pair< AString, AString > cWebPlugin::GetTabNameForRequest(const HTTPRequest * a_Request)
+std::pair<AString, AString> cWebPlugin::GetTabNameForRequest(const HTTPRequest & a_Request)
{
- std::pair< AString, AString > Names;
- AStringVector Split = StringSplit(a_Request->Path, "/");
+ AStringVector Split = StringSplit(a_Request.Path, "/");
+ if (Split.empty())
+ {
+ return std::make_pair(AString(), AString());
+ }
- if (Split.size() > 1)
+ cCSLock Lock(m_CSTabs);
+ cTabPtr Tab;
+ if (Split.size() > 2) // If we got the tab name, show that page
{
- sWebPluginTab * Tab = nullptr;
- if (Split.size() > 2) // If we got the tab name, show that page
+ for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr)
{
- for (TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr)
+ if ((*itr)->m_SafeTitle.compare(Split[2]) == 0) // This is the one!
{
- if ((*itr)->SafeTitle.compare(Split[2]) == 0) // This is the one!
- {
- Tab = *itr;
- break;
- }
- }
- }
- else // Otherwise show the first tab
- {
- if (GetTabs().size() > 0)
- {
- Tab = *GetTabs().begin();
+ return std::make_pair((*itr)->m_Title, (*itr)->m_SafeTitle);
}
}
+ // Tab name not found, display an "empty" page:
+ return std::make_pair(AString(), AString());
+ }
- if (Tab != nullptr)
- {
- Names.first = Tab->Title;
- Names.second = Tab->SafeTitle;
- }
+ // Show the first tab:
+ if (!m_Tabs.empty())
+ {
+ return std::make_pair(m_Tabs.front()->m_SafeTitle, m_Tabs.front()->m_SafeTitle);
}
- return Names;
+ // No tabs at all:
+ return std::make_pair(AString(), AString());
}
@@ -101,14 +108,16 @@ std::pair< AString, AString > cWebPlugin::GetTabNameForRequest(const HTTPRequest
AString cWebPlugin::SafeString(const AString & a_String)
{
AString RetVal;
- for (unsigned int i = 0; i < a_String.size(); ++i)
+ auto len = a_String.size();
+ RetVal.reserve(len);
+ for (size_t i = 0; i < len; ++i)
{
char c = a_String[i];
if (c == ' ')
{
c = '_';
}
- RetVal.push_back( c);
+ RetVal.push_back(c);
}
return RetVal;
}
@@ -116,3 +125,28 @@ AString cWebPlugin::SafeString(const AString & a_String)
+
+void cWebPlugin::AddNewWebTab(const AString & a_Title, int a_UserData)
+{
+ auto Tab = std::make_shared<cTab>(a_Title, a_UserData);
+ cCSLock Lock(m_CSTabs);
+ m_Tabs.push_back(Tab);
+}
+
+
+
+
+
+void cWebPlugin::ClearTabs(void)
+{
+ // Remove the webadmin tabs:
+ cTabPtrs Tabs;
+ {
+ cCSLock Lock(m_CSTabs);
+ std::swap(Tabs, m_Tabs);
+ }
+}
+
+
+
+
diff --git a/src/Bindings/WebPlugin.h b/src/Bindings/WebPlugin.h
index 9b825b918..ade4f4359 100644
--- a/src/Bindings/WebPlugin.h
+++ b/src/Bindings/WebPlugin.h
@@ -12,34 +12,67 @@ class cWebPlugin
{
public:
// tolua_end
+
+ struct cTab
+ {
+ AString m_Title;
+ AString m_SafeTitle;
+ int m_UserData;
+
+ cTab(const AString & a_Title, int a_UserData):
+ m_Title(a_Title),
+ m_SafeTitle(cWebPlugin::SafeString(a_Title)),
+ m_UserData(a_UserData)
+ {
+ }
+ };
+
+ typedef SharedPtr<cTab> cTabPtr;
+ typedef std::list<cTabPtr> cTabPtrs;
+ typedef std::list<std::pair<AString, AString>> cTabNames;
+
+
cWebPlugin();
+
virtual ~cWebPlugin();
// tolua_begin
+
+ /** Returns the title of the plugin, as it should be presented in the webadmin's pages tree. */
virtual const AString GetWebTitle(void) const = 0;
- virtual AString HandleWebRequest(const HTTPRequest * a_Request) = 0;
+ /** Sanitizes the input string, replacing spaces with underscores. */
+ static AString SafeString(const AString & a_String);
- static AString SafeString( const AString & a_String);
// tolua_end
- struct sWebPluginTab
- {
- std::string Title;
- std::string SafeTitle;
+ virtual AString HandleWebRequest(const HTTPRequest & a_Request) = 0;
- int UserData;
- };
+ /** Adds a new web tab with the specified contents. */
+ void AddNewWebTab(const AString & a_Title, int a_UserData);
+
+ /** Removes all the tabs. */
+ void ClearTabs(void);
- typedef std::list< sWebPluginTab* > TabList;
- TabList & GetTabs() { return m_Tabs; }
+ /** Returns all the tabs that this plugin has registered. */
+ const cTabPtrs & GetTabs(void) const { return m_Tabs; }
- typedef std::list< std::pair<AString, AString> > TabNameList;
- TabNameList GetTabNames(); // >> EXPORTED IN MANUALBINDINGS <<
- std::pair< AString, AString > GetTabNameForRequest(const HTTPRequest* a_Request);
+ /** Returns all of the tabs that this plugin has registered. */
+ cTabNames GetTabNames(void) const; // Exported in ManualBindings.cpp
+
+ /** Returns the tab that has the specified SafeTitle.
+ Returns nullptr if no such tab. */
+ cTabPtr GetTabBySafeTitle(const AString & a_SafeTitle) const;
+
+ std::pair<AString, AString> GetTabNameForRequest(const HTTPRequest & a_Request);
private:
- TabList m_Tabs;
+ /** All tabs that this plugin has registered.
+ Protected against multithreaded access by m_CSTabs. */
+ cTabPtrs m_Tabs;
+
+ /** Protects m_Tabs against multithreaded access. */
+ mutable cCriticalSection m_CSTabs;
}; // tolua_export
diff --git a/src/Bindings/virtual_method_hooks.lua b/src/Bindings/virtual_method_hooks.lua
deleted file mode 100644
index a52728ff8..000000000
--- a/src/Bindings/virtual_method_hooks.lua
+++ /dev/null
@@ -1,518 +0,0 @@
--- flags
-local disable_virtual_hooks = true
-local enable_pure_virtual = true
-local default_private_access = false
-
-
-
-
-
-local access = {public = 0, protected = 1, private = 2}
-
-function preparse_hook(p)
-
- if default_private_access then
- -- we need to make all structs 'public' by default
- p.code = string.gsub(p.code, "(struct[^;]*{)", "%1\npublic:\n")
- end
-end
-
-
-function parser_hook(s)
-
- local container = classContainer.curr -- get the current container
-
- if default_private_access then
- if not container.curr_member_access and container.classtype == 'class' then
- -- default access for classes is private
- container.curr_member_access = access.private
- end
- end
-
- -- try labels (public, private, etc)
- do
- local b,e,label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type'
- if b then
-
- -- found a label, get the new access value from the global 'access' table
- if access[label] then
- container.curr_member_access = access[label]
- end -- else ?
-
- return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:]
- end
- end
-
-
- local ret = nil
-
- if disable_virtual_hooks then
-
- return ret
- end
-
- local b,e,decl,arg = string.find(s, "^%s*virtual%s+([^%({~]+)(%b())")
- local const
- if b then
- local ret = string.sub(s, e+1)
- if string.find(ret, "^%s*const") then
- const = "const"
- ret = string.gsub(ret, "^%s*const", "")
- end
- local purev = false
- if string.find(ret, "^%s*=%s*0") then
- purev = true
- ret = string.gsub(ret, "^%s*=%s*0", "")
- end
- ret = string.gsub(ret, "^%s*%b{}", "")
-
- local func = Function(decl, arg, const)
- func.pure_virtual = purev
- --func.access = access
- func.original_sig = decl
-
- local curflags = classContainer.curr.flags
- if not curflags.virtual_class then
-
- curflags.virtual_class = VirtualClass()
- end
- curflags.virtual_class:add(func)
- curflags.pure_virtual = curflags.pure_virtual or purev
-
- return ret
- end
-
- return ret
-end
-
-
--- class VirtualClass
-classVirtualClass = {
- classtype = 'class',
- name = '',
- base = '',
- type = '',
- btype = '',
- ctype = '',
-}
-classVirtualClass.__index = classVirtualClass
-setmetatable(classVirtualClass,classClass)
-
-function classVirtualClass:add(f)
-
- local parent = classContainer.curr
- pop()
-
- table.insert(self.methods, {f=f})
-
- local name,sig
-
- -- doble negative means positive
- if f.name == 'new' and ((not self.flags.parent_object.flags.pure_virtual) or (enable_pure_virtual)) then
-
- name = self.original_name
- elseif f.name == 'delete' then
- name = '~'..self.original_name
- else
- if f.access ~= 2 and (not f.pure_virtual) and f.name ~= 'new' and f.name ~= 'delete' then
- name = f.mod.." "..f.type..f.ptr.." "..self.flags.parent_object.lname.."__"..f.name
- end
- end
-
- if name then
- sig = name..self:get_arg_list(f, true)..";\n"
- push(self)
- sig = preprocess(sig)
- self:parse(sig)
- pop()
- end
-
- push(parent)
-end
-
-function preprocess(sig)
-
- sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
- sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
- sig = gsub(sig,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*'
- sig = gsub(sig,"([^%w_])lua_State%s*%*","%1_lstate ") -- substitute 'lua_State*'
-
- return sig
-end
-
-function classVirtualClass:get_arg_list(f, decl)
-
- local ret = ""
- local sep = ""
- local i=1
- while f.args[i] do
-
- local arg = f.args[i]
- if decl then
- local ptr
- if arg.ret ~= '' then
- ptr = arg.ret
- else
- ptr = arg.ptr
- end
- local def = ""
- if arg.def and arg.def ~= "" then
-
- def = " = "..arg.def
- end
- ret = ret..sep..arg.mod.." "..arg.type..ptr.." "..arg.name..def
- else
- ret = ret..sep..arg.name
- end
-
- sep = ","
- i = i+1
- end
-
- return "("..ret..")"
-end
-
-function classVirtualClass:add_parent_virtual_methods(parent)
-
- parent = parent or _global_classes[self.flags.parent_object.btype]
-
- if not parent then return end
-
- if parent.flags.virtual_class then
-
- local vclass = parent.flags.virtual_class
- for k,v in ipairs(vclass.methods) do
- if v.f.name ~= 'new' and v.f.name ~= 'delete' and (not self:has_method(v.f)) then
- table.insert(self.methods, {f=v.f})
- end
- end
- end
-
- parent = _global_classes[parent.btype]
- if parent then
- self:add_parent_virtual_methods(parent)
- end
-end
-
-function classVirtualClass:has_method(f)
-
- for k,v in pairs(self.methods) do
- -- just match name for now
- if v.f.name == f.name then
- return true
- end
- end
-
- return false
-end
-
-function classVirtualClass:add_constructors()
-
- local i=1
- while self.flags.parent_object[i] do
-
- local v = self.flags.parent_object[i]
- if getmetatable(v) == classFunction and (v.name == 'new' or v.name == 'delete') then
-
- self:add(v)
- end
-
- i = i+1
- end
-
-end
-
---[[
-function classVirtualClass:requirecollection(t)
-
- self:add_constructors()
- local req = classClass.requirecollection(self, t)
- if req then
- output('class ',self.name,";")
- end
- return req
-end
---]]
-
-function classVirtualClass:supcode()
-
- -- pure virtual classes can have no default constructors on gcc 4
-
- if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
- output('#if (__GNUC__ == 4) || (__GNUC__ > 4 ) // I hope this works on Microsoft Visual studio .net server 2003 XP Compiler\n')
- end
-
- local ns
- if self.prox.classtype == 'namespace' then
- output('namespace ',self.prox.name, " {")
- ns = true
- end
-
- output("class "..self.original_name.." : public "..self.btype..", public ToluaBase {")
-
- output("public:\n")
-
- self:add_parent_virtual_methods()
-
- self:output_methods(self.btype)
- self:output_parent_methods()
-
- self:add_constructors()
-
- -- no constructor for pure virtual classes
- if (not self.flags.parent_object.flags.pure_virtual) or enable_pure_virtual then
-
- self:output_constructors()
- end
-
- output("};\n\n")
-
- if ns then
- output("};")
- end
-
- classClass.supcode(self)
-
- if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
- output('#endif // __GNUC__ >= 4\n')
- end
-
- -- output collector for custom class if required
- if self:requirecollection(_collect) and _collect[self.type] then
-
- output('\n')
- output('/* function to release collected object via destructor */')
- output('#ifdef __cplusplus\n')
- --for i,v in pairs(collect) do
- i,v = self.type, _collect[self.type]
- output('\nstatic int '..v..' (lua_State* tolua_S)')
- output('{')
- output(' '..i..'* self = ('..i..'*) tolua_tousertype(tolua_S,1,0);')
- output(' delete self;')
- output(' return 0;')
- output('}')
- --end
- output('#endif\n\n')
- end
-
-end
-
-function classVirtualClass:register(pre)
-
- -- pure virtual classes can have no default constructors on gcc 4
- if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
- output('#if (__GNUC__ == 4) || (__GNUC__ > 4 )\n')
- end
-
- classClass.register(self, pre)
-
- if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
- output('#endif // __GNUC__ >= 4\n')
- end
-end
-
-
---function classVirtualClass:requirecollection(_c)
--- if self.flags.parent_object.flags.pure_virtual then
--- return false
--- end
--- return classClass.requirecollection(self, _c)
---end
-
-function classVirtualClass:output_parent_methods()
-
- for k,v in ipairs(self.methods) do
-
- if v.f.access ~= 2 and (not v.f.pure_virtual) and v.f.name ~= 'new' and v.f.name ~= 'delete' then
-
- local rettype = v.f.mod.." "..v.f.type..v.f.ptr.." "
- local parent_name = rettype..self.btype.."__"..v.f.name
-
- local par_list = self:get_arg_list(v.f, true)
- local var_list = self:get_arg_list(v.f, false)
-
- -- the parent's virtual function
- output("\t"..parent_name..par_list.." {")
-
- output("\t\treturn (",rettype,")"..self.btype.."::"..v.f.name..var_list..";")
- output("\t};")
- end
- end
-end
-
-function classVirtualClass:output_methods(btype)
-
- for k,v in ipairs(self.methods) do
-
- if v.f.name ~= 'new' and v.f.name ~= 'delete' then
-
- self:output_method(v.f, btype)
- end
- end
- output("\n")
-end
-
-function classVirtualClass:output_constructors()
-
- for k,v in ipairs(self.methods) do
-
- if v.f.name == 'new' then
-
- local par_list = self:get_arg_list(v.f, true)
- local var_list = self:get_arg_list(v.f, false)
-
- output("\t",self.original_name,par_list,":",self.btype,var_list,"{};")
- end
- end
-end
-
-function classVirtualClass:output_method(f, btype)
-
- if f.access == 2 then -- private
- return
- end
-
- local ptr
- if f.ret ~= '' then
- ptr = f.ret
- else
- ptr = f.ptr
- end
-
- local rettype = f.mod.." "..f.type..f.ptr.." "
- local par_list = self:get_arg_list(f, true)
- local var_list = self:get_arg_list(f, false)
-
- if string.find(rettype, "%s*LuaQtGenericFlags%s*") then
-
- _,_,rettype = string.find(f.original_sig, "^%s*([^%s]+)%s+")
- end
-
- -- the caller of the lua method
- output("\t"..rettype.." "..f.name..par_list..f.const.." {")
- local fn = f.cname
- if f.access == 1 then
- fn = "NULL"
- end
- output('\t\tif (push_method("',f.lname,'", ',fn,')) {')
-
- --if f.type ~= 'void' then
- -- output("\t\t\tint top = lua_gettop(lua_state)-1;")
- --end
-
- -- push the parameters
- local argn = 0
- for i,arg in ipairs(f.args) do
- if arg.type ~= 'void' then
- local t,ct = isbasic(arg.type)
- if t and t ~= '' then
- if arg.ret == "*" then
- t = 'userdata'
- ct = 'void*'
- end
- output("\t\t\ttolua_push"..t.."(lua_state, ("..ct..")"..arg.name..");");
- else
- local m = arg.ptr
- if m and m~= "" then
- if m == "*" then m = "" end
- output("\t\t\ttolua_pushusertype(lua_state, (void*)"..m..arg.name..", \""..arg.type.."\");")
- else
- output("\t\t\tvoid* tolua_obj" .. argn .." = (void*)new "..arg.type.."("..arg.name..");\n")
- output('\t\t\ttolua_pushusertype_and_takeownership(lua_state, tolua_obj' .. argn .. ', "'..arg.type..'");\n')
- end
- end
- argn = argn+1
- end
- end
-
- -- call the function
- output("\t\t\tToluaBase::dbcall(lua_state, ",argn+1,", ")
-
- -- return value
- if f.type ~= 'void' then
- output("1);")
-
- local t,ct = isbasic(f.type)
- if t and t ~= '' then
- --output("\t\t\treturn ("..rettype..")tolua_to"..t.."(lua_state, top, 0);")
- output("\t\t\t",rettype,"tolua_ret = ("..rettype..")tolua_to"..t.."(lua_state, -1, 0);")
- else
-
- local mod = ""
- if f.ptr ~= "*" then
- mod = "*("..f.type.."*)"
- end
-
- --output("\t\t\treturn ("..rettype..")"..mod.."tolua_tousertype(lua_state, top, 0);")
- output("\t\t\t",rettype,"tolua_ret = ("..rettype..")"..mod.."tolua_tousertype(lua_state, -1, 0);")
- end
- output("\t\t\tlua_pop(lua_state, 1);")
- output("\t\t\treturn tolua_ret;")
- else
- output("0);")
- end
-
- -- handle non-implemeted function
- output("\t\t} else {")
-
- if f.pure_virtual then
-
- output('\t\t\tif (lua_state)')
- --output('\t\t\t\ttolua_error(lua_state, "pure-virtual method '..btype.."::"..f.name..' not implemented.", NULL);')
- output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' not implemented.");')
- output('\t\t\telse {')
- output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' called with no lua_state. Aborting");')
- output('\t\t\t\t::abort();')
- output('\t\t\t};')
- if( rettype == " std::string " ) then
- output('\t\t\treturn "";')
- else
- output('\t\t\treturn (',rettype,')0;')
- end
- else
-
- output('\t\t\treturn (',rettype,')',btype,'::',f.name,var_list,';')
- end
-
- output("\t\t};")
-
- output("\t};")
-end
-
-function VirtualClass()
-
- local parent = classContainer.curr
- pop()
-
- local name = "Lua__"..parent.original_name
-
- local c = _Class(_Container{name=name, base=parent.name, extra_bases=nil})
- setmetatable(c, classVirtualClass)
-
- local ft = getnamespace(c.parent)..c.original_name
- append_global_type(ft, c)
-
- push(parent)
-
- c.flags.parent_object = parent
- c.methods = {}
-
- push(c)
- c:parse("\nvoid tolua__set_instance(_lstate L, lua_Object lo);\n")
- pop()
-
- return c
-end
-
-
-
-
-
-
-function post_output_hook()
- print("Bindings have been generated.")
-end
-
-
-
-
diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp
index 4c3da0535..89cf18d4a 100644
--- a/src/BlockArea.cpp
+++ b/src/BlockArea.cpp
@@ -245,6 +245,26 @@ void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBL
+/** Combinator used for cBlockArea::msSimpleCompare merging */
+template <bool MetaValid>
+void MergeCombinatorSimpleCompare(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
+{
+ if ((a_DstType == a_SrcType) && (!MetaValid || (a_DstMeta == a_SrcMeta)))
+ {
+ // The blocktypes are the same, and the blockmetas are not present or are the same
+ a_DstType = E_BLOCK_AIR;
+ }
+ else
+ {
+ // The blocktypes or blockmetas differ
+ a_DstType = E_BLOCK_STONE;
+ }
+}
+
+
+
+
+
/** Combinator used for cBlockArea::msMask merging */
template <bool MetaValid>
void MergeCombinatorMask(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
@@ -1614,6 +1634,104 @@ void cBlockArea::GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTY
+size_t cBlockArea::CountNonAirBlocks(void) const
+{
+ // Check if blocktypes are valid:
+ if (m_BlockTypes == nullptr)
+ {
+ LOGWARNING("%s: BlockTypes have not been read!", __FUNCTION__);
+ return 0;
+ }
+
+ // Count the blocks:
+ size_t res = 0;
+ for (int y = 0; y < m_Size.y; y++)
+ {
+ for (int z = 0; z < m_Size.z; z++)
+ {
+ for (int x = 0; x < m_Size.x; x++)
+ {
+ if (m_BlockTypes[MakeIndex(x, y, z)] != E_BLOCK_AIR)
+ {
+ ++res;
+ }
+ } // for x
+ } // for z
+ } // for y
+ return res;
+}
+
+
+
+
+
+void cBlockArea::GetNonAirCropRelCoords(int & a_MinRelX, int & a_MinRelY, int & a_MinRelZ, int & a_MaxRelX, int & a_MaxRelY, int & a_MaxRelZ, BLOCKTYPE a_IgnoreBlockType)
+{
+ // Check if blocktypes are valid:
+ if (m_BlockTypes == nullptr)
+ {
+ LOGWARNING("%s: BlockTypes have not been read!", __FUNCTION__);
+ a_MinRelX = 1;
+ a_MaxRelX = 0;
+ return;
+ }
+
+ // Walk all the blocks and find the min and max coords for the non-ignored ones:
+ int MaxX = 0, MinX = m_Size.x - 1;
+ int MaxY = 0, MinY = m_Size.y - 1;
+ int MaxZ = 0, MinZ = m_Size.z - 1;
+ for (int y = 0; y < m_Size.y; y++)
+ {
+ for (int z = 0; z < m_Size.z; z++)
+ {
+ for (int x = 0; x < m_Size.x; x++)
+ {
+ if (m_BlockTypes[MakeIndex(x, y, z)] == a_IgnoreBlockType)
+ {
+ continue;
+ }
+ // The block is not ignored, update any coords that need updating:
+ if (x < MinX)
+ {
+ MinX = x;
+ }
+ if (x > MaxX)
+ {
+ MaxX = x;
+ }
+ if (y < MinY)
+ {
+ MinY = y;
+ }
+ if (y > MaxY)
+ {
+ MaxY = y;
+ }
+ if (z < MinZ)
+ {
+ MinZ = z;
+ }
+ if (z > MaxZ)
+ {
+ MaxZ = z;
+ }
+ } // for x
+ } // for z
+ } // for y
+
+ // Assign to the output:
+ a_MinRelX = MinX;
+ a_MinRelY = MinY;
+ a_MinRelZ = MinZ;
+ a_MaxRelX = MaxX;
+ a_MaxRelY = MaxY;
+ a_MaxRelZ = MaxZ;
+}
+
+
+
+
+
int cBlockArea::GetDataTypes(void) const
{
int res = 0;
@@ -2121,10 +2239,12 @@ void cBlockArea::RelSetData(
+
+
template <bool MetasValid>
void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas)
{
- // Block types are compulsory, block metas are voluntary
+ // Block types are compulsory, block metas are optional
if (!HasBlockTypes() || !a_Src.HasBlockTypes())
{
LOGWARNING("%s: cannot merge because one of the areas doesn't have blocktypes.", __FUNCTION__);
@@ -2230,6 +2350,20 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel
return;
} // case msDifference
+ case cBlockArea::msSimpleCompare:
+ {
+ InternalMergeBlocks<MetasValid, MergeCombinatorSimpleCompare<MetasValid> >(
+ m_BlockTypes, a_Src.GetBlockTypes(),
+ DstMetas, SrcMetas,
+ SizeX, SizeY, SizeZ,
+ SrcOffX, SrcOffY, SrcOffZ,
+ DstOffX, DstOffY, DstOffZ,
+ a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
+ m_Size.x, m_Size.y, m_Size.z
+ );
+ return;
+ } // case msSimpleCompare
+
case cBlockArea::msMask:
{
InternalMergeBlocks<MetasValid, MergeCombinatorMask<MetasValid> >(
diff --git a/src/BlockArea.h b/src/BlockArea.h
index 348e960dd..856df542f 100644
--- a/src/BlockArea.h
+++ b/src/BlockArea.h
@@ -54,6 +54,7 @@ public:
msLake,
msSpongePrint,
msDifference,
+ msSimpleCompare,
msMask,
} ;
@@ -156,6 +157,22 @@ public:
| A | sponge | A | Sponge is the NOP block
| * | B | B | Everything else overwrites anything
+ msDifference:
+ Used to determine the differences between two areas. Only the differring blocks are preserved:
+ | area block | |
+ | this | Src | result |
+ +------+-------+--------+
+ | A | A | air | Same blocks are replaced with air
+ | A | non-A | A | Differring blocks are kept from "this"
+
+ msSimpleCompare:
+ Used to determine the differences between two areas. Blocks that differ are replaced with stone, same blocks are replaced with air
+ | area block | |
+ | this | Src | result |
+ +------+-------+--------+
+ | A | A | air | Same blocks are replaced with air
+ | A | non-A | stone | Differring blocks are replaced with stone
+
msMask:
Combines two areas, the blocks that are the same are kept, differing ones are reset to air
| area block | |
@@ -286,8 +303,17 @@ public:
bool HasBlockMetas (void) const { return (m_BlockMetas != nullptr); }
bool HasBlockLights (void) const { return (m_BlockLight != nullptr); }
bool HasBlockSkyLights(void) const { return (m_BlockSkyLight != nullptr); }
-
+
+ /** Returns the count of blocks that are not air.
+ Returns 0 if blocktypes not available. Block metas are ignored (if present, air with any meta is still considered air). */
+ size_t CountNonAirBlocks(void) const;
+
// tolua_end
+
+ /** Returns the minimum and maximum coords in each direction for the first non-ignored block in each direction.
+ If there are no non-ignored blocks within the area, or blocktypes are not present, the returned values are reverse-ranges (MinX <- m_RangeX, MaxX <- 0 etc.)
+ Exported to Lua in ManualBindings.cpp. */
+ void GetNonAirCropRelCoords(int & a_MinRelX, int & a_MinRelY, int & a_MinRelZ, int & a_MaxRelX, int & a_MaxRelY, int & a_MaxRelZ, BLOCKTYPE a_IgnoreBlockType = E_BLOCK_AIR);
// Clients can use these for faster access to all blocktypes. Be careful though!
/** Returns the internal pointer to the block types */
diff --git a/src/BlockEntities/BlockEntityWithItems.h b/src/BlockEntities/BlockEntityWithItems.h
index 740dbca51..30a09bc02 100644
--- a/src/BlockEntities/BlockEntityWithItems.h
+++ b/src/BlockEntities/BlockEntityWithItems.h
@@ -76,7 +76,7 @@ protected:
cItemGrid m_Contents;
// cItemGrid::cListener overrides:
- virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum)
+ virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) override
{
UNUSED(a_SlotNum);
ASSERT(a_Grid == &m_Contents);
diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp
index bfd4b8322..203f47534 100644
--- a/src/BlockEntities/HopperEntity.cpp
+++ b/src/BlockEntities/HopperEntity.cpp
@@ -430,7 +430,7 @@ bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk)
}
// Try move from the output slot:
- if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsOutput, true))
+ if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsOutput))
{
cItem NewOutput(Furnace->GetOutputSlot());
Furnace->SetOutputSlot(NewOutput.AddCount(-1));
@@ -440,7 +440,7 @@ bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk)
// No output moved, check if we can move an empty bucket out of the fuel slot:
if (Furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET)
{
- if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsFuel, true))
+ if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsFuel))
{
Furnace->SetFuelSlot(cItem());
return true;
@@ -460,28 +460,13 @@ bool cHopperEntity::MoveItemsFromGrid(cBlockEntityWithItems & a_Entity)
cItemGrid & Grid = a_Entity.GetContents();
int NumSlots = Grid.GetNumSlots();
- // First try adding items of types already in the hopper:
for (int i = 0; i < NumSlots; i++)
{
if (Grid.IsSlotEmpty(i))
{
continue;
}
- if (MoveItemsFromSlot(a_Entity, i, false))
- {
- Grid.ChangeSlotCount(i, -1);
- return true;
- }
- }
-
- // No already existing stack can be topped up, try again with allowing new stacks:
- for (int i = 0; i < NumSlots; i++)
- {
- if (Grid.IsSlotEmpty(i))
- {
- continue;
- }
- if (MoveItemsFromSlot(a_Entity, i, true))
+ if (MoveItemsFromSlot(a_Entity, i))
{
Grid.ChangeSlotCount(i, -1);
return true;
@@ -495,21 +480,19 @@ bool cHopperEntity::MoveItemsFromGrid(cBlockEntityWithItems & a_Entity)
/// Moves one piece of the specified a_Entity's slot itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack.
-bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SlotNum, bool a_AllowNewStacks)
+bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SlotNum)
{
cItem One(a_Entity.GetSlot(a_SlotNum).CopyOne());
for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
{
if (m_Contents.IsSlotEmpty(i))
{
- if (a_AllowNewStacks)
+ if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum))
{
- if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum))
- {
- // Plugin disagrees with the move
- continue;
- }
+ // Plugin disagrees with the move
+ continue;
}
+
m_Contents.SetSlot(i, One);
return true;
}
@@ -521,8 +504,14 @@ bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_Sl
continue;
}
+ auto PreviousCount = m_Contents.GetSlot(i).m_ItemCount;
m_Contents.ChangeSlotCount(i, 1);
- return true;
+
+ if (PreviousCount == m_Contents.GetSlot(i).m_ItemCount + 1)
+ {
+ // Successfully added a new item. (Failure condition consistutes: stack full)
+ return true;
+ }
}
}
return false;
diff --git a/src/BlockEntities/HopperEntity.h b/src/BlockEntities/HopperEntity.h
index af99c526d..16d4cb6f9 100644
--- a/src/BlockEntities/HopperEntity.h
+++ b/src/BlockEntities/HopperEntity.h
@@ -74,7 +74,7 @@ protected:
bool MoveItemsFromGrid(cBlockEntityWithItems & a_Entity);
/// Moves one piece from the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack.
- bool MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SrcSlotNum, bool a_AllowNewStacks);
+ bool MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SrcSlotNum);
/// Moves items to the chest at the specified coords. Returns true if contents have changed
bool MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ);
diff --git a/src/BlockEntities/MobHeadEntity.cpp b/src/BlockEntities/MobHeadEntity.cpp
index 67e13ffb2..328ade23a 100644
--- a/src/BlockEntities/MobHeadEntity.cpp
+++ b/src/BlockEntities/MobHeadEntity.cpp
@@ -1,7 +1,7 @@
// MobHeadEntity.cpp
-// Implements the cMobHeadEntity class representing a single skull/head in the world
+// Implements the cMobHeadEntity class representing a single skull / head in the world
#include "Globals.h"
#include "MobHeadEntity.h"
diff --git a/src/BlockEntities/MobHeadEntity.h b/src/BlockEntities/MobHeadEntity.h
index 7f08c5ab2..b6760b40a 100644
--- a/src/BlockEntities/MobHeadEntity.h
+++ b/src/BlockEntities/MobHeadEntity.h
@@ -1,6 +1,6 @@
// MobHeadEntity.h
-// Declares the cMobHeadEntity class representing a single skull/head in the world
+// Declares the cMobHeadEntity class representing a single skull / head in the world
diff --git a/src/BlockEntities/NoteEntity.h b/src/BlockEntities/NoteEntity.h
index d3f85e9d2..f350ef4c4 100644
--- a/src/BlockEntities/NoteEntity.h
+++ b/src/BlockEntities/NoteEntity.h
@@ -52,7 +52,7 @@ public:
virtual void UsedBy(cPlayer * a_Player) override;
virtual void SendTo(cClientHandle &) override {}
- virtual void SetRedstonePower(bool a_Value)
+ virtual void SetRedstonePower(bool a_Value) override
{
if (a_Value)
{
diff --git a/src/BlockInServerPluginInterface.h b/src/BlockInServerPluginInterface.h
index 70c9944a8..d4759ce83 100644
--- a/src/BlockInServerPluginInterface.h
+++ b/src/BlockInServerPluginInterface.h
@@ -1,4 +1,12 @@
+// BlockInServerPluginInterface.h
+
+// Defines the cBlockInServerPluginInterface class that implements the cBlockPluginInterface for blocks, using the plugin manager
+
+
+
+
+
#pragma once
#include "Blocks/BlockPluginInterface.h"
@@ -16,7 +24,7 @@ class cBlockInServerPluginInterface :
public:
cBlockInServerPluginInterface(cWorld & a_World) : m_World(a_World) {}
- virtual bool CallHookBlockSpread(int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source)
+ virtual bool CallHookBlockSpread(int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source) override
{
return cPluginManager::Get()->CallHookBlockSpread(m_World, a_BlockX, a_BlockY, a_BlockZ, a_Source);
}
@@ -26,6 +34,16 @@ public:
return cPluginManager::Get()->CallHookBlockToPickups(m_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_Pickups);
}
+ virtual bool CallHookPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override
+ {
+ return cPluginManager::Get()->CallHookPlayerBreakingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta);
+ }
+
+ virtual bool CallHookPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override
+ {
+ return cPluginManager::Get()->CallHookPlayerBrokenBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta);
+ }
+
private:
cWorld & m_World;
};
diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp
index e56f4bfe0..dfa392d9b 100644
--- a/src/Blocks/BlockBed.cpp
+++ b/src/Blocks/BlockBed.cpp
@@ -14,7 +14,7 @@ void cBlockBedHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInt
NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
Vector3i ThisPos( a_BlockX, a_BlockY, a_BlockZ);
- Vector3i Direction = MetaDataToDirection( OldMeta & 0x7);
+ Vector3i Direction = MetaDataToDirection( OldMeta & 0x3);
if (OldMeta & 0x8)
{
// Was pillow
@@ -111,7 +111,7 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface
// Is foot end
VERIFY((Meta & 0x4) != 0x4); // Occupied flag should never be set, else our compilator (intended) is broken
- PillowDirection = MetaDataToDirection(Meta & 0x7);
+ PillowDirection = MetaDataToDirection(Meta & 0x3);
if (a_ChunkInterface.GetBlock(a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z) == E_BLOCK_BED) // Must always use pillow location for sleeping
{
a_WorldInterface.GetBroadcastManager().BroadcastUseBed(*a_Player, a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z);
diff --git a/src/Blocks/BlockComparator.h b/src/Blocks/BlockComparator.h
index 3443fc69e..ed73a162e 100644
--- a/src/Blocks/BlockComparator.h
+++ b/src/Blocks/BlockComparator.h
@@ -22,7 +22,7 @@ public:
virtual void OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override
{
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
- Meta ^= 0x04; // Toggle 3rd (addition/subtraction) bit with XOR
+ Meta ^= 0x04; // Toggle 3rd (addition / subtraction) bit with XOR
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta);
}
diff --git a/src/Blocks/BlockDoor.cpp b/src/Blocks/BlockDoor.cpp
index d2bf180be..a4e375cf0 100644
--- a/src/Blocks/BlockDoor.cpp
+++ b/src/Blocks/BlockDoor.cpp
@@ -50,10 +50,24 @@ void cBlockDoorHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterfac
UNUSED(a_CursorY);
UNUSED(a_CursorZ);
- if (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_WOODEN_DOOR)
+ switch (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ))
{
- ChangeDoor(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ);
- a_Player->GetWorld()->BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0, a_Player->GetClientHandle());
+ default:
+ {
+ ASSERT(!"Unhandled door block type");
+ }
+ case E_BLOCK_ACACIA_DOOR:
+ case E_BLOCK_BIRCH_DOOR:
+ case E_BLOCK_DARK_OAK_DOOR:
+ case E_BLOCK_JUNGLE_DOOR:
+ case E_BLOCK_SPRUCE_DOOR:
+ case E_BLOCK_IRON_DOOR:
+ case E_BLOCK_WOODEN_DOOR:
+ {
+ ChangeDoor(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ);
+ a_Player->GetWorld()->BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0, a_Player->GetClientHandle());
+ break;
+ }
}
}
@@ -134,7 +148,7 @@ NIBBLETYPE cBlockDoorHandler::MetaMirrorXY(NIBBLETYPE a_Meta)
return a_Meta;
}
- // Holds open/closed meta data. 0x0C == 1100.
+ // Holds open / closed meta data. 0x0C == 1100.
NIBBLETYPE OtherMeta = a_Meta & 0x0C;
// Mirrors according to a table. 0x03 == 0011.
@@ -152,7 +166,7 @@ NIBBLETYPE cBlockDoorHandler::MetaMirrorXY(NIBBLETYPE a_Meta)
NIBBLETYPE cBlockDoorHandler::MetaMirrorYZ(NIBBLETYPE a_Meta)
{
- // Top bit (0x08) contains door panel type (Top/Bottom panel) Only Bottom panels contain position data
+ // Top bit (0x08) contains door panel type (Top / Bottom panel) Only Bottom panels contain position data
// Return a_Meta if panel is a top panel (0x08 bit is set to 1)
// Note: Currently, you can not properly mirror the hinges on a double door. The orientation of the door is stored
@@ -165,7 +179,7 @@ NIBBLETYPE cBlockDoorHandler::MetaMirrorYZ(NIBBLETYPE a_Meta)
return a_Meta;
}
- // Holds open/closed meta data. 0x0C == 1100.
+ // Holds open / closed meta data. 0x0C == 1100.
NIBBLETYPE OtherMeta = a_Meta & 0x0C;
// Mirrors according to a table. 0x03 == 0011.
diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h
index 53f84b553..445383e7c 100644
--- a/src/Blocks/BlockDoor.h
+++ b/src/Blocks/BlockDoor.h
@@ -6,7 +6,7 @@
#include "Chunk.h"
#include "MetaRotator.h"
#include "ChunkInterface.h"
-
+#include "BlockSlab.h"
@@ -109,16 +109,31 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
{
- return ((a_RelY > 0) && CanBeOn(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)));
+ return ((a_RelY > 0) && CanBeOn(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ), a_Chunk.GetMeta(a_RelX, a_RelY - 1, a_RelZ)));
}
/** Returns true if door can be placed on the specified block type. */
- static bool CanBeOn(BLOCKTYPE a_BlockType)
+ static bool CanBeOn(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
- // Vanilla refuses to place doors on transparent blocks
+ // Vanilla refuses to place doors on transparent blocks, except top-half slabs and other doors
// We need to keep the door compatible with itself, otherwise the top half drops while the bottom half stays
- return !cBlockInfo::IsTransparent(a_BlockType) || IsDoorBlockType(a_BlockType);
+
+ // Doors can be placed on upside-down slabs
+ if (cBlockSlabHandler::IsAnySlabType(a_BlockType) && ((a_BlockMeta & 0x08) != 0))
+ {
+ return true;
+ }
+ // Doors can also be placed on other doors
+ else if (IsDoorBlockType(a_BlockType))
+ {
+ return true;
+ }
+ // Doors can not be placed on transparent blocks, but on any other block
+ else
+ {
+ return !cBlockInfo::IsTransparent(a_BlockType);
+ }
}
diff --git a/src/Blocks/BlockFire.h b/src/Blocks/BlockFire.h
index bafd385ab..2038df94b 100644
--- a/src/Blocks/BlockFire.h
+++ b/src/Blocks/BlockFire.h
@@ -106,7 +106,7 @@ public:
void FindAndSetPortalFrame(int X, int Y, int Z, cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface)
{
int MaxY = FindObsidianCeiling(X, Y, Z, a_ChunkInterface); // Get topmost obsidian block as reference for all other checks
- int X1 = X + 1, Z1 = Z + 1, X2 = X - 1, Z2 = Z - 1; // Duplicate XZ values, add/subtract one as we've checked the original already the line above
+ int X1 = X + 1, Z1 = Z + 1, X2 = X - 1, Z2 = Z - 1; // Duplicate XZ values, add / subtract one as we've checked the original already the line above
if (MaxY == 0) // Oh noes! Not a portal coordinate :(
{
@@ -139,7 +139,7 @@ public:
return;
}
- /** Evaluates if coordinates are a portal going XP/XM; returns true if so, and writes boundaries to variable
+ /** Evaluates if coordinates are a portal going XP / XM; returns true if so, and writes boundaries to variable
Takes coordinates of base block and Y coord of target obsidian ceiling */
bool FindPortalSliceX(int X1, int X2, int Y, int Z, int MaxY, cChunkInterface & a_ChunkInterface)
{
@@ -179,7 +179,7 @@ public:
return (FoundFrameXP && FoundFrameXM);
}
- /// Evaluates if coords are a portal going ZP/ZM; returns true if so, and writes boundaries to variable
+ /// Evaluates if coords are a portal going ZP / ZM; returns true if so, and writes boundaries to variable
bool FindPortalSliceZ(int X, int Y, int Z1, int Z2, int MaxY, cChunkInterface & a_ChunkInterface)
{
Dir = 2;
diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp
index 2de4a3e4c..452cc94a5 100644
--- a/src/Blocks/BlockHandler.cpp
+++ b/src/Blocks/BlockHandler.cpp
@@ -198,6 +198,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_CARPET: return new cBlockCarpetHandler (a_BlockType);
case E_BLOCK_CAULDRON: return new cBlockCauldronHandler (a_BlockType);
case E_BLOCK_CHEST: return new cBlockChestHandler (a_BlockType);
+ case E_BLOCK_CLAY: return new cBlockOreHandler (a_BlockType);
case E_BLOCK_COAL_ORE: return new cBlockOreHandler (a_BlockType);
case E_BLOCK_COCOA_POD: return new cBlockCocoaPodHandler (a_BlockType);
case E_BLOCK_COMMAND_BLOCK: return new cBlockCommandBlockHandler (a_BlockType);
@@ -353,7 +354,7 @@ bool cBlockHandler::GetPlacementBlockTypeMeta(
{
// By default, all blocks can be placed and the meta is copied over from the item's damage value:
a_BlockType = m_BlockType;
- a_BlockMeta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x0f);
+ a_BlockMeta = static_cast<NIBBLETYPE>(a_Player->GetEquippedItem().m_ItemDamage & 0x0f);
return true;
}
diff --git a/src/Blocks/BlockLeaves.h b/src/Blocks/BlockLeaves.h
index bd9a7414e..4d4610fd8 100644
--- a/src/Blocks/BlockLeaves.h
+++ b/src/Blocks/BlockLeaves.h
@@ -40,29 +40,41 @@ public:
{
cFastRandom rand;
- // Old leaves - 3 bits contain display; new leaves - 1st bit, shifted left two for saplings to understand
- if (rand.NextInt(6) == 0)
+ // There is a chance to drop a sapling that varies depending on the type of leaf broken.
+ // TODO: Take into account fortune for sapling drops.
+ int chance;
+ if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_JUNGLE))
+ {
+ // Jungle leaves have a 2.5% chance of dropping a sapling.
+ chance = rand.NextInt(40);
+ }
+ else
+ {
+ // Other leaves have a 5% chance of dropping a sapling.
+ chance = rand.NextInt(20);
+ }
+ if (chance == 0)
{
a_Pickups.push_back(
cItem(
E_BLOCK_SAPLING,
1,
- (m_BlockType == E_BLOCK_LEAVES) ? (a_BlockMeta & 0x03) : (2 << (a_BlockMeta & 0x01))
+ (m_BlockType == E_BLOCK_LEAVES) ? (a_BlockMeta & 0x03) : (4 + (a_BlockMeta & 0x01))
)
);
}
-
- // 1 % chance of dropping an apple, if the leaves' type is Apple Leaves
+
+ // 0.5 % chance of dropping an apple, if the leaves' type is Apple Leaves
if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_APPLE))
{
- if (rand.NextInt(101) == 0)
+ if (rand.NextInt(200) == 0)
{
a_Pickups.push_back(cItem(E_ITEM_RED_APPLE, 1, 0));
}
}
}
-
-
+
+
virtual void OnNeighborChanged(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override
{
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
diff --git a/src/Blocks/BlockLever.h b/src/Blocks/BlockLever.h
index f5bedea6c..2da138e5f 100644
--- a/src/Blocks/BlockLever.h
+++ b/src/Blocks/BlockLever.h
@@ -19,7 +19,7 @@ public:
virtual void OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override
{
- // Flip the ON bit on/off using the XOR bitwise operation
+ // Flip the ON bit on / off using the XOR bitwise operation
NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08);
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta);
diff --git a/src/Blocks/BlockOre.h b/src/Blocks/BlockOre.h
index 08d79f435..7e67e3450 100644
--- a/src/Blocks/BlockOre.h
+++ b/src/Blocks/BlockOre.h
@@ -50,6 +50,16 @@ public:
a_Pickups.push_back(cItem(E_ITEM_COAL));
break;
}
+ case E_BLOCK_NETHER_QUARTZ_ORE:
+ {
+ a_Pickups.push_back(cItem(E_ITEM_NETHER_QUARTZ));
+ break;
+ }
+ case E_BLOCK_CLAY:
+ {
+ a_Pickups.push_back(cItem(E_ITEM_CLAY, 4));
+ break;
+ }
default:
{
a_Pickups.push_back(cItem(m_BlockType));
diff --git a/src/Blocks/BlockPiston.h b/src/Blocks/BlockPiston.h
index 9a891025a..5933d4885 100644
--- a/src/Blocks/BlockPiston.h
+++ b/src/Blocks/BlockPiston.h
@@ -108,7 +108,7 @@ private:
case E_BLOCK_ENCHANTMENT_TABLE:
case E_BLOCK_END_PORTAL:
case E_BLOCK_END_PORTAL_FRAME:
- // Notice the lack of an E_BLOCK_ENDER_CHEST here; its because ender chests can totally be pushed/pulled in MCS :)
+ // Notice the lack of an E_BLOCK_ENDER_CHEST here; its because ender chests can totally be pushed / pulled in MCS :)
case E_BLOCK_FURNACE:
case E_BLOCK_LIT_FURNACE:
case E_BLOCK_INVERTED_DAYLIGHT_SENSOR:
diff --git a/src/Blocks/BlockPluginInterface.h b/src/Blocks/BlockPluginInterface.h
index b769bcf3e..6d49a248d 100644
--- a/src/Blocks/BlockPluginInterface.h
+++ b/src/Blocks/BlockPluginInterface.h
@@ -1,10 +1,25 @@
+// BlockPluginInterface.h
+
+// Declares the cBlockPluginInterface class representing an interface that the blockhandlers and itemhandlers use for calling plugins
+
+
+
+
+
#pragma once
+// fwd:
+class cPlayer;
+
+
+
+
+
/** This interface is used to decouple block handlers from the cPluginManager dependency through cWorld.
The block handlers call this interface, which is then implemented by the specific classes that
the caller provides.
@@ -16,6 +31,8 @@ public:
virtual bool CallHookBlockSpread(int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source) = 0;
virtual bool CallHookBlockToPickups(cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) = 0;
+ virtual bool CallHookPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
+ virtual bool CallHookPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
};
diff --git a/src/Blocks/BlockRail.h b/src/Blocks/BlockRail.h
index 02fe3aa95..a2e27a351 100644
--- a/src/Blocks/BlockRail.h
+++ b/src/Blocks/BlockRail.h
@@ -502,11 +502,11 @@ public:
// Save powered rail flag.
NIBBLETYPE OtherMeta = a_Meta & 0x08;
// Rotates according to table; 0x07 == 0111.
- // Rails can either be flat (North/South) or Ascending (Asc. East)
+ // Rails can either be flat (North / South) or Ascending (Asc. East)
switch (a_Meta & 0x07)
{
- case 0x00: return 0x01 + OtherMeta; // North/South -> East/West
- case 0x01: return 0x00 + OtherMeta; // East/West -> North/South
+ case 0x00: return 0x01 + OtherMeta; // North / South -> East / West
+ case 0x01: return 0x00 + OtherMeta; // East / West -> North / South
case 0x02: return 0x04 + OtherMeta; // Asc. East -> Asc. North
case 0x04: return 0x03 + OtherMeta; // Asc. North -> Asc. West
@@ -538,11 +538,11 @@ public:
// Save powered rail flag.
NIBBLETYPE OtherMeta = a_Meta & 0x08;
// Rotates according to table; 0x07 == 0111.
- // Rails can either be flat (North/South) or Ascending (Asc. East)
+ // Rails can either be flat (North / South) or Ascending (Asc. East)
switch (a_Meta & 0x07)
{
- case 0x00: return 0x01 + OtherMeta; // North/South -> East/West
- case 0x01: return 0x00 + OtherMeta; // East/West -> North/South
+ case 0x00: return 0x01 + OtherMeta; // North / South -> East / West
+ case 0x01: return 0x00 + OtherMeta; // East / West -> North / South
case 0x02: return 0x05 + OtherMeta; // Asc. East -> Asc. South
case 0x05: return 0x03 + OtherMeta; // Asc. South -> Asc. West
@@ -574,7 +574,7 @@ public:
// Save powered rail flag.
NIBBLETYPE OtherMeta = a_Meta & 0x08;
// Mirrors according to table; 0x07 == 0111.
- // Rails can either be flat (North/South) or Ascending (Asc. East)
+ // Rails can either be flat (North / South) or Ascending (Asc. East)
switch (a_Meta & 0x07)
{
case 0x05: return 0x04 + OtherMeta; // Asc. South -> Asc. North
@@ -605,7 +605,7 @@ public:
// Save powered rail flag.
NIBBLETYPE OtherMeta = a_Meta & 0x08;
// Mirrors according to table; 0x07 == 0111.
- // Rails can either be flat (North/South) or Ascending (Asc. East)
+ // Rails can either be flat (North / South) or Ascending (Asc. East)
switch (a_Meta & 0x07)
{
case 0x02: return 0x03 + OtherMeta; // Asc. East -> Asc. West
diff --git a/src/Blocks/BlockTorch.h b/src/Blocks/BlockTorch.h
index d63df94cf..3abc572f3 100644
--- a/src/Blocks/BlockTorch.h
+++ b/src/Blocks/BlockTorch.h
@@ -103,6 +103,11 @@ public:
case E_BLOCK_STAINED_GLASS:
case E_BLOCK_FENCE:
case E_BLOCK_NETHER_BRICK_FENCE:
+ case E_BLOCK_SPRUCE_FENCE:
+ case E_BLOCK_BIRCH_FENCE:
+ case E_BLOCK_JUNGLE_FENCE:
+ case E_BLOCK_DARK_OAK_FENCE:
+ case E_BLOCK_ACACIA_FENCE:
case E_BLOCK_COBBLESTONE_WALL:
{
// Torches can only be placed on top of these blocks
diff --git a/src/Blocks/BlockTrapdoor.h b/src/Blocks/BlockTrapdoor.h
index 8c96de0f1..dbb0b5a5b 100644
--- a/src/Blocks/BlockTrapdoor.h
+++ b/src/Blocks/BlockTrapdoor.h
@@ -35,7 +35,7 @@ public:
return;
}
- // Flip the ON bit on/off using the XOR bitwise operation
+ // Flip the ON bit on / off using the XOR bitwise operation
NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x04);
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta);
diff --git a/src/Blocks/BlockVine.h b/src/Blocks/BlockVine.h
index 00d7a69b8..3d06a8223 100644
--- a/src/Blocks/BlockVine.h
+++ b/src/Blocks/BlockVine.h
@@ -177,7 +177,7 @@ public:
}
- virtual void OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_BlockPluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
+ virtual void OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_BlockPluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override
{
UNUSED(a_ChunkInterface);
UNUSED(a_WorldInterface);
diff --git a/src/BoundingBox.h b/src/BoundingBox.h
index 928e62afa..8ea758ac7 100644
--- a/src/BoundingBox.h
+++ b/src/BoundingBox.h
@@ -57,10 +57,10 @@ public:
/// Returns true if a boundingbox specified by a_Min and a_Max is inside this bounding box
bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max);
- /// Returns true if the specified point is inside the bounding box specified by its min/max corners
+ /// Returns true if the specified point is inside the bounding box specified by its min / max corners
static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Point);
- /// Returns true if the specified point is inside the bounding box specified by its min/max corners
+ /// Returns true if the specified point is inside the bounding box specified by its min / max corners
static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, double a_X, double a_Y, double a_Z);
/** Returns true if this bounding box is intersected by the line specified by its two points
diff --git a/src/Broadcaster.cpp b/src/Broadcaster.cpp
new file mode 100644
index 000000000..594d12208
--- /dev/null
+++ b/src/Broadcaster.cpp
@@ -0,0 +1,47 @@
+
+#include "Globals.h"
+#include "Broadcaster.h"
+#include "World.h"
+#include "Chunk.h"
+
+cBroadcaster::cBroadcaster(cWorld * a_World) :
+ m_World(a_World)
+{
+}
+
+
+void cBroadcaster::BroadcastParticleEffect(const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, cClientHandle * a_Exclude)
+{
+ m_World->DoWithChunkAt(a_Src,
+ [=](cChunk & a_Chunk) -> bool
+ {
+ for (auto && client : a_Chunk.GetAllClients())
+ {
+ if (client == a_Exclude)
+ {
+ continue;
+ }
+ client->SendParticleEffect(a_ParticleName, a_Src.x, a_Src.y, a_Src.z, a_Offset.x, a_Offset.y, a_Offset.z, a_ParticleData, a_ParticleAmount);
+ };
+ return true;
+ });
+}
+
+
+void cBroadcaster::BroadcastParticleEffect(const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data, cClientHandle * a_Exclude)
+{
+ m_World->DoWithChunkAt(a_Src,
+ [=](cChunk & a_Chunk) -> bool
+ {
+ for (auto && client : a_Chunk.GetAllClients())
+ {
+ if (client == a_Exclude)
+ {
+ continue;
+ }
+ client->SendParticleEffect(a_ParticleName, a_Src, a_Offset, a_ParticleData, a_ParticleAmount, a_Data);
+ };
+ return true;
+ });
+}
+
diff --git a/src/Broadcaster.h b/src/Broadcaster.h
new file mode 100644
index 000000000..27d35fe4d
--- /dev/null
+++ b/src/Broadcaster.h
@@ -0,0 +1,20 @@
+
+class cWorld;
+
+#include <array>
+
+class cBroadcaster
+{
+
+public:
+
+ cBroadcaster(cWorld * a_World);
+
+ void BroadcastParticleEffect(const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, cClientHandle * a_Exclude = nullptr);
+
+ void BroadcastParticleEffect(const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data, cClientHandle * a_Exclude = nullptr);
+
+private:
+ cWorld * m_World;
+
+};
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b91c4f65a..1b721100d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -18,6 +18,7 @@ SET (SRCS
BlockArea.cpp
BlockID.cpp
BlockInfo.cpp
+ Broadcaster.cpp
BoundingBox.cpp
ByteBuffer.cpp
ChatColor.cpp
@@ -47,11 +48,13 @@ SET (SRCS
Logger.cpp
Map.cpp
MapManager.cpp
+ MemorySettingsRepository.cpp
MobCensus.cpp
MobFamilyCollecter.cpp
MobProximityCounter.cpp
MobSpawner.cpp
MonsterConfig.cpp
+ OverridesSettingsRepository.cpp
ProbabDistrib.cpp
RankManager.cpp
RCONServer.cpp
@@ -77,6 +80,7 @@ SET (HDRS
BlockInServerPluginInterface.h
BlockInfo.h
BlockTracer.h
+ Broadcaster.h
BoundingBox.h
BuildInfo.h.cmake
ByteBuffer.h
@@ -114,11 +118,13 @@ SET (HDRS
Map.h
MapManager.h
Matrix4.h
+ MemorySettingsRepository.h
MobCensus.h
MobFamilyCollecter.h
MobProximityCounter.h
MobSpawner.h
MonsterConfig.h
+ OverridesSettingsRepository.h
ProbabDistrib.h
RankManager.h
RCONServer.h
@@ -126,6 +132,7 @@ SET (HDRS
Scoreboard.h
Server.h
SetChunkData.h
+ SettingsRepositoryInterface.h
Statistics.h
StringCompression.h
StringUtils.h
@@ -140,6 +147,7 @@ SET (HDRS
include_directories(".")
include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/sqlite")
include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/SQLiteCpp/include")
+include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/TCLAP/include")
configure_file("BuildInfo.h.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/BuildInfo.h")
@@ -276,7 +284,7 @@ if (MSVC)
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/MCServer/lua51.dll ./lua51.dll
# Regenerate bindings:
- COMMAND tolua -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
+ COMMAND tolua -L BindingsProcessor.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/
# add any new generation dependencies here
@@ -324,4 +332,9 @@ endif ()
if (WIN32)
target_link_libraries(${EXECUTABLE} expat tolualib ws2_32.lib Psapi.lib)
endif()
-target_link_libraries(${EXECUTABLE} luaexpat jsoncpp polarssl zlib sqlite lua SQLiteCpp event_core event_extra)
+
+if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
+ add_flags_lnk(-L/usr/local/lib)
+endif()
+
+target_link_libraries(${EXECUTABLE} luaexpat jsoncpp mbedtls zlib sqlite lua SQLiteCpp event_core event_extra)
diff --git a/src/CheckBasicStyle.lua b/src/CheckBasicStyle.lua
index 19156b537..fa20f18f7 100644..100755
--- a/src/CheckBasicStyle.lua
+++ b/src/CheckBasicStyle.lua
@@ -43,6 +43,7 @@ local g_IgnoredFiles =
{
"Bindings/Bindings.h",
"Bindings/Bindings.cpp",
+ "Bindings/LuaState_Implementation.cpp",
"LeakFinder.cpp",
"LeakFinder.h",
"MersenneTwister.h",
@@ -149,6 +150,39 @@ local g_ViolationPatterns =
-- No space before a closing parenthesis:
{" %)", "Remove the space before \")\""},
+
+ -- Check spaces around "+":
+ {"^[a-zA-Z0-9]+%+[a-zA-Z0-9]+", "Add space around +"},
+ {"[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%+[a-zA-Z0-9]+", "Add space around +"},
+ --[[
+ -- Cannot check these because of text such as "X+" and "+2" appearing in some comments.
+ {"^[a-zA-Z0-9]+ %+[a-zA-Z0-9]+", "Add space after +"},
+ {"[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+ %+[a-zA-Z0-9]+", "Add space after +"},
+ {"^[a-zA-Z0-9]+%+ [a-zA-Z0-9]+", "Add space before +"},
+ {"[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%+ [a-zA-Z0-9]+", "Add space before +"},
+ --]]
+
+ -- Cannot check spaces around "-", because the minus is sometimes used as a hyphen between-words
+
+ -- Check spaces around "*":
+ {"^[a-zA-Z0-9]+%*[a-zA-Z0-9]+", "Add space around *"},
+ {"^[^\"]*[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%*[a-zA-Z0-9]+", "Add space around *"},
+ {"^[a-zB-Z0-9]+%* [a-zA-Z0-9]+", "Add space before *"},
+ {"^[^\"]*[!@#$%%%^&*() %[%]\t][a-zB-Z0-9]+%* [a-zA-Z0-9]+", "Add space before *"},
+
+ -- Check spaces around "/":
+ {"^[a-zA-Z0-9]+%/[a-zA-Z0-9]+", "Add space around /"},
+ {"^[^\"]*[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%/[a-zA-Z0-9]+", "Add space around /"},
+
+ -- Check spaces around "&":
+ {"^[a-zA-Z0-9]+%&[a-zA-Z0-9]+", "Add space around /"},
+ {"^[^\"]*[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%&[a-zA-Z0-9]+", "Add space around /"},
+ {"^[a-zA-Z0-9]+%& [a-zA-Z0-9]+", "Add space before &"},
+ {"^[^\"]*[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%& [a-zA-Z0-9]+", "Add space before &"},
+
+ -- We don't like "Type const *" and "Type const &". Use "const Type *" and "const Type &" instead:
+ {"const %&", "Use 'const Type &' instead of 'Type const &'"},
+ {"const %*", "Use 'const Type *' instead of 'Type const *'"},
}
@@ -266,14 +300,113 @@ end
+--- Array of files to process. Filled from cmdline arguments
+local ToProcess = {}
+
+
+
+
+
+--- Handlers for the command-line arguments
+-- Maps flag => function
+local CmdLineHandlers =
+{
+ -- "-f file" checks the specified file
+ ["-f"] = function (a_Args, a_Idx)
+ local fnam = a_Args[a_Idx + 1]
+ if not(fnam) then
+ error("Invalid flag: '-f' needs a filename following it.")
+ end
+ table.insert(ToProcess, fnam)
+ return a_Idx + 2 -- skip the filename in param parsing
+ end,
+
+ -- "-g" checks files reported by git as being committed.
+ ["-g"] = function (a_Args, a_Idx)
+ local f = io.popen("git diff --cached --name-only --diff-filter=ACMR")
+ for fnam in f:lines() do
+ table.insert(ToProcess, fnam)
+ end
+ end,
+
+ -- "-h" prints help and exits
+ ["-h"] = function (a_Args, a_Idx)
+ print([[
+Usage:")
+"CheckBasicStyle [<options>]
+
+Available options:
+-f <filename> - checks the specified filename
+-g - checks files reported by Git as being committed
+-h - prints this help and exits
+-l <listfile> - checks all files listed in the specified listfile
+-- - reads the list of files to check from stdin
+
+When no options are given, the script checks all files listed in the AllFiles.lst file.
+
+Only .cpp and .h files are ever checked.
+]])
+ os.exit(0)
+ end,
+
+ -- "-l listfile" loads the list of files to check from the specified listfile
+ ["-l"] = function (a_Args, a_Idx)
+ local listFile = a_Args[a_Idx + 1]
+ if not(listFile) then
+ error("Invalid flag: '-l' needs a filename following it.")
+ end
+ for fnam in io.lines(listFile) do
+ table.insert(ToProcess, fnam)
+ end
+ return a_Idx + 2 -- Skip the listfile in param parsing
+ end,
+
+ -- "--" reads the list of files from stdin
+ ["--"] = function (a_Args, a_Idx)
+ for fnam in io.lines() do
+ table.insert(ToProcess, fnam)
+ end
+ end,
+}
+
+
+
+
+
-- Remove buffering from stdout, so that the output appears immediately in IDEs:
io.stdout:setvbuf("no")
--- Process all files in the AllFiles.lst file (generated by cmake):
-for fnam in io.lines("AllFiles.lst") do
+-- Parse the cmdline arguments to see what files to check:
+local idx = 1
+while (arg[idx]) do
+ local handler = CmdLineHandlers[arg[idx]]
+ if not(handler) then
+ error("Unknown command-line argument #" .. idx .. ": " .. arg[idx])
+ end
+ idx = handler(arg, idx) or (idx + 1) -- Call the handler, let it change the next index if it wants
+end
+
+
+-- By default process all files in the AllFiles.lst file (generated by cmake):
+if not(arg[1]) then
+ for fnam in io.lines("AllFiles.lst") do
+ table.insert(ToProcess, fnam)
+ end
+end
+
+
+
+
+
+-- Process the files in the list:
+for _, fnam in ipairs(ToProcess) do
ProcessItem(fnam)
end
+
+
+
+
-- Report final verdict:
print("Number of violations found: " .. g_NumViolations)
if (g_NumViolations > 0) then
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index 95dc7708e..7d5f54373 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -472,26 +472,26 @@ void cChunk::Stay(bool a_Stay)
-void cChunk::CollectMobCensus(cMobCensus& toFill)
+void cChunk::CollectMobCensus(cMobCensus & toFill)
{
toFill.CollectSpawnableChunk(*this);
- std::list<const Vector3d*> playerPositions;
- cPlayer* currentPlayer;
- for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr)
+ std::list<const Vector3d *> playerPositions;
+ cPlayer * currentPlayer;
+ for (auto itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr)
{
currentPlayer = (*itr)->GetPlayer();
playerPositions.push_back(&(currentPlayer->GetPosition()));
}
Vector3d currentPosition;
- for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
+ for (auto itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
{
// LOGD("Counting entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass());
if ((*itr)->IsMob())
{
- cMonster& Monster = (cMonster&)(**itr);
+ auto & Monster = reinterpret_cast<cMonster &>(**itr);
currentPosition = Monster.GetPosition();
- for (std::list<const Vector3d*>::const_iterator itr2 = playerPositions.begin(); itr2 != playerPositions.end(); ++itr2)
+ for (auto itr2 = playerPositions.cbegin(); itr2 != playerPositions.cend(); ++itr2)
{
toFill.CollectMob(Monster, *this, (currentPosition - **itr2).SqrLength());
}
@@ -531,7 +531,7 @@ void cChunk::GetRandomBlockCoords(int & a_X, int & a_Y, int & a_Z)
-void cChunk::SpawnMobs(cMobSpawner& a_MobSpawner)
+void cChunk::SpawnMobs(cMobSpawner & a_MobSpawner)
{
int CenterX, CenterY, CenterZ;
GetRandomBlockCoords(CenterX, CenterY, CenterZ);
@@ -737,7 +737,7 @@ void cChunk::ProcessQueuedSetBlocks(void)
{
if (GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ) == itr->m_PreviousType)
{
- // Current world age is bigger than/equal to target world age - delay time reached AND
+ // Current world age is bigger than / equal to target world age - delay time reached AND
// Previous block type was the same as current block type (to prevent duplication)
SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); // SetMeta doesn't send to client
itr = m_SetBlockQueue.erase(itr);
@@ -751,7 +751,7 @@ void cChunk::ProcessQueuedSetBlocks(void)
}
else
{
- // Current world age is bigger than/equal to target world age - delay time reached
+ // Current world age is bigger than / equal to target world age - delay time reached
SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
itr = m_SetBlockQueue.erase(itr);
LOGD("Successfully set queued block - previous type ignored");
@@ -1026,7 +1026,7 @@ void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Bl
case E_BLOCK_FARMLAND:
{
// DEBUG: This is here to catch FS #349 - melons growing over other crops.
- LOG("Growing melon/pumpkin overwriting %s, growing on %s",
+ LOG("Growing melon / pumpkin overwriting %s, growing on %s",
ItemTypeToString(BlockType[CheckType]).c_str(),
ItemTypeToString(Soil).c_str()
);
@@ -1827,7 +1827,7 @@ bool cChunk::SetSignLines(int a_PosX, int a_PosY, int a_PosZ, const AString & a_
)
{
MarkDirty();
- (reinterpret_cast<cSignEntity *>(*itr))->SetLines(a_Line1, a_Line2, a_Line3, a_Line4);
+ reinterpret_cast<cSignEntity *>(*itr)->SetLines(a_Line1, a_Line2, a_Line3, a_Line4);
m_World->BroadcastBlockEntity(a_PosX, a_PosY, a_PosZ);
return true;
}
@@ -1839,7 +1839,7 @@ bool cChunk::SetSignLines(int a_PosX, int a_PosY, int a_PosZ, const AString & a_
-void cChunk::RemoveBlockEntity( cBlockEntity* a_BlockEntity)
+void cChunk::RemoveBlockEntity(cBlockEntity * a_BlockEntity)
{
MarkDirty();
m_BlockEntities.remove(a_BlockEntity);
diff --git a/src/Chunk.h b/src/Chunk.h
index e8c60a74b..f57769107 100644
--- a/src/Chunk.h
+++ b/src/Chunk.h
@@ -155,10 +155,10 @@ public:
void Stay(bool a_Stay = true);
/** Recence all mobs proximities to players in order to know what to do with them */
- void CollectMobCensus(cMobCensus& toFill);
+ void CollectMobCensus(cMobCensus & toFill);
/** Try to Spawn Monsters inside chunk */
- void SpawnMobs(cMobSpawner& a_MobSpawner);
+ void SpawnMobs(cMobSpawner & a_MobSpawner);
void Tick(std::chrono::milliseconds a_Dt);
@@ -439,6 +439,9 @@ public:
as at least one requests is active the chunk will be ticked). */
void SetAlwaysTicked(bool a_AlwaysTicked);
+ // Makes a copy of the list
+ cClientHandleList GetAllClients(void) const {return m_LoadedByClient; }
+
private:
friend class cChunkMap;
@@ -530,9 +533,6 @@ private:
/** Wakes up each simulator for its specific blocks; through all the blocks in the chunk */
void WakeUpSimulators(void);
-
- // Makes a copy of the list
- cClientHandleList GetAllClients(void) const {return m_LoadedByClient; }
/** Sends m_PendingSendBlocks to all clients */
void BroadcastPendingBlockChanges(void);
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index 87da86131..44acc2013 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -65,7 +65,7 @@ cChunkMap::~cChunkMap()
-void cChunkMap::RemoveLayer( cChunkLayer* a_Layer)
+void cChunkMap::RemoveLayer(cChunkLayer * a_Layer)
{
cCSLock Lock(m_CSLayers);
m_Layers.remove(a_Layer);
@@ -791,6 +791,28 @@ bool cChunkMap::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callb
}
+bool cChunkMap::DoWithChunkAt(Vector3i a_BlockPos, std::function<bool(cChunk &)> a_Callback)
+{
+ int ChunkX, ChunkZ;
+ cChunkDef::BlockToChunk(a_BlockPos.x, a_BlockPos.z, ChunkX, ChunkZ);
+ struct cCallBackWrapper : cChunkCallback
+ {
+ cCallBackWrapper(std::function<bool(cChunk &)> a_InnerCallback) :
+ m_Callback(a_InnerCallback)
+ {
+ }
+
+ virtual bool Item(cChunk * a_Chunk)
+ {
+ return m_Callback(*a_Chunk);
+ }
+
+ private:
+ std::function<bool(cChunk &)> m_Callback;
+ } callback(a_Callback);
+ return DoWithChunk(ChunkX, ChunkZ, callback);
+}
+
@@ -1880,10 +1902,12 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
if (ShouldDestroyBlocks)
{
cBlockArea area;
-
a_BlocksAffected.reserve(8 * ExplosionSizeInt * ExplosionSizeInt * ExplosionSizeInt);
-
- area.Read(m_World, bx - ExplosionSizeInt, (int)ceil(a_BlockX + ExplosionSizeInt), MinY, MaxY, bz - ExplosionSizeInt, (int)ceil(a_BlockZ + ExplosionSizeInt));
+ if (!area.Read(m_World, bx - ExplosionSizeInt, (int)ceil(a_BlockX + ExplosionSizeInt), MinY, MaxY, bz - ExplosionSizeInt, (int)ceil(a_BlockZ + ExplosionSizeInt)))
+ {
+ return;
+ }
+
for (int x = -ExplosionSizeInt; x < ExplosionSizeInt; x++)
{
for (int y = -ExplosionSizeInt; y < ExplosionSizeInt; y++)
@@ -2413,6 +2437,7 @@ bool cChunkMap::GenerateChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback *
// Try loading the chunk:
if ((Chunk == nullptr) || (!Chunk->IsValid()))
{
+ Chunk->SetPresence(cChunk::cpQueued);
class cPrepareLoadCallback: public cChunkCoordCallback
{
public:
@@ -2427,6 +2452,7 @@ bool cChunkMap::GenerateChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback *
virtual void Call(int a_CBChunkX, int a_CBChunkZ) override
{
// The chunk has been loaded or an error occurred, check if it's valid now:
+ cCSLock Lock(m_ChunkMap.m_CSLayers);
cChunkPtr CBChunk = m_ChunkMap.GetChunkNoLoad(a_CBChunkX, a_CBChunkZ);
if (CBChunk == nullptr)
@@ -2690,12 +2716,12 @@ void cChunkMap::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ)
-void cChunkMap::CollectMobCensus(cMobCensus& a_ToFill)
+void cChunkMap::CollectMobCensus(cMobCensus & a_ToFill)
{
cCSLock Lock(m_CSLayers);
- for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ for (auto && layer: m_Layers)
{
- (*itr)->CollectMobCensus(a_ToFill);
+ layer->CollectMobCensus(a_ToFill);
} // for itr - m_Layers
}
@@ -2704,12 +2730,12 @@ void cChunkMap::CollectMobCensus(cMobCensus& a_ToFill)
-void cChunkMap::SpawnMobs(cMobSpawner& a_MobSpawner)
+void cChunkMap::SpawnMobs(cMobSpawner & a_MobSpawner)
{
cCSLock Lock(m_CSLayers);
- for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ for (auto && layer: m_Layers)
{
- (*itr)->SpawnMobs(a_MobSpawner);
+ layer->SpawnMobs(a_MobSpawner);
} // for itr - m_Layers
}
@@ -2910,7 +2936,7 @@ cChunk * cChunkMap::cChunkLayer::FindChunk(int a_ChunkX, int a_ChunkZ)
-void cChunkMap::cChunkLayer::CollectMobCensus(cMobCensus& a_ToFill)
+void cChunkMap::cChunkLayer::CollectMobCensus(cMobCensus & a_ToFill)
{
for (size_t i = 0; i < ARRAYCOUNT(m_Chunks); i++)
{
@@ -2929,7 +2955,7 @@ void cChunkMap::cChunkLayer::CollectMobCensus(cMobCensus& a_ToFill)
-void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner& a_MobSpawner)
+void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner & a_MobSpawner)
{
for (size_t i = 0; i < ARRAYCOUNT(m_Chunks); i++)
{
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
index 0fac79c84..35f66f339 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -64,7 +64,7 @@ public:
static const int LAYER_SIZE = 32;
- cChunkMap(cWorld* a_World);
+ cChunkMap(cWorld * a_World);
~cChunkMap();
// Broadcast respective packets to all clients of the chunk where the event is taking place
@@ -104,6 +104,9 @@ public:
/** Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback */
bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback);
+ /** Calls the callback for the chunk at the block position specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback **/
+ bool DoWithChunkAt(Vector3i a_BlockPos, std::function<bool(cChunk &)> a_Callback);
+
/** Wakes up simulators for the specified block */
void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ);
@@ -361,11 +364,11 @@ public:
/** Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call */
void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ);
- /** Make a Mob census, of all mobs, their family, their chunk and theyr distance to closest player */
- void CollectMobCensus(cMobCensus& a_ToFill);
+ /** Make a Mob census, of all mobs, their family, their chunk and their distance to closest player */
+ void CollectMobCensus(cMobCensus & a_ToFill);
/** Try to Spawn Monsters inside all Chunks */
- void SpawnMobs(cMobSpawner& a_MobSpawner);
+ void SpawnMobs(cMobSpawner & a_MobSpawner);
void Tick(std::chrono::milliseconds a_Dt);
@@ -430,9 +433,10 @@ private:
void UnloadUnusedChunks(void);
/** Collect a mob census, of all mobs, their megatype, their chunk and their distance o closest player */
- void CollectMobCensus(cMobCensus& a_ToFill);
+ void CollectMobCensus(cMobCensus & a_ToFill);
+
/** Try to Spawn Monsters inside all Chunks */
- void SpawnMobs(cMobSpawner& a_MobSpawner);
+ void SpawnMobs(cMobSpawner & a_MobSpawner);
void Tick(std::chrono::milliseconds a_Dt);
diff --git a/src/ChunkSender.h b/src/ChunkSender.h
index 7cd7ddd86..8b187c5f9 100644
--- a/src/ChunkSender.h
+++ b/src/ChunkSender.h
@@ -13,7 +13,7 @@ And once they do, it requests the chunk data and sends it all away, either
sends to a specific client (QueueSendChunkTo)
Chunk data is queried using the cChunkDataCallback interface.
It is cached inside the ChunkSender object during the query and then processed after the query ends.
-Note that the data needs to be compressed only *after* the query finishes,
+Note that the data needs to be compressed only after the query finishes,
because the query callbacks run with ChunkMap's CS locked.
A client may remove itself from all direct requests(QueueSendChunkTo()) by calling RemoveClient();
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 2ce212421..13972b0f5 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -23,6 +23,7 @@
#include "Blocks/BlockSlab.h"
#include "Blocks/BlockBed.h"
#include "Blocks/ChunkInterface.h"
+#include "BlockInServerPluginInterface.h"
#include "Root.h"
@@ -435,7 +436,7 @@ bool cClientHandle::StreamNextChunk(void)
{
Vector3d Vector = Position + LookVector * cChunkDef::Width * Range;
- // Get the chunk from the x/z coords.
+ // Get the chunk from the x / z coords.
int RangeX, RangeZ = 0;
cChunkDef::BlockToChunk(FloorC(Vector.x), FloorC(Vector.z), RangeX, RangeZ);
@@ -453,7 +454,7 @@ bool cClientHandle::StreamNextChunk(void)
continue;
}
- // If the chunk already loading/loaded -> skip
+ // If the chunk already loading / loaded -> skip
if (
(std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), Coords) != m_ChunksToSend.end()) ||
(std::find(m_LoadedChunks.begin(), m_LoadedChunks.end(), Coords) != m_LoadedChunks.end())
@@ -491,7 +492,7 @@ bool cClientHandle::StreamNextChunk(void)
{
cChunkCoords Coords = *itr;
- // If the chunk already loading/loaded -> skip
+ // If the chunk already loading / loaded -> skip
if (
(std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), Coords) != m_ChunksToSend.end()) ||
(std::find(m_LoadedChunks.begin(), m_LoadedChunks.end(), Coords) != m_LoadedChunks.end())
@@ -1153,7 +1154,7 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc
return;
}
- // Set the last digging coords to the block being dug, so that they can be checked in DIG_FINISHED to avoid dig/aim bug in the client:
+ // Set the last digging coords to the block being dug, so that they can be checked in DIG_FINISHED to avoid dig / aim bug in the client:
m_HasStartedDigging = true;
m_LastDigBlockX = a_BlockX;
m_LastDigBlockY = a_BlockY;
@@ -1200,7 +1201,7 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo
(m_LastDigBlockZ != a_BlockZ)
)
{
- LOGD("Prevented a dig/aim bug in the client (finish {%d, %d, %d} vs start {%d, %d, %d}, HSD: %s)",
+ LOGD("Prevented a dig / aim bug in the client (finish {%d, %d, %d} vs start {%d, %d, %d}, HSD: %s)",
a_BlockX, a_BlockY, a_BlockZ,
m_LastDigBlockX, m_LastDigBlockY, m_LastDigBlockZ,
(m_HasStartedDigging ? "True" : "False")
@@ -1340,7 +1341,14 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
{
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
- World->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, m_Player); // 2 block high things
+ if (a_BlockY < cChunkDef::Height - 1)
+ {
+ World->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, m_Player); // 2 block high things
+ }
+ if (a_BlockY > 1)
+ {
+ World->SendBlockTo(a_BlockX, a_BlockY - 1, a_BlockZ, m_Player); // 2 block high things
+ }
m_Player->GetInventory().SendEquippedSlot();
}
}
@@ -1351,7 +1359,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
if (!CheckBlockInteractionsRate())
{
- Kick("Too many blocks were placed/interacted with per unit time - hacked client?");
+ Kick("Too many blocks were placed / interacted with per unit time - hacked client?");
return;
}
@@ -1432,7 +1440,8 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
// A plugin doesn't agree with using the item, abort
return;
}
- ItemHandler->OnItemUse(World, m_Player, Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ cBlockInServerPluginInterface PluginInterface(*World);
+ ItemHandler->OnItemUse(World, m_Player, PluginInterface, Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
PlgMgr->CallHookPlayerUsedItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
}
}
@@ -1464,7 +1473,7 @@ void cClientHandle::HandleChat(const AString & a_Message)
Color.clear();
}
Msg.AddTextPart(AString("<") + m_Player->GetName() + "> ", Color);
- Msg.ParseText(a_Message);
+ Msg.ParseText(Message);
Msg.UnderlineUrls();
m_Player->GetWorld()->BroadcastChat(Msg);
}
@@ -2374,6 +2383,15 @@ void cClientHandle::SendParticleEffect(const AString & a_ParticleName, float a_S
+void cClientHandle::SendParticleEffect(const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data)
+{
+ m_Protocol->SendParticleEffect(a_ParticleName, a_Src, a_Offset, a_ParticleData, a_ParticleAmount, a_Data);
+}
+
+
+
+
+
void cClientHandle::SendPickupSpawn(const cPickup & a_Pickup)
{
m_Protocol->SendPickupSpawn(a_Pickup);
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index e004c3bfb..bcfa55825 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -22,6 +22,7 @@
#include "ChunkSender.h"
+#include <array>
@@ -178,6 +179,7 @@ public: // tolua_export
void SendMapInfo (int a_ID, unsigned int a_Scale);
void SendPaintingSpawn (const cPainting & a_Painting);
void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount);
+ void SendParticleEffect (const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data);
void SendPickupSpawn (const cPickup & a_Pickup);
void SendPlayerAbilities (void);
void SendPlayerListAddPlayer (const cPlayer & a_Player);
@@ -396,7 +398,7 @@ private:
cPlayer * m_Player;
- bool m_HasSentDC; ///< True if a D/C packet has been sent in either direction
+ bool m_HasSentDC; ///< True if a Disconnect packet has been sent in either direction
// Chunk position when the last StreamChunks() was called; used to avoid re-streaming while in the same chunk
int m_LastStreamedChunkX;
@@ -421,7 +423,7 @@ private:
int m_BlockDigAnimY;
int m_BlockDigAnimZ;
- // To avoid dig/aim bug in the client, store the last position given in a DIG_START packet and compare to that when processing the DIG_FINISH packet:
+ // To avoid dig / aim bug in the client, store the last position given in a DIG_START packet and compare to that when processing the DIG_FINISH packet:
bool m_HasStartedDigging;
int m_LastDigBlockX;
int m_LastDigBlockY;
diff --git a/src/CommandOutput.cpp b/src/CommandOutput.cpp
index 510461d81..255ec3e9b 100644
--- a/src/CommandOutput.cpp
+++ b/src/CommandOutput.cpp
@@ -29,29 +29,32 @@ void cCommandOutputCallback::Out(const char * a_Fmt, ...)
////////////////////////////////////////////////////////////////////////////////
-// cLogCommandOutputCallback:
+// cStringAccumCommandOutputCallback:
-void cLogCommandOutputCallback::Out(const AString & a_Text)
+void cStringAccumCommandOutputCallback::Out(const AString & a_Text)
{
- m_Buffer.append(a_Text);
+ m_Accum.append(a_Text);
}
+////////////////////////////////////////////////////////////////////////////////
+// cLogCommandOutputCallback:
+
void cLogCommandOutputCallback::Finished(void)
{
// Log each line separately:
- size_t len = m_Buffer.length();
+ size_t len = m_Accum.length();
size_t last = 0;
for (size_t i = 0; i < len; i++)
{
- switch (m_Buffer[i])
+ switch (m_Accum[i])
{
case '\n':
{
- LOG("%s", m_Buffer.substr(last, i - last).c_str());
+ LOG("%s", m_Accum.substr(last, i - last).c_str());
last = i + 1;
break;
}
@@ -59,11 +62,11 @@ void cLogCommandOutputCallback::Finished(void)
} // for i - m_Buffer[]
if (last < len)
{
- LOG("%s", m_Buffer.substr(last).c_str());
+ LOG("%s", m_Accum.substr(last).c_str());
}
// Clear the buffer for the next command output:
- m_Buffer.clear();
+ m_Accum.clear();
}
diff --git a/src/CommandOutput.h b/src/CommandOutput.h
index daa9430c0..6265b74ea 100644
--- a/src/CommandOutput.h
+++ b/src/CommandOutput.h
@@ -47,18 +47,36 @@ class cNullCommandOutputCallback :
-/// Sends all command output to a log, line by line, when the command finishes processing
-class cLogCommandOutputCallback :
+/** Accumulates all command output into a string. */
+class cStringAccumCommandOutputCallback:
public cCommandOutputCallback
{
+ typedef cCommandOutputCallback super;
+
public:
// cCommandOutputCallback overrides:
virtual void Out(const AString & a_Text) override;
- virtual void Finished(void) override;
-
+ virtual void Finished(void) override {}
+
+ /** Returns the accumulated command output in a string. */
+ const AString & GetAccum(void) const { return m_Accum; }
+
protected:
- /// Output is stored here until the command finishes processing
- AString m_Buffer;
+ /** Output is stored here until the command finishes processing */
+ AString m_Accum;
+} ;
+
+
+
+
+
+/// Sends all command output to a log, line by line, when the command finishes processing
+class cLogCommandOutputCallback :
+ public cStringAccumCommandOutputCallback
+{
+public:
+ // cStringAccumCommandOutputCallback overrides:
+ virtual void Finished(void) override;
} ;
diff --git a/src/CraftingRecipes.cpp b/src/CraftingRecipes.cpp
index 202fb900e..472044fa3 100644
--- a/src/CraftingRecipes.cpp
+++ b/src/CraftingRecipes.cpp
@@ -366,6 +366,7 @@ void cCraftingRecipes::ClearRecipes(void)
void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine)
{
+ // Remove any spaces within the line:
AString RecipeLine(a_RecipeLine);
RecipeLine.erase(std::remove_if(RecipeLine.begin(), RecipeLine.end(), isspace), RecipeLine.end());
@@ -672,10 +673,10 @@ cCraftingRecipes::cRecipe * cCraftingRecipes::MatchRecipe(const cItem * a_Crafti
if (
(itrS->x >= a_GridWidth) ||
(itrS->y >= a_GridHeight) ||
- (Item.m_ItemType != a_CraftingGrid[GridID].m_ItemType) || // same item type?
+ (Item.m_ItemType != a_CraftingGrid[GridID].m_ItemType) || // same item type?
(Item.m_ItemCount > a_CraftingGrid[GridID].m_ItemCount) || // not enough items
(
- (Item.m_ItemDamage > 0) && // should compare damage values?
+ (Item.m_ItemDamage >= 0) && // should compare damage values?
(Item.m_ItemDamage != a_CraftingGrid[GridID].m_ItemDamage)
)
)
diff --git a/src/Defines.h b/src/Defines.h
index c2f46c241..787eacab8 100644
--- a/src/Defines.h
+++ b/src/Defines.h
@@ -229,7 +229,7 @@ inline const char * ClickActionToString(eClickAction a_ClickAction)
-/** Returns a blockface mirrored around the Y axis (doesn't change up/down). */
+/** Returns a blockface mirrored around the Y axis (doesn't change up / down). */
inline eBlockFace MirrorBlockFaceY(eBlockFace a_BlockFace)
{
switch (a_BlockFace)
diff --git a/src/Enchantments.h b/src/Enchantments.h
index e8e84d43c..9d3f342d4 100644
--- a/src/Enchantments.h
+++ b/src/Enchantments.h
@@ -14,7 +14,7 @@
-// fwd: WorldStorage/FastNBT.h
+// fwd: "WorldStorage/FastNBT.h"
class cFastNBTWriter;
class cParsedNBT;
@@ -138,10 +138,10 @@ public:
bool operator !=(const cEnchantments & a_Other) const;
/** Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments") */
- friend void EnchantmentSerializer::WriteToNBTCompound(cEnchantments const& a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName);
+ friend void EnchantmentSerializer::WriteToNBTCompound(const cEnchantments & a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName);
/** Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments) */
- friend void EnchantmentSerializer::ParseFromNBT(cEnchantments& a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx);
+ friend void EnchantmentSerializer::ParseFromNBT(cEnchantments & a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx);
protected:
/** Maps enchantment ID -> enchantment level */
diff --git a/src/Endianness.h b/src/Endianness.h
index ed9637fcc..a415903a5 100644
--- a/src/Endianness.h
+++ b/src/Endianness.h
@@ -21,7 +21,7 @@ inline UInt64 HostToNetwork8(const void * a_Value)
-inline UInt32 HostToNetwork4(const void* a_Value)
+inline UInt32 HostToNetwork4(const void * a_Value)
{
UInt32 buf;
memcpy( &buf, a_Value, sizeof( buf));
diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp
index 3c1fabb1b..32952100c 100644
--- a/src/Entities/ArrowEntity.cpp
+++ b/src/Entities/ArrowEntity.cpp
@@ -21,6 +21,8 @@ cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a
{
SetSpeed(a_Speed);
SetMass(0.1);
+ SetGravity(-20.0f);
+ SetAirDrag(0.2f);
SetYawFromSpeed();
SetPitchFromSpeed();
LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}",
@@ -48,6 +50,8 @@ cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) :
{
m_PickupState = psInCreative;
}
+ SetGravity(-20.0f);
+ SetAirDrag(0.01f);
}
diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp
index 6177eb32f..4ad418be4 100644
--- a/src/Entities/Boat.cpp
+++ b/src/Entities/Boat.cpp
@@ -16,7 +16,9 @@
cBoat::cBoat(double a_X, double a_Y, double a_Z) :
super(etBoat, a_X, a_Y, a_Z, 0.98, 0.7)
{
- SetMass(20.f);
+ SetMass(20.0f);
+ SetGravity(-16.0f);
+ SetAirDrag(0.05f);
SetMaxHealth(6);
SetHealth(6);
}
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index c8df6b4b1..dca44488b 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -36,6 +36,7 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
m_bHasSentNoSpeed(true),
m_bOnGround(false),
m_Gravity(-9.81f),
+ m_AirDrag(0.02f),
m_LastPos(a_X, a_Y, a_Z),
m_IsInitialized(false),
m_WorldTravellingFrom(nullptr),
@@ -246,7 +247,6 @@ void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_R
if (a_Attacker != nullptr)
{
Heading = a_Attacker->GetLookVector() * (a_Attacker->IsSprinting() ? 16 : 11);
- Heading.y = 1.6;
}
TDI.Knockback = Heading * a_KnockbackAmount;
@@ -943,6 +943,7 @@ void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
// Normal gravity
fallspeed = m_Gravity * DtSec.count();
+ NextSpeed -= NextSpeed * (m_AirDrag * 20.0f) * DtSec.count();
}
NextSpeed.y += static_cast<float>(fallspeed);
}
@@ -999,7 +1000,7 @@ void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
NextSpeed += m_WaterSpeed;
- if (NextSpeed.SqrLength() > 0.f)
+ if (NextSpeed.SqrLength() > 0.0f)
{
cTracer Tracer(GetWorld());
// Distance traced is an integer, so we round up from the distance we should go (Speed * Delta), else we will encounter collision detection failurse
@@ -1015,20 +1016,20 @@ void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
// Block hit was within our projected path
// Begin by stopping movement in the direction that we hit something. The Normal is the line perpendicular to a 2D face and in this case, stores what block face was hit through either -1 or 1.
// For example: HitNormal.y = -1 : BLOCK_FACE_YM; HitNormal.y = 1 : BLOCK_FACE_YP
- if (Tracer.HitNormal.x != 0.f)
+ if (Tracer.HitNormal.x != 0.0f)
{
- NextSpeed.x = 0.f;
+ NextSpeed.x = 0.0f;
}
- if (Tracer.HitNormal.y != 0.f)
+ if (Tracer.HitNormal.y != 0.0f)
{
- NextSpeed.y = 0.f;
+ NextSpeed.y = 0.0f;
}
- if (Tracer.HitNormal.z != 0.f)
+ if (Tracer.HitNormal.z != 0.0f)
{
- NextSpeed.z = 0.f;
+ NextSpeed.z = 0.0f;
}
- if (Tracer.HitNormal.y == 1.f) // Hit BLOCK_FACE_YP, we are on the ground
+ if (Tracer.HitNormal.y == 1.0f) // Hit BLOCK_FACE_YP, we are on the ground
{
m_bOnGround = true;
}
@@ -1308,7 +1309,8 @@ bool cEntity::DetectPortal()
if (IsPlayer())
{
- ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimOverworld); // Send a respawn packet before world is loaded/generated so the client isn't left in limbo
+ // Send a respawn packet before world is loaded / generated so the client isn't left in limbo
+ ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimOverworld);
}
return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()), false);
@@ -1401,14 +1403,25 @@ bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
return false;
}
+ // Ask the plugins if the entity is allowed to changing the world
+ if (cRoot::Get()->GetPluginManager()->CallHookEntityChangingWorld(*this, *a_World))
+ {
+ // A Plugin doesn't allow the entity to changing the world
+ return false;
+ }
+
// Remove all links to the old world
SetWorldTravellingFrom(GetWorld()); // cChunk::Tick() handles entity removal
GetWorld()->BroadcastDestroyEntity(*this);
// Queue add to new world
a_World->AddEntity(this);
+ cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD
SetWorld(a_World);
+ // Entity changed the world, call the hook
+ cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*this, *OldWorld);
+
return true;
}
@@ -1687,8 +1700,8 @@ void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude)
{
m_World->BroadcastEntityRelMove(*this, (char)DiffX, (char)DiffY, (char)DiffZ, a_Exclude);
}
- // Clients seem to store two positions, one for the velocity packet and one for the teleport/relmove packet
- // The latter is only changed with a relmove/teleport, and m_LastPos stores this position
+ // Clients seem to store two positions, one for the velocity packet and one for the teleport / relmove packet
+ // The latter is only changed with a relmove / teleport, and m_LastPos stores this position
m_LastPos = GetPosition();
}
else
@@ -1971,7 +1984,7 @@ void cEntity::SteerVehicle(float a_Forward, float a_Sideways)
{
return;
}
- if ((a_Forward != 0.f) || (a_Sideways != 0.f))
+ if ((a_Forward != 0.0f) || (a_Sideways != 0.0f))
{
m_AttachedTo->HandleSpeedFromAttachee(a_Forward, a_Sideways);
}
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
index 9bb1837f1..fecbb9bf5 100644
--- a/src/Entities/Entity.h
+++ b/src/Entities/Entity.h
@@ -270,6 +270,10 @@ public:
float GetGravity(void) const { return m_Gravity; }
void SetGravity(float a_Gravity) { m_Gravity = a_Gravity; }
+
+ float GetAirDrag(void) const { return m_AirDrag; }
+
+ void SetAirDrag(float a_AirDrag) { m_AirDrag = a_AirDrag; }
/// Sets the rotation to match the speed vector (entity goes "face-forward")
void SetYawFromSpeed(void);
@@ -470,7 +474,7 @@ protected:
static cCriticalSection m_CSCount;
static UInt32 m_EntityCount;
- /** Measured in meter/second (m/s) */
+ /** Measured in meters / second (m / s) */
Vector3d m_Speed;
/** The ID of the entity that is guaranteed to be unique within a single run of the server.
@@ -490,7 +494,7 @@ protected:
/** Stores whether head yaw has been set manually */
bool m_bDirtyHead;
- /** Stores whether our yaw/pitch/roll (body orientation) has been set manually */
+ /** Stores whether our yaw / pitch / roll (body orientation) has been set manually */
bool m_bDirtyOrientation;
/** Stores whether we have sent a Velocity packet with a speed of zero (no speed) to the client
@@ -504,6 +508,12 @@ protected:
For realistic effects, this should be negative. For spaaaaaaace, this can be zero or even positive */
float m_Gravity;
+ /** Stores the air drag that is applied to the entity every tick, measured in speed ratio per tick
+ Acts as air friction and slows down flight
+ Will be interpolated if the server tick rate varies
+ Data: http://minecraft.gamepedia.com/Entity#Motion_of_entities */
+ float m_AirDrag;
+
/** Last position sent to client via the Relative Move or Teleport packets (not Velocity)
Only updated if cEntity::BroadcastMovementUpdate() is called! */
Vector3d m_LastPos;
diff --git a/src/Entities/EntityEffect.cpp b/src/Entities/EntityEffect.cpp
index bae686b77..c8be414d4 100644
--- a/src/Entities/EntityEffect.cpp
+++ b/src/Entities/EntityEffect.cpp
@@ -102,11 +102,11 @@ int cEntityEffect::GetPotionEffectDuration(short a_ItemDamage)
// If potion is level II, half the duration. If not, stays the same
TierCoeff = (GetPotionEffectIntensity(a_ItemDamage) > 0) ? 0.5 : 1;
- // If potion is extended, multiply duration by 8/3. If not, stays the same
+ // If potion is extended, multiply duration by 8 / 3. If not, stays the same
// Extended potion if sixth lowest bit is set
ExtCoeff = (a_ItemDamage & 0x40) ? (8.0 / 3.0) : 1;
- // If potion is splash potion, multiply duration by 3/4. If not, stays the same
+ // If potion is splash potion, multiply duration by 3 / 4. If not, stays the same
SplashCoeff = IsPotionDrinkable(a_ItemDamage) ? 1 : 0.75;
// Ref.:
diff --git a/src/Entities/FallingBlock.cpp b/src/Entities/FallingBlock.cpp
index 7301a3c9d..4a165909a 100644
--- a/src/Entities/FallingBlock.cpp
+++ b/src/Entities/FallingBlock.cpp
@@ -16,6 +16,8 @@ cFallingBlock::cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_Block
m_BlockMeta(a_BlockMeta),
m_OriginalPosition(a_BlockPosition)
{
+ SetGravity(-16.0f);
+ SetAirDrag(0.02f);
}
diff --git a/src/Entities/FireChargeEntity.cpp b/src/Entities/FireChargeEntity.cpp
index aba32602f..f6c665156 100644
--- a/src/Entities/FireChargeEntity.cpp
+++ b/src/Entities/FireChargeEntity.cpp
@@ -12,6 +12,7 @@ cFireChargeEntity::cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y
{
SetSpeed(a_Speed);
SetGravity(0);
+ SetAirDrag(0);
}
diff --git a/src/Entities/FireworkEntity.cpp b/src/Entities/FireworkEntity.cpp
index 32eaf669a..89f69f113 100644
--- a/src/Entities/FireworkEntity.cpp
+++ b/src/Entities/FireworkEntity.cpp
@@ -13,6 +13,8 @@ cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, do
m_TicksToExplosion(a_Item.m_FireworkItem.m_FlightTimeInTicks),
m_FireworkItem(a_Item)
{
+ SetGravity(0);
+ SetAirDrag(0);
}
diff --git a/src/Entities/Floater.cpp b/src/Entities/Floater.cpp
index cf8dd6c6f..0c868270d 100644
--- a/src/Entities/Floater.cpp
+++ b/src/Entities/Floater.cpp
@@ -6,7 +6,7 @@
#include "Floater.h"
#include "Player.h"
#include "../ClientHandle.h"
-
+#include "Broadcaster.h"
@@ -145,12 +145,12 @@ void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
LOGD("Started producing particles for floater %i", GetUniqueID());
m_ParticlePos.Set(GetPosX() + (-4 + m_World->GetTickRandomNumber(8)), GetPosY(), GetPosZ() + (-4 + m_World->GetTickRandomNumber(8)));
- m_World->BroadcastParticleEffect("splash", (float) m_ParticlePos.x, (float) m_ParticlePos.y, (float) m_ParticlePos.z, 0, 0, 0, 0, 15);
+ m_World->GetBroadcaster().BroadcastParticleEffect("splash", static_cast<Vector3f>(m_ParticlePos), Vector3f{}, 0, 15);
}
else if (m_CountDownTime < 20)
{
m_ParticlePos = (m_ParticlePos + (GetPosition() - m_ParticlePos) / 6);
- m_World->BroadcastParticleEffect("splash", (float) m_ParticlePos.x, (float) m_ParticlePos.y, (float) m_ParticlePos.z, 0, 0, 0, 0, 15);
+ m_World->GetBroadcaster().BroadcastParticleEffect("splash", static_cast<Vector3f>(m_ParticlePos), Vector3f{}, 0, 15);
}
m_CountDownTime--;
diff --git a/src/Entities/GhastFireballEntity.cpp b/src/Entities/GhastFireballEntity.cpp
index 9e4cb387e..c64fb2a17 100644
--- a/src/Entities/GhastFireballEntity.cpp
+++ b/src/Entities/GhastFireballEntity.cpp
@@ -12,6 +12,7 @@ cGhastFireballEntity::cGhastFireballEntity(cEntity * a_Creator, double a_X, doub
{
SetSpeed(a_Speed);
SetGravity(0);
+ SetAirDrag(0);
}
diff --git a/src/Entities/HangingEntity.h b/src/Entities/HangingEntity.h
index 507502ac6..9d783006c 100644
--- a/src/Entities/HangingEntity.h
+++ b/src/Entities/HangingEntity.h
@@ -27,7 +27,10 @@ public:
eBlockFace GetFacing() const { return cHangingEntity::ProtocolFaceToBlockFace(m_Facing); }
/** Set the direction in which the entity is facing. */
- void SetFacing(eBlockFace a_Facing) { m_Facing = cHangingEntity::BlockFaceToProtocolFace(a_Facing); }
+ void SetFacing(eBlockFace a_Facing)
+ {
+ m_Facing = cHangingEntity::BlockFaceToProtocolFace(a_Facing);
+ }
// tolua_end
@@ -37,7 +40,7 @@ public:
/** Set the direction in which the entity is facing. */
void SetProtocolFacing(Byte a_Facing)
{
- ASSERT((a_Facing <= 3) && (a_Facing >= 0));
+ ASSERT(a_Facing <= 3);
m_Facing = a_Facing;
}
diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp
index ee10cf6b3..b80759d24 100644
--- a/src/Entities/Minecart.cpp
+++ b/src/Entities/Minecart.cpp
@@ -92,7 +92,9 @@ cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) :
m_DetectorRailPosition(0, 0, 0),
m_bIsOnDetectorRail(false)
{
- SetMass(20.f);
+ SetMass(20.0f);
+ SetGravity(-16.0f);
+ SetAirDrag(0.05f);
SetMaxHealth(6);
SetHealth(6);
SetWidth(1);
@@ -903,7 +905,7 @@ bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
}
/* Check to which side the minecart is to be pushed.
- Let's consider a z-x-coordinate system where the minecart is the center (0/0).
+ Let's consider a z-x-coordinate system where the minecart is the center (0, 0).
The minecart moves along the line x = -z, the perpendicular line to this is x = z.
In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */
if (
@@ -952,7 +954,7 @@ bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
}
/* Check to which side the minecart is to be pushed.
- Let's consider a z-x-coordinate system where the minecart is the center (0/0).
+ Let's consider a z-x-coordinate system where the minecart is the center (0, 0).
The minecart moves along the line x = z, the perpendicular line to this is x = -z.
In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */
if (
diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h
index 898776e71..05eaf16e9 100644
--- a/src/Entities/Minecart.h
+++ b/src/Entities/Minecart.h
@@ -29,7 +29,7 @@ public:
enum ePayload
{
mpNone = 0, // Empty minecart, ridable by player or mobs
- mpChest = 1, // Minecart-with-chest, can store a grid of 3*8 items
+ mpChest = 1, // Minecart-with-chest, can store a grid of 3 * 8 items
mpFurnace = 2, // Minecart-with-furnace, can be powered
mpTNT = 3, // Minecart-with-TNT, can be blown up with activator rail
mpHopper = 5, // Minecart-with-hopper, can be hopper
@@ -54,8 +54,7 @@ protected:
cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z);
/** Handles physics on normal rails
- For each tick, slow down on flat rails, speed up or slow down on ascending/descending rails (depending on direction), and turn on curved rails
- */
+ For each tick, slow down on flat rails, speed up or slow down on ascending / descending rails (depending on direction), and turn on curved rails. */
void HandleRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt);
/** Handles powered rail physics
@@ -136,7 +135,7 @@ protected:
virtual void Destroyed() override;
// cItemGrid::cListener overrides:
- virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum)
+ virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) override
{
UNUSED(a_SlotNum);
ASSERT(a_Grid == &m_Contents);
diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp
index baf8a2f3b..fcb686e28 100644
--- a/src/Entities/Pawn.cpp
+++ b/src/Entities/Pawn.cpp
@@ -13,6 +13,8 @@ cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) :
super(a_EntityType, 0, 0, 0, a_Width, a_Height)
, m_EntityEffects(tEffectMap())
{
+ SetGravity(-32.0f);
+ SetAirDrag(0.02f);
}
diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp
index 9f2609894..f2f76dbf9 100644
--- a/src/Entities/Pickup.cpp
+++ b/src/Entities/Pickup.cpp
@@ -91,7 +91,8 @@ cPickup::cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_It
, m_bCollected(false)
, m_bIsPlayerCreated(IsPlayerCreated)
{
- SetGravity(-10.5f);
+ SetGravity(-16.0f);
+ SetAirDrag(0.02f);
SetMaxHealth(5);
SetHealth(5);
SetSpeed(a_SpeedX, a_SpeedY, a_SpeedZ);
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index c89e7b87c..e3e3fac4f 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -94,7 +94,7 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) :
SetMaxHealth(MAX_HEALTH);
m_Health = MAX_HEALTH;
-
+
m_LastPlayerListTime = std::chrono::steady_clock::now();
m_PlayerName = a_PlayerName;
@@ -106,7 +106,7 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) :
SetPosY(World->GetSpawnY());
SetPosZ(World->GetSpawnZ());
SetBedPos(Vector3i(static_cast<int>(World->GetSpawnX()), static_cast<int>(World->GetSpawnY()), static_cast<int>(World->GetSpawnZ())));
-
+
LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}",
a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ()
);
@@ -128,7 +128,14 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) :
m_IsFlying = true;
}
}
-
+
+ if (m_GameMode == gmSpectator) // If player is reconnecting to the server in spectator mode
+ {
+ m_CanFly = true;
+ m_IsFlying = true;
+ m_bVisible = false;
+ }
+
cRoot::Get()->GetServer()->PlayerCreated(this);
}
@@ -145,17 +152,17 @@ cPlayer::~cPlayer(void)
}
LOGD("Deleting cPlayer \"%s\" at %p, ID %d", GetName().c_str(), this, GetUniqueID());
-
+
// Notify the server that the player is being destroyed
cRoot::Get()->GetServer()->PlayerDestroying(this);
SaveToDisk();
m_ClientHandle = nullptr;
-
+
delete m_InventoryWindow;
m_InventoryWindow = nullptr;
-
+
LOGD("Player %p deleted", this);
}
@@ -201,7 +208,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
m_ClientHandle = nullptr;
return;
}
-
+
if (!m_ClientHandle->IsPlaying())
{
// We're not yet in the game, ignore everything
@@ -210,21 +217,21 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
}
m_Stats.AddValue(statMinutesPlayed, 1);
-
+
if (!a_Chunk.IsValid())
{
// This may happen if the cPlayer is created before the chunks have the chance of being loaded / generated (#83)
return;
}
-
+
super::Tick(a_Dt, a_Chunk);
-
+
// Handle charging the bow:
if (m_IsChargingBow)
{
m_BowCharge += 1;
}
-
+
// Handle updating experience
if (m_bDirtyExperience)
{
@@ -236,7 +243,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
// Apply food exhaustion from movement:
ApplyFoodExhaustionFromMovement();
-
+
if (cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this, m_LastPos, GetPosition()))
{
CanMove = false;
@@ -257,10 +264,10 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
FinishEating();
}
-
+
HandleFood();
}
-
+
if (m_IsFishing)
{
HandleFloater();
@@ -355,7 +362,7 @@ float cPlayer::GetXpPercentage()
int currentLevel_XpBase = XpForLevel(currentLevel);
return static_cast<float>(m_CurrentXp - currentLevel_XpBase) /
- static_cast<float>(XpForLevel(1+currentLevel) - currentLevel_XpBase);
+ static_cast<float>(XpForLevel(1 + currentLevel) - currentLevel_XpBase);
}
@@ -364,7 +371,7 @@ float cPlayer::GetXpPercentage()
bool cPlayer::SetCurrentExperience(int a_CurrentXp)
{
- if (!(a_CurrentXp >= 0) || (a_CurrentXp > (std::numeric_limits<int>().max() - m_LifetimeTotalXp)))
+ if (!(a_CurrentXp >= 0) || (a_CurrentXp > (std::numeric_limits<int>::max() - m_LifetimeTotalXp)))
{
LOGWARNING("Tried to update experiece with an invalid Xp value: %d", a_CurrentXp);
return false; // oops, they gave us a dodgey number
@@ -403,7 +410,7 @@ int cPlayer::DeltaExperience(int a_Xp_delta)
m_LifetimeTotalXp += a_Xp_delta;
}
- LOGD("Player \"%s\" gained/lost %d experience, total is now: %d", GetName().c_str(), a_Xp_delta, m_CurrentXp);
+ LOGD("Player \"%s\" gained / lost %d experience, total is now: %d", GetName().c_str(), a_Xp_delta, m_CurrentXp);
// Set experience to be updated
m_bDirtyExperience = true;
@@ -460,7 +467,7 @@ void cPlayer::SetTouchGround(bool a_bTouchGround)
{
return;
}
-
+
m_bTouchGround = a_bTouchGround;
if (!m_bTouchGround)
@@ -509,7 +516,7 @@ void cPlayer::SetTouchGround(bool a_bTouchGround)
{
// cPlayer makes sure damage isn't applied in creative, no need to check here
TakeDamage(dtFalling, nullptr, Damage, Damage, 0);
-
+
// Fall particles
GetWorld()->BroadcastSoundParticleEffect(2006, POSX_TOINT, static_cast<int>(GetPosY()) - 1, POSZ_TOINT, Damage /* Used as particle effect speed modifier */);
}
@@ -541,7 +548,7 @@ void cPlayer::SetFoodLevel(int a_FoodLevel)
m_FoodSaturationLevel = 5.0;
return;
}
-
+
m_FoodLevel = FoodLevel;
SendHealth();
}
@@ -609,7 +616,7 @@ void cPlayer::StartEating(void)
{
// Set the timer:
m_EatingFinishTick = m_World->GetWorldAge() + EATING_TICKS;
-
+
// Send the packets:
m_World->BroadcastEntityAnimation(*this, 3);
m_World->BroadcastEntityMetadata(*this);
@@ -623,7 +630,7 @@ void cPlayer::FinishEating(void)
{
// Reset the timer:
m_EatingFinishTick = -1;
-
+
// Send the packets:
m_ClientHandle->SendEntityStatus(*this, esPlayerEatingAccepted);
m_World->BroadcastEntityMetadata(*this);
@@ -757,7 +764,7 @@ void cPlayer::SetSprintingMaxSpeed(double a_Speed)
void cPlayer::SetFlyingMaxSpeed(double a_Speed)
{
m_FlyingMaxSpeed = a_Speed;
-
+
// Update the flying speed, always:
m_ClientHandle->SendPlayerAbilities();
}
@@ -769,7 +776,7 @@ void cPlayer::SetFlyingMaxSpeed(double a_Speed)
void cPlayer::SetCrouch(bool a_IsCrouched)
{
// Set the crouch status, broadcast to all visible players
-
+
if (a_IsCrouched == m_IsCrouched)
{
// No change
@@ -790,7 +797,7 @@ void cPlayer::SetSprint(bool a_IsSprinting)
// No change
return;
}
-
+
m_IsSprinting = a_IsSprinting;
m_ClientHandle->SendPlayerMaxSpeed();
}
@@ -876,7 +883,7 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
}
}
}
-
+
if (super::DoTakeDamage(a_TDI))
{
// Any kind of damage adds food exhaustion
@@ -1005,7 +1012,7 @@ void cPlayer::Respawn(void)
m_Health = GetMaxHealth();
SetInvulnerableTicks(20);
-
+
// Reset food level:
m_FoodLevel = MAX_FOOD_LEVEL;
m_FoodSaturationLevel = 5.0;
@@ -1017,7 +1024,7 @@ void cPlayer::Respawn(void)
// ToDo: send score to client? How?
m_ClientHandle->SendRespawn(GetWorld()->GetDimension(), true);
-
+
// Extinguish the fire:
StopBurning();
@@ -1151,7 +1158,7 @@ void cPlayer::CloseWindow(bool a_CanRefuse)
m_CurrentWindow = m_InventoryWindow;
return;
}
-
+
if (m_CurrentWindow->ClosedByPlayer(*this, a_CanRefuse) || !a_CanRefuse)
{
// Close accepted, go back to inventory window (the default):
@@ -1189,21 +1196,17 @@ void cPlayer::SetGameMode(eGameMode a_GameMode)
LOGWARNING("%s: Setting invalid gamemode: %d", GetName().c_str(), a_GameMode);
return;
}
-
+
if (m_GameMode == a_GameMode)
{
// Gamemode already set
return;
}
-
+
m_GameMode = a_GameMode;
m_ClientHandle->SendGameMode(a_GameMode);
- if (!(IsGameModeCreative() || IsGameModeSpectator()))
- {
- SetFlying(false);
- SetCanFly(false);
- }
+ SetCapabilities();
m_World->BroadcastPlayerListUpdateGameMode(*this);
}
@@ -1215,6 +1218,30 @@ void cPlayer::SetGameMode(eGameMode a_GameMode)
void cPlayer::LoginSetGameMode( eGameMode a_GameMode)
{
m_GameMode = a_GameMode;
+
+ SetCapabilities();
+}
+
+
+
+
+
+void cPlayer::SetCapabilities()
+{
+ if (!IsGameModeCreative() || IsGameModeSpectator())
+ {
+ SetFlying(false);
+ SetCanFly(false);
+ }
+
+ if (IsGameModeSpectator())
+ {
+ SetVisible(false);
+ }
+ else
+ {
+ SetVisible(true);
+ }
}
@@ -1306,12 +1333,12 @@ void cPlayer::SendRotation(double a_YawDegrees, double a_PitchDegrees)
Vector3d cPlayer::GetThrowStartPos(void) const
{
Vector3d res = GetEyePosition();
-
+
// Adjust the position to be just outside the player's bounding box:
res.x += 0.16 * cos(GetPitch());
res.y += -0.1;
res.z += 0.16 * sin(GetPitch());
-
+
return res;
}
@@ -1323,9 +1350,9 @@ Vector3d cPlayer::GetThrowSpeed(double a_SpeedCoeff) const
{
Vector3d res = GetLookVector();
res.Normalize();
-
+
// TODO: Add a slight random change (+-0.0075 in each direction)
-
+
return res * a_SpeedCoeff;
}
@@ -1370,13 +1397,13 @@ void cPlayer::MoveTo( const Vector3d & a_NewPos)
}
return;
}
-
+
// TODO: should do some checks to see if player is not moving through terrain
// TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too
Vector3d DeltaPos = a_NewPos - GetPosition();
UpdateMovementStats(DeltaPos);
-
+
SetPosition( a_NewPos);
SetStance(a_NewPos.y + 1.62);
}
@@ -1411,17 +1438,26 @@ bool cPlayer::HasPermission(const AString & a_Permission)
// Empty permission request is always granted
return true;
}
-
+
AStringVector Split = StringSplit(a_Permission, ".");
+ // Iterate over all restrictions; if any matches, then return failure:
+ for (auto & Restriction: m_SplitRestrictions)
+ {
+ if (PermissionMatches(Split, Restriction))
+ {
+ return false;
+ }
+ } // for Restriction - m_SplitRestrictions[]
+
// Iterate over all granted permissions; if any matches, then return success:
- for (AStringVectorVector::const_iterator itr = m_SplitPermissions.begin(), end = m_SplitPermissions.end(); itr != end; ++itr)
+ for (auto & Permission: m_SplitPermissions)
{
- if (PermissionMatches(Split, *itr))
+ if (PermissionMatches(Split, Permission))
{
return true;
}
- } // for itr - m_SplitPermissions[]
+ } // for Permission - m_SplitPermissions[]
// No granted permission matches
return false;
@@ -1574,7 +1610,7 @@ void cPlayer::TossItems(const cItems & a_Items)
{
return;
}
-
+
m_Stats.AddValue(statItemsDropped, (StatValue)a_Items.Size());
double vX = 0, vY = 0, vZ = 0;
@@ -1596,19 +1632,30 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
// Don't move to same world
return false;
}
-
+
+ // Ask the plugins if the player is allowed to changing the world
+ if (cRoot::Get()->GetPluginManager()->CallHookEntityChangingWorld(*this, *a_World))
+ {
+ // A Plugin doesn't allow the player to changing the world
+ return false;
+ }
+
// Send the respawn packet:
if (a_ShouldSendRespawn && (m_ClientHandle != nullptr))
{
m_ClientHandle->SendRespawn(a_World->GetDimension());
}
+ // Broadcast for other people that the player is gone.
+ GetWorld()->BroadcastDestroyEntity(*this);
+
// Remove player from the old world
SetWorldTravellingFrom(GetWorld()); // cChunk handles entity removal
GetWorld()->RemovePlayer(this, false);
// Queue adding player to the new world, including all the necessary adjustments to the object
a_World->AddPlayer(this);
+ cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD
SetWorld(a_World); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value
// Update the view distance.
@@ -1619,7 +1666,13 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
{
m_ClientHandle->SendWeather(a_World->GetWeather());
}
-
+
+ // Broadcast the player into the new world.
+ a_World->BroadcastSpawnEntity(*this);
+
+ // Player changed the world, call the hook
+ cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*this, *OldWorld);
+
return true;
}
@@ -1636,7 +1689,7 @@ bool cPlayer::LoadFromDisk(cWorldPtr & a_World)
{
return true;
}
-
+
// Load from the offline UUID file, if allowed:
AString OfflineUUID = cClientHandle::GenerateOfflineUUID(GetName());
const char * OfflineUsage = " (unused)";
@@ -1648,7 +1701,7 @@ bool cPlayer::LoadFromDisk(cWorldPtr & a_World)
return true;
}
}
-
+
// Load from the old-style name-based file, if allowed:
if (cRoot::Get()->GetServer()->ShouldLoadNamedPlayerData())
{
@@ -1663,7 +1716,7 @@ bool cPlayer::LoadFromDisk(cWorldPtr & a_World)
return true;
}
}
-
+
// None of the files loaded successfully
LOG("Player data file not found for %s (%s, offline %s%s), will be reset to defaults.",
GetName().c_str(), m_UUID.c_str(), OfflineUUID.c_str(), OfflineUsage
@@ -1740,7 +1793,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World)
{
m_CanFly = true;
}
-
+
m_Inventory.LoadFromJson(root["inventory"]);
cEnderChestEntity::LoadFromJson(root["enderchestinventory"], m_EnderChestContents);
@@ -1756,14 +1809,14 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World)
m_LastBedPos.z = root.get("SpawnZ", a_World->GetSpawnZ()).asInt();
// Load the player stats.
- // We use the default world name (like bukkit) because stats are shared between dimensions/worlds.
+ // We use the default world name (like bukkit) because stats are shared between dimensions / worlds.
cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats);
StatSerializer.Load();
-
+
LOGD("Player %s was read from file \"%s\", spawning at {%.2f, %.2f, %.2f} in world \"%s\"",
GetName().c_str(), a_FileName.c_str(), GetPosX(), GetPosY(), GetPosZ(), a_World->GetName().c_str()
);
-
+
return true;
}
@@ -1853,7 +1906,7 @@ bool cPlayer::SaveToDisk()
}
// Save the player stats.
- // We use the default world name (like bukkit) because stats are shared between dimensions/worlds.
+ // We use the default world name (like bukkit) because stats are shared between dimensions / worlds.
cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats);
if (!StatSerializer.Save())
{
@@ -2061,7 +2114,7 @@ void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos)
}
else
{
- if (Value >= 25) // Ignore small/slow movement
+ if (Value >= 25) // Ignore small / slow movement
{
m_Stats.AddValue(statDistFlown, Value);
}
@@ -2119,7 +2172,7 @@ void cPlayer::ApplyFoodExhaustionFromMovement()
{
return;
}
-
+
// Calculate the distance travelled, update the last pos:
Vector3d Movement(GetPosition() - m_LastPos);
Movement.y = 0; // Only take XZ movement into account
@@ -2163,15 +2216,24 @@ void cPlayer::LoadRank(void)
RankMgr->UpdatePlayerName(m_UUID, m_PlayerName);
}
m_Permissions = RankMgr->GetPlayerPermissions(m_UUID);
+ m_Restrictions = RankMgr->GetPlayerRestrictions(m_UUID);
RankMgr->GetRankVisuals(m_Rank, m_MsgPrefix, m_MsgSuffix, m_MsgNameColorCode);
// Break up the individual permissions on each dot, into m_SplitPermissions:
m_SplitPermissions.clear();
m_SplitPermissions.reserve(m_Permissions.size());
- for (AStringVector::const_iterator itr = m_Permissions.begin(), end = m_Permissions.end(); itr != end; ++itr)
+ for (auto & Permission: m_Permissions)
+ {
+ m_SplitPermissions.push_back(StringSplit(Permission, "."));
+ } // for Permission - m_Permissions[]
+
+ // Break up the individual restrictions on each dot, into m_SplitRestrictions:
+ m_SplitRestrictions.clear();
+ m_SplitRestrictions.reserve(m_Restrictions.size());
+ for (auto & Restriction: m_Restrictions)
{
- m_SplitPermissions.push_back(StringSplit(*itr, "."));
- } // for itr - m_Permissions[]
+ m_SplitRestrictions.push_back(StringSplit(Restriction, "."));
+ } // for itr - m_Restrictions[]
}
@@ -2313,7 +2375,7 @@ AString cPlayer::GetUUIDFileName(const AString & a_UUID)
{
AString UUID = cMojangAPI::MakeUUIDDashed(a_UUID);
ASSERT(UUID.length() == 36);
-
+
AString res("players/");
res.append(UUID, 0, 2);
res.push_back('/');
@@ -2321,7 +2383,3 @@ AString cPlayer::GetUUIDFileName(const AString & a_UUID)
res.append(".json");
return res;
}
-
-
-
-
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index 3dae58dc1..a84fdd0c7 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -26,42 +26,42 @@ class cPlayer :
public cPawn
{
typedef cPawn super;
-
+
public:
static const int MAX_HEALTH;
-
+
static const int MAX_FOOD_LEVEL;
-
+
/** Number of ticks it takes to eat an item */
static const int EATING_TICKS;
-
+
// tolua_end
-
+
CLASS_PROTODEF(cPlayer)
-
+
cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName);
-
+
virtual ~cPlayer();
virtual void SpawnOn(cClientHandle & a_Client) override;
-
+
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk &) override { UNUSED(a_Dt); }
/** Returns the curently equipped weapon; empty item if none */
virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); }
-
+
/** Returns the currently equipped helmet; empty item if none */
virtual cItem GetEquippedHelmet(void) const override { return m_Inventory.GetEquippedHelmet(); }
-
+
/** Returns the currently equipped chestplate; empty item if none */
virtual cItem GetEquippedChestplate(void) const override { return m_Inventory.GetEquippedChestplate(); }
/** Returns the currently equipped leggings; empty item if none */
virtual cItem GetEquippedLeggings(void) const override { return m_Inventory.GetEquippedLeggings(); }
-
+
/** Returns the currently equipped boots; empty item if none */
virtual cItem GetEquippedBoots(void) const override { return m_Inventory.GetEquippedBoots(); }
@@ -104,16 +104,16 @@ public:
static int CalcLevelFromXp(int a_CurrentXp);
// tolua_end
-
+
/** Starts charging the equipped bow */
void StartChargingBow(void);
-
+
/** Finishes charging the current bow. Returns the number of ticks for which the bow has been charged */
int FinishChargingBow(void);
-
+
/** Cancels the current bow charging */
void CancelChargingBow(void);
-
+
/** Returns true if the player is currently charging the bow */
bool IsChargingBow(void) const { return m_IsChargingBow; }
@@ -122,13 +122,13 @@ public:
double GetEyeHeight(void) const; // tolua_export
Vector3d GetEyePosition(void) const; // tolua_export
virtual bool IsOnGround(void) const override { return m_bTouchGround; }
- inline double GetStance(void) const { return GetPosY() + 1.62; } // tolua_export // TODO: Proper stance when crouching etc.
+ inline double GetStance(void) const { return m_Stance; } // tolua_export
inline cInventory & GetInventory(void) { return m_Inventory; } // tolua_export
inline const cInventory & GetInventory(void) const { return m_Inventory; }
/** Gets the contents of the player's associated enderchest */
cItemGrid & GetEnderChestContents(void) { return m_EnderChestContents; }
-
+
inline const cItem & GetEquippedItem(void) const { return GetInventory().GetEquippedItem(); } // tolua_export
/** Returns whether the player is climbing (ladders, vines etc.) */
@@ -137,43 +137,49 @@ public:
virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) override;
// tolua_begin
-
+
/** Sends the "look" packet to the player, forcing them to set their rotation to the specified values.
a_YawDegrees is clipped to range [-180, +180),
a_PitchDegrees is clipped to range [-180, +180) but the client only uses [-90, +90]
*/
void SendRotation(double a_YawDegrees, double a_PitchDegrees);
-
+
/** Returns the position where projectiles thrown by this player should start, player eye position + adjustment */
Vector3d GetThrowStartPos(void) const;
-
+
/** Returns the initial speed vector of a throw, with a 3D length of a_SpeedCoeff. */
Vector3d GetThrowSpeed(double a_SpeedCoeff) const;
-
+
/** Returns the current gamemode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable */
eGameMode GetGameMode(void) const { return m_GameMode; }
-
+
/** Returns the current effective gamemode (inherited gamemode is resolved before returning) */
eGameMode GetEffectiveGameMode(void) const { return (m_GameMode == gmNotSet) ? m_World->GetGameMode() : m_GameMode; }
-
+
/** Sets the gamemode for the player.
The gamemode may be gmNotSet, in that case the player inherits the world's gamemode.
Updates the gamemode on the client (sends the packet)
*/
void SetGameMode(eGameMode a_GameMode);
+ // Sets the current gamemode, doesn't check validity, doesn't send update packets to client
+ void LoginSetGameMode(eGameMode a_GameMode);
+
+ // Updates player's capabilities - flying, visibility, etc. from their gamemode.
+ void SetCapabilities();
+
/** Returns true if the player is in Creative mode, either explicitly, or by inheriting from current world */
bool IsGameModeCreative(void) const;
-
+
/** Returns true if the player is in Survival mode, either explicitly, or by inheriting from current world */
bool IsGameModeSurvival(void) const;
-
+
/** Returns true if the player is in Adventure mode, either explicitly, or by inheriting from current world */
bool IsGameModeAdventure(void) const;
-
+
/** Returns true if the player is in Spectator mode, either explicitly, or by inheriting from current world */
bool IsGameModeSpectator(void) const;
-
+
AString GetIP(void) const { return m_IP; } // tolua_export
/** Returns the associated team, nullptr if none */
@@ -195,11 +201,8 @@ public:
If the achievement has been already awarded to the player, this method will just increment the stat counter.
Returns the _new_ stat value. (0 = Could not award achievement) */
unsigned int AwardAchievement(const eStatistic a_Ach);
-
+
void SetIP(const AString & a_IP);
-
- // Sets the current gamemode, doesn't check validity, doesn't send update packets to client
- void LoginSetGameMode(eGameMode a_GameMode);
/** Forces the player to move in the given direction.
@deprecated Use SetSpeed instead. */
@@ -210,15 +213,15 @@ public:
cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export
const cWindow * GetWindow(void) const { return m_CurrentWindow; }
-
+
/** Opens the specified window; closes the current one first using CloseWindow() */
void OpenWindow(cWindow * a_Window); // Exported in ManualBindings.cpp
-
+
// tolua_begin
-
+
/** Closes the current window, resets current window to m_InventoryWindow. A plugin may refuse the closing if a_CanRefuse is true */
void CloseWindow(bool a_CanRefuse = true);
-
+
/** Closes the current window if it matches the specified ID, resets current window to m_InventoryWindow */
void CloseWindowIfID(char a_WindowID, bool a_CanRefuse = true);
@@ -243,7 +246,7 @@ public:
const AString & GetName(void) const { return m_PlayerName; }
void SetName(const AString & a_Name) { m_PlayerName = a_Name; }
-
+
// tolua_end
bool HasPermission(const AString & a_Permission); // tolua_export
@@ -254,10 +257,13 @@ public:
static bool PermissionMatches(const AStringVector & a_Permission, const AStringVector & a_Template); // Exported in ManualBindings with AString params
/** Returns all the permissions that the player has assigned to them. */
- const AStringVector & GetPermissions(void) { return m_Permissions; } // Exported in ManualBindings.cpp
+ const AStringVector & GetPermissions(void) const { return m_Permissions; } // Exported in ManualBindings.cpp
+
+ /** Returns all the restrictions that the player has assigned to them. */
+ const AStringVector & GetRestrictions(void) const { return m_Restrictions; } // Exported in ManualBindings.cpp
// tolua_begin
-
+
/** Returns the full color code to use for this player, based on their rank.
The returned value either is empty, or includes the cChatColor::Delimiter. */
AString GetColor(void) const;
@@ -276,15 +282,15 @@ public:
/** Heals the player by the specified amount of HPs (positive only); sends health update */
virtual void Heal(int a_Health) override;
-
+
int GetFoodLevel (void) const { return m_FoodLevel; }
double GetFoodSaturationLevel (void) const { return m_FoodSaturationLevel; }
int GetFoodTickTimer (void) const { return m_FoodTickTimer; }
double GetFoodExhaustionLevel (void) const { return m_FoodExhaustionLevel; }
-
+
/** Returns true if the player is satiated, i. e. their foodlevel is at the max and they cannot eat anymore */
bool IsSatiated(void) const { return (m_FoodLevel >= MAX_FOOD_LEVEL); }
-
+
void SetFoodLevel (int a_FoodLevel);
void SetFoodSaturationLevel (double a_FoodSaturationLevel);
void SetFoodTickTimer (int a_FoodTickTimer);
@@ -295,10 +301,10 @@ public:
/** Adds the specified exhaustion to m_FoodExhaustion. Expects only positive values. */
void AddFoodExhaustion(double a_Exhaustion);
-
+
/** Returns true if the player is currently in the process of eating the currently equipped item */
bool IsEating(void) const { return (m_EatingFinishTick >= 0); }
-
+
/** Returns true if the player is currently flying. */
bool IsFlying(void) const { return m_IsFlying; }
@@ -326,16 +332,16 @@ public:
GetWorld()->BroadcastEntityAnimation(*this, 2);
}
}
-
+
/** Starts eating the currently equipped item. Resets the eating timer and sends the proper animation packet */
void StartEating(void);
-
+
/** Finishes eating the currently equipped item. Consumes the item, updates health and broadcasts the packets */
void FinishEating(void);
-
+
/** Aborts the current eating operation */
void AbortEating(void);
-
+
virtual void KilledBy(TakeDamageInfo & a_TDI) override;
virtual void Killed(cEntity * a_Victim) override;
@@ -353,69 +359,69 @@ public:
bool SaveToDisk(void);
typedef cWorld * cWorldPtr;
-
+
/** Loads the player data from the disk file
Sets a_World to the world where the player will spawn, based on the stored world name or the default world by calling LoadFromFile()
Returns true on success, false on failure
*/
bool LoadFromDisk(cWorldPtr & a_World);
-
+
/** Loads the player data from the specified file
Sets a_World to the world where the player will spawn, based on the stored world name or the default world
Returns true on success, false on failure
*/
bool LoadFromFile(const AString & a_FileName, cWorldPtr & a_World);
-
+
const AString & GetLoadedWorldName() { return m_LoadedWorldName; }
void UseEquippedItem(int a_Amount = 1);
-
+
void SendHealth(void);
void SendExperience(void);
-
+
/** In UI windows, get the item that the player is dragging */
cItem & GetDraggingItem(void) {return m_DraggingItem; }
-
+
// In UI windows, when inventory-painting:
/** Clears the list of slots that are being inventory-painted. To be used by cWindow only */
void ClearInventoryPaintSlots(void);
-
+
/** Adds a slot to the list for inventory painting. To be used by cWindow only */
void AddInventoryPaintSlot(int a_SlotNum);
-
+
/** Returns the list of slots currently stored for inventory painting. To be used by cWindow only */
const cSlotNums & GetInventoryPaintSlots(void) const;
-
+
// tolua_begin
-
+
/** Returns the current relative maximum speed (takes current sprinting / flying state into account) */
double GetMaxSpeed(void) const;
-
+
/** Gets the normal relative maximum speed */
double GetNormalMaxSpeed(void) const { return m_NormalMaxSpeed; }
-
+
/** Gets the sprinting relative maximum speed */
double GetSprintingMaxSpeed(void) const { return m_SprintingMaxSpeed; }
-
+
/** Gets the flying relative maximum speed */
double GetFlyingMaxSpeed(void) const { return m_FlyingMaxSpeed; }
-
+
/** Sets the normal relative maximum speed. Sends the update to player, if needed. */
void SetNormalMaxSpeed(double a_Speed);
-
+
/** Sets the sprinting relative maximum speed. Sends the update to player, if needed. */
void SetSprintingMaxSpeed(double a_Speed);
-
+
/** Sets the flying relative maximum speed. Sends the update to player, if needed. */
void SetFlyingMaxSpeed(double a_Speed);
-
+
/** Sets the crouch status, broadcasts to all visible players */
void SetCrouch(bool a_IsCrouched);
-
+
/** Starts or stops sprinting, sends the max speed update to the client, if needed */
void SetSprint(bool a_IsSprinting);
-
+
/** Flags the player as flying */
void SetFlying(bool a_IsFlying);
@@ -439,17 +445,17 @@ public:
/** Sets the player's bed (home) position */
void SetBedPos(const Vector3i & a_Pos) { m_LastBedPos = a_Pos; }
-
+
// tolua_end
/** Update movement-related statistics. */
void UpdateMovementStats(const Vector3d & a_DeltaPos);
-
+
// tolua_begin
/** Returns wheter the player can fly or not. */
virtual bool CanFly(void) const { return m_CanFly; }
-
+
/** Returns the UUID (short format) that has been read from the client, or empty string if not available. */
const AString & GetUUID(void) const { return m_UUID; }
@@ -480,16 +486,16 @@ public:
bool PlaceBlocks(const sSetBlockVector & a_Blocks);
// cEntity overrides:
- virtual bool IsCrouched (void) const { return m_IsCrouched; }
- virtual bool IsSprinting(void) const { return m_IsSprinting; }
- virtual bool IsRclking (void) const { return IsEating() || IsChargingBow(); }
+ virtual bool IsCrouched (void) const override { return m_IsCrouched; }
+ virtual bool IsSprinting(void) const override { return m_IsSprinting; }
+ virtual bool IsRclking (void) const override { return IsEating() || IsChargingBow(); }
- virtual void Detach(void);
+ virtual void Detach(void) override;
/** Called by cClientHandle when the client is being destroyed.
The player removes its m_ClientHandle ownership so that the ClientHandle gets deleted. */
void RemoveClientHandle(void);
-
+
protected:
typedef std::vector<std::vector<AString> > AStringVectorVector;
@@ -500,10 +506,18 @@ protected:
/** All the permissions that this player has, based on their rank. */
AStringVector m_Permissions;
+ /** All the restrictions that this player has, based on their rank. */
+ AStringVector m_Restrictions;
+
/** All the permissions that this player has, based on their rank, split into individual dot-delimited parts.
This is used mainly by the HasPermission() function to optimize the lookup. */
AStringVectorVector m_SplitPermissions;
+ /** All the restrictions that this player has, based on their rank, split into individual dot-delimited parts.
+ This is used mainly by the HasPermission() function to optimize the lookup. */
+ AStringVectorVector m_SplitRestrictions;
+
+
// Message visuals:
AString m_MsgPrefix, m_MsgSuffix;
AString m_MsgNameColorCode;
@@ -524,16 +538,16 @@ protected:
// Food-related variables:
/** Represents the food bar, one point equals half a "drumstick" */
int m_FoodLevel;
-
+
/** "Overcharge" for the m_FoodLevel; is depleted before m_FoodLevel */
double m_FoodSaturationLevel;
-
+
/** Count-up to the healing or damaging action, based on m_FoodLevel */
int m_FoodTickTimer;
-
+
/** A "buffer" which adds up hunger before it is substracted from m_FoodSaturationLevel or m_FoodLevel. Each action adds a little */
double m_FoodExhaustionLevel;
-
+
float m_LastJumpHeight;
float m_LastGroundHeight;
bool m_bTouchGround;
@@ -553,31 +567,31 @@ protected:
eGameMode m_GameMode;
AString m_IP;
-
+
/** The item being dragged by the cursor while in a UI window */
cItem m_DraggingItem;
std::chrono::steady_clock::time_point m_LastPlayerListTime;
cClientHandlePtr m_ClientHandle;
-
+
cSlotNums m_InventoryPaintSlots;
-
+
/** Max speed, relative to the game default.
1 means regular speed, 2 means twice as fast, 0.5 means half-speed.
Default value is 1. */
double m_NormalMaxSpeed;
-
+
/** Max speed, relative to the game default max speed, when sprinting.
1 means regular speed, 2 means twice as fast, 0.5 means half-speed.
Default value is 1.3. */
double m_SprintingMaxSpeed;
-
+
/** Max speed, relative to the game default flying max speed, when flying.
1 means regular speed, 2 means twice as fast, 0.5 means half-speed.
Default value is 1. */
double m_FlyingMaxSpeed;
-
+
bool m_IsCrouched;
bool m_IsSprinting;
bool m_IsFlying;
@@ -618,7 +632,7 @@ protected:
Will not apply food penalties if found to be true; will set to false after processing
*/
bool m_bIsTeleporting;
-
+
/** The short UUID (no dashes) of the player, as read from the ClientHandle.
If no ClientHandle is given, the UUID is initialized to empty. */
AString m_UUID;
@@ -631,14 +645,14 @@ protected:
void ResolvePermissions(void);
void ResolveGroups(void);
- virtual void Destroyed(void);
+ virtual void Destroyed(void) override;
- /** Filters out damage for creative mode/friendly fire */
+ /** Filters out damage for creative mode / friendly fire */
virtual bool DoTakeDamage(TakeDamageInfo & TDI) override;
/** Stops players from burning in creative mode */
virtual void TickBurning(cChunk & a_Chunk) override;
-
+
/** Called in each tick to handle food-related processing */
void HandleFood(void);
@@ -655,7 +669,3 @@ protected:
This can be used both for online and offline UUIDs. */
AString GetUUIDFileName(const AString & a_UUID);
} ; // tolua_export
-
-
-
-
diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp
index 4684e3e09..05b7669cd 100644
--- a/src/Entities/ProjectileEntity.cpp
+++ b/src/Entities/ProjectileEntity.cpp
@@ -227,6 +227,8 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a
),
m_IsInGround(false)
{
+ SetGravity(-12.0f);
+ SetAirDrag(0.01f);
}
@@ -242,6 +244,8 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Ve
SetSpeed(a_Speed);
SetYawFromSpeed();
SetPitchFromSpeed();
+ SetGravity(-12.0f);
+ SetAirDrag(0.01f);
}
@@ -349,9 +353,11 @@ void cProjectileEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a
return;
}
- const Vector3d PerTickSpeed = GetSpeed() / 20;
+ auto DtSec = std::chrono::duration_cast<std::chrono::duration<double>>(a_Dt);
+
+ const Vector3d DeltaSpeed = GetSpeed() * DtSec.count();
const Vector3d Pos = GetPosition();
- const Vector3d NextPos = Pos + PerTickSpeed;
+ const Vector3d NextPos = Pos + DeltaSpeed;
// Test for entity collisions:
cProjectileEntityCollisionCallback EntityCollisionCallback(this, Pos, NextPos);
@@ -388,8 +394,8 @@ void cProjectileEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a
// Add slowdown and gravity effect to the speed:
Vector3d NewSpeed(GetSpeed());
- NewSpeed.y += m_Gravity / 20;
- NewSpeed *= TracerCallback.GetSlowdownCoeff();
+ NewSpeed.y += m_Gravity * DtSec.count();
+ NewSpeed -= NewSpeed * (m_AirDrag * 20.0f) * DtSec.count();
SetSpeed(NewSpeed);
SetYawFromSpeed();
SetPitchFromSpeed();
diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h
index de460a8ad..7ee87f93a 100644
--- a/src/Entities/ProjectileEntity.h
+++ b/src/Entities/ProjectileEntity.h
@@ -109,7 +109,7 @@ protected:
eKind m_ProjectileKind;
/** The structure for containing the entity ID and name who has created this projectile
- The ID and/or name may be nullptr (e.g. for dispensers/mobs). */
+ The ID and / or name may be nullptr (e.g. for dispensers / mobs). */
CreatorData m_CreatorData;
/** True if the projectile has hit the ground and is stuck there */
diff --git a/src/Entities/TNTEntity.cpp b/src/Entities/TNTEntity.cpp
index a89d2f300..d849bd4c9 100644
--- a/src/Entities/TNTEntity.cpp
+++ b/src/Entities/TNTEntity.cpp
@@ -12,6 +12,8 @@ cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, int a_FuseTicks) :
super(etTNT, a_X, a_Y, a_Z, 0.98, 0.98),
m_FuseTicks(a_FuseTicks)
{
+ SetGravity(-16.0f);
+ SetAirDrag(0.02f);
}
@@ -22,6 +24,8 @@ cTNTEntity::cTNTEntity(const Vector3d & a_Pos, int a_FuseTicks) :
super(etTNT, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98),
m_FuseTicks(a_FuseTicks)
{
+ SetGravity(-16.0f);
+ SetAirDrag(0.4f);
}
diff --git a/src/Entities/WitherSkullEntity.cpp b/src/Entities/WitherSkullEntity.cpp
index a7e774bba..dc95e3edd 100644
--- a/src/Entities/WitherSkullEntity.cpp
+++ b/src/Entities/WitherSkullEntity.cpp
@@ -16,6 +16,8 @@ cWitherSkullEntity::cWitherSkullEntity(cEntity * a_Creator, double a_X, double a
super(pkWitherSkull, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
{
SetSpeed(a_Speed);
+ SetGravity(0);
+ SetAirDrag(0);
}
diff --git a/src/FastRandom.cpp b/src/FastRandom.cpp
index 737b13535..c1716f026 100644
--- a/src/FastRandom.cpp
+++ b/src/FastRandom.cpp
@@ -6,12 +6,28 @@
#include "Globals.h"
#include "FastRandom.h"
+#include <random>
+
#ifdef _WIN32
- #define thread_local __declspec(thread)
+ #define thread_local static __declspec(thread)
+#elif defined __APPLE__
+ #define thread_local static __thread
#endif
-thread_local unsigned int m_Counter = 0;
-
+static unsigned int GetRandomSeed()
+{
+ thread_local bool SeedCounterInitialized = 0;
+ thread_local unsigned int SeedCounter = 0;
+
+ if (!SeedCounterInitialized)
+ {
+ std::random_device rd;
+ std::uniform_int_distribution<unsigned int> dist;
+ SeedCounter = dist(rd);
+ SeedCounterInitialized = true;
+ }
+ return ++SeedCounter;
+}
@@ -92,7 +108,7 @@ public:
cFastRandom::cFastRandom(void) :
- m_LinearRand(m_Counter++)
+ m_LinearRand(GetRandomSeed())
{
}
@@ -136,7 +152,7 @@ int cFastRandom::GenerateRandomInteger(int a_Begin, int a_End)
// MTRand:
MTRand::MTRand() :
- m_MersenneRand(m_Counter++)
+ m_MersenneRand(GetRandomSeed())
{
}
diff --git a/src/Generating/Caves.cpp b/src/Generating/Caves.cpp
index 1e8dbef90..0a3673711 100644
--- a/src/Generating/Caves.cpp
+++ b/src/Generating/Caves.cpp
@@ -237,7 +237,7 @@ bool cCaveTunnel::RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints &
return true;
}
- // Smoothing: for each line segment, add points on its 1/4 lengths
+ // Smoothing: for each line segment, add points on its 1 / 4 lengths
bool res = false;
size_t Num = a_Src.size() - 2; // this many intermediary points
a_Dst.clear();
@@ -488,7 +488,7 @@ void cCaveTunnel::ProcessChunk(
continue;
}
- // Carve out a sphere around the xyz point, m_Radius in diameter; skip 3/7 off the top and bottom:
+ // Carve out a sphere around the xyz point, m_Radius in diameter; skip 3 / 7 off the top and bottom:
int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc
int DifY = itr->m_BlockY;
int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc
@@ -529,7 +529,7 @@ void cCaveTunnel::ProcessChunk(
/*
#ifdef _DEBUG
- // For debugging purposes, outline the shape of the cave using glowstone, *after* carving the entire cave:
+ // For debugging purposes, outline the shape of the cave using glowstone, after carving the entire cave:
for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr)
{
int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc
diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp
index 854aac60d..72d8f5258 100644
--- a/src/Generating/ChunkGenerator.cpp
+++ b/src/Generating/ChunkGenerator.cpp
@@ -209,7 +209,7 @@ void cChunkGenerator::Execute(void)
{
if ((NumChunksGenerated > 16) && (clock() - LastReportTick > CLOCKS_PER_SEC))
{
- LOG("Chunk generator performance: %.2f ch/s (%d ch total)",
+ LOG("Chunk generator performance: %.2f ch / sec (%d ch total)",
(double)NumChunksGenerated * CLOCKS_PER_SEC/ (clock() - GenerationStart),
NumChunksGenerated
);
@@ -241,7 +241,7 @@ void cChunkGenerator::Execute(void)
// Display perf info once in a while:
if ((NumChunksGenerated > 16) && (clock() - LastReportTick > 2 * CLOCKS_PER_SEC))
{
- LOG("Chunk generator performance: %.2f ch/s (%d ch total)",
+ LOG("Chunk generator performance: %.2f ch / sec (%d ch total)",
(double)NumChunksGenerated * CLOCKS_PER_SEC / (clock() - GenerationStart),
NumChunksGenerated
);
diff --git a/src/Generating/CompoGen.cpp b/src/Generating/CompoGen.cpp
index cb9c04fd7..7cadc881a 100644
--- a/src/Generating/CompoGen.cpp
+++ b/src/Generating/CompoGen.cpp
@@ -218,7 +218,7 @@ void cCompoGenClassic::InitializeCompoGen(cIniFile & a_IniFile)
cCompoGenNether::cCompoGenNether(int a_Seed) :
m_Noise1(a_Seed + 10),
m_Noise2(a_Seed * a_Seed * 10 + a_Seed * 1000 + 6000),
- m_Threshold(0)
+ m_MaxThreshold(25000)
{
}
@@ -282,17 +282,16 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc:
// Interpolate between FloorLo and FloorHi:
for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++)
{
+ int Threshold = static_cast<int>(m_Noise1.CubicNoise2D(static_cast<float>(BaseX + x) / 75, static_cast<float>(BaseZ + z) / 75) * m_MaxThreshold);
int Lo = FloorLo[x + 17 * z] / 256;
int Hi = FloorHi[x + 17 * z] / 256;
for (int y = 0; y < SEGMENT_HEIGHT; y++)
{
int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT;
- BLOCKTYPE Block = E_BLOCK_AIR;
- if (Val < m_Threshold) // Don't calculate if the block should be Netherrack or Soulsand when it's already decided that it's air.
+ if (Val < Threshold) // Don't calculate if the block should be Netherrack when it's already decided that it's air.
{
- Block = E_BLOCK_NETHERRACK;
+ a_ChunkDesc.SetBlockType(x, y + Segment, z, E_BLOCK_NETHERRACK);
}
- a_ChunkDesc.SetBlockType(x, y + Segment, z, Block);
}
}
@@ -329,7 +328,7 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc:
void cCompoGenNether::InitializeCompoGen(cIniFile & a_IniFile)
{
- m_Threshold = a_IniFile.GetValueSetI("Generator", "NetherThreshold", m_Threshold);
+ m_MaxThreshold = a_IniFile.GetValueSetF("Generator", "NetherMaxThreshold", m_MaxThreshold);
}
diff --git a/src/Generating/CompoGen.h b/src/Generating/CompoGen.h
index 3847688cd..d4d38bfdd 100644
--- a/src/Generating/CompoGen.h
+++ b/src/Generating/CompoGen.h
@@ -99,7 +99,7 @@ protected:
cNoise m_Noise1;
cNoise m_Noise2;
- int m_Threshold;
+ double m_MaxThreshold;
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp
index 4a670b064..6b7643ddb 100644
--- a/src/Generating/ComposableGenerator.cpp
+++ b/src/Generating/ComposableGenerator.cpp
@@ -347,6 +347,10 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
AString HeightDistrib = a_IniFile.GetValueSet ("Generator", "DungeonRoomsHeightDistrib", "0, 0; 10, 10; 11, 500; 40, 500; 60, 40; 90, 1");
m_FinishGens.push_back(cFinishGenPtr(new cDungeonRoomsFinisher(m_ShapeGen, Seed, GridSize, MaxSize, MinSize, HeightDistrib)));
}
+ else if (NoCaseCompare(*itr, "GlowStone") == 0)
+ {
+ m_FinishGens.push_back(cFinishGenPtr(new cFinishGenGlowStone(Seed)));
+ }
else if (NoCaseCompare(*itr, "Ice") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenIce));
@@ -447,7 +451,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
else if (NoCaseCompare(*itr, "NetherForts") == 0)
{
int GridSize = a_IniFile.GetValueSetI("Generator", "NetherFortsGridSize", 512);
- int MaxOffset = a_IniFile.GetValueSetI("Generator", "NetherFortMaxOffset", 128);
+ int MaxOffset = a_IniFile.GetValueSetI("Generator", "NetherFortsMaxOffset", 128);
int MaxDepth = a_IniFile.GetValueSetI("Generator", "NetherFortsMaxDepth", 12);
m_FinishGens.push_back(cFinishGenPtr(new cNetherFortGen(Seed, GridSize, MaxOffset, MaxDepth)));
}
diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp
index 5839a4ccc..c988224e6 100644
--- a/src/Generating/FinishGen.cpp
+++ b/src/Generating/FinishGen.cpp
@@ -180,6 +180,118 @@ void cFinishGenNetherClumpFoliage::TryPlaceClump(cChunkDesc & a_ChunkDesc, int a
////////////////////////////////////////////////////////////////////////////////
+// cFinishGenGlowStone:
+
+void cFinishGenGlowStone::GenFinish(cChunkDesc & a_ChunkDesc)
+{
+ int ChunkX = a_ChunkDesc.GetChunkX();
+ int ChunkZ = a_ChunkDesc.GetChunkZ();
+
+ // Change the number of attempts to create a vein depending on the maximum height of the chunk. A standard Nether could have 5 veins at most.
+ int NumGlowStone = m_Noise.IntNoise2DInt(ChunkX, ChunkZ) % a_ChunkDesc.GetMaxHeight() / 23;
+
+ for (int i = 1; i <= NumGlowStone; i++)
+ {
+ // The maximum size for a string of glowstone can get 3 - 5 blocks long
+ int Size = 3 + m_Noise.IntNoise3DInt(ChunkX, i, ChunkZ) % 3;
+
+ // Generate X / Z coordinates.
+ int X = Size + (m_Noise.IntNoise2DInt(i, Size) % (cChunkDef::Width - Size * 2));
+ int Z = Size + (m_Noise.IntNoise2DInt(X, i) % (cChunkDef::Width - Size * 2));
+
+ int Height = a_ChunkDesc.GetHeight(X, Z);
+ for (int y = Height; y > Size; y--)
+ {
+ if (!cBlockInfo::IsSolid(a_ChunkDesc.GetBlockType(X, y, Z)))
+ {
+ // Current block isn't solid, bail out
+ continue;
+ }
+
+ if (a_ChunkDesc.GetBlockType(X, y - 1, Z) != E_BLOCK_AIR)
+ {
+ // The block below isn't air, bail out
+ continue;
+ }
+
+ if ((m_Noise.IntNoise3DInt(X, y, Z) % 100) < 95)
+ {
+ // Have a 5% chance of creating the glowstone
+ continue;
+ }
+
+ TryPlaceGlowstone(a_ChunkDesc, X, y, Z, Size, 5 + m_Noise.IntNoise3DInt(X, y, Z) % 7);
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cFinishGenGlowStone::TryPlaceGlowstone(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, int a_Size, int a_NumStrings)
+{
+ // The starting point of every glowstone string
+ Vector3i StartPoint = Vector3i(a_RelX, a_RelY, a_RelZ);
+
+ // Array with possible directions for a string of glowstone to go to.
+ const Vector3i AvailableDirections[] =
+ {
+ { -1, 0, 0 }, { 1, 0, 0 },
+ { 0, -1, 0 }, // Don't let the glowstone go up
+ { 0, 0, -1 }, { 0, 0, 1 },
+
+ // Diagonal direction. Only X or Z with Y.
+ // If all were changed the glowstone string looks awkward
+ { 0, -1, 1 }, { 1, -1, 0 },
+ { 0, -1, -1 }, { -1, -1, 0 },
+
+ };
+
+ for (int i = 1; i <= a_NumStrings; i++)
+ {
+ // The current position of the string that is being generated
+ Vector3i CurrentPos = Vector3i(StartPoint);
+
+ // A vector where the previous direction of a glowstone string is stored.
+ // This is used to make the strings change direction when going one block further
+ Vector3i PreviousDirection = Vector3i();
+
+ for (int j = 0; j < a_Size; j++)
+ {
+ Vector3i Direction = AvailableDirections[m_Noise.IntNoise3DInt(CurrentPos.x, CurrentPos.y * i, CurrentPos.z) % ARRAYCOUNT(AvailableDirections)];
+ int Attempts = 2; // multiply by 1 would make no difference, so multiply by 2 instead
+
+ while (Direction.Equals(PreviousDirection))
+ {
+ // To make the glowstone branches look better we want to make the direction change every time.
+ Direction = AvailableDirections[m_Noise.IntNoise3DInt(CurrentPos.x, CurrentPos.y * i * Attempts, CurrentPos.z) % ARRAYCOUNT(AvailableDirections)];
+ Attempts++;
+ }
+
+ // Update the previous direction variable
+ PreviousDirection = Direction;
+
+ // Update the position of the glowstone string
+ CurrentPos += Direction;
+ if (cBlockInfo::IsSolid(a_ChunkDesc.GetBlockType(CurrentPos.x, CurrentPos.y, CurrentPos.z)) && (a_ChunkDesc.GetBlockType(CurrentPos.x, CurrentPos.y, CurrentPos.z) != E_BLOCK_GLOWSTONE))
+ {
+ // The glowstone hit something solid, and it wasn't glowstone. Stop the string.
+ break;
+ }
+
+ // Place a glowstone block.
+ a_ChunkDesc.SetBlockType(CurrentPos.x, CurrentPos.y, CurrentPos.z, E_BLOCK_GLOWSTONE);
+ }
+ }
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
// cFinishGenTallGrass:
void cFinishGenTallGrass::GenFinish(cChunkDesc & a_ChunkDesc)
@@ -523,7 +635,7 @@ void cFinishGenSoulsandRims::GenFinish(cChunkDesc & a_ChunkDesc)
{
// The current block is air. Let's bail ut.
BLOCKTYPE Block = a_ChunkDesc.GetBlockType(x, y, z);
- if (Block == E_BLOCK_AIR)
+ if (Block != E_BLOCK_NETHERRACK)
{
continue;
}
@@ -1147,7 +1259,7 @@ bool cFinishGenPassiveMobs::TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int a_RelX
}
if (
(BlockUnderFeet != E_BLOCK_GRASS) &&
- ((AnimalToSpawn == mtSheep) || (AnimalToSpawn == mtChicken) || (AnimalToSpawn == mtPig))
+ ((AnimalToSpawn == mtWolf) || (AnimalToSpawn == mtRabbit) || (AnimalToSpawn == mtCow) || (AnimalToSpawn == mtSheep) || (AnimalToSpawn == mtChicken) || (AnimalToSpawn == mtPig))
)
{
return false;
@@ -1162,6 +1274,7 @@ bool cFinishGenPassiveMobs::TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int a_RelX
double AnimalZ = static_cast<double>(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ + 0.5);
cMonster * NewMob = cMonster::NewMonsterFromType(AnimalToSpawn);
+ NewMob->SetHealth(NewMob->GetMaxHealth());
NewMob->SetPosition(AnimalX, AnimalY, AnimalZ);
a_ChunkDesc.GetEntities().push_back(NewMob);
LOGD("Spawning %s #%i at {%.02f, %.02f, %.02f}", NewMob->GetClass(), NewMob->GetUniqueID(), AnimalX, AnimalY, AnimalZ);
diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h
index 70696c4f8..aa50335d4 100644
--- a/src/Generating/FinishGen.h
+++ b/src/Generating/FinishGen.h
@@ -70,6 +70,28 @@ protected:
+class cFinishGenGlowStone :
+ public cFinishGen
+{
+public:
+ cFinishGenGlowStone(int a_Seed) :
+ m_Noise(a_Seed),
+ m_Seed(a_Seed)
+ {
+ }
+
+protected:
+ cNoise m_Noise;
+ int m_Seed;
+
+ void TryPlaceGlowstone(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, int a_Size, int a_NumStrings);
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
class cFinishGenTallGrass :
public cFinishGen
{
diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp
index 54567cb4d..e34ffc57c 100644
--- a/src/Generating/HeiGen.cpp
+++ b/src/Generating/HeiGen.cpp
@@ -667,7 +667,7 @@ public:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
{
- // Generate the biomes for the 3*3 neighbors:
+ // Generate the biomes for the 3 * 3 neighbors:
cChunkDef::BiomeMap neighborBiomes[3][3];
for (int z = 0; z < 3; z++) for (int x = 0; x < 3; x++)
{
diff --git a/src/Generating/IntGen.h b/src/Generating/IntGen.h
index 854563f41..9cc881639 100644
--- a/src/Generating/IntGen.h
+++ b/src/Generating/IntGen.h
@@ -5,7 +5,7 @@
/*
The integers generated may be interpreted in several ways:
-- land/see designators
+- land / sea designators
- 0 = ocean
- >0 = land
- biome group
@@ -310,7 +310,7 @@ public:
}
}
- // Copy from Cache into a_Values; take into account the even/odd offsets in a_Min:
+ // Copy from Cache into a_Values; take into account the even / odd offsets in a_Min:
for (int z = 0; z < SizeZ; ++z)
{
memcpy(a_Values + z * SizeX, cache + (z + (a_MinZ & 1)) * lowStepX + (a_MinX & 1), SizeX * sizeof(int));
diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp
index eadc66a4e..63e88c2a8 100644
--- a/src/Generating/Noise3DGenerator.cpp
+++ b/src/Generating/Noise3DGenerator.cpp
@@ -680,7 +680,7 @@ void cBiomalNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_Ch
void cBiomalNoise3DComposable::CalcBiomeParamArrays(int a_ChunkX, int a_ChunkZ, ChunkParam & a_HeightAmp, ChunkParam & a_MidPoint)
{
- // Generate the 3*3 chunks of biomes around this chunk:
+ // Generate the 3 * 3 chunks of biomes around this chunk:
cChunkDef::BiomeMap neighborBiomes[3 * 3];
for (int z = 0; z < 3; z++)
{
diff --git a/src/Generating/ProtIntGen.h b/src/Generating/ProtIntGen.h
index e709222fe..9e471e8bb 100644
--- a/src/Generating/ProtIntGen.h
+++ b/src/Generating/ProtIntGen.h
@@ -229,7 +229,7 @@ public:
}
}
- // Copy from Cache into a_Values; take into account the even/odd offsets in a_Min:
+ // Copy from Cache into a_Values; take into account the even / odd offsets in a_Min:
for (int z = 0; z < a_SizeZ; ++z)
{
memcpy(a_Values + z * a_SizeX, cache + (z + (a_MinZ & 1)) * lowStepX + (a_MinX & 1), a_SizeX * sizeof(int));
diff --git a/src/Generating/RainbowRoadsGen.cpp b/src/Generating/RainbowRoadsGen.cpp
index c3c07cdec..fd4a81692 100644
--- a/src/Generating/RainbowRoadsGen.cpp
+++ b/src/Generating/RainbowRoadsGen.cpp
@@ -61,7 +61,7 @@ protected:
/** The noise used as a pseudo-random generator */
cNoise m_Noise;
- /** Maximum size, in X/Z blocks, of the village (radius from the origin) */
+ /** Maximum size, in X / Z blocks, of the village (radius from the origin) */
int m_MaxSize;
/** Borders of the vilalge - no item may reach out of this cuboid. */
diff --git a/src/Generating/RainbowRoadsGen.h b/src/Generating/RainbowRoadsGen.h
index 5813e1d14..52d48ceca 100644
--- a/src/Generating/RainbowRoadsGen.h
+++ b/src/Generating/RainbowRoadsGen.h
@@ -34,7 +34,7 @@ protected:
/** Maximum depth of the generator tree*/
int m_MaxDepth;
- /** Maximum size, in X/Z blocks, of the base (radius from the origin) */
+ /** Maximum size, in X / Z blocks, of the structure (radius from the origin) */
int m_MaxSize;
diff --git a/src/Generating/Ravines.cpp b/src/Generating/Ravines.cpp
index 70b9d0b62..9f8f69139 100644
--- a/src/Generating/Ravines.cpp
+++ b/src/Generating/Ravines.cpp
@@ -124,7 +124,7 @@ cStructGenRavines::cRavine::cRavine(int a_GridX, int a_GridZ, int a_OriginX, int
void cStructGenRavines::cRavine::GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise)
{
- // Modify the size slightly to have different-sized ravines (1/2 to 1/1 of a_Size):
+ // Modify the size slightly to have different-sized ravines (1 / 2 to 1 / 1 of a_Size):
a_Size = (512 + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 11 * a_BlockZ, a_BlockX + a_BlockZ) / 17) % 512)) * a_Size / 1024;
// The complete offset of the ravine from its cellpoint, up to 2 * a_Size in each direction
@@ -177,7 +177,7 @@ void cStructGenRavines::cRavine::RefineDefPoints(const cRavDefPoints & a_Src, cR
return;
}
- // Smoothing: for each line segment, add points on its 1/4 lengths
+ // Smoothing: for each line segment, add points on its 1 / 4 lengths
size_t Num = a_Src.size() - 2; // this many intermediary points
a_Dst.clear();
a_Dst.reserve(Num * 2 + 2);
diff --git a/src/Generating/StructGen.cpp b/src/Generating/StructGen.cpp
index 2d5a73739..7572cdcbf 100644
--- a/src/Generating/StructGen.cpp
+++ b/src/Generating/StructGen.cpp
@@ -311,7 +311,7 @@ void cStructGenOreNests::GenerateOre(int a_ChunkX, int a_ChunkZ, BLOCKTYPE a_Ore
rnd /= cChunkDef::Width;
int BaseY = rnd % a_MaxHeight;
rnd /= a_MaxHeight;
- int NestSize = a_NestSize + (rnd % (a_NestSize / 4)); // The actual nest size may be up to 1/4 larger
+ int NestSize = a_NestSize + (rnd % (a_NestSize / 4)); // The actual nest size may be up to 1 / 4 larger
int Num = 0;
while (Num < NestSize)
{
diff --git a/src/Generating/TestRailsGen.cpp b/src/Generating/TestRailsGen.cpp
index 66ab8b33f..8256b55a0 100644
--- a/src/Generating/TestRailsGen.cpp
+++ b/src/Generating/TestRailsGen.cpp
@@ -61,7 +61,7 @@ protected:
/** The noise used as a pseudo-random generator */
cNoise m_Noise;
- /** Maximum size, in X/Z blocks, of the structure (radius from the origin) */
+ /** Maximum size, in X / Z blocks, of the structure (radius from the origin) */
int m_MaxSize;
/** Borders of the structure - no item may reach out of this cuboid. */
diff --git a/src/Generating/TestRailsGen.h b/src/Generating/TestRailsGen.h
index 5b0ce95f4..88e38b368 100644
--- a/src/Generating/TestRailsGen.h
+++ b/src/Generating/TestRailsGen.h
@@ -31,10 +31,10 @@ protected:
/** The noise used for generating random numbers */
cNoise m_Noise;
- /** Maximum depth of the generator tree*/
+ /** Maximum depth of the generator tree */
int m_MaxDepth;
- /** Maximum size, in X/Z blocks, of the base (radius from the origin) */
+ /** Maximum size, in X / Z blocks, of the structure (radius from the origin) */
int m_MaxSize;
diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp
index 9e72a688f..72fe5f819 100644
--- a/src/Generating/Trees.cpp
+++ b/src/Generating/Trees.cpp
@@ -499,7 +499,7 @@ void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Nois
PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
h--;
- // Third and fourth layers - BigO2 and maybe 2*Corners:
+ // Third and fourth layers - BigO2 and maybe 2 * Corners:
for (int Row = 0; Row < 2; Row++)
{
PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
@@ -673,7 +673,7 @@ void GetTallBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_
PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
h--;
- // Third and fourth layers - BigO2 and maybe 2*Corners:
+ // Third and fourth layers - BigO2 and maybe 2 * Corners:
for (int Row = 0; Row < 2; Row++)
{
PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
diff --git a/src/Generating/TwoHeights.cpp b/src/Generating/TwoHeights.cpp
index e75c301de..06c474458 100644
--- a/src/Generating/TwoHeights.cpp
+++ b/src/Generating/TwoHeights.cpp
@@ -69,7 +69,7 @@ public:
}
- virtual void InitializeShapeGen(cIniFile & a_IniFile)
+ virtual void InitializeShapeGen(cIniFile & a_IniFile) override
{
m_FrequencyX = static_cast<NOISE_DATATYPE>(a_IniFile.GetValueSetF("Generator", "TwoHeightsFrequencyX", 40));
m_FrequencyY = static_cast<NOISE_DATATYPE>(a_IniFile.GetValueSetF("Generator", "TwoHeightsFrequencyY", 40));
diff --git a/src/Generating/UnderwaterBaseGen.cpp b/src/Generating/UnderwaterBaseGen.cpp
index 8916f4b5f..dbc4aef1b 100644
--- a/src/Generating/UnderwaterBaseGen.cpp
+++ b/src/Generating/UnderwaterBaseGen.cpp
@@ -61,7 +61,7 @@ protected:
/** The noise used as a pseudo-random generator */
cNoise m_Noise;
- /** Maximum size, in X/Z blocks, of the village (radius from the origin) */
+ /** Maximum size, in X / Z blocks, of the base (radius from the origin) */
int m_MaxSize;
/** Borders of the vilalge - no item may reach out of this cuboid. */
diff --git a/src/Generating/UnderwaterBaseGen.h b/src/Generating/UnderwaterBaseGen.h
index b24eb1075..9433a1905 100644
--- a/src/Generating/UnderwaterBaseGen.h
+++ b/src/Generating/UnderwaterBaseGen.h
@@ -34,7 +34,7 @@ protected:
/** Maximum depth of the generator tree*/
int m_MaxDepth;
- /** Maximum size, in X/Z blocks, of the base (radius from the origin) */
+ /** Maximum size, in X / Z blocks, of the structure (radius from the origin) */
int m_MaxSize;
/** The underlying biome generator that defines whether the base is created or not */
diff --git a/src/Generating/VillageGen.cpp b/src/Generating/VillageGen.cpp
index f7d9a8316..27146e757 100644
--- a/src/Generating/VillageGen.cpp
+++ b/src/Generating/VillageGen.cpp
@@ -162,7 +162,7 @@ protected:
/** The noise used as a pseudo-random generator */
cNoise m_Noise;
- /** Maximum size, in X/Z blocks, of the village (radius from the origin) */
+ /** Maximum size, in X / Z blocks, of the village (radius from the origin) */
int m_MaxSize;
/** The density for this village. Used to refrain from populating all house connectors. Range [0, 100] */
@@ -259,13 +259,13 @@ protected:
// cPiecePool overrides:
- virtual cPieces GetPiecesWithConnector(int a_ConnectorType)
+ virtual cPieces GetPiecesWithConnector(int a_ConnectorType) override
{
return m_Prefabs.GetPiecesWithConnector(a_ConnectorType);
}
- virtual cPieces GetStartingPieces(void)
+ virtual cPieces GetStartingPieces(void) override
{
return m_Prefabs.GetStartingPieces();
}
diff --git a/src/Generating/VillageGen.h b/src/Generating/VillageGen.h
index ed8d739df..c384ed9e5 100644
--- a/src/Generating/VillageGen.h
+++ b/src/Generating/VillageGen.h
@@ -32,7 +32,7 @@ protected:
/** Maximum depth of the generator tree*/
int m_MaxDepth;
- /** Maximum size, in X/Z blocks, of the village (radius from the origin) */
+ /** Maximum size, in X / Z blocks, of the village (radius from the origin) */
int m_MaxSize;
/** Minimum density - percentage of allowed house connections. Range [0, 100] */
diff --git a/src/Globals.h b/src/Globals.h
index bd180c08f..27d944fcc 100644
--- a/src/Globals.h
+++ b/src/Globals.h
@@ -18,9 +18,9 @@
// Useful warnings from warning level 4:
#pragma warning(3 : 4189) // Local variable is initialized but not referenced
- #pragma warning(3 : 4245) // Conversion from 'type1' to 'type2', signed/unsigned mismatch
+ #pragma warning(3 : 4245) // Conversion from 'type1' to 'type2', signed / unsigned mismatch
#pragma warning(3 : 4310) // Cast truncates constant value
- #pragma warning(3 : 4389) // Signed/unsigned mismatch
+ #pragma warning(3 : 4389) // Signed / unsigned mismatch
#pragma warning(3 : 4505) // Unreferenced local function has been removed
#pragma warning(3 : 4701) // Potentially unitialized local variable used
#pragma warning(3 : 4702) // Unreachable code
@@ -433,10 +433,14 @@ typename std::enable_if<std::is_arithmetic<T>::value, C>::type CeilC(T a_Value)
//temporary replacement for std::make_unique until we get c++14
-template <class T, class... Args>
-std::unique_ptr<T> make_unique(Args&&... args)
+
+namespace cpp14
{
- return std::unique_ptr<T>(new T(args...));
+ template <class T, class... Args>
+ std::unique_ptr<T> make_unique(Args&&... args)
+ {
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+ }
}
// a tick is 50 ms
diff --git a/src/HTTPServer/EnvelopeParser.h b/src/HTTPServer/EnvelopeParser.h
index e96d80abe..fe226d1f8 100644
--- a/src/HTTPServer/EnvelopeParser.h
+++ b/src/HTTPServer/EnvelopeParser.h
@@ -61,7 +61,7 @@ public:
AString m_LastValue;
- /** Notifies the callback of the key/value stored in m_LastKey/m_LastValue, then erases them */
+ /** Notifies the callback of the key / value stored in m_LastKey / m_LastValue, then erases them */
void NotifyLast(void);
/** Parses one line of header data. Returns true if successful */
diff --git a/src/HTTPServer/HTTPConnection.cpp b/src/HTTPServer/HTTPConnection.cpp
index de12b36ce..a5c6afd18 100644
--- a/src/HTTPServer/HTTPConnection.cpp
+++ b/src/HTTPServer/HTTPConnection.cpp
@@ -38,7 +38,9 @@ cHTTPConnection::~cHTTPConnection()
void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Response)
{
- SendData(Printf("%d %s\r\nContent-Length: 0\r\n\r\n", a_StatusCode, a_Response.c_str()));
+ SendData(Printf("HTTP/1.1 %d %s\r\n", a_StatusCode, a_Response.c_str()));
+ SendData(Printf("Content-Length: %u\r\n\r\n", static_cast<unsigned>(a_Response.size())));
+ SendData(a_Response.data(), a_Response.size());
m_State = wcsRecvHeaders;
}
diff --git a/src/HTTPServer/HTTPConnection.h b/src/HTTPServer/HTTPConnection.h
index 8ecc4a4d4..e1ebeb9ee 100644
--- a/src/HTTPServer/HTTPConnection.h
+++ b/src/HTTPServer/HTTPConnection.h
@@ -41,7 +41,8 @@ public:
cHTTPConnection(cHTTPServer & a_HTTPServer);
virtual ~cHTTPConnection();
- /** Sends HTTP status code together with a_Reason (used for HTTP errors) */
+ /** Sends HTTP status code together with a_Reason (used for HTTP errors).
+ Sends the a_Reason as the body as well, so that browsers display it. */
void SendStatusAndReason(int a_StatusCode, const AString & a_Reason);
/** Sends the "401 unauthorized" reply together with instructions on authorizing, using the specified realm */
diff --git a/src/HTTPServer/HTTPMessage.cpp b/src/HTTPServer/HTTPMessage.cpp
index c87b3cc8b..6762a22f8 100644
--- a/src/HTTPServer/HTTPMessage.cpp
+++ b/src/HTTPServer/HTTPMessage.cpp
@@ -202,7 +202,7 @@ size_t cHTTPRequest::ParseRequestLine(const char * a_Data, size_t a_Size)
m_IsValid = false;
return AString::npos;
}
- // Check that there's HTTP/version at the end
+ // Check that there's HTTP / version at the end
if (strncmp(m_IncomingHeaderData.c_str() + URLEnd + 1, "HTTP/1.", 7) != 0)
{
m_IsValid = false;
diff --git a/src/IniFile.cpp b/src/IniFile.cpp
index 3a213a90e..cd98cce57 100644
--- a/src/IniFile.cpp
+++ b/src/IniFile.cpp
@@ -1,11 +1,11 @@
// IniFile.cpp: Implementation of the CIniFile class.
// Written by: Adam Clauss
// Email: cabadam@houston.rr.com
-// You may use this class/code as you wish in your programs. Feel free to distribute it, and
+// You may use this class / code as you wish in your programs. Feel free to distribute it, and
// email suggested changes to me.
//
// Rewritten by: Shane Hill
-// Date: 21/08/2001
+// Date: 2001-08-21
// Email: Shane.Hill@dsto.defence.gov.au
// Reason: Remove dependancy on MFC. Code should compile on any
// platform.
@@ -49,6 +49,9 @@ cIniFile::cIniFile(void) :
bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect)
{
+
+ m_Filename = a_FileName;
+
// Normally you would use ifstream, but the SGI CC compiler has
// a few bugs with ifstream. So ... fstream used.
fstream f;
@@ -56,7 +59,8 @@ bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect)
AString keyname, valuename, value;
AString::size_type pLeft, pRight;
bool IsFromExampleRedirect = false;
-
+
+
f.open((FILE_IO_PREFIX + a_FileName).c_str(), ios::in);
if (f.fail())
{
@@ -650,7 +654,7 @@ void cIniFile::Clear(void)
-bool cIniFile::HasValue(const AString & a_KeyName, const AString & a_ValueName)
+bool cIniFile::HasValue(const AString & a_KeyName, const AString & a_ValueName) const
{
// Find the key:
int keyID = FindKey(a_KeyName);
@@ -889,8 +893,36 @@ void cIniFile::RemoveBom(AString & a_line) const
+bool cIniFile::KeyExists(AString a_keyname) const
+{
+ return FindKey(a_keyname) != noID;
+}
+
+
+
+
+
+std::vector<std::pair<AString, AString>> cIniFile::GetValues(AString a_keyName)
+{
+ std::vector<std::pair<AString, AString>> ret;
+ int keyID = FindKey(a_keyName);
+ if (keyID == noID)
+ {
+ return ret;
+ }
+ for (size_t valueID = 0; valueID < keys[keyID].names.size(); ++valueID)
+ {
+ ret.emplace_back(keys[keyID].names[valueID], keys[keyID].values[valueID]);
+ }
+ return ret;
+}
+
+
+
+
+
AStringVector ReadUpgradeIniPorts(
- cIniFile & a_IniFile,
+ cSettingsRepositoryInterface & a_Settings,
const AString & a_KeyName,
const AString & a_PortsValueName,
const AString & a_OldIPv4ValueName,
@@ -899,23 +931,34 @@ AStringVector ReadUpgradeIniPorts(
)
{
// Read the regular value, but don't use the default (in order to detect missing value for upgrade):
- AStringVector Ports = StringSplitAndTrim(a_IniFile.GetValue(a_KeyName, a_PortsValueName), ";,");
+
+ AStringVector Ports;
+
+ for (auto pair : a_Settings.GetValues(a_KeyName))
+ {
+ if (pair.first != a_PortsValueName)
+ {
+ continue;
+ }
+ AStringVector temp = StringSplitAndTrim(pair.second, ";,");
+ Ports.insert(Ports.end(), temp.begin(), temp.end());
+ }
if (Ports.empty())
{
// Historically there were two separate entries for IPv4 and IPv6, merge them and migrate:
- AString Ports4 = a_IniFile.GetValue(a_KeyName, a_OldIPv4ValueName, a_DefaultValue);
- AString Ports6 = a_IniFile.GetValue(a_KeyName, a_OldIPv6ValueName);
+ AString Ports4 = a_Settings.GetValue(a_KeyName, a_OldIPv4ValueName, a_DefaultValue);
+ AString Ports6 = a_Settings.GetValue(a_KeyName, a_OldIPv6ValueName);
Ports = MergeStringVectors(StringSplitAndTrim(Ports4, ";,"), StringSplitAndTrim(Ports6, ";,"));
- a_IniFile.DeleteValue(a_KeyName, a_OldIPv4ValueName);
- a_IniFile.DeleteValue(a_KeyName, a_OldIPv6ValueName);
+ a_Settings.DeleteValue(a_KeyName, a_OldIPv4ValueName);
+ a_Settings.DeleteValue(a_KeyName, a_OldIPv6ValueName);
// If those weren't present or were empty, use the default:"
if (Ports.empty())
{
Ports = StringSplitAndTrim(a_DefaultValue, ";,");
}
- a_IniFile.SetValue(a_KeyName, a_PortsValueName, StringsConcat(Ports, ','));
+ a_Settings.SetValue(a_KeyName, a_PortsValueName, StringsConcat(Ports, ','));
}
return Ports;
@@ -923,4 +966,3 @@ AStringVector ReadUpgradeIniPorts(
-
diff --git a/src/IniFile.h b/src/IniFile.h
index 3e717723f..2a4113fb4 100644
--- a/src/IniFile.h
+++ b/src/IniFile.h
@@ -1,14 +1,14 @@
// IniFile.cpp: Implementation of the CIniFile class.
// Written by: Adam Clauss
// Email: cabadam@tamu.edu
-// You may use this class/code as you wish in your programs. Feel free to distribute it, and
+// You may use this class / code as you wish in your programs. Feel free to distribute it, and
// email suggested changes to me.
//
// Rewritten by: Shane Hill
-// Date: 21/08/2001
+// Date: 2001-08-21
// Email: Shane.Hill@dsto.defence.gov.au
// Reason: Remove dependancy on MFC. Code should compile on any
-// platform. Tested on Windows/Linux/Irix
+// platform. Tested on Windows / Linux / Irix
////////////////////////////////////////////////////////////////////////////////
/*
@@ -18,7 +18,7 @@
#pragma once
-
+#include "SettingsRepositoryInterface.h"
#define MAX_KEYNAME 128
#define MAX_VALUENAME 128
@@ -30,10 +30,12 @@
// tolua_begin
-class cIniFile
+class cIniFile : public cSettingsRepositoryInterface
{
private:
bool m_IsCaseInsensitive;
+
+ AString m_Filename;
struct key
{
@@ -53,15 +55,19 @@ private:
void RemoveBom(AString & a_line) const;
public:
-
- enum errors
- {
- noID = -1,
- };
/// Creates a new instance with no data
cIniFile(void);
+// tolua_end
+ virtual ~cIniFile() = default;
+
+ virtual std::vector<std::pair<AString, AString>> GetValues(AString a_keyName) override;
+
+ virtual bool KeyExists(const AString a_keyName) const override;
+
+// tolua_begin
+
// Sets whether or not keynames and valuenames should be case sensitive.
// The default is case insensitive.
void CaseSensitive (void) { m_IsCaseInsensitive = false; }
@@ -77,11 +83,13 @@ public:
/// Writes data stored in class to the specified ini file
bool WriteFile(const AString & a_FileName) const;
+ virtual bool Flush() override { return WriteFile(m_Filename); }
+
/// Deletes all stored ini data (but doesn't touch the file)
void Clear(void);
/** Returns true iff the specified value exists. */
- bool HasValue(const AString & a_KeyName, const AString & a_ValueName);
+ bool HasValue(const AString & a_KeyName, const AString & a_ValueName) const override;
/// Returns index of specified key, or noID if not found
int FindKey(const AString & keyname) const;
@@ -93,7 +101,7 @@ public:
int GetNumKeys(void) const { return (int)keys.size(); }
/// Add a key name
- int AddKeyName(const AString & keyname);
+ int AddKeyName(const AString & keyname) override;
// Returns key names by index.
AString GetKeyName(const int keyID) const;
@@ -108,8 +116,8 @@ public:
// Gets value of [keyname] valuename =.
// Overloaded to return string, int, and double.
- // Returns defValue if key/value not found.
- AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const;
+ // Returns defValue if key / value not found.
+ AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const override;
AString GetValue (const int keyID, const int valueID, const AString & defValue = "") const;
double GetValueF(const AString & keyname, const AString & valuename, const double defValue = 0) const;
int GetValueI(const AString & keyname, const AString & valuename, const int defValue = 0) const;
@@ -119,18 +127,18 @@ public:
}
// Gets the value; if not found, write the default to the INI file
- AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = "");
+ AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = "") override;
double GetValueSetF(const AString & keyname, const AString & valuename, const double defValue = 0.0);
- int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0);
- Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0);
- bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false)
+ int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0) override;
+ Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0) override;
+ bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) override
{
return (GetValueSetI(keyname, valuename, defValue ? 1 : 0) != 0);
}
// Adds a new value to the specified key.
// If a value of the same name already exists, creates another one (non-standard INI file)
- void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value);
+ void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) override;
void AddValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value);
void AddValueB(const AString & a_KeyName, const AString & a_ValueName, const bool a_Value)
{
@@ -143,8 +151,8 @@ public:
// Returns true if value set, false otherwise.
// Overloaded to accept string, int, and double.
bool SetValue (const int keyID, const int valueID, const AString & value);
- bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true);
- bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true);
+ bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true) override;
+ bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true) override;
bool SetValueI(const AString & a_Keyname, const AString & a_ValueName, const Int64 a_Value, const bool a_CreateIfNotExists = true);
bool SetValueB(const AString & a_KeyName, const AString & a_ValueName, const bool a_Value, const bool a_CreateIfNotExists = true)
{
@@ -155,7 +163,7 @@ public:
// Deletes specified value.
// Returns true if value existed and deleted, false otherwise.
bool DeleteValueByID(const int keyID, const int valueID);
- bool DeleteValue(const AString & keyname, const AString & valuename);
+ bool DeleteValue(const AString & keyname, const AString & valuename) override;
// Deletes specified key and all values contained within.
// Returns true if key existed and deleted, false otherwise.
@@ -196,15 +204,15 @@ public:
bool AddKeyComment(const int keyID, const AString & comment);
/// Add a key comment
- bool AddKeyComment(const AString & keyname, const AString & comment);
+ bool AddKeyComment(const AString & keyname, const AString & comment) override;
/// Return a key comment
AString GetKeyComment(const int keyID, const int commentID) const;
- AString GetKeyComment(const AString & keyname, const int commentID) const;
+ AString GetKeyComment(const AString & keyname, const int commentID) const override;
// Delete a key comment.
bool DeleteKeyComment(const int keyID, const int commentID);
- bool DeleteKeyComment(const AString & keyname, const int commentID);
+ bool DeleteKeyComment(const AString & keyname, const int commentID) override;
// Delete all comments for a key.
bool DeleteKeyComments(const int keyID);
@@ -217,12 +225,12 @@ public:
-/** Reads the list of ports from the INI file, possibly upgrading from IPv4/IPv6-specific values into new version-agnostic value.
+/** Reads the list of ports from the INI file, possibly upgrading from IPv4 / IPv6-specific values into new version-agnostic value.
Reads the list of ports from a_PortsValueName. If that value doesn't exist or is empty, the list is combined from values
in a_OldIPv4ValueName and a_OldIPv6ValueName; in this case the old values are removed from the INI file.
If there is none of the three values or they are all empty, the default is used and stored in the Ports value. */
AStringVector ReadUpgradeIniPorts(
- cIniFile & a_IniFile,
+ cSettingsRepositoryInterface & a_Settings,
const AString & a_KeyName,
const AString & a_PortsValueName,
const AString & a_OldIPv4ValueName,
diff --git a/src/Inventory.cpp b/src/Inventory.cpp
index fba6f4aea..c595da5b0 100644
--- a/src/Inventory.cpp
+++ b/src/Inventory.cpp
@@ -499,7 +499,7 @@ int cInventory::ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum)
#if 0
-bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode /* = 0 */)
+bool cInventory::AddToBar(cItem & a_Item, const int a_Offset, const int a_Size, bool * a_bChangedSlots, int a_Mode /* = 0 */)
{
// Fill already present stacks
if (a_Mode < 2)
diff --git a/src/Inventory.h b/src/Inventory.h
index 311f64562..b2a8f658b 100644
--- a/src/Inventory.h
+++ b/src/Inventory.h
@@ -171,7 +171,7 @@ public:
bool LoadFromJson(Json::Value & a_Value);
protected:
- bool AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode = 0);
+ bool AddToBar(cItem & a_Item, const int a_Offset, const int a_Size, bool * a_bChangedSlots, int a_Mode = 0);
cItemGrid m_ArmorSlots;
cItemGrid m_InventorySlots;
diff --git a/src/Items/ItemArmor.h b/src/Items/ItemArmor.h
index 2436df5bd..252b94df9 100644
--- a/src/Items/ItemArmor.h
+++ b/src/Items/ItemArmor.h
@@ -17,8 +17,13 @@ public:
{
}
+
+
/** Move the armor to the armor slot of the player's inventory */
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
{
int SlotNum;
if (ItemCategory::IsHelmet(a_Item.m_ItemType))
@@ -60,6 +65,8 @@ public:
return true;
}
+
+
virtual bool CanRepairWithRawMaterial(short a_ItemType) override
{
switch (m_ItemType)
diff --git a/src/Items/ItemBoat.h b/src/Items/ItemBoat.h
index 7faac1e32..452d86775 100644
--- a/src/Items/ItemBoat.h
+++ b/src/Items/ItemBoat.h
@@ -28,9 +28,12 @@ public:
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
{
- if ((a_Dir != BLOCK_FACE_YM) && (a_Dir != BLOCK_FACE_NONE))
+ if ((a_BlockFace != BLOCK_FACE_YM) && (a_BlockFace != BLOCK_FACE_NONE))
{
return false;
}
diff --git a/src/Items/ItemBow.h b/src/Items/ItemBow.h
index cf2fe9ad2..5164ddf59 100644
--- a/src/Items/ItemBow.h
+++ b/src/Items/ItemBow.h
@@ -26,8 +26,12 @@ public:
{
}
+
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
{
ASSERT(a_Player != nullptr);
@@ -40,6 +44,7 @@ public:
a_Player->StartChargingBow();
return true;
}
+
virtual void OnItemShoot(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override
diff --git a/src/Items/ItemBucket.h b/src/Items/ItemBucket.h
index 871db821c..45b4030a3 100644
--- a/src/Items/ItemBucket.h
+++ b/src/Items/ItemBucket.h
@@ -23,13 +23,18 @@ public:
}
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
+
+
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
{
switch (m_ItemType)
{
- case E_ITEM_BUCKET: return ScoopUpFluid(a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir);
- case E_ITEM_LAVA_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_LAVA);
- case E_ITEM_WATER_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_WATER);
+ case E_ITEM_BUCKET: return ScoopUpFluid(a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ case E_ITEM_LAVA_BUCKET: return PlaceFluid (a_World, a_Player, a_PluginInterface, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, E_BLOCK_LAVA);
+ case E_ITEM_WATER_BUCKET: return PlaceFluid (a_World, a_Player, a_PluginInterface, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, E_BLOCK_WATER);
default:
{
ASSERT(!"Unhandled ItemType");
@@ -40,7 +45,7 @@ public:
- bool ScoopUpFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
+ bool ScoopUpFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace)
{
if (a_BlockFace != BLOCK_FACE_NONE)
{
@@ -75,6 +80,19 @@ public:
return false;
}
+ // Check to see if destination block is too far away
+ // Reach Distance Multiplayer = 5 Blocks
+ if ((BlockPos.x - a_Player->GetPosX() > 5) || (BlockPos.z - a_Player->GetPosZ() > 5))
+ {
+ return false;
+ }
+
+ // Remove water / lava block (unless plugins disagree)
+ if (!a_Player->PlaceBlock(BlockPos.x, BlockPos.y, BlockPos.z, E_BLOCK_AIR, 0))
+ {
+ return false;
+ }
+
// Give new bucket, filled with fluid when the gamemode is not creative:
if (!a_Player->IsGameModeCreative())
{
@@ -85,25 +103,40 @@ public:
ASSERT(!"Inventory bucket mismatch");
return true;
}
- a_Player->GetInventory().AddItem(cItem(NewItem), true, true);
+ if (a_Player->GetInventory().AddItem(cItem(NewItem), true, true) != 1)
+ {
+ // The bucket didn't fit, toss it as a pickup:
+ a_Player->TossPickup(cItem(NewItem));
+ }
}
- // Remove water / lava block
- a_Player->GetWorld()->SetBlock(BlockPos.x, BlockPos.y, BlockPos.z, E_BLOCK_AIR, 0);
return true;
}
- bool PlaceFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_FluidBlock)
+
+ bool PlaceFluid(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_FluidBlock
+ )
{
if (a_BlockFace != BLOCK_FACE_NONE)
{
return false;
}
- BLOCKTYPE CurrentBlock;
+ BLOCKTYPE CurrentBlockType;
+ NIBBLETYPE CurrentBlockMeta;
+ eBlockFace EntryFace;
Vector3i BlockPos;
- if (!GetPlacementCoordsFromTrace(a_World, a_Player, BlockPos, CurrentBlock))
+ if (!GetPlacementCoordsFromTrace(a_World, a_Player, BlockPos, CurrentBlockType, CurrentBlockMeta, EntryFace))
+ {
+ return false;
+ }
+
+ // Check to see if destination block is too far away
+ // Reach Distance Multiplayer = 5 Blocks
+ if ((BlockPos.x - a_Player->GetPosX() > 5) || (BlockPos.z - a_Player->GetPosZ() > 5))
{
return false;
}
@@ -125,23 +158,29 @@ public:
}
// Wash away anything that was there prior to placing:
- if (cFluidSimulator::CanWashAway(CurrentBlock))
+ if (cFluidSimulator::CanWashAway(CurrentBlockType))
{
- cBlockHandler * Handler = BlockHandler(CurrentBlock);
+ if (a_PluginInterface.CallHookPlayerBreakingBlock(*a_Player, BlockPos.x, BlockPos.y, BlockPos.z, EntryFace, CurrentBlockType, CurrentBlockMeta))
+ {
+ // Plugin disagrees with the washing-away
+ return false;
+ }
+
+ cBlockHandler * Handler = BlockHandler(CurrentBlockType);
if (Handler->DoesDropOnUnsuitable())
{
cChunkInterface ChunkInterface(a_World->GetChunkMap());
- cBlockInServerPluginInterface PluginInterface(*a_World);
- Handler->DropBlock(ChunkInterface, *a_World, PluginInterface, a_Player, a_BlockX, a_BlockY, a_BlockZ);
+ Handler->DropBlock(ChunkInterface, *a_World, a_PluginInterface, a_Player, BlockPos.x, BlockPos.y, BlockPos.z);
}
+ a_PluginInterface.CallHookPlayerBrokenBlock(*a_Player, BlockPos.x, BlockPos.y, BlockPos.z, EntryFace, CurrentBlockType, CurrentBlockMeta);
}
- a_World->SetBlock(BlockPos.x, BlockPos.y, BlockPos.z, a_FluidBlock, 0);
-
- return true;
+ // Place the actual fluid block:
+ return a_Player->PlaceBlock(BlockPos.x, BlockPos.y, BlockPos.z, a_FluidBlock, 0);
}
+
bool GetBlockFromTrace(cWorld * a_World, cPlayer * a_Player, Vector3i & a_BlockPos)
{
class cCallbacks :
@@ -190,20 +229,25 @@ public:
}
- bool GetPlacementCoordsFromTrace(cWorld * a_World, cPlayer * a_Player, Vector3i & a_BlockPos, BLOCKTYPE & a_BlockType)
+
+ bool GetPlacementCoordsFromTrace(cWorld * a_World, cPlayer * a_Player, Vector3i & a_BlockPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta, eBlockFace & a_BlockFace)
{
class cCallbacks :
public cBlockTracer::cCallbacks
{
public:
Vector3i m_Pos;
- BLOCKTYPE m_ReplacedBlock;
+ BLOCKTYPE m_ReplacedBlockType;
+ NIBBLETYPE m_ReplacedBlockMeta;
+ eBlockFace m_EntryFace;
virtual bool OnNextBlock(int a_CBBlockX, int a_CBBlockY, int a_CBBlockZ, BLOCKTYPE a_CBBlockType, NIBBLETYPE a_CBBlockMeta, char a_CBEntryFace) override
{
if (a_CBBlockType != E_BLOCK_AIR)
{
- m_ReplacedBlock = a_CBBlockType;
+ m_ReplacedBlockType = a_CBBlockType;
+ m_ReplacedBlockMeta = a_CBBlockMeta;
+ m_EntryFace = static_cast<eBlockFace>(a_CBEntryFace);
if (!cFluidSimulator::CanWashAway(a_CBBlockType) && !IsBlockLiquid(a_CBBlockType))
{
AddFaceDirection(a_CBBlockX, a_CBBlockY, a_CBBlockZ, (eBlockFace)a_CBEntryFace); // Was an unwashawayable block, can't overwrite it!
@@ -219,12 +263,14 @@ public:
Vector3d Start(a_Player->GetEyePosition());
Vector3d End(a_Player->GetEyePosition() + a_Player->GetLookVector() * 5);
- // cTracer::Trace returns true when whole line was traversed. By returning true when we hit something, we ensure that this never happens if liquid could be placed
+ // cTracer::Trace returns true when whole line was traversed. By returning true from the callback when we hit something, we ensure that this never happens if liquid could be placed
// Use this to judge whether the position is valid
if (!Tracer.Trace(Start.x, Start.y, Start.z, End.x, End.y, End.z))
{
a_BlockPos = Callbacks.m_Pos;
- a_BlockType = Callbacks.m_ReplacedBlock;
+ a_BlockType = Callbacks.m_ReplacedBlockType;
+ a_BlockMeta = Callbacks.m_ReplacedBlockMeta;
+ a_BlockFace = Callbacks.m_EntryFace;
return true;
}
diff --git a/src/Items/ItemChest.h b/src/Items/ItemChest.h
index b6579c423..1d23975cd 100644
--- a/src/Items/ItemChest.h
+++ b/src/Items/ItemChest.h
@@ -78,7 +78,7 @@ public:
!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(&a_Player, PlaceMeta)
)
{
- // Tried to place a block *into* another?
+ // Tried to place a block into another?
// Happens when you place a block aiming at side of block with a torch on it or stem beside it
return false;
}
diff --git a/src/Items/ItemDoor.h b/src/Items/ItemDoor.h
index 524c49a5c..ddd3d4e20 100644
--- a/src/Items/ItemDoor.h
+++ b/src/Items/ItemDoor.h
@@ -40,7 +40,7 @@ public:
}
// The door needs a compatible block below it:
- if (!cBlockDoorHandler::CanBeOn(a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)))
+ if (!cBlockDoorHandler::CanBeOn(a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ), a_World.GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ)))
{
return false;
}
diff --git a/src/Items/ItemDye.h b/src/Items/ItemDye.h
index bfcd0bac4..273af826a 100644
--- a/src/Items/ItemDye.h
+++ b/src/Items/ItemDye.h
@@ -19,7 +19,12 @@ public:
{
}
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override
+
+
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
{
// Handle growing the plants:
if (a_Item.m_ItemDamage == E_META_DYE_WHITE)
diff --git a/src/Items/ItemEmptyMap.h b/src/Items/ItemEmptyMap.h
index 9238d771b..6e944b4da 100644
--- a/src/Items/ItemEmptyMap.h
+++ b/src/Items/ItemEmptyMap.h
@@ -27,12 +27,17 @@ public:
{
}
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
+
+
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
{
UNUSED(a_Item);
UNUSED(a_BlockX);
UNUSED(a_BlockZ);
- UNUSED(a_Dir);
+ UNUSED(a_BlockFace);
// The map center is fixed at the central point of the 8x8 block of chunks you are standing in when you right-click it.
@@ -60,3 +65,7 @@ public:
return true;
}
} ;
+
+
+
+
diff --git a/src/Items/ItemFishingRod.h b/src/Items/ItemFishingRod.h
index 6350a38ba..5bf4b29b7 100644
--- a/src/Items/ItemFishingRod.h
+++ b/src/Items/ItemFishingRod.h
@@ -93,9 +93,14 @@ public:
{
}
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
+
+
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
{
- if (a_Dir != BLOCK_FACE_NONE)
+ if (a_BlockFace != BLOCK_FACE_NONE)
{
return false;
}
diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp
index 621cf9501..b7f89809e 100644
--- a/src/Items/ItemHandler.cpp
+++ b/src/Items/ItemHandler.cpp
@@ -72,7 +72,7 @@ cItemHandler * cItemHandler::m_ItemHandler[2268];
cItemHandler * cItemHandler::GetItemHandler(int a_ItemType)
{
- if ((a_ItemType < 0) || ((size_t)a_ItemType >= ARRAYCOUNT(m_ItemHandler)))
+ if ((a_ItemType < 0) || (static_cast<size_t>(a_ItemType) >= ARRAYCOUNT(m_ItemHandler)))
{
// Either nothing (-1), or bad value, both cases should return the air handler
if (a_ItemType < -1)
@@ -287,7 +287,7 @@ cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType)
void cItemHandler::Deinit()
{
- for (int i = 0; i < 2267; i++)
+ for (size_t i = 0; i < ARRAYCOUNT(m_ItemHandler); i++)
{
delete m_ItemHandler[i];
m_ItemHandler[i] = nullptr;
@@ -336,7 +336,7 @@ bool cItemHandler::OnPlayerPlace(
if (
BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() ||
BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(&a_Player, ClickedBlockMeta)
- )
+ )
{
cChunkInterface ChunkInterface(a_World.GetChunkMap());
BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ);
@@ -362,7 +362,7 @@ bool cItemHandler::OnPlayerPlace(
!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(&a_Player, PlaceMeta)
)
{
- // Tried to place a block *into* another?
+ // Tried to place a block into another?
// Happens when you place a block aiming at side of block with a torch on it or stem beside it
return false;
}
@@ -411,15 +411,19 @@ bool cItemHandler::OnPlayerPlace(
-bool cItemHandler::OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir)
+bool cItemHandler::OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+)
{
UNUSED(a_World);
UNUSED(a_Player);
+ UNUSED(a_PluginInterface);
UNUSED(a_Item);
UNUSED(a_BlockX);
UNUSED(a_BlockY);
UNUSED(a_BlockZ);
- UNUSED(a_Dir);
+ UNUSED(a_BlockFace);
return false;
}
@@ -539,8 +543,9 @@ char cItemHandler::GetMaxStackSize(void)
case E_ITEM_COMPASS: return 64;
case E_ITEM_COOKED_CHICKEN: return 64;
case E_ITEM_COOKED_FISH: return 64;
- case E_ITEM_COOKED_PORKCHOP: return 64;
case E_ITEM_COOKED_MUTTON: return 64;
+ case E_ITEM_COOKED_PORKCHOP: return 64;
+ case E_ITEM_COOKED_RABBIT: return 64;
case E_ITEM_COOKIE: return 64;
case E_ITEM_DARK_OAK_DOOR: return 64;
case E_ITEM_DIAMOND: return 64;
@@ -575,6 +580,7 @@ char cItemHandler::GetMaxStackSize(void)
case E_ITEM_MELON_SEEDS: return 64;
case E_ITEM_MELON_SLICE: return 64;
case E_ITEM_NETHER_BRICK: return 64;
+ case E_ITEM_NETHER_QUARTZ: return 64;
case E_ITEM_NETHER_WART: return 64;
case E_ITEM_PAINTING: return 64;
case E_ITEM_PAPER: return 64;
@@ -591,6 +597,7 @@ char cItemHandler::GetMaxStackSize(void)
case E_ITEM_RAW_FISH: return 64;
case E_ITEM_RAW_MUTTON: return 64;
case E_ITEM_RAW_PORKCHOP: return 64;
+ case E_ITEM_RAW_RABBIT: return 64;
case E_ITEM_RED_APPLE: return 64;
case E_ITEM_REDSTONE_DUST: return 64;
case E_ITEM_REDSTONE_REPEATER: return 64;
@@ -753,7 +760,7 @@ bool cItemHandler::GetPlacementBlockTypeMeta(
return false;
}
- cBlockHandler * BlockH = BlockHandler((BLOCKTYPE)m_ItemType);
+ cBlockHandler * BlockH = BlockHandler(static_cast<BLOCKTYPE>(m_ItemType));
cChunkInterface ChunkInterface(a_World->GetChunkMap());
return BlockH->GetPlacementBlockTypeMeta(
ChunkInterface, a_Player,
diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h
index 3ac664798..ec88aeb99 100644
--- a/src/Items/ItemHandler.h
+++ b/src/Items/ItemHandler.h
@@ -4,6 +4,7 @@
#include "../Defines.h"
#include "../Item.h"
#include "../Entities/EntityEffect.h"
+#include "../Blocks/BlockPluginInterface.h"
@@ -56,8 +57,12 @@ public:
);
- /** Called when the player tries to use the item (right mouse button). Return false to make the item unusable. DEFAULT: False */
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir);
+ /** Called when the player tries to use the item (right mouse button).
+ Return false to abort the usage. DEFAULT: False */
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ );
/** Called when the client sends the SHOOT status in the lclk packet */
diff --git a/src/Items/ItemHoe.h b/src/Items/ItemHoe.h
index ae3723323..ce6355ee7 100644
--- a/src/Items/ItemHoe.h
+++ b/src/Items/ItemHoe.h
@@ -18,9 +18,14 @@ public:
{
}
- virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
+
+
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
{
- if ((a_Dir == BLOCK_FACE_NONE) || (a_BlockY >= cChunkDef::Height))
+ if ((a_BlockFace == BLOCK_FACE_NONE) || (a_BlockY >= cChunkDef::Height))
{
return false;
}
diff --git a/src/Items/ItemItemFrame.h b/src/Items/ItemItemFrame.h
index 5d22c1cb8..77a5bf47c 100644
--- a/src/Items/ItemItemFrame.h
+++ b/src/Items/ItemItemFrame.h
@@ -19,21 +19,26 @@ public:
}
- virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
+
+
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
{
- if ((a_Dir == BLOCK_FACE_NONE) || (a_Dir == BLOCK_FACE_YP) || (a_Dir == BLOCK_FACE_YM))
+ if ((a_BlockFace == BLOCK_FACE_NONE) || (a_BlockFace == BLOCK_FACE_YP) || (a_BlockFace == BLOCK_FACE_YM))
{
// Client sends this if clicked on top or bottom face
return false;
}
- AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_Dir); // Make sure block that will be occupied is free
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); // Make sure block that will be occupied is free
BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
- AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_Dir, true); // We want the clicked block, so go back again
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); // We want the clicked block, so go back again
if (Block == E_BLOCK_AIR)
{
- cItemFrame * ItemFrame = new cItemFrame(a_Dir, a_BlockX, a_BlockY, a_BlockZ);
+ cItemFrame * ItemFrame = new cItemFrame(a_BlockFace, a_BlockX, a_BlockY, a_BlockZ);
if (!ItemFrame->Initialize(*a_World))
{
delete ItemFrame;
diff --git a/src/Items/ItemLighter.h b/src/Items/ItemLighter.h
index 9f98bf85f..24641dce6 100644
--- a/src/Items/ItemLighter.h
+++ b/src/Items/ItemLighter.h
@@ -19,7 +19,12 @@ public:
{
}
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override
+
+
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
{
if (a_BlockFace < 0)
{
@@ -59,7 +64,7 @@ public:
}
default:
{
- // Light a fire next to/on top of the block if air:
+ // Light a fire next to / on top of the block if air:
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
{
diff --git a/src/Items/ItemLilypad.h b/src/Items/ItemLilypad.h
index b9d837384..7eede5864 100644
--- a/src/Items/ItemLilypad.h
+++ b/src/Items/ItemLilypad.h
@@ -29,7 +29,11 @@ public:
}
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override
+
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
{
if (a_BlockFace > BLOCK_FACE_NONE)
{
@@ -67,7 +71,7 @@ public:
cBlockInfo::FullyOccupiesVoxel(a_CBBlockType)
)
{
- // Can't place lilypad on air/in another block!
+ // Can't place lilypad on air / in another block!
return true;
}
m_HasHitFluid = true;
diff --git a/src/Items/ItemMinecart.h b/src/Items/ItemMinecart.h
index ed0a4711c..e7d2cf8cd 100644
--- a/src/Items/ItemMinecart.h
+++ b/src/Items/ItemMinecart.h
@@ -27,9 +27,12 @@ public:
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
{
- if (a_Dir < 0)
+ if (a_BlockFace < 0)
{
return false;
}
diff --git a/src/Items/ItemPainting.h b/src/Items/ItemPainting.h
index d6f2e24b4..dd35931dd 100644
--- a/src/Items/ItemPainting.h
+++ b/src/Items/ItemPainting.h
@@ -19,15 +19,20 @@ public:
{
}
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
+
+
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
{
- if ((a_Dir == BLOCK_FACE_NONE) || (a_Dir == BLOCK_FACE_YM) || (a_Dir == BLOCK_FACE_YP))
+ if ((a_BlockFace == BLOCK_FACE_NONE) || (a_BlockFace == BLOCK_FACE_YM) || (a_BlockFace == BLOCK_FACE_YP))
{
// Paintings can't be flatly placed
return false;
}
- AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_Dir); // Make sure block that will be occupied is free
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); // Make sure block that will be occupied is free
BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
if (Block == E_BLOCK_AIR)
@@ -65,7 +70,7 @@ public:
{ "BurningSkull" }
};
- cPainting * Painting = new cPainting(gPaintingTitlesList[a_World->GetTickRandomNumber(ARRAYCOUNT(gPaintingTitlesList) - 1)].Title, a_Dir, a_BlockX, a_BlockY, a_BlockZ);
+ cPainting * Painting = new cPainting(gPaintingTitlesList[a_World->GetTickRandomNumber(ARRAYCOUNT(gPaintingTitlesList) - 1)].Title, a_BlockFace, a_BlockX, a_BlockY, a_BlockZ);
Painting->Initialize(*a_World);
if (!a_Player->IsGameModeCreative())
diff --git a/src/Items/ItemPotion.h b/src/Items/ItemPotion.h
index 798573846..a176c591e 100644
--- a/src/Items/ItemPotion.h
+++ b/src/Items/ItemPotion.h
@@ -26,7 +26,10 @@ public:
}
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
{
short PotionDamage = a_Item.m_ItemDamage;
diff --git a/src/Items/ItemSign.h b/src/Items/ItemSign.h
index dabbdbba1..3da93e2f1 100644
--- a/src/Items/ItemSign.h
+++ b/src/Items/ItemSign.h
@@ -25,7 +25,7 @@ public:
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ
- )
+ ) override
{
// If the regular placement doesn't work, do no further processing:
if (!super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
diff --git a/src/Items/ItemSlab.h b/src/Items/ItemSlab.h
index b0b5ce005..3a78cc016 100644
--- a/src/Items/ItemSlab.h
+++ b/src/Items/ItemSlab.h
@@ -39,10 +39,15 @@ public:
int a_CursorX, int a_CursorY, int a_CursorZ
) override
{
+ // Prepare sound effect
+ AString PlaceSound = cBlockInfo::GetPlaceSound(m_ItemType);
+ float Volume = 1.0f, Pitch = 0.8f;
+
// Special slab handling - placing a slab onto another slab produces a dblslab instead:
BLOCKTYPE ClickedBlockType;
NIBBLETYPE ClickedBlockMeta;
a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlockType, ClickedBlockMeta);
+ // If clicked on a half slab directly
if (
(ClickedBlockType == m_ItemType) && // Placing the same slab material
((ClickedBlockMeta & 0x07) == a_EquippedItem.m_ItemDamage) // Placing the same slab sub-kind (and existing slab is single)
@@ -54,6 +59,7 @@ public:
((ClickedBlockMeta & 0x08) == 0)
)
{
+ a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch);
return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07);
}
@@ -63,11 +69,28 @@ public:
((ClickedBlockMeta & 0x08) != 0)
)
{
+ a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch);
return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07);
}
}
+ // Checking the type of block that should be placed
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ BLOCKTYPE PlaceBlockType;
+ NIBBLETYPE PlaceBlockMeta;
+ a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlockType, PlaceBlockMeta);
+ // If it's a slab combine into a doubleslab (means that clicked on side, top or bottom of a block adjacent to a half slab)
+ if (
+ (PlaceBlockType == m_ItemType) && // Placing the same slab material
+ ((PlaceBlockMeta & 0x07) == a_EquippedItem.m_ItemDamage) // Placing the same slab sub-kind (and existing slab is single)
+ )
+ {
+ a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch);
+ return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, PlaceBlockMeta & 0x07);
+ }
+
// The slabs didn't combine, use the default handler to place the slab:
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true);
bool res = super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
/*
diff --git a/src/Items/ItemSpawnEgg.h b/src/Items/ItemSpawnEgg.h
index a07e4ef49..b67fe074d 100644
--- a/src/Items/ItemSpawnEgg.h
+++ b/src/Items/ItemSpawnEgg.h
@@ -19,7 +19,11 @@ public:
}
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override
+
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
{
if (a_BlockFace < 0)
{
diff --git a/src/Items/ItemThrowable.h b/src/Items/ItemThrowable.h
index cdcbdab3b..0e06623fc 100644
--- a/src/Items/ItemThrowable.h
+++ b/src/Items/ItemThrowable.h
@@ -26,7 +26,11 @@ public:
}
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
+
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
{
Vector3d Pos = a_Player->GetThrowStartPos();
Vector3d Speed = a_Player->GetLookVector() * m_SpeedCoeff;
@@ -128,7 +132,12 @@ public:
{
}
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
+
+
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
{
if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR)
{
@@ -149,3 +158,7 @@ public:
}
};
+
+
+
+
diff --git a/src/Logger.h b/src/Logger.h
index 5e65de8a8..176c6e810 100644
--- a/src/Logger.h
+++ b/src/Logger.h
@@ -46,10 +46,10 @@ private:
-extern void LOG(const char* a_Format, ...) FORMATSTRING(1, 2);
-extern void LOGINFO(const char* a_Format, ...) FORMATSTRING(1, 2);
-extern void LOGWARN(const char* a_Format, ...) FORMATSTRING(1, 2);
-extern void LOGERROR(const char* a_Format, ...) FORMATSTRING(1, 2);
+extern void LOG (const char * a_Format, ...) FORMATSTRING(1, 2);
+extern void LOGINFO (const char * a_Format, ...) FORMATSTRING(1, 2);
+extern void LOGWARN (const char * a_Format, ...) FORMATSTRING(1, 2);
+extern void LOGERROR(const char * a_Format, ...) FORMATSTRING(1, 2);
diff --git a/src/Map.cpp b/src/Map.cpp
index 5e57cc8ec..5f296a5b2 100644
--- a/src/Map.cpp
+++ b/src/Map.cpp
@@ -591,7 +591,7 @@ void cMap::SendTo(cClientHandle & a_Client)
for (unsigned int i = 0; i < m_Width; ++i)
{
- const Byte* Colors = &m_Data[i * m_Height];
+ const Byte * Colors = &m_Data[i * m_Height];
a_Client.SendMapColumn(m_ID, i, 0, Colors, m_Height, m_Scale);
}
diff --git a/src/Map.h b/src/Map.h
index 3e775231a..a764eda50 100644
--- a/src/Map.h
+++ b/src/Map.h
@@ -140,7 +140,7 @@ public:
/** Update a circular region around the specified player. */
void UpdateRadius(cPlayer & a_Player, unsigned int a_Radius);
- /** Send next update packet to the specified player and remove invalid decorators/clients. */
+ /** Send next update packet to the specified player and remove invalid decorators / clients. */
void UpdateClient(cPlayer * a_Player);
// tolua_begin
diff --git a/src/MemorySettingsRepository.cpp b/src/MemorySettingsRepository.cpp
new file mode 100644
index 000000000..21b4c769c
--- /dev/null
+++ b/src/MemorySettingsRepository.cpp
@@ -0,0 +1,312 @@
+
+#include "Globals.h"
+
+#include "MemorySettingsRepository.h"
+
+
+
+
+bool cMemorySettingsRepository::KeyExists(const AString keyname) const
+{
+ return m_Map.count(keyname) != 0;
+}
+
+
+
+
+
+bool cMemorySettingsRepository::HasValue(const AString & a_KeyName, const AString & a_ValueName) const
+{
+ auto outerIter = m_Map.find(a_KeyName);
+ if (outerIter == m_Map.end())
+ {
+ return false;
+ }
+ auto iter = outerIter->second.find(a_ValueName);
+ if (iter == outerIter->second.end())
+ {
+ return false;
+ }
+ return true;
+}
+
+
+
+
+int cMemorySettingsRepository::AddKeyName(const AString & a_keyname)
+{
+ m_Map.emplace(a_keyname, std::unordered_multimap<AString, sValue>{});
+ return 0;
+}
+
+
+
+
+
+bool cMemorySettingsRepository::AddKeyComment(const AString & keyname, const AString & comment)
+{
+ return false;
+}
+
+
+
+
+
+AString cMemorySettingsRepository::GetKeyComment(const AString & keyname, const int commentID) const
+{
+ return "";
+}
+
+
+
+
+
+bool cMemorySettingsRepository::DeleteKeyComment(const AString & keyname, const int commentID)
+{
+ return false;
+}
+
+
+
+
+
+
+void cMemorySettingsRepository::AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value)
+{
+ if (m_Writable)
+ {
+ m_Map[a_KeyName].emplace(a_ValueName, sValue(a_Value));
+ }
+}
+
+
+
+
+void cMemorySettingsRepository::AddValue (const AString & a_KeyName, const AString & a_ValueName, Int64 a_Value)
+{
+ if (m_Writable)
+ {
+ m_Map[a_KeyName].emplace(a_ValueName, sValue(a_Value));
+ }
+}
+
+
+
+
+
+void cMemorySettingsRepository::AddValue (const AString & a_KeyName, const AString & a_ValueName, bool a_Value)
+{
+ if (m_Writable)
+ {
+ m_Map[a_KeyName].emplace(a_ValueName, sValue(a_Value));
+ }
+}
+
+
+
+
+
+std::vector<std::pair<AString, AString>> cMemorySettingsRepository::GetValues(AString a_keyName)
+{
+ std::vector<std::pair<AString, AString>> ret;
+ for (auto pair : m_Map[a_keyName])
+ {
+ ret.emplace_back(pair.first, pair.second.getStringValue());
+ }
+ return ret;
+}
+
+
+
+
+
+AString cMemorySettingsRepository::GetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & defValue) const
+{
+ auto outerIter = m_Map.find(a_KeyName);
+ if (outerIter == m_Map.end())
+ {
+ return defValue;
+ }
+ auto iter = outerIter->second.find(a_ValueName);
+ if (iter == outerIter->second.end())
+ {
+ return defValue;
+ }
+ return iter->second.getStringValue();
+}
+
+
+
+
+
+AString cMemorySettingsRepository::GetValueSet (const AString & a_KeyName, const AString & a_ValueName, const AString & defValue)
+{
+ auto outerIter = m_Map.find(a_KeyName);
+ if (outerIter == m_Map.end())
+ {
+ AddValue(a_KeyName, a_ValueName, defValue);
+ return defValue;
+ }
+ auto iter = outerIter->second.find(a_ValueName);
+ if (iter == outerIter->second.end())
+ {
+ AddValue(a_KeyName, a_ValueName, defValue);
+ return defValue;
+ }
+ return iter->second.getStringValue();
+}
+
+
+
+
+
+int cMemorySettingsRepository::GetValueSetI(const AString & a_KeyName, const AString & a_ValueName, const int defValue)
+{
+ auto outerIter = m_Map.find(a_KeyName);
+ if (outerIter == m_Map.end())
+ {
+ AddValue(a_KeyName, a_ValueName, static_cast<Int64>(defValue));
+ return defValue;
+ }
+ auto iter = outerIter->second.find(a_ValueName);
+ if (iter == outerIter->second.end())
+ {
+ AddValue(a_KeyName, a_ValueName, static_cast<Int64>(defValue));
+ return defValue;
+ }
+ return static_cast<int>(iter->second.getIntValue());
+}
+
+
+
+
+
+Int64 cMemorySettingsRepository::GetValueSetI(const AString & a_KeyName, const AString & a_ValueName, const Int64 defValue)
+{
+ auto outerIter = m_Map.find(a_KeyName);
+ if (outerIter == m_Map.end())
+ {
+ AddValue(a_KeyName, a_ValueName, defValue);
+ return defValue;
+ }
+ auto iter = outerIter->second.find(a_ValueName);
+ if (iter == outerIter->second.end())
+ {
+ AddValue(a_KeyName, a_ValueName, defValue);
+ return defValue;
+ }
+ return iter->second.getIntValue();
+}
+
+
+
+
+bool cMemorySettingsRepository::GetValueSetB(const AString & a_KeyName, const AString & a_ValueName, const bool defValue)
+{
+ auto outerIter = m_Map.find(a_KeyName);
+ if (outerIter == m_Map.end())
+ {
+ AddValue(a_KeyName, a_ValueName, defValue);
+ return defValue;
+ }
+ auto iter = outerIter->second.find(a_ValueName);
+ if (iter == outerIter->second.end())
+ {
+ AddValue(a_KeyName, a_ValueName, defValue);
+ return defValue;
+ }
+ return iter->second.getBoolValue();
+}
+
+
+
+
+
+bool cMemorySettingsRepository::SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists)
+{
+ if (!m_Writable)
+ {
+ return false;
+ }
+ auto outerIter = m_Map.find(a_KeyName);
+ if (outerIter == m_Map.end())
+ {
+ if (a_CreateIfNotExists)
+ {
+ AddValue(a_KeyName, a_ValueName, a_Value);
+ }
+ return a_CreateIfNotExists;
+ }
+ auto iter = outerIter->second.find(a_ValueName);
+ if (iter == outerIter->second.end())
+ {
+ if (a_CreateIfNotExists)
+ {
+ AddValue(a_KeyName, a_ValueName, a_Value);
+ }
+ return a_CreateIfNotExists;
+ }
+ iter->second = sValue(a_Value);
+ return true;
+}
+
+
+
+
+bool cMemorySettingsRepository::SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists)
+{
+ if (!m_Writable)
+ {
+ return false;
+ }
+ auto outerIter = m_Map.find(a_KeyName);
+ if (outerIter == m_Map.end())
+ {
+ if (a_CreateIfNotExists)
+ {
+ AddValue(a_KeyName, a_ValueName, static_cast<Int64>(a_Value));
+ }
+ return a_CreateIfNotExists;
+ }
+ auto iter = outerIter->second.find(a_ValueName);
+ if (iter == outerIter->second.end())
+ {
+ if (a_CreateIfNotExists)
+ {
+ AddValue(a_KeyName, a_ValueName, static_cast<Int64>(a_Value));
+ }
+ return a_CreateIfNotExists;
+ }
+ iter->second = sValue(static_cast<Int64>(a_Value));
+ return true;
+}
+
+
+
+
+
+bool cMemorySettingsRepository::DeleteValue(const AString & a_KeyName, const AString & a_ValueName)
+{
+ if (!m_Writable)
+ {
+ return false;
+ }
+ auto outerIter = m_Map.find(a_KeyName);
+ if (outerIter == m_Map.end())
+ {
+ return false;
+ }
+ auto iter = outerIter->second.find(a_ValueName);
+ if (iter == outerIter->second.end())
+ {
+ return false;
+ }
+ outerIter->second.erase(iter);
+ return true;
+}
+
+bool cMemorySettingsRepository::Flush()
+{
+ return true;
+}
+
diff --git a/src/MemorySettingsRepository.h b/src/MemorySettingsRepository.h
new file mode 100644
index 000000000..335bc4513
--- /dev/null
+++ b/src/MemorySettingsRepository.h
@@ -0,0 +1,80 @@
+
+#pragma once
+
+#include "SettingsRepositoryInterface.h"
+
+#include <unordered_map>
+
+class cMemorySettingsRepository : public cSettingsRepositoryInterface
+{
+public:
+
+ virtual bool KeyExists(const AString keyname) const override;
+
+ virtual bool HasValue(const AString & a_KeyName, const AString & a_ValueName) const override;
+
+ virtual int AddKeyName(const AString & keyname) override;
+
+ virtual bool AddKeyComment(const AString & keyname, const AString & comment) override;
+
+ virtual AString GetKeyComment(const AString & keyname, const int commentID) const override;
+
+ virtual bool DeleteKeyComment(const AString & keyname, const int commentID) override;
+
+ virtual void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) override;
+ void AddValue (const AString & a_KeyName, const AString & a_ValueName, const Int64 a_Value);
+ void AddValue (const AString & a_KeyName, const AString & a_ValueName, const bool a_Value);
+
+ virtual std::vector<std::pair<AString, AString>> GetValues(AString a_keyName) override;
+
+ virtual AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const override;
+
+
+ virtual AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = "") override;
+ virtual int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0) override;
+ virtual Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0) override;
+ virtual bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) override;
+
+ virtual bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true) override;
+ virtual bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true) override;
+
+ virtual bool DeleteValue(const AString & keyname, const AString & valuename) override;
+
+ virtual bool Flush() override;
+
+ void SetReadOnly()
+ {
+ m_Writable = false;
+ }
+
+private:
+
+ bool m_Writable = true;
+
+ struct sValue
+ {
+ sValue(AString value) : m_Type(eType::String), m_stringValue (value) {}
+ sValue(Int64 value) : m_Type(eType::Int64), m_intValue(value) {}
+ sValue(bool value) : m_Type(eType::Bool), m_boolValue(value) {}
+
+ AString getStringValue() const { ASSERT(m_Type == eType::String); return m_stringValue; }
+ Int64 getIntValue() const { ASSERT(m_Type == eType::Int64); return m_intValue; }
+ bool getBoolValue() const { ASSERT(m_Type == eType::Bool); return m_boolValue; }
+ private:
+ enum class eType
+ {
+ String,
+ Int64,
+ Bool
+ } m_Type;
+ AString m_stringValue;
+ union
+ {
+ Int64 m_intValue;
+ bool m_boolValue;
+ };
+ };
+
+ std::unordered_map<AString, std::unordered_multimap<AString, sValue>> m_Map{};
+};
+
diff --git a/src/MobCensus.cpp b/src/MobCensus.cpp
index 4109aff07..1a69d8370 100644
--- a/src/MobCensus.cpp
+++ b/src/MobCensus.cpp
@@ -19,8 +19,8 @@ void cMobCensus::CollectMob(cMonster & a_Monster, cChunk & a_Chunk, double a_Dis
bool cMobCensus::IsCapped(cMonster::eFamily a_MobFamily)
{
- const int ratio = 319; // This should be 256 as we are only supposed to take account from chunks that are in 17x17 from a player
- // but for now, we use all chunks loaded by players. that means 19 x 19 chunks. That's why we use 256 * (19*19) / (17*17) = 319
+ const int ratio = 319; // This should be 256 as we are only supposed to take account from chunks that are in 17 x 17 from a player
+ // but for now, we use all chunks loaded by players. that means 19 x 19 chunks. That's why we use 256 * (19 * 19) / (17 * 17) = 319
// MG TODO : code the correct count
if ((GetCapMultiplier(a_MobFamily) * GetNumChunks()) / ratio >= m_MobFamilyCollecter.GetNumberOfCollectedMobs(a_MobFamily))
{
diff --git a/src/MobCensus.h b/src/MobCensus.h
index e3892bec6..3dc7b2a8e 100644
--- a/src/MobCensus.h
+++ b/src/MobCensus.h
@@ -32,8 +32,8 @@ public:
// MG TODO : code the correct rule (not loaded chunk but short distant from players)
void CollectSpawnableChunk(cChunk & a_Chunk);
- /// Collect a mob - it's distance to player, it's family ...
- void CollectMob(cMonster& a_Monster, cChunk& a_Chunk, double a_Distance);
+ /// Collect a mob - its distance to player, its family ...
+ void CollectMob(cMonster & a_Monster, cChunk & a_Chunk, double a_Distance);
/// Returns true if the family is capped (i.e. there are more mobs of this family than max)
bool IsCapped(cMonster::eFamily a_MobFamily);
diff --git a/src/MobProximityCounter.cpp b/src/MobProximityCounter.cpp
index cfd52440b..1bdac2f88 100644
--- a/src/MobProximityCounter.cpp
+++ b/src/MobProximityCounter.cpp
@@ -6,7 +6,11 @@
#include "Entities/Entity.h"
#include "Chunk.h"
-void cMobProximityCounter::CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance)
+
+
+
+
+void cMobProximityCounter::CollectMob(cEntity & a_Monster, cChunk & a_Chunk, double a_Distance)
{
// LOGD("Collecting monster %s, with distance %f", a_Monster->GetClass(), a_Distance);
tMonsterToDistance::iterator it = m_MonsterToDistance.find(&a_Monster);
diff --git a/src/MobProximityCounter.h b/src/MobProximityCounter.h
index 2dabeaa21..a3c0c125a 100644
--- a/src/MobProximityCounter.h
+++ b/src/MobProximityCounter.h
@@ -16,19 +16,19 @@ protected :
// structs used for later maps (see m_MonsterToDistance and m_DistanceToMonster)
struct sDistanceAndChunk
{
- sDistanceAndChunk(double a_Distance, cChunk& a_Chunk) : m_Distance(a_Distance), m_Chunk(&a_Chunk) {}
+ sDistanceAndChunk(double a_Distance, cChunk & a_Chunk) : m_Distance(a_Distance), m_Chunk(&a_Chunk) {}
double m_Distance;
- cChunk* m_Chunk;
+ cChunk * m_Chunk;
};
struct sMonsterAndChunk
{
- sMonsterAndChunk(cEntity& a_Monster, cChunk& a_Chunk) : m_Monster(a_Monster), m_Chunk(a_Chunk) {}
- cEntity& m_Monster;
- cChunk& m_Chunk;
+ sMonsterAndChunk(cEntity & a_Monster, cChunk & a_Chunk) : m_Monster(a_Monster), m_Chunk(a_Chunk) {}
+ cEntity & m_Monster;
+ cChunk & m_Chunk;
};
public :
- typedef std::map<cEntity*, sDistanceAndChunk> tMonsterToDistance;
+ typedef std::map<cEntity *, sDistanceAndChunk> tMonsterToDistance;
typedef std::multimap<double, sMonsterAndChunk> tDistanceToMonster;
protected :
@@ -50,7 +50,7 @@ public :
// count a mob on a specified chunk with specified distance to an unkown player
// if the distance is shortest than the one collected, this become the new closest
// distance and the chunk become the "hosting" chunk (that is the one that will perform the action)
- void CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance);
+ void CollectMob(cEntity & a_Monster, cChunk & a_Chunk, double a_Distance);
// return the mobs that are within the range of distance of the closest player they are
// that means that if a mob is 30 m from a player and 150 m from another one. It will be
diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp
index dfeab1461..468b0cfb2 100644
--- a/src/MobSpawner.cpp
+++ b/src/MobSpawner.cpp
@@ -321,9 +321,9 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
-cMonster* cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int& a_MaxPackSize)
+cMonster * cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int & a_MaxPackSize)
{
- cMonster* toReturn = nullptr;
+ cMonster * toReturn = nullptr;
if (m_NewPack)
{
m_MobType = ChooseMobType(a_Biome);
diff --git a/src/MobSpawner.h b/src/MobSpawner.h
index e8b8f191b..8b651d6bf 100644
--- a/src/MobSpawner.h
+++ b/src/MobSpawner.h
@@ -39,7 +39,7 @@ public :
// if this is the first of a Pack : determine the type of monster
// BlockType & BlockMeta are used to decide what kind of Mob can Spawn here
// MaxPackSize is set to the maximal size for a pack this type of mob
- cMonster * TryToSpawnHere(cChunk * a_Chunk, int A_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int& a_MaxPackSize);
+ cMonster * TryToSpawnHere(cChunk * a_Chunk, int A_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int & a_MaxPackSize);
// mark the beginning of a new Pack
// all mobs of the same Pack are the same type
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp
index 526b39e39..648599999 100644
--- a/src/Mobs/AggressiveMonster.cpp
+++ b/src/Mobs/AggressiveMonster.cpp
@@ -36,11 +36,7 @@ void cAggressiveMonster::InStateChasing(std::chrono::milliseconds a_Dt)
return;
}
}
-
- if (!IsMovingToTargetPosition())
- {
- MoveToPosition(m_Target->GetPosition());
- }
+ MoveToPosition(m_Target->GetPosition());
}
}
@@ -80,9 +76,11 @@ void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
}
cTracer LineOfSight(GetWorld());
- Vector3d AttackDirection(m_Target->GetPosition() - GetPosition());
+ Vector3d MyHeadPosition = GetPosition() + Vector3d(0, GetHeight(), 0);
+ Vector3d AttackDirection(m_Target->GetPosition() + Vector3d(0, m_Target->GetHeight(), 0) - MyHeadPosition);
- if (ReachedFinalDestination() && !LineOfSight.Trace(GetPosition(), AttackDirection, (int)AttackDirection.Length()))
+
+ if (ReachedFinalDestination() && !LineOfSight.Trace(MyHeadPosition, AttackDirection, static_cast<int>(AttackDirection.Length())))
{
// Attack if reached destination, target isn't null, and have a clear line of sight to target (so won't attack through walls)
Attack(a_Dt);
@@ -100,7 +98,7 @@ void cAggressiveMonster::Attack(std::chrono::milliseconds a_Dt)
{
return;
}
-
+
// Setting this higher gives us more wiggle room for attackrate
m_AttackInterval = 0.0;
m_Target->TakeDamage(dtMobAttack, this, m_AttackDamage, 0);
diff --git a/src/Mobs/Bat.cpp b/src/Mobs/Bat.cpp
index c072d4f48..e187e928a 100644
--- a/src/Mobs/Bat.cpp
+++ b/src/Mobs/Bat.cpp
@@ -9,6 +9,8 @@
cBat::cBat(void) :
super("Bat", mtBat, "mob.bat.hurt", "mob.bat.death", 0.5, 0.9)
{
+ SetGravity(-2.0f);
+ SetAirDrag(0.05f);
}
diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp
index 89eeb3709..d4ad24166 100644
--- a/src/Mobs/Blaze.cpp
+++ b/src/Mobs/Blaze.cpp
@@ -11,6 +11,8 @@
cBlaze::cBlaze(void) :
super("Blaze", mtBlaze, "mob.blaze.hit", "mob.blaze.death", 0.6, 1.8)
{
+ SetGravity(-8.0f);
+ SetAirDrag(0.05f);
}
diff --git a/src/Mobs/CMakeLists.txt b/src/Mobs/CMakeLists.txt
index 7a291dcf2..ffbcdf3ea 100644
--- a/src/Mobs/CMakeLists.txt
+++ b/src/Mobs/CMakeLists.txt
@@ -24,6 +24,7 @@ SET (SRCS
Mooshroom.cpp
PassiveAggressiveMonster.cpp
PassiveMonster.cpp
+ Path.cpp
Pig.cpp
Rabbit.cpp
Sheep.cpp
@@ -62,6 +63,7 @@ SET (HDRS
Ocelot.h
PassiveAggressiveMonster.h
PassiveMonster.h
+ Path.h
Pig.h
Rabbit.h
Sheep.h
diff --git a/src/Mobs/Horse.h b/src/Mobs/Horse.h
index be283705e..27168ebae 100644
--- a/src/Mobs/Horse.h
+++ b/src/Mobs/Horse.h
@@ -26,7 +26,7 @@ public:
bool IsEating (void) const {return m_bIsEating; }
bool IsRearing (void) const {return m_bIsRearing; }
bool IsMthOpen (void) const {return m_bIsMouthOpen; }
- bool IsTame (void) const {return m_bIsTame; }
+ bool IsTame (void) const override {return m_bIsTame; }
int GetHorseType (void) const {return m_Type; }
int GetHorseColor (void) const {return m_Color; }
int GetHorseStyle (void) const {return m_Style; }
diff --git a/src/Mobs/IronGolem.cpp b/src/Mobs/IronGolem.cpp
index dae4615e4..b0e76daca 100644
--- a/src/Mobs/IronGolem.cpp
+++ b/src/Mobs/IronGolem.cpp
@@ -8,7 +8,7 @@
cIronGolem::cIronGolem(void) :
- super("IronGolem", mtIronGolem, "mob.IronGolem.hit", "mob.IronGolem.death", 1.4, 2.9)
+ super("IronGolem", mtIronGolem, "mob.irongolem.hit", "mob.irongolem.death", 1.4, 2.9)
{
}
diff --git a/src/Mobs/MagmaCube.cpp b/src/Mobs/MagmaCube.cpp
index 3e9abc108..c5dd0def0 100644
--- a/src/Mobs/MagmaCube.cpp
+++ b/src/Mobs/MagmaCube.cpp
@@ -7,7 +7,7 @@
cMagmaCube::cMagmaCube(int a_Size) :
- super("MagmaCube", mtMagmaCube, "mob.MagmaCube.big", "mob.MagmaCube.big", 0.6 * a_Size, 0.6 * a_Size),
+ super("MagmaCube", mtMagmaCube, Printf("mob.magmacube.%s", GetSizeName(a_Size).c_str()), Printf("mob.magmacube.%s", GetSizeName(a_Size).c_str()), 0.6 * a_Size, 0.6 * a_Size),
m_Size(a_Size)
{
}
@@ -27,4 +27,14 @@ void cMagmaCube::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-
+AString cMagmaCube::GetSizeName(int a_Size)
+{
+ if (a_Size > 1)
+ {
+ return "big";
+ }
+ else
+ {
+ return "small";
+ }
+}
diff --git a/src/Mobs/MagmaCube.h b/src/Mobs/MagmaCube.h
index d66ea423a..b914dc867 100644
--- a/src/Mobs/MagmaCube.h
+++ b/src/Mobs/MagmaCube.h
@@ -19,10 +19,14 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
int GetSize(void) const { return m_Size; }
+
+ /** Returns the text describing the slime's size, as used by the client's resource subsystem for sounds.
+ Returns either "big" or "small". */
+ static AString GetSizeName(int a_Size);
protected:
- /// Size of the MagmaCube, 1 .. 3, with 1 being the smallest
+ /// Size of the MagmaCube, 1, 2 and 4, with 1 being the smallest
int m_Size;
} ;
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index a86497753..1da4124ed 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -13,7 +13,7 @@
#include "../Chunk.h"
#include "../FastRandom.h"
-
+#include "Path.h"
@@ -38,6 +38,7 @@ static const struct
{mtEnderman, "enderman", "Enderman"},
{mtEnderDragon, "enderdragon", "EnderDragon"},
{mtGhast, "ghast", "Ghast"},
+ {mtGiant, "giant", "Giant"},
{mtGuardian, "guardian", "Guardian"},
{mtHorse, "horse", "EntityHorse"},
{mtIronGolem, "irongolem", "VillagerGolem"},
@@ -73,8 +74,12 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_EMState(IDLE)
, m_EMPersonality(AGGRESSIVE)
, m_Target(nullptr)
- , m_bMovingToDestination(false)
+ , m_Path(nullptr)
+ , m_IsFollowingPath(false)
+ , m_GiveUpCounter(0)
+ , m_TicksSinceLastPathReset(1000)
, m_LastGroundHeight(POSY_TOINT)
+ , m_JumpCoolDown(0)
, m_IdleInterval(0)
, m_DestroyTimer(0)
, m_MobType(a_MobType)
@@ -84,7 +89,7 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_SoundDeath(a_SoundDeath)
, m_AttackRate(3)
, m_AttackDamage(1)
- , m_AttackRange(2)
+ , m_AttackRange(1)
, m_AttackInterval(0)
, m_SightDistance(25)
, m_DropChanceWeapon(0.085f)
@@ -93,8 +98,9 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_DropChanceLeggings(0.085f)
, m_DropChanceBoots(0.085f)
, m_CanPickUpLoot(true)
+ , m_TicksSinceLastDamaged(100)
, m_BurnsInDaylight(false)
- , m_RelativeWalkSpeed(1.0)
+ , m_RelativeWalkSpeed(1)
{
if (!a_ConfigName.empty())
{
@@ -115,89 +121,159 @@ void cMonster::SpawnOn(cClientHandle & a_Client)
-void cMonster::TickPathFinding()
+bool cMonster::TickPathFinding(cChunk & a_Chunk)
{
- const int PosX = POSX_TOINT;
- const int PosY = POSY_TOINT;
- const int PosZ = POSZ_TOINT;
-
- std::vector<Vector3d> m_PotentialCoordinates;
- m_TraversedCoordinates.push_back(Vector3i(PosX, PosY, PosZ));
-
- static const struct // Define which directions to try to move to
+ if (!m_IsFollowingPath)
{
- int x, z;
- } gCrossCoords[] =
+ return false;
+ }
+ if (m_TicksSinceLastPathReset < 1000)
{
- { 1, 0},
- {-1, 0},
- { 0, 1},
- { 0, -1},
- } ;
-
- if ((PosY - 1 < 0) || (PosY + 2 >= cChunkDef::Height) /* PosY + 1 will never be true if PosY + 2 is not */)
+ // No need to count beyond 1000. 1000 is arbitary here.
+ ++m_TicksSinceLastPathReset;
+ }
+
+ if (ReachedFinalDestination())
{
- // Too low/high, can't really do anything
- FinishPathFinding();
- return;
+ StopMovingToPosition();
+ return false;
}
- for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
+ if ((m_FinalDestination - m_PathFinderDestination).Length() > 0.25) // if the distance between where we're going and where we should go is too big.
{
- if (IsCoordinateInTraversedList(Vector3i(gCrossCoords[i].x + PosX, PosY, gCrossCoords[i].z + PosZ)))
+ /* If we reached the last path waypoint,
+ Or if we haven't re-calculated for too long.
+ Interval is proportional to distance squared, and its minimum is 10.
+ (Recalculate lots when close, calculate rarely when far) */
+ if (
+ ((GetPosition() - m_PathFinderDestination).Length() < 0.25) ||
+ ((m_TicksSinceLastPathReset > 10) && (m_TicksSinceLastPathReset > (0.4 * (m_FinalDestination - GetPosition()).SqrLength())))
+ )
{
- continue;
+ /* Re-calculating is expensive when there's no path to target, and it results in mobs freezing very often as a result of always recalculating.
+ This is a workaround till we get better path recalculation. */
+ if (!m_NoPathToTarget)
+ {
+ ResetPathFinding();
+ }
}
+ }
- BLOCKTYPE BlockAtY = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY, gCrossCoords[i].z + PosZ);
- BLOCKTYPE BlockAtYP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 1, gCrossCoords[i].z + PosZ);
- BLOCKTYPE BlockAtYPP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 2, gCrossCoords[i].z + PosZ);
- int LowestY = FindFirstNonAirBlockPosition(gCrossCoords[i].x + PosX, gCrossCoords[i].z + PosZ);
- BLOCKTYPE BlockAtLowestY = (LowestY >= cChunkDef::Height) ? E_BLOCK_AIR : m_World->GetBlock(gCrossCoords[i].x + PosX, LowestY, gCrossCoords[i].z + PosZ);
+ if (m_Path == nullptr)
+ {
+ if (!EnsureProperDestination(a_Chunk))
+ {
+ StopMovingToPosition(); // Invalid chunks, probably world is loading or something, cancel movement.
+ return false;
+ }
+ m_NoPathToTarget = false;
+ m_NoMoreWayPoints = false;
+ m_PathFinderDestination = m_FinalDestination;
+ m_Path = new cPath(a_Chunk, GetPosition(), m_PathFinderDestination, 20, GetWidth(), GetHeight());
+ }
- if (
- (!cBlockInfo::IsSolid(BlockAtY)) &&
- (!cBlockInfo::IsSolid(BlockAtYP)) &&
- (!IsBlockLava(BlockAtLowestY)) &&
- (BlockAtLowestY != E_BLOCK_CACTUS) &&
- (PosY - LowestY < FALL_DAMAGE_HEIGHT)
- )
+ switch (m_Path->Step(a_Chunk))
+ {
+ case ePathFinderStatus::NEARBY_FOUND:
{
- m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY, gCrossCoords[i].z + PosZ));
+ m_NoPathToTarget = true;
+ m_PathFinderDestination = m_Path->AcceptNearbyPath();
+ break;
}
- else if (
- (cBlockInfo::IsSolid(BlockAtY)) &&
- (BlockAtY != E_BLOCK_CACTUS) &&
- (!cBlockInfo::IsSolid(BlockAtYP)) &&
- (!cBlockInfo::IsSolid(BlockAtYPP)) &&
- (BlockAtY != E_BLOCK_FENCE) &&
- (BlockAtY != E_BLOCK_FENCE_GATE)
- )
+
+ case ePathFinderStatus::PATH_NOT_FOUND:
+ {
+ StopMovingToPosition(); // Try to calculate a path again.
+ // Note that the next time may succeed, e.g. if a player breaks a barrier.
+ break;
+ }
+ case ePathFinderStatus::CALCULATING:
{
- m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY + 1, gCrossCoords[i].z + PosZ));
+ // Pathfinder needs more time
+ break;
+ }
+ case ePathFinderStatus::PATH_FOUND:
+ {
+ if (m_NoMoreWayPoints || (--m_GiveUpCounter == 0))
+ {
+ ResetPathFinding(); // Try to calculate a path again.
+ return false;
+ }
+ else if (!m_Path->IsLastPoint()) // Have we arrived at the next cell, as denoted by m_NextWayPointPosition?
+ {
+ if ((m_Path->IsFirstPoint() || ReachedNextWaypoint()))
+ {
+ m_NextWayPointPosition = m_Path->GetNextPoint();
+ m_GiveUpCounter = 40; // Give up after 40 ticks (2 seconds) if failed to reach m_NextWayPointPosition.
+ }
+ }
+ else
+ {
+ m_NoMoreWayPoints = true;
+ }
+ return true;
}
}
- if (!m_PotentialCoordinates.empty())
+ return false;
+}
+
+
+
+
+
+void cMonster::MoveToWayPoint(cChunk & a_Chunk)
+{
+ if (m_JumpCoolDown == 0)
{
- Vector3f ShortestCoords = m_PotentialCoordinates.front();
- for (std::vector<Vector3d>::const_iterator itr = m_PotentialCoordinates.begin(); itr != m_PotentialCoordinates.end(); ++itr)
+ if (DoesPosYRequireJump(FloorC(m_NextWayPointPosition.y)))
{
- Vector3f Distance = m_FinalDestination - ShortestCoords;
- Vector3f Distance2 = m_FinalDestination - *itr;
- if (Distance.SqrLength() > Distance2.SqrLength())
+ if (
+ (IsOnGround() && (GetSpeedX() == 0) && (GetSpeedY() == 0)) ||
+ (IsSwimming() && (m_GiveUpCounter < 15))
+ )
{
- ShortestCoords = *itr;
+ m_bOnGround = false;
+ m_JumpCoolDown = 20;
+ // TODO: Change to AddSpeedY once collision detection is fixed - currently, mobs will go into blocks attempting to jump without a teleport
+ AddPosY(1.6); // Jump!!
+ SetSpeedX(3.2 * (m_NextWayPointPosition.x - GetPosition().x)); // Move forward in a preset speed.
+ SetSpeedZ(3.2 * (m_NextWayPointPosition.z - GetPosition().z)); // The numbers were picked based on trial and error and 1.6 and 3.2 are perfect.
}
}
-
- m_Destination = ShortestCoords;
- m_Destination.z += 0.5f;
- m_Destination.x += 0.5f;
}
else
{
- FinishPathFinding();
+ --m_JumpCoolDown;
+ }
+
+ Vector3d Distance = m_NextWayPointPosition - GetPosition();
+ if ((Distance.x != 0) || (Distance.z != 0))
+ {
+ Distance.y = 0;
+ Distance.Normalize();
+
+ if (m_bOnGround)
+ {
+ Distance *= 2.5f;
+ }
+ else if (IsSwimming())
+ {
+ Distance *= 1.3f;
+ }
+ else
+ {
+ // Don't let the mob move too much if he's falling.
+ Distance *= 0.25f;
+ }
+ // Apply walk speed:
+ Distance *= m_RelativeWalkSpeed;
+ /* Reduced default speed.
+ Close to Vanilla, easier for mobs to follow m_NextWayPointPositions, hence
+ better pathfinding. */
+ Distance *= 0.5;
+ AddSpeedX(Distance.x);
+ AddSpeedZ(Distance.z);
}
}
@@ -205,47 +281,130 @@ void cMonster::TickPathFinding()
-void cMonster::MoveToPosition(const Vector3d & a_Position)
+bool cMonster::EnsureProperDestination(cChunk & a_Chunk)
{
- FinishPathFinding();
+ cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(m_FinalDestination.x), FloorC(m_FinalDestination.z));
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+
+ if ((Chunk == nullptr) || !Chunk->IsValid())
+ {
+ return false;
+ }
+
+ int RelX = FloorC(m_FinalDestination.x) - Chunk->GetPosX() * cChunkDef::Width;
+ int RelZ = FloorC(m_FinalDestination.z) - Chunk->GetPosZ() * cChunkDef::Width;
+
+ // If destination in the air, first try to go 1 block north, or east, or west.
+ // This fixes the player leaning issue.
+ // If that failed, we instead go down to the lowest air block.
+ Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
+ if (!cBlockInfo::IsSolid(BlockType))
+ {
+ bool InTheAir = true;
+ int x, z;
+ for (z = -1; z <= 1; ++z)
+ {
+ for (x = -1; x <= 1; ++x)
+ {
+ if ((x==0) && (z==0))
+ {
+ continue;
+ }
+ Chunk = a_Chunk.GetNeighborChunk(FloorC(m_FinalDestination.x+x), FloorC(m_FinalDestination.z+z));
+ if ((Chunk == nullptr) || !Chunk->IsValid())
+ {
+ return false;
+ }
+ RelX = FloorC(m_FinalDestination.x+x) - Chunk->GetPosX() * cChunkDef::Width;
+ RelZ = FloorC(m_FinalDestination.z+z) - Chunk->GetPosZ() * cChunkDef::Width;
+ Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
+ if (cBlockInfo::IsSolid(BlockType))
+ {
+ m_FinalDestination.x += x;
+ m_FinalDestination.z += z;
+ InTheAir = false;
+ goto breakBothLoops;
+ }
+ }
+ }
+ breakBothLoops:
+
+ // Go down to the lowest air block.
+ if (InTheAir)
+ {
+ while (m_FinalDestination.y > 0)
+ {
+ Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
+ if (cBlockInfo::IsSolid(BlockType))
+ {
+ break;
+ }
+ m_FinalDestination.y -= 1;
+ }
+ }
+ }
+
+ // If destination in water, go up to the highest water block.
+ // If destination in solid, go up to first air block.
+ bool InWater = false;
+ while (m_FinalDestination.y < cChunkDef::Height)
+ {
+ Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y), RelZ, BlockType, BlockMeta);
+ if (BlockType == E_BLOCK_STATIONARY_WATER)
+ {
+ InWater = true;
+ }
+ else if (cBlockInfo::IsSolid(BlockType))
+ {
+ InWater = false;
+ }
+ else
+ {
+ break;
+ }
+ m_FinalDestination.y += 1;
+ }
+ if (InWater)
+ {
+ m_FinalDestination.y -= 1;
+ }
- m_FinalDestination = a_Position;
- m_bMovingToDestination = true;
- TickPathFinding();
+
+ return true;
}
-bool cMonster::IsCoordinateInTraversedList(Vector3i a_Coords)
+
+
+void cMonster::MoveToPosition(const Vector3d & a_Position)
{
- return (std::find(m_TraversedCoordinates.begin(), m_TraversedCoordinates.end(), a_Coords) != m_TraversedCoordinates.end());
+ m_FinalDestination = a_Position;
+ m_IsFollowingPath = true;
}
-bool cMonster::ReachedDestination()
+void cMonster::StopMovingToPosition()
{
- if ((m_Destination - GetPosition()).Length() < 0.5f)
- {
- return true;
- }
-
- return false;
+ m_IsFollowingPath = false;
}
-bool cMonster::ReachedFinalDestination()
+
+void cMonster::ResetPathFinding(void)
{
- if ((GetPosition() - m_FinalDestination).Length() <= m_AttackRange)
+ m_TicksSinceLastPathReset = 0;
+ if (m_Path != nullptr)
{
- return true;
+ delete m_Path;
+ m_Path = nullptr;
}
-
- return false;
}
@@ -255,10 +414,11 @@ bool cMonster::ReachedFinalDestination()
void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
+ GET_AND_VERIFY_CURRENT_CHUNK(Chunk, POSX_TOINT, POSZ_TOINT);
if (m_Health <= 0)
{
- // The mob is dead, but we're still animating the "puff" they leave when they die
+ // The mob is dead, but we're still animating the "puff" they leave when they die.
m_DestroyTimer += a_Dt;
if (m_DestroyTimer > std::chrono::seconds(1))
{
@@ -267,73 +427,36 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
return;
}
+ if (m_TicksSinceLastDamaged < 100)
+ {
+ ++m_TicksSinceLastDamaged;
+ }
if ((m_Target != nullptr) && m_Target->IsDestroyed())
{
m_Target = nullptr;
}
- // Burning in daylight
- HandleDaylightBurning(a_Chunk);
-
- if (m_bMovingToDestination)
+ // Process the undead burning in daylight.
+ HandleDaylightBurning(*Chunk, WouldBurnAt(GetPosition(), *Chunk));
+ if (TickPathFinding(*Chunk))
{
- if (m_bOnGround)
- {
- if (DoesPosYRequireJump((int)floor(m_Destination.y)))
- {
- m_bOnGround = false;
-
- // TODO: Change to AddSpeedY once collision detection is fixed - currently, mobs will go into blocks attempting to jump without a teleport
- AddPosY(1.2); // Jump!!
- }
- }
-
- Vector3d Distance = m_Destination - GetPosition();
- if (!ReachedDestination() && !ReachedFinalDestination()) // If we haven't reached any sort of destination, move
+ /* If I burn in daylight, and I won't burn where I'm standing, and I'll burn in my next position, and at least one of those is true:
+ 1. I am idle
+ 2. I was not hurt by a player recently.
+ Then STOP. */
+ if (
+ m_BurnsInDaylight && ((m_TicksSinceLastDamaged >= 100) || (m_EMState == IDLE)) &&
+ WouldBurnAt(m_NextWayPointPosition, *Chunk) &&
+ !WouldBurnAt(GetPosition(), *Chunk)
+ )
{
- Distance.y = 0;
- Distance.Normalize();
-
- if (m_bOnGround)
- {
- Distance *= 2.5f;
- }
- else if (IsSwimming())
- {
- Distance *= 1.3f;
- }
- else
- {
- // Don't let the mob move too much if he's falling.
- Distance *= 0.25f;
- }
-
- // Apply walk speed:
- Distance *= m_RelativeWalkSpeed;
-
- AddSpeedX(Distance.x);
- AddSpeedZ(Distance.z);
-
- // It's too buggy!
- /*
- if (m_EMState == ESCAPING)
- {
- // Runs Faster when escaping :D otherwise they just walk away
- SetSpeedX (GetSpeedX() * 2.f);
- SetSpeedZ (GetSpeedZ() * 2.f);
- }
- */
+ // If we burn in daylight, and we would burn at the next step, and we won't burn where we are right now, and we weren't provoked recently:
+ StopMovingToPosition();
+ m_GiveUpCounter = 40; // This doesn't count as giving up, keep the giveup timer as is.
}
else
{
- if (ReachedFinalDestination()) // If we have reached the ultimate, final destination, stop pathfinding and attack if appropriate
- {
- FinishPathFinding();
- }
- else
- {
- TickPathFinding(); // We have reached the next point in our path, calculate another point
- }
+ MoveToWayPoint(*Chunk);
}
}
@@ -344,13 +467,13 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
case IDLE:
{
- // If enemy passive we ignore checks for player visibility
+ // If enemy passive we ignore checks for player visibility.
InStateIdle(a_Dt);
break;
}
case CHASING:
{
- // If we do not see a player anymore skip chasing action
+ // If we do not see a player anymore skip chasing action.
InStateChasing(a_Dt);
break;
}
@@ -359,7 +482,6 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
InStateEscaping(a_Dt);
break;
}
-
case ATTACKING: break;
} // switch (m_EMState)
@@ -369,6 +491,7 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+
void cMonster::SetPitchAndYawFromDestination()
{
Vector3d FinalDestination = m_FinalDestination;
@@ -376,31 +499,36 @@ void cMonster::SetPitchAndYawFromDestination()
{
if (m_Target->IsPlayer())
{
- FinalDestination.y = ((cPlayer *)m_Target)->GetStance();
+ FinalDestination.y = static_cast<cPlayer *>(m_Target)->GetStance() - 1;
}
else
{
- FinalDestination.y = GetHeight();
+ FinalDestination.y = m_Target->GetPosY() + GetHeight();
}
}
+
+
+ Vector3d BodyDistance = m_NextWayPointPosition - GetPosition();
+ double BodyRotation, BodyPitch;
+ BodyDistance.Normalize();
+ VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, BodyRotation, BodyPitch);
+ SetYaw(BodyRotation);
+
Vector3d Distance = FinalDestination - GetPosition();
- if (Distance.SqrLength() > 0.1f)
{
+ double HeadRotation, HeadPitch;
+ Distance.Normalize();
+ VectorToEuler(Distance.x, Distance.y, Distance.z, HeadRotation, HeadPitch);
+ if (std::abs(BodyRotation - HeadRotation) < 120)
{
- double Rotation, Pitch;
- Distance.Normalize();
- VectorToEuler(Distance.x, Distance.y, Distance.z, Rotation, Pitch);
- SetHeadYaw(Rotation);
- SetPitch(-Pitch);
+ SetHeadYaw(HeadRotation);
+ SetPitch(-HeadPitch);
}
-
+ else // We're not an owl. If it's more than 120, don't look behind and instead look at where you're walking.
{
- Vector3d BodyDistance = m_Destination - GetPosition();
- double Rotation, Pitch;
- Distance.Normalize();
- VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, Rotation, Pitch);
- SetYaw(Rotation);
+ SetHeadYaw(BodyRotation);
+ SetPitch(-BodyPitch);
}
}
}
@@ -408,6 +536,7 @@ void cMonster::SetPitchAndYawFromDestination()
+
void cMonster::HandleFalling()
{
if (m_bOnGround)
@@ -459,7 +588,6 @@ int cMonster::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ)
-
bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
{
if (!super::DoTakeDamage(a_TDI))
@@ -475,6 +603,7 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
if (a_TDI.Attacker != nullptr)
{
m_Target = a_TDI.Attacker;
+ m_TicksSinceLastDamaged = 0;
}
return true;
}
@@ -640,7 +769,7 @@ void cMonster::EventLosePlayer(void)
void cMonster::InStateIdle(std::chrono::milliseconds a_Dt)
{
- if (m_bMovingToDestination)
+ if (m_IsFollowingPath)
{
return; // Still getting there
}
@@ -660,14 +789,8 @@ void cMonster::InStateIdle(std::chrono::milliseconds a_Dt)
if ((Dist.SqrLength() > 2) && (rem >= 3))
{
Vector3d Destination(GetPosX() + Dist.x, 0, GetPosZ() + Dist.z);
-
- int NextHeight = FindFirstNonAirBlockPosition(Destination.x, Destination.z);
-
- if (IsNextYPosReachable(NextHeight))
- {
- Destination.y = NextHeight;
- MoveToPosition(Destination);
- }
+ Destination.y = FindFirstNonAirBlockPosition(Destination.x, Destination.z);
+ MoveToPosition(Destination);
}
}
}
@@ -691,7 +814,7 @@ void cMonster::InStateChasing(std::chrono::milliseconds a_Dt)
void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt)
{
UNUSED(a_Dt);
-
+
if (m_Target != nullptr)
{
Vector3d newloc = GetPosition();
@@ -770,7 +893,7 @@ AString cMonster::MobTypeToString(eMonsterType a_MobType)
return g_MobTypeNames[i].m_lcName;
}
}
-
+
// Not found:
return "";
}
@@ -865,7 +988,7 @@ cMonster::eFamily cMonster::FamilyFromType(eMonsterType a_Type)
case mtWolf: return mfHostile;
case mtZombie: return mfHostile;
case mtZombiePigman: return mfHostile;
-
+
case mtInvalidType: break;
}
ASSERT(!"Unhandled mob type");
@@ -905,7 +1028,7 @@ cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType)
{
case mtMagmaCube:
{
- toReturn = new cMagmaCube(Random.NextInt(2) + 1);
+ toReturn = new cMagmaCube(1 << Random.NextInt(3)); // Size 1, 2 or 4
break;
}
case mtSlime:
@@ -1040,7 +1163,7 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel)
a_Drops.push_back(GetEquippedHelmet());
}
}
-
+
if (r1.randInt() % 200 < ((m_DropChanceChestplate * 200) + (a_LootingLevel * 2)))
{
if (!GetEquippedChestplate().IsEmpty())
@@ -1048,7 +1171,7 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel)
a_Drops.push_back(GetEquippedChestplate());
}
}
-
+
if (r1.randInt() % 200 < ((m_DropChanceLeggings * 200) + (a_LootingLevel * 2)))
{
if (!GetEquippedLeggings().IsEmpty())
@@ -1056,7 +1179,7 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel)
a_Drops.push_back(GetEquippedLeggings());
}
}
-
+
if (r1.randInt() % 200 < ((m_DropChanceBoots * 200) + (a_LootingLevel * 2)))
{
if (!GetEquippedBoots().IsEmpty())
@@ -1086,36 +1209,26 @@ void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, short a_LootingLevel)
-void cMonster::HandleDaylightBurning(cChunk & a_Chunk)
+void cMonster::HandleDaylightBurning(cChunk & a_Chunk, bool WouldBurn)
{
if (!m_BurnsInDaylight)
{
return;
}
-
+
int RelY = POSY_TOINT;
if ((RelY < 0) || (RelY >= cChunkDef::Height))
{
// Outside the world
return;
}
-
- int RelX = POSX_TOINT - GetChunkX() * cChunkDef::Width;
- int RelZ = POSZ_TOINT - GetChunkZ() * cChunkDef::Width;
-
if (!a_Chunk.IsLightValid())
{
m_World->QueueLightChunk(GetChunkX(), GetChunkZ());
return;
}
- if (
- (a_Chunk.GetSkyLight(RelX, RelY, RelZ) == 15) && // In the daylight
- (a_Chunk.GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand
- (GetWorld()->GetTimeOfDay() < (12000 + 1000)) && // It is nighttime
- !IsOnFire() && // Not already burning
- GetWorld()->IsWeatherSunnyAt(POSX_TOINT, POSZ_TOINT) // Not raining
- )
+ if (!IsOnFire() && WouldBurn)
{
// Burn for 100 ticks, then decide again
StartBurning(100);
@@ -1125,11 +1238,35 @@ void cMonster::HandleDaylightBurning(cChunk & a_Chunk)
-cMonster::eFamily cMonster::GetMobFamily(void) const
+bool cMonster::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk)
{
- return FamilyFromType(m_MobType);
+ cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(a_Location.x), FloorC(a_Location.z));
+ if ((Chunk == nullptr) || (!Chunk->IsValid()))
+ {
+ return false;
+ }
+
+ int RelX = FloorC(a_Location.x) - Chunk->GetPosX() * cChunkDef::Width;
+ int RelY = FloorC(a_Location.y);
+ int RelZ = FloorC(a_Location.z) - Chunk->GetPosZ() * cChunkDef::Width;
+
+ if (
+ (Chunk->GetSkyLight(RelX, RelY, RelZ) == 15) && // In the daylight
+ (Chunk->GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand
+ (GetWorld()->GetTimeOfDay() < (12000 + 1000)) && // It is nighttime
+ GetWorld()->IsWeatherSunnyAt(POSX_TOINT, POSZ_TOINT) // Not raining
+ )
+ {
+ return true;
+ }
+ return false;
}
+
+cMonster::eFamily cMonster::GetMobFamily(void) const
+{
+ return FamilyFromType(m_MobType);
+}
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
index 21ed0c25a..c4043b0e5 100644
--- a/src/Mobs/Monster.h
+++ b/src/Mobs/Monster.h
@@ -10,11 +10,12 @@
-
-
class cClientHandle;
class cWorld;
+// Fwd: cPath
+enum class ePathFinderStatus;
+class cPath;
@@ -60,8 +61,9 @@ public:
virtual void OnRightClicked(cPlayer & a_Player) override;
+ /** Engage pathfinder and tell it to calculate a path to a given position, and move the mobile accordingly
+ Currently, the mob will only start moving to a new position after the position it is currently going to is reached. */
virtual void MoveToPosition(const Vector3d & a_Position); // tolua_export
- virtual bool ReachedDestination(void);
// tolua_begin
eMonsterType GetMobType(void) const { return m_MobType; }
@@ -158,19 +160,32 @@ public:
protected:
- /* ======= PATHFINDING ======= */
-
/** A pointer to the entity this mobile is aiming to reach */
cEntity * m_Target;
+ cPath * m_Path; // TODO unique ptr
+
+ /** Stores if mobile is currently moving towards the ultimate, final destination */
+ bool m_IsFollowingPath;
+
+ /* If 0, will give up reaching the next m_NextWayPointPosition and will re-compute path. */
+ int m_GiveUpCounter;
+ int m_TicksSinceLastPathReset;
+
/** Coordinates of the next position that should be reached */
- Vector3d m_Destination;
+ Vector3d m_NextWayPointPosition;
+
/** Coordinates for the ultimate, final destination. */
Vector3d m_FinalDestination;
- /** Returns if the ultimate, final destination has been reached */
- bool ReachedFinalDestination(void);
- /** Stores if mobile is currently moving towards the ultimate, final destination */
- bool m_bMovingToDestination;
+ /** Coordinates for the ultimate, final destination last given to the pathfinder. */
+ Vector3d m_PathFinderDestination;
+
+ /** True if there's no path to target and we're walking to an approximated location. */
+ bool m_NoPathToTarget;
+
+ /** Whether The mob has finished their path, note that this does not imply reaching the destination,
+ the destination may sometimes differ from the current path. */
+ bool m_NoMoreWayPoints;
/** Finds the lowest non-air block position (not the highest, as cWorld::GetHeight does)
If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1
@@ -178,44 +193,50 @@ protected:
If no suitable position is found, returns cChunkDef::Height. */
int FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ);
- /** Returns if a monster can actually reach a given height by jumping or walking */
- inline bool IsNextYPosReachable(int a_PosY)
- {
- return (
- (a_PosY <= POSY_TOINT) ||
- DoesPosYRequireJump(a_PosY)
- );
- }
+ /** Returns if the ultimate, final destination has been reached */
+ bool ReachedFinalDestination(void) { return ((m_FinalDestination - GetPosition()).SqrLength() < (m_AttackRange * m_AttackRange)); }
+
+ /** Returns if the intermediate waypoint of m_NextWayPointPosition has been reached */
+ bool ReachedNextWaypoint(void) { return ((m_NextWayPointPosition - GetPosition()).SqrLength() < 0.25); }
+
/** Returns if a monster can reach a given height by jumping */
inline bool DoesPosYRequireJump(int a_PosY)
{
return ((a_PosY > POSY_TOINT) && (a_PosY == POSY_TOINT + 1));
}
- /** A semi-temporary list to store the traversed coordinates during active pathfinding so we don't visit them again */
- std::vector<Vector3i> m_TraversedCoordinates;
- /** Returns if coordinate is in the traversed list */
- bool IsCoordinateInTraversedList(Vector3i a_Coords);
+ /** Finds the next place to go by calculating a path and setting the m_NextWayPointPosition variable for the next block to head to
+ This is based on the ultimate, final destination and the current position, as well as the A* algorithm, and any environmental hazards
+ Returns if a path is ready, and therefore if the mob should move to m_NextWayPointPosition
+ */
+ bool TickPathFinding(cChunk & a_Chunk);
- /** Finds the next place to go
- This is based on the ultimate, final destination and the current position, as well as the traversed coordinates, and any environmental hazards */
- void TickPathFinding(void);
- /** Finishes a pathfinding task, be it due to failure or something else */
- inline void FinishPathFinding(void)
- {
- m_TraversedCoordinates.clear();
- m_bMovingToDestination = false;
- }
- /** Sets the body yaw and head yaw/pitch based on next/ultimate destinations */
- void SetPitchAndYawFromDestination(void);
+ /** Move in a straight line to the next waypoint in the path, will jump if needed. */
+ void MoveToWayPoint(cChunk & a_Chunk);
- /* =========================== */
- /* ========= FALLING ========= */
+ /** Ensures the destination is not buried underground or under water. Also ensures the destination is not in the air.
+ Only the Y coordinate of m_FinalDestination might be changed.
+ 1. If m_FinalDestination is the position of a water block, m_FinalDestination's Y will be modified to point to the heighest water block in the pool in the current column.
+ 2. If m_FinalDestination is the position of a solid, m_FinalDestination's Y will be modified to point to the first airblock above the solid in the current column.
+ 3. If m_FinalDestination is the position of an air block, Y will keep decreasing until hitting either a solid or water.
+ Now either 1 or 2 is performed. */
+ bool EnsureProperDestination(cChunk & a_Chunk);
+
+ /** Resets a pathfinding task, be it due to failure or something else
+ Resets the pathfinder. If m_IsFollowingPath is true, TickPathFinding starts a brand new path.
+ Should only be called by the pathfinder, cMonster::Tick or StopMovingToPosition. */
+ void ResetPathFinding(void);
+
+ /** Stops pathfinding
+ Calls ResetPathFinding and sets m_IsFollowingPath to false */
+ void StopMovingToPosition();
+
+ /** Sets the body yaw and head yaw / pitch based on next / ultimate destinations */
+ void SetPitchAndYawFromDestination(void);
virtual void HandleFalling(void);
int m_LastGroundHeight;
-
- /* =========================== */
+ int m_JumpCoolDown;
std::chrono::milliseconds m_IdleInterval;
std::chrono::milliseconds m_DestroyTimer;
@@ -239,10 +260,11 @@ protected:
float m_DropChanceLeggings;
float m_DropChanceBoots;
bool m_CanPickUpLoot;
+ int m_TicksSinceLastDamaged; // How many ticks ago we were last damaged by a player?
- void HandleDaylightBurning(cChunk & a_Chunk);
+ void HandleDaylightBurning(cChunk & a_Chunk, bool WouldBurn);
+ bool WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk);
bool m_BurnsInDaylight;
-
double m_RelativeWalkSpeed;
/** Adds a random number of a_Item between a_Min and a_Max to itemdrops a_Drops*/
diff --git a/src/Mobs/Path.cpp b/src/Mobs/Path.cpp
new file mode 100644
index 000000000..6f3d43305
--- /dev/null
+++ b/src/Mobs/Path.cpp
@@ -0,0 +1,491 @@
+
+#include "Globals.h"
+
+#include <cmath>
+
+#include "Path.h"
+#include "../Chunk.h"
+
+#define JUMP_G_COST 20
+
+#define DISTANCE_MANHATTAN 0 // 1: More speed, a bit less accuracy 0: Max accuracy, less speed.
+#define HEURISTICS_ONLY 0 // 1: Much more speed, much less accurate.
+#define CALCULATIONS_PER_STEP 10 // Higher means more CPU load but faster path calculations.
+// The only version which guarantees the shortest path is 0, 0.
+
+
+
+
+
+
+
+
+bool compareHeuristics::operator()(cPathCell * & a_Cell1, cPathCell * & a_Cell2)
+{
+ return a_Cell1->m_F > a_Cell2->m_F;
+}
+
+
+
+
+
+/* cPath implementation */
+cPath::cPath(
+ cChunk & a_Chunk,
+ const Vector3d & a_StartingPoint, const Vector3d & a_EndingPoint, int a_MaxSteps,
+ double a_BoundingBoxWidth, double a_BoundingBoxHeight,
+ int a_MaxUp, int a_MaxDown
+) :
+
+ m_CurrentPoint(0), // GetNextPoint increments this to 1, but that's fine, since the first cell is always a_StartingPoint
+ m_Chunk(&a_Chunk),
+ m_BadChunkFound(false)
+{
+ // TODO: if src not walkable OR dest not walkable, then abort.
+ // Borrow a new "isWalkable" from ProcessIfWalkable, make ProcessIfWalkable also call isWalkable
+
+ a_BoundingBoxWidth = 1; // Until we improve physics, if ever.
+
+ m_BoundingBoxWidth = ceil(a_BoundingBoxWidth);
+ m_BoundingBoxHeight = ceil(a_BoundingBoxHeight);
+ m_HalfWidth = a_BoundingBoxWidth / 2;
+
+ int HalfWidthInt = a_BoundingBoxWidth / 2;
+ m_Source.x = floor(a_StartingPoint.x - HalfWidthInt);
+ m_Source.y = floor(a_StartingPoint.y);
+ m_Source.z = floor(a_StartingPoint.z - HalfWidthInt);
+
+ m_Destination.x = floor(a_EndingPoint.x - HalfWidthInt);
+ m_Destination.y = floor(a_EndingPoint.y);
+ m_Destination.z = floor(a_EndingPoint.z - HalfWidthInt);
+
+ if (GetCell(m_Source)->m_IsSolid || GetCell(m_Destination)->m_IsSolid)
+ {
+ m_Status = ePathFinderStatus::PATH_NOT_FOUND;
+ return;
+ }
+
+ m_NearestPointToTarget = GetCell(m_Source);
+ m_Status = ePathFinderStatus::CALCULATING;
+ m_StepsLeft = a_MaxSteps;
+
+ ProcessCell(GetCell(a_StartingPoint), nullptr, 0);
+ m_Chunk = nullptr;
+}
+
+
+
+
+
+cPath::~cPath()
+{
+ if (m_Status == ePathFinderStatus::CALCULATING)
+ {
+ FinishCalculation();
+ }
+}
+
+
+
+
+
+ePathFinderStatus cPath::Step(cChunk & a_Chunk)
+{
+ m_Chunk = &a_Chunk;
+ if (m_Status != ePathFinderStatus::CALCULATING)
+ {
+ return m_Status;
+ }
+
+ if (m_BadChunkFound)
+ {
+ FinishCalculation(ePathFinderStatus::PATH_NOT_FOUND);
+ return m_Status;
+ }
+
+ if (m_StepsLeft == 0)
+ {
+ AttemptToFindAlternative();
+ }
+ else
+ {
+ --m_StepsLeft;
+ int i;
+ for (i = 0; i < CALCULATIONS_PER_STEP; ++i)
+ {
+ if (Step_Internal()) // Step_Internal returns true when no more calculation is needed.
+ {
+ break; // if we're here, m_Status must have changed either to PATH_FOUND or PATH_NOT_FOUND.
+ }
+ }
+
+ m_Chunk = nullptr;
+ }
+ return m_Status;
+}
+
+
+
+
+
+Vector3i cPath::AcceptNearbyPath()
+{
+ ASSERT(m_Status == ePathFinderStatus::NEARBY_FOUND);
+ m_Status = ePathFinderStatus::PATH_FOUND;
+ return m_Destination;
+}
+
+
+
+
+
+bool cPath::IsSolid(const Vector3i & a_Location)
+{
+ ASSERT(m_Chunk != nullptr);
+
+ auto Chunk = m_Chunk->GetNeighborChunk(a_Location.x, a_Location.z);
+ if ((Chunk == nullptr) || !Chunk->IsValid())
+ {
+ m_BadChunkFound = true;
+ return true;
+ }
+ m_Chunk = Chunk;
+
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ int RelX = a_Location.x - m_Chunk->GetPosX() * cChunkDef::Width;
+ int RelZ = a_Location.z - m_Chunk->GetPosZ() * cChunkDef::Width;
+
+ m_Chunk->GetBlockTypeMeta(RelX, a_Location.y, RelZ, BlockType, BlockMeta);
+ if ((BlockType == E_BLOCK_FENCE) || (BlockType == E_BLOCK_FENCE_GATE))
+ {
+ GetCell(a_Location + Vector3i(0, 1, 0))->m_IsSolid = true; // Mobs will always think that the fence is 2 blocks high and therefore won't jump over.
+ }
+ if (BlockType == E_BLOCK_STATIONARY_WATER)
+ {
+ GetCell(a_Location + Vector3i(0, -1, 0))->m_IsSolid = true;
+ }
+
+ return cBlockInfo::IsSolid(BlockType);
+}
+
+
+
+
+
+bool cPath::Step_Internal()
+{
+ cPathCell * CurrentCell = OpenListPop();
+
+ // Path not reachable.
+ if (CurrentCell == nullptr)
+ {
+ AttemptToFindAlternative();
+ return true;
+ }
+
+ // Path found.
+ if (CurrentCell->m_Location == m_Destination)
+ {
+ BuildPath();
+ FinishCalculation(ePathFinderStatus::PATH_FOUND);
+ return true;
+ }
+
+ // Calculation not finished yet.
+ // Check if we have a new NearestPoint.
+ // TODO I don't like this that much, there should be a smarter way.
+ if ((m_Destination - CurrentCell->m_Location).Length() < 5)
+ {
+ if (m_Rand.NextInt(4) == 0)
+ {
+ m_NearestPointToTarget = CurrentCell;
+ }
+ }
+ else if (CurrentCell->m_H < m_NearestPointToTarget->m_H)
+ {
+ m_NearestPointToTarget = CurrentCell;
+ }
+ // process a currentCell by inspecting all neighbors.
+
+
+ // Check North, South, East, West on our height.
+ ProcessIfWalkable(CurrentCell->m_Location + Vector3i(1, 0, 0), CurrentCell, 10);
+ ProcessIfWalkable(CurrentCell->m_Location + Vector3i(-1, 0, 0), CurrentCell, 10);
+ ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 0, 1), CurrentCell, 10);
+ ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 0, -1), CurrentCell, 10);
+
+ // Check diagonals on XY plane.
+ for (int x = -1; x <= 1; x += 2)
+ {
+ if (GetCell(CurrentCell->m_Location + Vector3i(x, 0, 0))->m_IsSolid) // If there's a solid our east / west.
+ {
+ ProcessIfWalkable(CurrentCell->m_Location + Vector3i(x, 1, 0), CurrentCell, JUMP_G_COST); // Check east / west-up.
+ }
+ else
+ {
+ ProcessIfWalkable(CurrentCell->m_Location + Vector3i(x, -1, 0), CurrentCell, 14); // Else check east / west-down.
+ }
+ }
+
+ // Check diagonals on the YZ plane.
+ for (int z = -1; z <= 1; z += 2)
+ {
+ if (GetCell(CurrentCell->m_Location + Vector3i(0, 0, z))->m_IsSolid) // If there's a solid our east / west.
+ {
+ ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 1, z), CurrentCell, JUMP_G_COST); // Check east / west-up.
+ }
+ else
+ {
+ ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, -1, z), CurrentCell, 14); // Else check east / west-down.
+ }
+ }
+
+ // Check diagonals on the XZ plane. (Normal diagonals, this plane is special because of gravity, etc)
+ for (int x = -1; x <= 1; x += 2)
+ {
+ for (int z = -1; z <= 1; z += 2)
+ {
+ // This condition prevents diagonal corner cutting.
+ if (!GetCell(CurrentCell->m_Location + Vector3i(x, 0, 0))->m_IsSolid && !GetCell(CurrentCell->m_Location + Vector3i(0, 0, z))->m_IsSolid)
+ {
+ // This prevents falling of "sharp turns" e.g. a 1x1x20 rectangle in the air which breaks in a right angle suddenly.
+ if (GetCell(CurrentCell->m_Location + Vector3i(x, -1, 0))->m_IsSolid && GetCell(CurrentCell->m_Location + Vector3i(0, -1, z))->m_IsSolid)
+ {
+ ProcessIfWalkable(CurrentCell->m_Location + Vector3i(x, 0, z), CurrentCell, 14); // 14 is a good enough approximation of sqrt(10 + 10).
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+
+
+
+
+void cPath::AttemptToFindAlternative()
+{
+ if (m_NearestPointToTarget == GetCell(m_Source))
+ {
+ FinishCalculation(ePathFinderStatus::PATH_NOT_FOUND);
+ }
+ else
+ {
+ m_Destination = m_NearestPointToTarget->m_Location;
+ BuildPath();
+ FinishCalculation(ePathFinderStatus::NEARBY_FOUND);
+ }
+}
+
+
+
+
+
+void cPath::BuildPath()
+{
+ cPathCell * CurrentCell = GetCell(m_Destination);
+ do
+ {
+ m_PathPoints.push_back(CurrentCell->m_Location); // Populate the cPath with points.
+ CurrentCell = CurrentCell->m_Parent;
+ } while (CurrentCell != nullptr);
+}
+
+
+
+
+
+void cPath::FinishCalculation()
+{
+ m_Map.clear();
+ m_OpenList = std::priority_queue<cPathCell *, std::vector<cPathCell *>, compareHeuristics>{};
+}
+
+
+
+
+
+void cPath::FinishCalculation(ePathFinderStatus a_NewStatus)
+{
+ if (m_BadChunkFound)
+ {
+ a_NewStatus = ePathFinderStatus::PATH_NOT_FOUND;
+ }
+ m_Status = a_NewStatus;
+ FinishCalculation();
+}
+
+
+
+
+
+void cPath::OpenListAdd(cPathCell * a_Cell)
+{
+ a_Cell->m_Status = eCellStatus::OPENLIST;
+ m_OpenList.push(a_Cell);
+ #ifdef COMPILING_PATHFIND_DEBUGGER
+ si::setBlock(a_Cell->m_Location.x, a_Cell->m_Location.y, a_Cell->m_Location.z, debug_open, SetMini(a_Cell));
+ #endif
+}
+
+
+
+
+
+cPathCell * cPath::OpenListPop() // Popping from the open list also means adding to the closed list.
+{
+ if (m_OpenList.size() == 0)
+ {
+ return nullptr; // We've exhausted the search space and nothing was found, this will trigger a PATH_NOT_FOUND or NEARBY_FOUND status.
+ }
+
+ cPathCell * Ret = m_OpenList.top();
+ m_OpenList.pop();
+ Ret->m_Status = eCellStatus::CLOSEDLIST;
+ #ifdef COMPILING_PATHFIND_DEBUGGER
+si::setBlock((Ret)->m_Location.x, (Ret)->m_Location.y, (Ret)->m_Location.z, debug_closed, SetMini(Ret));
+ #endif
+ return Ret;
+}
+
+
+
+
+
+void cPath::ProcessIfWalkable(const Vector3i & a_Location, cPathCell * a_Parent, int a_Cost)
+{
+ cPathCell * cell = GetCell(a_Location);
+ int x, y, z;
+
+ // Make sure we fit in the position.
+ for (y = 0; y < m_BoundingBoxHeight; ++y)
+ {
+ for (x = 0; x < m_BoundingBoxWidth; ++x)
+ {
+ for (z = 0; z < m_BoundingBoxWidth; ++z)
+ {
+ if (GetCell(a_Location + Vector3i(x, y, z))->m_IsSolid)
+ {
+ return;
+ }
+ }
+ }
+ }
+
+ /*y =-1;
+ for (x = 0; x < m_BoundingBoxWidth; ++x)
+ {
+ for (z = 0; z < m_BoundingBoxWidth; ++z)
+ {
+ if (!GetCell(a_Location + Vector3i(x, y, z))->m_IsSolid)
+ {
+ return;
+ }
+ }
+ }
+ ProcessCell(cell, a_Parent, a_Cost);*/
+
+ // Make sure there's at least 1 piece of solid below us.
+
+ bool GroundFlag = false;
+ y =-1;
+ for (x = 0; x < m_BoundingBoxWidth; ++x)
+ {
+ for (z = 0; z < m_BoundingBoxWidth; ++z)
+ {
+ if (GetCell(a_Location + Vector3i(x, y, z))->m_IsSolid)
+ {
+ GroundFlag = true;
+ break;
+ }
+ }
+ }
+
+ if (GroundFlag)
+ {
+ ProcessCell(cell, a_Parent, a_Cost);
+ }
+}
+
+
+
+
+
+void cPath::ProcessCell(cPathCell * a_Cell, cPathCell * a_Caller, int a_GDelta)
+{
+ // Case 1: Cell is in the closed list, ignore it.
+ if (a_Cell->m_Status == eCellStatus::CLOSEDLIST)
+ {
+ return;
+ }
+ if (a_Cell->m_Status == eCellStatus::NOLIST) // Case 2: The cell is not in any list.
+ {
+ // Cell is walkable, add it to the open list.
+ // Note that non-walkable cells are filtered out in Step_internal();
+ // Special case: Start cell goes here, gDelta is 0, caller is NULL.
+ a_Cell->m_Parent = a_Caller;
+ if (a_Caller != nullptr)
+ {
+ a_Cell->m_G = a_Caller->m_G + a_GDelta;
+ }
+ else
+ {
+ a_Cell->m_G = 0;
+ }
+
+ // Calculate H. This is A*'s Heuristics value.
+ #if DISTANCE_MANHATTAN == 1
+ // Manhattan distance. DeltaX + DeltaY + DeltaZ.
+ a_Cell->m_H = 10 * (abs(a_Cell->m_Location.x-m_Destination.x) + abs(a_Cell->m_Location.y-m_Destination.y) + abs(a_Cell->m_Location.z-m_Destination.z));
+ #else
+ // Euclidian distance. sqrt(DeltaX^2 + DeltaY^2 + DeltaZ^2), more precise.
+ a_Cell->m_H = static_cast<decltype(a_Cell->m_H)>((a_Cell->m_Location - m_Destination).Length() * 10);
+ #endif
+
+ #if HEURISTICS_ONLY == 1
+ a_Cell->m_F = a_Cell->m_H; // Greedy search. https://en.wikipedia.org/wiki/Greedy_search
+ #else
+ a_Cell->m_F = a_Cell->m_H + a_Cell->m_G; // Regular A*.
+ #endif
+
+ OpenListAdd(a_Cell);
+ return;
+ }
+
+ // Case 3: Cell is in the open list, check if G and H need an update.
+ int NewG = a_Caller->m_G + a_GDelta;
+ if (NewG < a_Cell->m_G)
+ {
+ a_Cell->m_G = NewG;
+ a_Cell->m_H = a_Cell->m_F + a_Cell->m_G;
+ a_Cell->m_Parent = a_Caller;
+ }
+
+}
+
+
+
+
+
+cPathCell * cPath::GetCell(const Vector3i & a_Location)
+{
+ // Create the cell in the hash table if it's not already there.
+ if (m_Map.count(a_Location) == 0) // Case 1: Cell is not on any list. We've never checked this cell before.
+ {
+ m_Map[a_Location].m_Location = a_Location;
+ m_Map[a_Location].m_IsSolid = IsSolid(a_Location);
+ m_Map[a_Location].m_Status = eCellStatus::NOLIST;
+ #ifdef COMPILING_PATHFIND_DEBUGGER
+ #ifdef COMPILING_PATHFIND_DEBUGGER_MARK_UNCHECKED
+ si::setBlock(a_Location.x, a_Location.y, a_Location.z, debug_unchecked, Cell->m_IsSolid ? NORMAL : MINI);
+ #endif
+ #endif
+ return &m_Map[a_Location];
+ }
+ else
+ {
+ return &m_Map[a_Location];
+ }
+}
diff --git a/src/Mobs/Path.h b/src/Mobs/Path.h
new file mode 100644
index 000000000..3b9c0400e
--- /dev/null
+++ b/src/Mobs/Path.h
@@ -0,0 +1,186 @@
+
+#pragma once
+
+/*
+// Needed Fwds: cPath
+enum class ePathFinderStatus;
+class cPath;
+*/
+
+#include "../FastRandom.h"
+#ifdef COMPILING_PATHFIND_DEBUGGER
+ /* Note: the COMPILING_PATHFIND_DEBUGGER flag is used by Native / WiseOldMan95 to debug
+ this class outside of MCServer. This preprocessor flag is never set when compiling MCServer. */
+ #include "PathFinderIrrlicht_Head.h"
+#endif
+
+#include <unordered_map>
+
+//fwd: ../Chunk.h
+class cChunk;
+
+/* Various little structs and classes */
+enum class ePathFinderStatus {CALCULATING, PATH_FOUND, PATH_NOT_FOUND, NEARBY_FOUND};
+enum class eCellStatus {OPENLIST, CLOSEDLIST, NOLIST};
+struct cPathCell
+{
+ Vector3i m_Location; // Location of the cell in the world.
+ int m_F, m_G, m_H; // F, G, H as defined in regular A*.
+ eCellStatus m_Status; // Which list is the cell in? Either non, open, or closed.
+ cPathCell * m_Parent; // Cell's parent, as defined in regular A*.
+ bool m_IsSolid; // Is the cell an air or a solid? Partial solids are currently considered solids.
+};
+
+
+
+
+
+class compareHeuristics
+{
+public:
+ bool operator()(cPathCell * & a_V1, cPathCell * & a_V2);
+};
+
+
+
+
+
+class cPath
+{
+public:
+ /** Creates a pathfinder instance. A Mob will probably need a single pathfinder instance for its entire life.
+
+ Note that if you have a man-sized mob (1x1x2, zombies, etc), you are advised to call this function without parameters
+ because the declaration might change in later version of the pathFinder, and a parameter-less call always assumes a man-sized mob.
+
+ If your mob is not man-sized, you are advised to use cPath(width, height), this would be compatible with future versions,
+ but please be aware that as of now those parameters will be ignored and your mob will be assumed to be man sized.
+
+ @param a_BoundingBoxWidth the character's boundingbox width in blocks. Currently the parameter is ignored and 1 is assumed.
+ @param a_BoundingBoxHeight the character's boundingbox width in blocks. Currently the parameter is ignored and 2 is assumed.
+ @param a_MaxUp the character's max jump height in blocks. Currently the parameter is ignored and 1 is assumed.
+ @param a_MaxDown How far is the character willing to fall? Currently the parameter is ignored and 1 is assumed. */
+ /** Attempts to find a path starting from source to destination.
+ After calling this, you are expected to call Step() once per tick or once per several ticks until it returns true. You should then call getPath() to obtain the path.
+ Calling this before a path is found resets the current path and starts another search.
+ @param a_StartingPoint The function expects this position to be the lowest block the mob is in, a rule of thumb: "The block where the Zombie's knees are at".
+ @param a_EndingPoint "The block where the Zombie's knees want to be".
+ @param a_MaxSteps The maximum steps before giving up. */
+ cPath(
+ cChunk & a_Chunk,
+ const Vector3d & a_StartingPoint, const Vector3d & a_EndingPoint, int a_MaxSteps,
+ double a_BoundingBoxWidth, double a_BoundingBoxHeight,
+ int a_MaxUp = 1, int a_MaxDown = 1
+ );
+
+ /** Destroys the path and frees its memory. */
+ ~cPath();
+
+ /** Performs part of the path calculation and returns the appropriate status.
+ If NEARBY_FOUND is returned, it means that the destination is not reachable, but a nearby destination
+ is reachable. If the user likes the alternative destination, they can call AcceptNearbyPath to treat the path as found,
+ and to make consequent calls to step return PATH_FOUND*/
+ ePathFinderStatus Step(cChunk & a_Chunk);
+
+ /** Called after the PathFinder's step returns NEARBY_FOUND.
+ Changes the PathFinder status from NEARBY_FOUND to PATH_FOUND, returns the nearby destination that
+ the PathFinder found a path to. */
+ Vector3i AcceptNearbyPath();
+
+ /* Point retrieval functions, inlined for performance. */
+ /** Returns the next point in the path. */
+ inline Vector3d GetNextPoint()
+ {
+ ASSERT(m_Status == ePathFinderStatus::PATH_FOUND);
+ Vector3i Point = m_PathPoints[m_PathPoints.size() - 1 - (++m_CurrentPoint)];
+ return Vector3d(Point.x + m_HalfWidth, Point.y, Point.z + m_HalfWidth);
+ }
+ /** Checks whether this is the last point or not. Never call getnextPoint when this is true. */
+ inline bool IsLastPoint()
+ {
+ ASSERT(m_Status == ePathFinderStatus::PATH_FOUND);
+ return (m_CurrentPoint == m_PathPoints.size() - 1);
+ }
+ inline bool IsFirstPoint()
+ {
+ ASSERT(m_Status == ePathFinderStatus::PATH_FOUND);
+ return (m_CurrentPoint == 0);
+ }
+ /** Get the point at a_index. Remark: Internally, the indexes are reversed. */
+ inline Vector3d GetPoint(size_t a_index)
+ {
+ ASSERT(m_Status == ePathFinderStatus::PATH_FOUND);
+ ASSERT(a_index < m_PathPoints.size());
+ Vector3i Point = m_PathPoints[m_PathPoints.size() - 1 - a_index];
+ return Vector3d(Point.x + m_HalfWidth, Point.y, Point.z + m_HalfWidth);
+ }
+ /** Returns the total number of points this path has. */
+ inline int GetPointCount()
+ {
+ if (m_Status != ePathFinderStatus::PATH_FOUND)
+ {
+ return 0;
+ }
+ return m_PathPoints.size();
+ }
+
+ struct VectorHasher
+ {
+ std::size_t operator()(const Vector3i & a_Vector) const
+ {
+ // Guaranteed to have no hash collisions for any 128x128x128 area. Suitable for pathfinding.
+ int32_t t = 0;
+ t += (int8_t)a_Vector.x;
+ t = t << 8;
+ t += (int8_t)a_Vector.y;
+ t = t << 8;
+ t += (int8_t)a_Vector.z;
+ t = t << 8;
+ return (size_t)t;
+ }
+ };
+private:
+
+ /* General */
+ bool IsSolid(const Vector3i & a_Location); // Query our hosting world and ask it if there's a solid at a_location.
+ bool Step_Internal(); // The public version just calls this version * CALCULATIONS_PER_CALL times.
+ void FinishCalculation(); // Clears the memory used for calculating the path.
+ void FinishCalculation(ePathFinderStatus a_NewStatus); // Clears the memory used for calculating the path and changes the status.
+ void AttemptToFindAlternative();
+ void BuildPath();
+
+ /* Openlist and closedlist management */
+ void OpenListAdd(cPathCell * a_Cell);
+ cPathCell * OpenListPop();
+ void ProcessIfWalkable(const Vector3i &a_Location, cPathCell * a_Parent, int a_Cost);
+
+ /* Map management */
+ void ProcessCell(cPathCell * a_Cell, cPathCell * a_Caller, int a_GDelta);
+ cPathCell * GetCell(const Vector3i & a_location);
+
+ /* Pathfinding fields */
+ std::priority_queue<cPathCell *, std::vector<cPathCell *>, compareHeuristics> m_OpenList;
+ std::unordered_map<Vector3i, cPathCell, VectorHasher> m_Map;
+ Vector3i m_Destination;
+ Vector3i m_Source;
+ int m_BoundingBoxWidth;
+ int m_BoundingBoxHeight;
+ double m_HalfWidth;
+ int m_StepsLeft;
+ cPathCell * m_NearestPointToTarget;
+ cFastRandom m_Rand;
+
+ /* Control fields */
+ ePathFinderStatus m_Status;
+
+ /* Final path fields */
+ size_t m_CurrentPoint;
+ std::vector<Vector3i> m_PathPoints;
+
+ /* Interfacing with the world */
+ cChunk * m_Chunk; // Only valid inside Step()!
+ bool m_BadChunkFound;
+ #ifdef COMPILING_PATHFIND_DEBUGGER
+ #include "../path_irrlicht.cpp"
+ #endif
+};
diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp
index edd4d9de4..56d6abfd5 100644
--- a/src/Mobs/Pig.cpp
+++ b/src/Mobs/Pig.cpp
@@ -90,7 +90,6 @@ void cPig::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
if (m_Attachee->IsPlayer() && (m_Attachee->GetEquippedWeapon().m_ItemType == E_ITEM_CARROT_ON_STICK))
{
MoveToPosition((m_Attachee->GetPosition()) + (m_Attachee->GetLookVector()*10));
- m_bMovingToDestination = true;
}
}
}
diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp
index c0cdec035..ec24f167e 100644
--- a/src/Mobs/Sheep.cpp
+++ b/src/Mobs/Sheep.cpp
@@ -98,7 +98,7 @@ void cSheep::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
if (m_TimeToStopEating > 0)
{
- m_bMovingToDestination = false; // The sheep should not move when he's eating
+ StopMovingToPosition();
m_TimeToStopEating--;
if (m_TimeToStopEating == 0)
diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp
index 331c8e8ad..f99404669 100644
--- a/src/Mobs/Skeleton.cpp
+++ b/src/Mobs/Skeleton.cpp
@@ -37,7 +37,7 @@ void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer)
else
{
AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_ARROW);
-
+
}
AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_BONE);
AddRandomArmorDropItem(a_Drops, LootingLevel);
@@ -48,25 +48,6 @@ void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-void cSkeleton::MoveToPosition(const Vector3d & a_Position)
-{
- // If the destination is sufficiently skylight challenged AND the skeleton isn't on fire then block the movement
- if (
- !IsOnFire() &&
- (m_World->GetBlockSkyLight((int)floor(a_Position.x), (int)floor(a_Position.y), (int)floor(a_Position.z)) - m_World->GetSkyDarkness() > 8)
- )
- {
- m_bMovingToDestination = false;
- return;
- }
-
- super::MoveToPosition(a_Position);
-}
-
-
-
-
-
void cSkeleton::Attack(std::chrono::milliseconds a_Dt)
{
m_AttackInterval += (static_cast<float>(a_Dt.count()) / 1000) * m_AttackRate;
diff --git a/src/Mobs/Skeleton.h b/src/Mobs/Skeleton.h
index 9c49c52fb..1b6ce4bf2 100644
--- a/src/Mobs/Skeleton.h
+++ b/src/Mobs/Skeleton.h
@@ -11,19 +11,18 @@ class cSkeleton :
public cAggressiveMonster
{
typedef cAggressiveMonster super;
-
+
public:
cSkeleton(bool IsWither);
CLASS_PROTODEF(cSkeleton)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual void MoveToPosition(const Vector3d & a_Position) override;
virtual void Attack(std::chrono::milliseconds a_Dt) override;
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
virtual bool IsUndead(void) override { return true; }
-
+
bool IsWither(void) const { return m_bIsWither; }
private:
diff --git a/src/Mobs/Slime.cpp b/src/Mobs/Slime.cpp
index e42501e47..7fc4821d8 100644
--- a/src/Mobs/Slime.cpp
+++ b/src/Mobs/Slime.cpp
@@ -89,7 +89,7 @@ void cSlime::KilledBy(TakeDamageInfo & a_TDI)
-const AString cSlime::GetSizeName(int a_Size) const
+AString cSlime::GetSizeName(int a_Size)
{
if (a_Size > 1)
{
diff --git a/src/Mobs/Slime.h b/src/Mobs/Slime.h
index 29605992d..40131b101 100644
--- a/src/Mobs/Slime.h
+++ b/src/Mobs/Slime.h
@@ -27,7 +27,7 @@ public:
/** Returns the text describing the slime's size, as used by the client's resource subsystem for sounds.
Returns either "big" or "small". */
- const AString GetSizeName(int a_Size) const;
+ static AString GetSizeName(int a_Size);
protected:
diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp
index 6f647ac18..e4953d546 100644
--- a/src/Mobs/Villager.cpp
+++ b/src/Mobs/Villager.cpp
@@ -156,7 +156,7 @@ void cVillager::HandleFarmerPrepareFarmCrops()
void cVillager::HandleFarmerTryHarvestCrops()
{
// Harvest the crops if the villager isn't moving and if the crops are closer then 2 blocks.
- if (!m_bMovingToDestination && (GetPosition() - m_CropsPos).Length() < 2)
+ if (!m_IsFollowingPath && (GetPosition() - m_CropsPos).Length() < 2)
{
// Check if the blocks didn't change while the villager was walking to the coordinates.
BLOCKTYPE CropBlock = m_World->GetBlock(m_CropsPos.x, m_CropsPos.y, m_CropsPos.z);
diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp
index b3eefdf79..3c2ec1520 100644
--- a/src/Mobs/Wolf.cpp
+++ b/src/Mobs/Wolf.cpp
@@ -5,6 +5,7 @@
#include "../World.h"
#include "../Entities/Player.h"
#include "../Items/ItemHandler.h"
+#include "Broadcaster.h"
@@ -83,13 +84,13 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
SetIsTame(true);
SetOwner(a_Player.GetName(), a_Player.GetUUID());
m_World->BroadcastEntityStatus(*this, esWolfTamed);
- m_World->BroadcastParticleEffect("heart", (float) GetPosX(), (float) GetPosY(), (float) GetPosZ(), 0, 0, 0, 0, 5);
+ m_World->GetBroadcaster().BroadcastParticleEffect("heart", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5);
}
else
{
// Taming failed
m_World->BroadcastEntityStatus(*this, esWolfTaming);
- m_World->BroadcastParticleEffect("smoke", (float) GetPosX(), (float) GetPosY(), (float) GetPosZ(), 0, 0, 0, 0, 5);
+ m_World->GetBroadcaster().BroadcastParticleEffect("smoke", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5);
}
}
}
@@ -137,7 +138,7 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
}
}
}
-
+
m_World->BroadcastEntityMetadata(*this);
}
@@ -203,7 +204,7 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
}
else if (IsSitting())
{
- m_bMovingToDestination = false;
+ StopMovingToPosition();
}
}
diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h
index 73ffb55c2..5de83acd8 100644
--- a/src/Mobs/Wolf.h
+++ b/src/Mobs/Wolf.h
@@ -25,8 +25,8 @@ public:
virtual void Attack(std::chrono::milliseconds a_Dt) override;
// Get functions
- bool IsSitting (void) const { return m_IsSitting; }
- bool IsTame (void) const { return m_IsTame; }
+ bool IsSitting (void) const override { return m_IsSitting; }
+ bool IsTame (void) const override { return m_IsTame; }
bool IsBegging (void) const { return m_IsBegging; }
bool IsAngry (void) const { return m_IsAngry; }
AString GetOwnerName (void) const { return m_OwnerName; }
diff --git a/src/Mobs/Zombie.cpp b/src/Mobs/Zombie.cpp
index 63042e252..fa4ac855d 100644
--- a/src/Mobs/Zombie.cpp
+++ b/src/Mobs/Zombie.cpp
@@ -37,26 +37,3 @@ void cZombie::GetDrops(cItems & a_Drops, cEntity * a_Killer)
AddRandomArmorDropItem(a_Drops, LootingLevel);
AddRandomWeaponDropItem(a_Drops, LootingLevel);
}
-
-
-
-
-
-void cZombie::MoveToPosition(const Vector3d & a_Position)
-{
- // If the destination is sufficiently skylight challenged AND the skeleton isn't on fire then block the movement
- if (
- !IsOnFire() &&
- (m_World->GetBlockSkyLight((int)floor(a_Position.x), (int)floor(a_Position.y), (int)floor(a_Position.z)) - m_World->GetSkyDarkness() > 8)
- )
- {
- m_bMovingToDestination = false;
- return;
- }
-
- super::MoveToPosition(a_Position);
-}
-
-
-
-
diff --git a/src/Mobs/Zombie.h b/src/Mobs/Zombie.h
index 809c2a6fe..47a9f1904 100644
--- a/src/Mobs/Zombie.h
+++ b/src/Mobs/Zombie.h
@@ -10,17 +10,15 @@ class cZombie :
public cAggressiveMonster
{
typedef cAggressiveMonster super;
-
+
public:
cZombie(bool a_IsVillagerZombie);
CLASS_PROTODEF(cZombie)
-
- virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual void MoveToPosition(const Vector3d & a_Position) override;
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
virtual bool IsUndead(void) override { return true; }
-
+
bool IsVillagerZombie(void) const { return m_IsVillagerZombie; }
bool IsConverting (void) const { return m_IsConverting; }
diff --git a/src/MonsterConfig.h b/src/MonsterConfig.h
index 371d324c2..50979c44a 100644
--- a/src/MonsterConfig.h
+++ b/src/MonsterConfig.h
@@ -23,7 +23,7 @@ public:
private:
struct sAttributesStruct;
struct sMonsterConfigState;
- sMonsterConfigState* m_pState;
+ sMonsterConfigState * m_pState;
void Initialize();
} ;
diff --git a/src/Noise/Noise.cpp b/src/Noise/Noise.cpp
index 0249ab6c1..d11c47bc2 100644
--- a/src/Noise/Noise.cpp
+++ b/src/Noise/Noise.cpp
@@ -633,10 +633,10 @@ NOISE_DATATYPE cNoise::CubicNoise3D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOIS
};
const NOISE_DATATYPE FracX = (a_X) - BaseX;
- const NOISE_DATATYPE x1interp1 = CubicInterpolate( points1[0][0], points1[0][1], points1[0][2], points1[0][3], FracX);
- const NOISE_DATATYPE x1interp2 = CubicInterpolate( points1[1][0], points1[1][1], points1[1][2], points1[1][3], FracX);
- const NOISE_DATATYPE x1interp3 = CubicInterpolate( points1[2][0], points1[2][1], points1[2][2], points1[2][3], FracX);
- const NOISE_DATATYPE x1interp4 = CubicInterpolate( points1[3][0], points1[3][1], points1[3][2], points1[3][3], FracX);
+ const NOISE_DATATYPE x1interp1 = CubicInterpolate(points1[0][0], points1[0][1], points1[0][2], points1[0][3], FracX);
+ const NOISE_DATATYPE x1interp2 = CubicInterpolate(points1[1][0], points1[1][1], points1[1][2], points1[1][3], FracX);
+ const NOISE_DATATYPE x1interp3 = CubicInterpolate(points1[2][0], points1[2][1], points1[2][2], points1[2][3], FracX);
+ const NOISE_DATATYPE x1interp4 = CubicInterpolate(points1[3][0], points1[3][1], points1[3][2], points1[3][3], FracX);
const NOISE_DATATYPE points2[4][4] =
{
@@ -646,45 +646,45 @@ NOISE_DATATYPE cNoise::CubicNoise3D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOIS
{ IntNoise3D(BaseX - 1, BaseY + 2, BaseZ), IntNoise3D(BaseX, BaseY + 2, BaseZ), IntNoise3D(BaseX + 1, BaseY + 2, BaseZ), IntNoise3D(BaseX + 2, BaseY + 2, BaseZ), },
};
- const NOISE_DATATYPE x2interp1 = CubicInterpolate( points2[0][0], points2[0][1], points2[0][2], points2[0][3], FracX);
- const NOISE_DATATYPE x2interp2 = CubicInterpolate( points2[1][0], points2[1][1], points2[1][2], points2[1][3], FracX);
- const NOISE_DATATYPE x2interp3 = CubicInterpolate( points2[2][0], points2[2][1], points2[2][2], points2[2][3], FracX);
- const NOISE_DATATYPE x2interp4 = CubicInterpolate( points2[3][0], points2[3][1], points2[3][2], points2[3][3], FracX);
+ const NOISE_DATATYPE x2interp1 = CubicInterpolate(points2[0][0], points2[0][1], points2[0][2], points2[0][3], FracX);
+ const NOISE_DATATYPE x2interp2 = CubicInterpolate(points2[1][0], points2[1][1], points2[1][2], points2[1][3], FracX);
+ const NOISE_DATATYPE x2interp3 = CubicInterpolate(points2[2][0], points2[2][1], points2[2][2], points2[2][3], FracX);
+ const NOISE_DATATYPE x2interp4 = CubicInterpolate(points2[3][0], points2[3][1], points2[3][2], points2[3][3], FracX);
const NOISE_DATATYPE points3[4][4] =
{
- { IntNoise3D( BaseX-1, BaseY-1, BaseZ+1), IntNoise3D( BaseX, BaseY-1, BaseZ+1), IntNoise3D( BaseX+1, BaseY-1, BaseZ+1), IntNoise3D( BaseX+2, BaseY-1, BaseZ + 1), },
- { IntNoise3D( BaseX-1, BaseY, BaseZ+1), IntNoise3D( BaseX, BaseY, BaseZ+1), IntNoise3D( BaseX+1, BaseY, BaseZ+1), IntNoise3D( BaseX+2, BaseY, BaseZ + 1), },
- { IntNoise3D( BaseX-1, BaseY+1, BaseZ+1), IntNoise3D( BaseX, BaseY+1, BaseZ+1), IntNoise3D( BaseX+1, BaseY+1, BaseZ+1), IntNoise3D( BaseX+2, BaseY+1, BaseZ + 1), },
- { IntNoise3D( BaseX-1, BaseY+2, BaseZ+1), IntNoise3D( BaseX, BaseY+2, BaseZ+1), IntNoise3D( BaseX+1, BaseY+2, BaseZ+1), IntNoise3D( BaseX+2, BaseY+2, BaseZ + 1), },
+ { IntNoise3D(BaseX - 1, BaseY - 1, BaseZ + 1), IntNoise3D(BaseX, BaseY - 1, BaseZ + 1), IntNoise3D(BaseX + 1, BaseY - 1, BaseZ + 1), IntNoise3D(BaseX + 2, BaseY - 1, BaseZ + 1), },
+ { IntNoise3D(BaseX - 1, BaseY, BaseZ + 1), IntNoise3D(BaseX, BaseY, BaseZ + 1), IntNoise3D(BaseX + 1, BaseY, BaseZ + 1), IntNoise3D(BaseX + 2, BaseY, BaseZ + 1), },
+ { IntNoise3D(BaseX - 1, BaseY + 1, BaseZ + 1), IntNoise3D(BaseX, BaseY + 1, BaseZ + 1), IntNoise3D(BaseX + 1, BaseY + 1, BaseZ + 1), IntNoise3D(BaseX + 2, BaseY + 1, BaseZ + 1), },
+ { IntNoise3D(BaseX - 1, BaseY + 2, BaseZ + 1), IntNoise3D(BaseX, BaseY + 2, BaseZ + 1), IntNoise3D(BaseX + 1, BaseY + 2, BaseZ + 1), IntNoise3D(BaseX + 2, BaseY + 2, BaseZ + 1), },
};
- const NOISE_DATATYPE x3interp1 = CubicInterpolate( points3[0][0], points3[0][1], points3[0][2], points3[0][3], FracX);
- const NOISE_DATATYPE x3interp2 = CubicInterpolate( points3[1][0], points3[1][1], points3[1][2], points3[1][3], FracX);
- const NOISE_DATATYPE x3interp3 = CubicInterpolate( points3[2][0], points3[2][1], points3[2][2], points3[2][3], FracX);
- const NOISE_DATATYPE x3interp4 = CubicInterpolate( points3[3][0], points3[3][1], points3[3][2], points3[3][3], FracX);
+ const NOISE_DATATYPE x3interp1 = CubicInterpolate(points3[0][0], points3[0][1], points3[0][2], points3[0][3], FracX);
+ const NOISE_DATATYPE x3interp2 = CubicInterpolate(points3[1][0], points3[1][1], points3[1][2], points3[1][3], FracX);
+ const NOISE_DATATYPE x3interp3 = CubicInterpolate(points3[2][0], points3[2][1], points3[2][2], points3[2][3], FracX);
+ const NOISE_DATATYPE x3interp4 = CubicInterpolate(points3[3][0], points3[3][1], points3[3][2], points3[3][3], FracX);
const NOISE_DATATYPE points4[4][4] =
{
- { IntNoise3D( BaseX-1, BaseY-1, BaseZ+2), IntNoise3D( BaseX, BaseY-1, BaseZ+2), IntNoise3D( BaseX+1, BaseY-1, BaseZ+2), IntNoise3D( BaseX+2, BaseY-1, BaseZ+2), },
- { IntNoise3D( BaseX-1, BaseY, BaseZ+2), IntNoise3D( BaseX, BaseY, BaseZ+2), IntNoise3D( BaseX+1, BaseY, BaseZ+2), IntNoise3D( BaseX+2, BaseY, BaseZ+2), },
- { IntNoise3D( BaseX-1, BaseY+1, BaseZ+2), IntNoise3D( BaseX, BaseY+1, BaseZ+2), IntNoise3D( BaseX+1, BaseY+1, BaseZ+2), IntNoise3D( BaseX+2, BaseY+1, BaseZ+2), },
- { IntNoise3D( BaseX-1, BaseY+2, BaseZ+2), IntNoise3D( BaseX, BaseY+2, BaseZ+2), IntNoise3D( BaseX+1, BaseY+2, BaseZ+2), IntNoise3D( BaseX+2, BaseY+2, BaseZ+2), },
+ { IntNoise3D(BaseX - 1, BaseY - 1, BaseZ + 2), IntNoise3D(BaseX, BaseY - 1, BaseZ + 2), IntNoise3D(BaseX + 1, BaseY - 1, BaseZ + 2), IntNoise3D(BaseX + 2, BaseY - 1, BaseZ + 2), },
+ { IntNoise3D(BaseX - 1, BaseY, BaseZ + 2), IntNoise3D(BaseX, BaseY, BaseZ + 2), IntNoise3D(BaseX + 1, BaseY, BaseZ + 2), IntNoise3D(BaseX + 2, BaseY, BaseZ + 2), },
+ { IntNoise3D(BaseX - 1, BaseY + 1, BaseZ + 2), IntNoise3D(BaseX, BaseY + 1, BaseZ + 2), IntNoise3D(BaseX + 1, BaseY + 1, BaseZ + 2), IntNoise3D(BaseX + 2, BaseY + 1, BaseZ + 2), },
+ { IntNoise3D(BaseX - 1, BaseY + 2, BaseZ + 2), IntNoise3D(BaseX, BaseY + 2, BaseZ + 2), IntNoise3D(BaseX + 1, BaseY + 2, BaseZ + 2), IntNoise3D(BaseX + 2, BaseY + 2, BaseZ + 2), },
};
- const NOISE_DATATYPE x4interp1 = CubicInterpolate( points4[0][0], points4[0][1], points4[0][2], points4[0][3], FracX);
- const NOISE_DATATYPE x4interp2 = CubicInterpolate( points4[1][0], points4[1][1], points4[1][2], points4[1][3], FracX);
- const NOISE_DATATYPE x4interp3 = CubicInterpolate( points4[2][0], points4[2][1], points4[2][2], points4[2][3], FracX);
- const NOISE_DATATYPE x4interp4 = CubicInterpolate( points4[3][0], points4[3][1], points4[3][2], points4[3][3], FracX);
+ const NOISE_DATATYPE x4interp1 = CubicInterpolate(points4[0][0], points4[0][1], points4[0][2], points4[0][3], FracX);
+ const NOISE_DATATYPE x4interp2 = CubicInterpolate(points4[1][0], points4[1][1], points4[1][2], points4[1][3], FracX);
+ const NOISE_DATATYPE x4interp3 = CubicInterpolate(points4[2][0], points4[2][1], points4[2][2], points4[2][3], FracX);
+ const NOISE_DATATYPE x4interp4 = CubicInterpolate(points4[3][0], points4[3][1], points4[3][2], points4[3][3], FracX);
const NOISE_DATATYPE FracY = (a_Y) - BaseY;
- const NOISE_DATATYPE yinterp1 = CubicInterpolate( x1interp1, x1interp2, x1interp3, x1interp4, FracY);
- const NOISE_DATATYPE yinterp2 = CubicInterpolate( x2interp1, x2interp2, x2interp3, x2interp4, FracY);
- const NOISE_DATATYPE yinterp3 = CubicInterpolate( x3interp1, x3interp2, x3interp3, x3interp4, FracY);
- const NOISE_DATATYPE yinterp4 = CubicInterpolate( x4interp1, x4interp2, x4interp3, x4interp4, FracY);
+ const NOISE_DATATYPE yinterp1 = CubicInterpolate(x1interp1, x1interp2, x1interp3, x1interp4, FracY);
+ const NOISE_DATATYPE yinterp2 = CubicInterpolate(x2interp1, x2interp2, x2interp3, x2interp4, FracY);
+ const NOISE_DATATYPE yinterp3 = CubicInterpolate(x3interp1, x3interp2, x3interp3, x3interp4, FracY);
+ const NOISE_DATATYPE yinterp4 = CubicInterpolate(x4interp1, x4interp2, x4interp3, x4interp4, FracY);
const NOISE_DATATYPE FracZ = (a_Z) - BaseZ;
- return CubicInterpolate( yinterp1, yinterp2, yinterp3, yinterp4, FracZ);
+ return CubicInterpolate(yinterp1, yinterp2, yinterp3, yinterp4, FracZ);
}
diff --git a/src/OSSupport/CriticalSection.h b/src/OSSupport/CriticalSection.h
index d52f049d2..a95a9a0cd 100644
--- a/src/OSSupport/CriticalSection.h
+++ b/src/OSSupport/CriticalSection.h
@@ -13,7 +13,7 @@ public:
void Lock(void);
void Unlock(void);
- // IsLocked/IsLockedByCurrentThread are only used in ASSERT statements, but because of the changes with ASSERT they must always be defined
+ // IsLocked / IsLockedByCurrentThread are only used in ASSERT statements, but because of the changes with ASSERT they must always be defined
// The fake versions (in Release) will not effect the program in any way
#ifdef _DEBUG
cCriticalSection(void);
diff --git a/src/OSSupport/Errors.cpp b/src/OSSupport/Errors.cpp
index a5361e1a6..407799495 100644
--- a/src/OSSupport/Errors.cpp
+++ b/src/OSSupport/Errors.cpp
@@ -22,7 +22,7 @@ AString GetOSErrorString( int a_ErrNo)
// According to http://linux.die.net/man/3/strerror_r there are two versions of strerror_r():
- #if !defined(__APPLE__) && ( _GNU_SOURCE) && !defined(ANDROID_NDK) // GNU version of strerror_r()
+ #if !defined(__APPLE__) && defined( _GNU_SOURCE) && !defined(ANDROID_NDK) // GNU version of strerror_r()
char * res = strerror_r( errno, buffer, ARRAYCOUNT(buffer));
if (res != nullptr)
diff --git a/src/OSSupport/File.cpp b/src/OSSupport/File.cpp
index 8957dfaef..43105b230 100644
--- a/src/OSSupport/File.cpp
+++ b/src/OSSupport/File.cpp
@@ -453,6 +453,108 @@ AString cFile::ReadWholeFile(const AString & a_FileName)
+AString cFile::ChangeFileExt(const AString & a_FileName, const AString & a_NewExt)
+{
+ auto res = a_FileName;
+
+ // If the path separator is the last character of the string, return the string unmodified (refers to a folder):
+ #if defined(_MSC_VER)
+ // Find either path separator - MSVC CRT accepts slashes as separators, too
+ auto LastPathSep = res.find_last_of("/\\");
+ #elif defined(_WIN32)
+ // Windows with different CRTs support only the backslash separator
+ auto LastPathSep = res.rfind('\\');
+ #else
+ // Linux supports only the slash separator
+ auto LastPathSep = res.rfind('/');
+ #endif
+ if ((LastPathSep != AString::npos) && (LastPathSep + 1 == res.size()))
+ {
+ return res;
+ }
+
+ // Append or replace the extension:
+ auto DotPos = res.rfind('.');
+ if (
+ (DotPos == AString::npos) || // No dot found
+ ((LastPathSep != AString::npos) && (LastPathSep > DotPos)) // Last dot is before the last path separator (-> in folder name)
+ )
+ {
+ // No extension, just append the new one:
+ if (!a_NewExt.empty() && (a_NewExt[0] != '.'))
+ {
+ // a_NewExt doesn't start with a dot, insert one:
+ res.push_back('.');
+ }
+ res.append(a_NewExt);
+ }
+ else
+ {
+ // Replace existing extension:
+ if (!a_NewExt.empty() && (a_NewExt[0] != '.'))
+ {
+ // a_NewExt doesn't start with a dot, keep the current one:
+ res.erase(DotPos + 1, AString::npos);
+ }
+ else
+ {
+ res.erase(DotPos, AString::npos);
+ }
+ res.append(a_NewExt);
+ }
+ return res;
+}
+
+
+
+
+
+unsigned cFile::GetLastModificationTime(const AString & a_FileName)
+{
+ struct stat st;
+ if (stat(a_FileName.c_str(), &st) < 0)
+ {
+ return 0;
+ }
+ #ifdef _WIN32
+ // Windows returns times in local time already
+ return static_cast<unsigned>(st.st_mtime);
+ #else
+ // Linux returns UTC time, convert to local timezone:
+ return static_cast<unsigned>(mktime(localtime(&st.st_mtime)));
+ #endif
+}
+
+
+
+
+
+AString cFile::GetPathSeparator(void)
+{
+ #ifdef _WIN32
+ return "\\";
+ #else
+ return "/";
+ #endif
+}
+
+
+
+
+
+AString cFile::GetExecutableExt(void)
+{
+ #ifdef _WIN32
+ return ".exe";
+ #else
+ return "";
+ #endif
+}
+
+
+
+
+
int cFile::Printf(const char * a_Fmt, ...)
{
AString buf;
diff --git a/src/OSSupport/File.h b/src/OSSupport/File.h
index ac6d1ab21..1b5e71a17 100644
--- a/src/OSSupport/File.h
+++ b/src/OSSupport/File.h
@@ -62,7 +62,7 @@ public:
{
fmRead, // Read-only. If the file doesn't exist, object will not be valid
fmWrite, // Write-only. If the file already exists, it will be overwritten
- fmReadWrite, // Read/write. If the file already exists, it will be left intact; writing will overwrite the data from the beginning
+ fmReadWrite, // Read / write. If the file already exists, it will be left intact; writing will overwrite the data from the beginning
fmAppend // Write-only. If the file already exists cursor will be moved to the end of the file
} ;
@@ -124,9 +124,30 @@ public:
/** Creates a new folder with the specified name. Returns true if successful. Path may be relative or absolute */
static bool CreateFolder(const AString & a_FolderPath);
- /** Returns the entire contents of the specified file as a string. Returns empty string on error. */
+ // tolua_end
+
+ /** Returns the entire contents of the specified file as a string. Returns empty string on error.
+ Exported manually in ManualBindings.cpp due to #1914 - ToLua code doesn't work well with binary files. */
static AString ReadWholeFile(const AString & a_FileName);
+ // tolua_begin
+
+ /** Returns a_FileName with its extension changed to a_NewExt.
+ a_FileName may contain path specification. */
+ static AString ChangeFileExt(const AString & a_FileName, const AString & a_NewExt);
+
+ /** Returns the last modification time (in current timezone) of the specified file.
+ The value returned is in the same units as the value returned by time() function.
+ If the file is not found / accessible, zero is returned. */
+ static unsigned GetLastModificationTime(const AString & a_FileName);
+
+ /** Returns the path separator used by the current platform.
+ Note that the platform / CRT may support additional path separators (such as slashes on Windows), these don't get reported. */
+ static AString GetPathSeparator(void);
+
+ /** Returns the customary executable extension used by the current platform. */
+ static AString GetExecutableExt(void);
+
// tolua_end
/** Returns the list of all items in the specified folder (files, folders, nix pipes, whatever's there). */
diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h
index 95a935bbe..1162d7fc6 100644
--- a/src/OSSupport/Network.h
+++ b/src/OSSupport/Network.h
@@ -162,7 +162,7 @@ public:
/** Returns the local port to which the underlying socket is bound. */
virtual UInt16 GetPort(void) const = 0;
- /** Sends the specified payload in a single UDP datagram to the specified host+port combination.
+ /** Sends the specified payload in a single UDP datagram to the specified host + port combination.
Note that in order to send to a broadcast address, you need to call EnableBroadcasts() first. */
virtual bool Send(const AString & a_Payload, const AString & a_Host, UInt16 a_Port) = 0;
diff --git a/src/OSSupport/Semaphore.h b/src/OSSupport/Semaphore.h
index adc531ed8..57fa4bdb2 100644
--- a/src/OSSupport/Semaphore.h
+++ b/src/OSSupport/Semaphore.h
@@ -9,7 +9,7 @@ public:
void Wait();
void Signal();
private:
- void* m_Handle; // HANDLE pointer
+ void * m_Handle; // HANDLE pointer
#ifndef _WIN32
bool m_bNamed;
diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp
index c6f1978ad..ae6ba04f1 100644
--- a/src/OSSupport/TCPLinkImpl.cpp
+++ b/src/OSSupport/TCPLinkImpl.cpp
@@ -23,7 +23,6 @@ cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks):
m_RemotePort(0),
m_ShouldShutdown(false)
{
- LOGD("Created new cTCPLinkImpl at %p with BufferEvent at %p", this, m_BufferEvent);
}
@@ -38,8 +37,6 @@ cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_L
m_RemotePort(0),
m_ShouldShutdown(false)
{
- LOGD("Created new cTCPLinkImpl at %p with BufferEvent at %p", this, m_BufferEvent);
-
// Update the endpoint addresses:
UpdateLocalAddress();
UpdateAddress(a_Address, a_AddrLen, m_RemoteIP, m_RemotePort);
@@ -51,7 +48,6 @@ cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_L
cTCPLinkImpl::~cTCPLinkImpl()
{
- LOGD("Deleting cTCPLinkImpl at %p with BufferEvent at %p", this, m_BufferEvent);
bufferevent_free(m_BufferEvent);
}
@@ -216,8 +212,6 @@ void cTCPLinkImpl::WriteCallback(bufferevent * a_BufferEvent, void * a_Self)
void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self)
{
- LOGD("cTCPLink event callback for link %p, BEV %p; what = 0x%02x", a_Self, a_BufferEvent, a_What);
-
ASSERT(a_Self != nullptr);
cTCPLinkImplPtr Self = static_cast<cTCPLinkImpl *>(a_Self)->m_Self;
diff --git a/src/OSSupport/UDPEndpointImpl.cpp b/src/OSSupport/UDPEndpointImpl.cpp
index 31ca107ce..68117e5a0 100644
--- a/src/OSSupport/UDPEndpointImpl.cpp
+++ b/src/OSSupport/UDPEndpointImpl.cpp
@@ -384,7 +384,7 @@ void cUDPEndpointImpl::Open(UInt16 a_Port)
// Failed to create IPv6 socket, create an IPv4 one instead:
m_IsMainSockIPv6 = false;
err = EVUTIL_SOCKET_ERROR();
- LOGD("Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err));
+ LOGD("UDP: Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err));
m_MainSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (!IsValidSocket(m_MainSock))
{
diff --git a/src/OverridesSettingsRepository.cpp b/src/OverridesSettingsRepository.cpp
new file mode 100644
index 000000000..e63f2c44c
--- /dev/null
+++ b/src/OverridesSettingsRepository.cpp
@@ -0,0 +1,273 @@
+
+#include "Globals.h"
+#include "OverridesSettingsRepository.h"
+
+cOverridesSettingsRepository::cOverridesSettingsRepository(std::unique_ptr<cSettingsRepositoryInterface> a_Main, std::unique_ptr<cSettingsRepositoryInterface> a_Overrides) :
+ m_Main(std::move(a_Main)),
+ m_Overrides(std::move(a_Overrides))
+{
+}
+
+
+
+
+
+bool cOverridesSettingsRepository::KeyExists(const AString a_keyName) const
+{
+ return m_Overrides->KeyExists(a_keyName) || m_Main->KeyExists(a_keyName);
+}
+
+
+
+
+bool cOverridesSettingsRepository::HasValue(const AString & a_KeyName, const AString & a_ValueName) const
+{
+ return m_Overrides->HasValue(a_KeyName, a_ValueName) || m_Main->HasValue(a_KeyName, a_ValueName);
+}
+
+
+
+
+
+int cOverridesSettingsRepository::AddKeyName(const AString & a_keyname)
+{
+
+ if (m_Overrides->KeyExists(a_keyname))
+ {
+ m_Overrides->AddKeyName(a_keyname);
+ return 0;
+ }
+
+ return m_Main->AddKeyName(a_keyname);
+}
+
+
+
+
+
+bool cOverridesSettingsRepository::AddKeyComment(const AString & a_keyname, const AString & a_comment)
+{
+ if (m_Overrides->KeyExists(a_keyname))
+ {
+ return m_Overrides->AddKeyComment(a_keyname, a_comment);
+ }
+
+ return m_Main->AddKeyComment(a_keyname, a_comment);
+}
+
+
+
+
+
+AString cOverridesSettingsRepository::GetKeyComment(const AString & a_keyname, const int a_commentID) const
+{
+
+ if (m_Overrides->KeyExists(a_keyname))
+ {
+ return m_Overrides->GetKeyComment(a_keyname, a_commentID);
+ }
+
+ return m_Main->GetKeyComment(a_keyname, a_commentID);
+}
+
+
+
+
+
+bool cOverridesSettingsRepository::DeleteKeyComment(const AString & a_keyname, const int a_commentID)
+{
+ if (m_Overrides->KeyExists(a_keyname))
+ {
+ return m_Overrides->DeleteKeyComment(a_keyname, a_commentID);
+ }
+
+ return m_Main->DeleteKeyComment(a_keyname, a_commentID);
+}
+
+
+
+
+
+void cOverridesSettingsRepository::AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value)
+{
+ if (m_Overrides->HasValue(a_KeyName, a_ValueName))
+ {
+ m_Overrides->AddValue(a_KeyName, a_ValueName, a_Value);
+ }
+ else
+ {
+ m_Main->AddValue(a_KeyName, a_ValueName, a_Value);
+ }
+}
+
+
+
+
+
+std::vector<std::pair<AString, AString>> cOverridesSettingsRepository::GetValues(AString a_keyName)
+{
+ auto overrides = m_Overrides->GetValues(a_keyName);
+ auto main = m_Main->GetValues(a_keyName);
+ std::sort(overrides.begin(), overrides.end(), [](std::pair<AString, AString> a, std::pair<AString, AString> b) -> bool { return a < b ;});
+ std::sort(main.begin(), main.end(), [](std::pair<AString, AString> a, std::pair<AString, AString> b) -> bool { return a < b ;});
+
+ std::vector<std::pair<AString, AString>> ret;
+
+
+ size_t overridesIndex = 0;
+ for (auto pair : main)
+ {
+ if (overridesIndex >= overrides.size())
+ {
+ ret.push_back(pair);
+ continue;
+ }
+ if (pair.first == overrides[overridesIndex].first)
+ {
+ continue;
+ }
+ while (pair.first > overrides[overridesIndex].first)
+ {
+ ret.push_back(overrides[overridesIndex]);
+ overridesIndex++;
+ }
+ ret.push_back(pair);
+ }
+ return ret;
+}
+
+
+
+
+
+AString cOverridesSettingsRepository::GetValue(const AString & a_KeyName, const AString & a_ValueName, const AString & defValue) const
+{
+ if (m_Overrides->HasValue(a_KeyName, a_ValueName))
+ {
+ return m_Overrides->GetValue(a_KeyName, a_ValueName, defValue);
+ }
+ else
+ {
+ return m_Main->GetValue(a_KeyName, a_ValueName, defValue);
+ }
+}
+
+
+
+
+
+AString cOverridesSettingsRepository::GetValueSet (const AString & a_KeyName, const AString & a_ValueName, const AString & defValue)
+{
+ if (m_Overrides->HasValue(a_KeyName, a_ValueName))
+ {
+ return m_Overrides->GetValueSet(a_KeyName, a_ValueName, defValue);
+ }
+ else
+ {
+ return m_Main->GetValueSet(a_KeyName, a_ValueName, defValue);
+ }
+}
+
+
+
+
+
+int cOverridesSettingsRepository::GetValueSetI(const AString & a_KeyName, const AString & a_ValueName, const int defValue)
+{
+ if (m_Overrides->HasValue(a_KeyName, a_ValueName))
+ {
+ return m_Overrides->GetValueSetI(a_KeyName, a_ValueName, defValue);
+ }
+ else
+ {
+ return m_Main->GetValueSetI(a_KeyName, a_ValueName, defValue);
+ }
+}
+
+
+
+
+
+Int64 cOverridesSettingsRepository::GetValueSetI(const AString & a_KeyName, const AString & a_ValueName, const Int64 defValue)
+{
+ if (m_Overrides->HasValue(a_KeyName, a_ValueName))
+ {
+ return m_Overrides->GetValueSetI(a_KeyName, a_ValueName, defValue);
+ }
+ else
+ {
+ return m_Main->GetValueSetI(a_KeyName, a_ValueName, defValue);
+ }
+}
+
+
+
+
+
+bool cOverridesSettingsRepository::GetValueSetB(const AString & a_KeyName, const AString & a_ValueName, const bool defValue)
+{
+ if (m_Overrides->HasValue(a_KeyName, a_ValueName))
+ {
+ return m_Overrides->GetValueSetB(a_KeyName, a_ValueName, defValue);
+ }
+ else
+ {
+ return m_Main->GetValueSetB(a_KeyName, a_ValueName, defValue);
+ }
+}
+
+
+
+
+
+bool cOverridesSettingsRepository::SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists)
+{
+ if (m_Overrides->HasValue(a_KeyName, a_ValueName))
+ {
+ return m_Overrides->SetValue(a_KeyName, a_ValueName, a_Value, a_CreateIfNotExists);
+ }
+ else
+ {
+ return m_Main->SetValue(a_KeyName, a_ValueName, a_Value, a_CreateIfNotExists);
+ }
+}
+
+
+
+
+
+bool cOverridesSettingsRepository::SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists)
+{
+ if (m_Overrides->HasValue(a_KeyName, a_ValueName))
+ {
+ return m_Overrides->SetValueI(a_KeyName, a_ValueName, a_Value, a_CreateIfNotExists);
+ }
+ else
+ {
+ return m_Main->SetValueI(a_KeyName, a_ValueName, a_Value, a_CreateIfNotExists);
+ }
+}
+
+
+
+
+
+bool cOverridesSettingsRepository::DeleteValue(const AString & a_KeyName, const AString & a_ValueName)
+{
+ if (m_Overrides->HasValue(a_KeyName, a_ValueName))
+ {
+ return m_Overrides->DeleteValue(a_KeyName, a_ValueName);
+ }
+ else
+ {
+ return m_Main->DeleteValue(a_KeyName, a_ValueName);
+ }
+}
+
+
+
+bool cOverridesSettingsRepository::Flush()
+{
+ return m_Overrides->Flush() && m_Main->Flush();
+}
+
diff --git a/src/OverridesSettingsRepository.h b/src/OverridesSettingsRepository.h
new file mode 100644
index 000000000..04a53997f
--- /dev/null
+++ b/src/OverridesSettingsRepository.h
@@ -0,0 +1,52 @@
+
+#pragma once
+
+#include "SettingsRepositoryInterface.h"
+
+#include <unordered_map>
+
+class cOverridesSettingsRepository : public cSettingsRepositoryInterface
+{
+
+public:
+ cOverridesSettingsRepository(std::unique_ptr<cSettingsRepositoryInterface> a_Main, std::unique_ptr<cSettingsRepositoryInterface> a_Overrides);
+
+ virtual ~cOverridesSettingsRepository() = default;
+
+ virtual bool KeyExists(const AString keyname) const override;
+
+ virtual bool HasValue(const AString & a_KeyName, const AString & a_ValueName) const override;
+
+ virtual int AddKeyName(const AString & keyname) override;
+
+ virtual bool AddKeyComment(const AString & keyname, const AString & comment) override;
+
+ virtual AString GetKeyComment(const AString & keyname, const int commentID) const override;
+
+ virtual bool DeleteKeyComment(const AString & keyname, const int commentID) override;
+
+ virtual void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) override;
+
+ virtual std::vector<std::pair<AString, AString>> GetValues(AString a_keyName) override;
+
+ virtual AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const override;
+
+ virtual AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = "") override;
+ virtual int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0) override;
+ virtual Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0) override;
+ virtual bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) override;
+
+ virtual bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true) override;
+ virtual bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true) override;
+
+ virtual bool DeleteValue(const AString & keyname, const AString & valuename) override;
+
+ virtual bool Flush() override;
+
+private:
+
+ std::unique_ptr<cSettingsRepositoryInterface> m_Main;
+ std::unique_ptr<cSettingsRepositoryInterface> m_Overrides;
+
+};
+
diff --git a/src/PolarSSL++/BlockingSslClientSocket.cpp b/src/PolarSSL++/BlockingSslClientSocket.cpp
index 821125b31..f5ad2f08c 100644
--- a/src/PolarSSL++/BlockingSslClientSocket.cpp
+++ b/src/PolarSSL++/BlockingSslClientSocket.cpp
@@ -54,19 +54,19 @@ class cBlockingSslClientSocketLinkCallbacks:
}
- virtual void OnReceivedData(const char * a_Data, size_t a_Length)
+ virtual void OnReceivedData(const char * a_Data, size_t a_Length) override
{
m_Socket.OnReceivedData(a_Data, a_Length);
}
- virtual void OnRemoteClosed(void)
+ virtual void OnRemoteClosed(void) override
{
m_Socket.OnDisconnected();
}
- virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg)
+ virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
{
m_Socket.OnDisconnected();
}
diff --git a/src/PolarSSL++/BlockingSslClientSocket.h b/src/PolarSSL++/BlockingSslClientSocket.h
index 319e82bf2..462ee95a7 100644
--- a/src/PolarSSL++/BlockingSslClientSocket.h
+++ b/src/PolarSSL++/BlockingSslClientSocket.h
@@ -21,6 +21,11 @@ class cBlockingSslClientSocket :
{
public:
cBlockingSslClientSocket(void);
+
+ ~cBlockingSslClientSocket(void)
+ {
+ Disconnect();
+ }
/** Connects to the specified server and performs SSL handshake.
Returns true if successful, false on failure. Sets internal error text on failure. */
diff --git a/src/PolarSSL++/CMakeLists.txt b/src/PolarSSL++/CMakeLists.txt
index 39d41292d..b11d16e33 100644
--- a/src/PolarSSL++/CMakeLists.txt
+++ b/src/PolarSSL++/CMakeLists.txt
@@ -37,6 +37,6 @@ if(NOT MSVC)
add_library(PolarSSL++ ${SRCS} ${HDRS})
if (UNIX)
- target_link_libraries(PolarSSL++ polarssl)
+ target_link_libraries(PolarSSL++ mbedtls)
endif()
endif()
diff --git a/src/ProbabDistrib.cpp b/src/ProbabDistrib.cpp
index c34c75982..ec3a4d85f 100644
--- a/src/ProbabDistrib.cpp
+++ b/src/ProbabDistrib.cpp
@@ -133,6 +133,7 @@ int cProbabDistrib::MapValue(int a_OrigValue) const
// Linearly interpolate between Lo and Hi:
int ProbDif = m_Cumulative[Hi].m_Probability - m_Cumulative[Lo].m_Probability;
+ ProbDif = (ProbDif != 0) ? ProbDif : 1;
int ValueDif = m_Cumulative[Hi].m_Value - m_Cumulative[Lo].m_Value;
return m_Cumulative[Lo].m_Value + (a_OrigValue - m_Cumulative[Lo].m_Probability) * ValueDif / ProbDif;
}
diff --git a/src/Protocol/Authenticator.cpp b/src/Protocol/Authenticator.cpp
index c9e4296a2..bfbe5028d 100644
--- a/src/Protocol/Authenticator.cpp
+++ b/src/Protocol/Authenticator.cpp
@@ -19,6 +19,10 @@
#define DEFAULT_AUTH_SERVER "sessionserver.mojang.com"
#define DEFAULT_AUTH_ADDRESS "/session/minecraft/hasJoined?username=%USERNAME%&serverId=%SERVERID%"
+
+
+
+
cAuthenticator::cAuthenticator(void) :
super("cAuthenticator"),
m_Server(DEFAULT_AUTH_SERVER),
@@ -40,11 +44,11 @@ cAuthenticator::~cAuthenticator()
-void cAuthenticator::ReadINI(cIniFile & IniFile)
+void cAuthenticator::ReadSettings(cSettingsRepositoryInterface & a_Settings)
{
- m_Server = IniFile.GetValueSet ("Authentication", "Server", DEFAULT_AUTH_SERVER);
- m_Address = IniFile.GetValueSet ("Authentication", "Address", DEFAULT_AUTH_ADDRESS);
- m_ShouldAuthenticate = IniFile.GetValueSetB("Authentication", "Authenticate", true);
+ m_Server = a_Settings.GetValueSet ("Authentication", "Server", DEFAULT_AUTH_SERVER);
+ m_Address = a_Settings.GetValueSet ("Authentication", "Address", DEFAULT_AUTH_ADDRESS);
+ m_ShouldAuthenticate = a_Settings.GetValueSetB("Authentication", "Authenticate", true);
}
@@ -69,9 +73,9 @@ void cAuthenticator::Authenticate(int a_ClientID, const AString & a_UserName, co
-void cAuthenticator::Start(cIniFile & IniFile)
+void cAuthenticator::Start(cSettingsRepositoryInterface & a_Settings)
{
- ReadINI(IniFile);
+ ReadSettings(a_Settings);
m_ShouldTerminate = false;
super::Start();
}
@@ -267,3 +271,7 @@ bool cAuthenticator::GetPlayerProperties(const AString & a_UUID, Json::Value & a
return true;
}
*/
+
+
+
+
diff --git a/src/Protocol/Authenticator.h b/src/Protocol/Authenticator.h
index 853eff535..02b349256 100644
--- a/src/Protocol/Authenticator.h
+++ b/src/Protocol/Authenticator.h
@@ -14,7 +14,7 @@
#include "../OSSupport/IsThread.h"
-
+class cSettingsRepositoryInterface;
@@ -40,13 +40,13 @@ public:
~cAuthenticator();
/** (Re-)read server and address from INI: */
- void ReadINI(cIniFile & IniFile);
+ void ReadSettings(cSettingsRepositoryInterface & a_Settings);
/** Queues a request for authenticating a user. If the auth fails, the user will be kicked */
void Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash);
/** Starts the authenticator thread. The thread may be started and stopped repeatedly */
- void Start(cIniFile & IniFile);
+ void Start(cSettingsRepositoryInterface & a_Settings);
/** Stops the authenticator thread. The thread may be started and stopped repeatedly */
void Stop(void);
diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp
index 60fd5f935..2b9c06779 100644
--- a/src/Protocol/ChunkDataSerializer.cpp
+++ b/src/Protocol/ChunkDataSerializer.cpp
@@ -68,7 +68,7 @@ const AString & cChunkDataSerializer::Serialize(int a_Version, int a_ChunkX, int
void cChunkDataSerializer::Serialize29(AString & a_Data)
{
- // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream)
+ // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib can stream)
const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width;
const int MetadataOffset = sizeof(m_BlockTypes);
@@ -126,7 +126,7 @@ void cChunkDataSerializer::Serialize29(AString & a_Data)
void cChunkDataSerializer::Serialize39(AString & a_Data)
{
- // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream)
+ // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib can stream)
const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width;
const int MetadataOffset = sizeof(m_BlockTypes);
diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp
index 570754204..110590359 100644
--- a/src/Protocol/MojangAPI.cpp
+++ b/src/Protocol/MojangAPI.cpp
@@ -38,12 +38,36 @@ const int MAX_PER_QUERY = 100;
-/** This is the data of the root certs for Starfield Technologies, the CA that signed sessionserver.mojang.com's cert:
-Downloaded from http://certs.starfieldtech.com/repository/ */
-static const AString & StarfieldCACert(void)
+/** Returns the CA certificates that should be trusted for Mojang-related connections. */
+static const AString & GetCACerts(void)
{
static const AString Cert(
- // G2 cert
+ // Equifax root CA cert
+ // Currently used for signing *.mojang.com's cert
+ // Exported from Mozilla Firefox's built-in CA repository
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV\n"
+ "UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy\n"
+ "dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1\n"
+ "MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx\n"
+ "dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B\n"
+ "AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f\n"
+ "BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A\n"
+ "cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC\n"
+ "AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ\n"
+ "MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm\n"
+ "aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw\n"
+ "ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj\n"
+ "IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF\n"
+ "MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA\n"
+ "A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y\n"
+ "7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh\n"
+ "1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4\n"
+ "-----END CERTIFICATE-----\n\n"
+
+ // Starfield G2 cert
+ // This is the data of the root certs for Starfield Technologies, the CA that used to sign sessionserver.mojang.com's cert
+ // Downloaded from http://certs.starfieldtech.com/repository/
"-----BEGIN CERTIFICATE-----\n"
"MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx\n"
"EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT\n"
@@ -67,7 +91,8 @@ static const AString & StarfieldCACert(void)
"pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1\n"
"mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0\n"
"-----END CERTIFICATE-----\n\n"
- // Original (G1) cert:
+
+ // Starfield original (G1) cert:
"-----BEGIN CERTIFICATE-----\n"
"MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl\n"
"MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp\n"
@@ -226,12 +251,12 @@ cMojangAPI::~cMojangAPI()
-void cMojangAPI::Start(cIniFile & a_SettingsIni, bool a_ShouldAuth)
+void cMojangAPI::Start(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth)
{
- m_NameToUUIDServer = a_SettingsIni.GetValueSet("MojangAPI", "NameToUUIDServer", DEFAULT_NAME_TO_UUID_SERVER);
- m_NameToUUIDAddress = a_SettingsIni.GetValueSet("MojangAPI", "NameToUUIDAddress", DEFAULT_NAME_TO_UUID_ADDRESS);
- m_UUIDToProfileServer = a_SettingsIni.GetValueSet("MojangAPI", "UUIDToProfileServer", DEFAULT_UUID_TO_PROFILE_SERVER);
- m_UUIDToProfileAddress = a_SettingsIni.GetValueSet("MojangAPI", "UUIDToProfileAddress", DEFAULT_UUID_TO_PROFILE_ADDRESS);
+ m_NameToUUIDServer = a_Settings.GetValueSet("MojangAPI", "NameToUUIDServer", DEFAULT_NAME_TO_UUID_SERVER);
+ m_NameToUUIDAddress = a_Settings.GetValueSet("MojangAPI", "NameToUUIDAddress", DEFAULT_NAME_TO_UUID_ADDRESS);
+ m_UUIDToProfileServer = a_Settings.GetValueSet("MojangAPI", "UUIDToProfileServer", DEFAULT_UUID_TO_PROFILE_SERVER);
+ m_UUIDToProfileAddress = a_Settings.GetValueSet("MojangAPI", "UUIDToProfileAddress", DEFAULT_UUID_TO_PROFILE_ADDRESS);
LoadCachesFromDisk();
if (a_ShouldAuth)
{
@@ -390,7 +415,7 @@ bool cMojangAPI::SecureRequest(const AString & a_ServerName, const AString & a_R
{
// Connect the socket:
cBlockingSslClientSocket Socket;
- Socket.SetTrustedRootCertsFromString(StarfieldCACert(), a_ServerName);
+ Socket.SetTrustedRootCertsFromString(GetCACerts(), a_ServerName);
if (!Socket.Connect(a_ServerName, 443))
{
LOGWARNING("%s: Can't connect to %s: %s", __FUNCTION__, a_ServerName.c_str(), Socket.GetLastErrorText().c_str());
@@ -434,7 +459,6 @@ bool cMojangAPI::SecureRequest(const AString & a_ServerName, const AString & a_R
a_Response.append((const char *)buf, (size_t)ret);
}
- Socket.Disconnect();
return true;
}
@@ -659,7 +683,7 @@ void cMojangAPI::QueryNamesToUUIDs(AStringVector & a_NamesToQuery)
a_NamesToQuery.erase(a_NamesToQuery.begin(), itr);
Json::FastWriter Writer;
AString RequestBody = Writer.write(root);
-
+
// Create the HTTP request:
AString Request;
Request += "POST " + m_NameToUUIDAddress + " HTTP/1.0\r\n"; // We need to use HTTP 1.0 because we don't handle Chunked transfer encoding
@@ -667,7 +691,7 @@ void cMojangAPI::QueryNamesToUUIDs(AStringVector & a_NamesToQuery)
Request += "User-Agent: MCServer\r\n";
Request += "Connection: close\r\n";
Request += "Content-Type: application/json\r\n";
- Request += Printf("Content-Length: %u\r\n", (unsigned)RequestBody.length());
+ Request += Printf("Content-Length: %u\r\n", static_cast<unsigned>(RequestBody.length()));
Request += "\r\n";
Request += RequestBody;
diff --git a/src/Protocol/MojangAPI.h b/src/Protocol/MojangAPI.h
index 0dc2617b6..bea950740 100644
--- a/src/Protocol/MojangAPI.h
+++ b/src/Protocol/MojangAPI.h
@@ -25,7 +25,7 @@ namespace Json
-
+class cSettingsRepositoryInterface;
// tolua_begin
class cMojangAPI
@@ -38,7 +38,7 @@ public:
/** Initializes the API; reads the settings from the specified ini file.
Loads cached results from disk. */
- void Start(cIniFile & a_SettingsIni, bool a_ShouldAuth);
+ void Start(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth);
/** Connects to the specified server using SSL, sends the given request and receives the response.
Checks Mojang certificates using the hard-coded Starfield root CA certificate.
diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h
index 95adc23e9..7be72014a 100644
--- a/src/Protocol/Protocol.h
+++ b/src/Protocol/Protocol.h
@@ -16,6 +16,8 @@
#include "../Map.h"
#include "../ByteBuffer.h"
+#include <array>
+
@@ -99,6 +101,7 @@ public:
virtual void SendPlayerAbilities (void) = 0;
virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) = 0;
virtual void SendParticleEffect (const AString & a_SoundName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount) = 0;
+ virtual void SendParticleEffect (const AString & a_SoundName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data) = 0;
virtual void SendPlayerListAddPlayer (const cPlayer & a_Player) = 0;
virtual void SendPlayerListRemovePlayer (const cPlayer & a_Player) = 0;
virtual void SendPlayerListUpdateGameMode (const cPlayer & a_Player) = 0;
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index e1e48e43f..0bd219fb1 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -492,7 +492,7 @@ void cProtocol172::SendEntityVelocity(const cEntity & a_Entity)
cPacketizer Pkt(*this, 0x12); // Entity Velocity packet
Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
- // 400 = 8000 / 20 ... Conversion from our speed in m/s to 8000 m/tick
+ // 400 = 8000 / 20 ... Conversion from our speed in m / s to 8000 m / tick
Pkt.WriteBEInt16(static_cast<short>(a_Entity.GetSpeedX() * 400));
Pkt.WriteBEInt16(static_cast<short>(a_Entity.GetSpeedY() * 400));
Pkt.WriteBEInt16(static_cast<short>(a_Entity.GetSpeedZ() * 400));
@@ -813,6 +813,16 @@ void cProtocol172::SendParticleEffect(const AString & a_ParticleName, float a_Sr
+void cProtocol172::SendParticleEffect(const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data)
+{
+ // 1.72 doesn't support extra data
+ this->SendParticleEffect(a_ParticleName, a_Src.x, a_Src.y, a_Src.z, a_Offset.x, a_Offset.y, a_Offset.z, a_ParticleData, a_ParticleAmount);
+}
+
+
+
+
+
void cProtocol172::SendPlayerListAddPlayer(const cPlayer & a_Player)
{
ASSERT(m_State == 3); // In game mode?
@@ -1791,7 +1801,7 @@ void cProtocol172::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
// Version:
Json::Value Version;
- Version["name"] = "1.7.2";
+ Version["name"] = "MCServer 1.7.2";
Version["protocol"] = 4;
// Players:
@@ -2567,7 +2577,7 @@ void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
for (int loretag = NBT.GetFirstChild(displaytag); loretag >= 0; loretag = NBT.GetNextSibling(loretag)) // Loop through array of strings
{
- AppendPrintf(Lore, "%s`", NBT.GetString(loretag).c_str()); // Append the lore with a grave accent/backtick, used internally by MCS to display a new line in the client; don't forget to c_str ;)
+ AppendPrintf(Lore, "%s`", NBT.GetString(loretag).c_str()); // Append the lore with a grave accent / backtick, used internally by MCS to display a new line in the client; don't forget to c_str ;)
}
a_Item.m_Lore = Lore;
@@ -3233,7 +3243,7 @@ void cProtocol176::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
// Version:
Json::Value Version;
- Version["name"] = "1.7.6";
+ Version["name"] = "MCServer 1.7.6";
Version["protocol"] = 5;
// Players:
diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h
index 553856eaa..ead2935b0 100644
--- a/src/Protocol/Protocol17x.h
+++ b/src/Protocol/Protocol17x.h
@@ -100,6 +100,7 @@ public:
virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override;
virtual void SendPaintingSpawn (const cPainting & a_Painting) override;
virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount) override;
+ virtual void SendParticleEffect (const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data) override;
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
virtual void SendPlayerAbilities (void) override;
virtual void SendPlayerListAddPlayer (const cPlayer & a_Player) override;
diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp
index 20b55f461..d06022ce0 100644
--- a/src/Protocol/Protocol18x.cpp
+++ b/src/Protocol/Protocol18x.cpp
@@ -479,7 +479,7 @@ void cProtocol180::SendEntityVelocity(const cEntity & a_Entity)
cPacketizer Pkt(*this, 0x12); // Entity Velocity packet
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
- // 400 = 8000 / 20 ... Conversion from our speed in m/s to 8000 m/tick
+ // 400 = 8000 / 20 ... Conversion from our speed in m / s to 8000 m / tick
Pkt.WriteBEInt16((short)(a_Entity.GetSpeedX() * 400));
Pkt.WriteBEInt16((short)(a_Entity.GetSpeedY() * 400));
Pkt.WriteBEInt16((short)(a_Entity.GetSpeedZ() * 400));
@@ -814,6 +814,50 @@ void cProtocol180::SendParticleEffect(const AString & a_ParticleName, float a_Sr
+void cProtocol180::SendParticleEffect(const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data)
+{
+ ASSERT(m_State == 3); // In game mode?
+ int ParticleID = GetParticleID(a_ParticleName);
+
+ cPacketizer Pkt(*this, 0x2A);
+ Pkt.WriteBEInt32(ParticleID);
+ Pkt.WriteBool(false);
+ Pkt.WriteBEFloat(a_Src.x);
+ Pkt.WriteBEFloat(a_Src.y);
+ Pkt.WriteBEFloat(a_Src.z);
+ Pkt.WriteBEFloat(a_Offset.x);
+ Pkt.WriteBEFloat(a_Offset.y);
+ Pkt.WriteBEFloat(a_Offset.z);
+ Pkt.WriteBEFloat(a_ParticleData);
+ Pkt.WriteBEInt32(a_ParticleAmount);
+ switch (ParticleID)
+ {
+ // iconcrack
+ case 36:
+ {
+ Pkt.WriteVarInt32(static_cast<UInt32>(a_Data[0]));
+ Pkt.WriteVarInt32(static_cast<UInt32>(a_Data[1]));
+ break;
+ }
+ // blockcrack
+ // blockdust
+ case 37:
+ case 38:
+ {
+ Pkt.WriteVarInt32(static_cast<UInt32>(a_Data[0]));
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+
+
+
+
void cProtocol180::SendPlayerListAddPlayer(const cPlayer & a_Player)
{
ASSERT(m_State == 3); // In game mode?
@@ -2056,7 +2100,7 @@ void cProtocol180::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
// Version:
Json::Value Version;
- Version["name"] = "1.8";
+ Version["name"] = "MCServer 1.8";
Version["protocol"] = 47;
// Players:
@@ -2192,7 +2236,7 @@ void cProtocol180::HandlePacketLoginStart(cByteBuffer & a_ByteBuffer)
void cProtocol180::HandlePacketAnimation(cByteBuffer & a_ByteBuffer)
{
- m_Client->HandleAnimation(1); // Packet exists solely for arm-swing notification
+ m_Client->HandleAnimation(0); // Packet exists solely for arm-swing notification
}
@@ -2307,7 +2351,7 @@ void cProtocol180::HandlePacketCreativeInventoryAction(cByteBuffer & a_ByteBuffe
{
return;
}
- m_Client->HandleCreativeInventory(SlotNum, Item, (SlotNum == SLOT_NUM_OUTSIDE) ? caLeftClickOutside : caLeftClick);
+ m_Client->HandleCreativeInventory(SlotNum, Item, (SlotNum == -1) ? caLeftClickOutside : caLeftClick);
}
@@ -2396,7 +2440,7 @@ void cProtocol180::HandlePacketPlayerPos(cByteBuffer & a_ByteBuffer)
HANDLE_READ(a_ByteBuffer, ReadBEDouble, double, PosY);
HANDLE_READ(a_ByteBuffer, ReadBEDouble, double, PosZ);
HANDLE_READ(a_ByteBuffer, ReadBool, bool, IsOnGround);
- m_Client->HandlePlayerPos(PosX, PosY, PosZ, PosY + 1.62, IsOnGround);
+ m_Client->HandlePlayerPos(PosX, PosY, PosZ, PosY + (m_Client->GetPlayer()->IsCrouched() ? 1.54 : 1.62), IsOnGround);
}
@@ -2761,7 +2805,7 @@ void cProtocol180::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
if (!NBT.IsValid())
{
AString HexDump;
- CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16);
+ CreateHexDump(HexDump, a_Metadata.data(), std::max<size_t>(a_Metadata.size(), 1024), 16);
LOGWARNING("Cannot parse NBT item metadata: (" SIZE_T_FMT " bytes)\n%s", a_Metadata.size(), HexDump.c_str());
return;
}
@@ -2796,7 +2840,7 @@ void cProtocol180::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
for (int loretag = NBT.GetFirstChild(displaytag); loretag >= 0; loretag = NBT.GetNextSibling(loretag)) // Loop through array of strings
{
- AppendPrintf(Lore, "%s`", NBT.GetString(loretag).c_str()); // Append the lore with a grave accent/backtick, used internally by MCS to display a new line in the client; don't forget to c_str ;)
+ AppendPrintf(Lore, "%s`", NBT.GetString(loretag).c_str()); // Append the lore with a grave accent / backtick, used internally by MCS to display a new line in the client; don't forget to c_str ;)
}
a_Item.m_Lore = Lore;
diff --git a/src/Protocol/Protocol18x.h b/src/Protocol/Protocol18x.h
index 06a57b216..6143e8b4e 100644
--- a/src/Protocol/Protocol18x.h
+++ b/src/Protocol/Protocol18x.h
@@ -98,6 +98,7 @@ public:
virtual void SendPlayerAbilities (void) override;
virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override;
virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount) override;
+ virtual void SendParticleEffect (const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data) override;
virtual void SendPlayerListAddPlayer (const cPlayer & a_Player) override;
virtual void SendPlayerListRemovePlayer (const cPlayer & a_Player) override;
virtual void SendPlayerListUpdateGameMode (const cPlayer & a_Player) override;
diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp
index 6c599dedc..477f2d71e 100644
--- a/src/Protocol/ProtocolRecognizer.cpp
+++ b/src/Protocol/ProtocolRecognizer.cpp
@@ -449,6 +449,17 @@ void cProtocolRecognizer::SendParticleEffect(const AString & a_ParticleName, flo
+
+void cProtocolRecognizer::SendParticleEffect(const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data)
+{
+ ASSERT(m_Protocol != nullptr);
+ m_Protocol->SendParticleEffect(a_ParticleName, a_Src, a_Offset, a_ParticleData, a_ParticleAmount, a_Data);
+}
+
+
+
+
+
void cProtocolRecognizer::SendPaintingSpawn(const cPainting & a_Painting)
{
m_Protocol->SendPaintingSpawn(a_Painting);
diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h
index 8454549e3..956b5dcc0 100644
--- a/src/Protocol/ProtocolRecognizer.h
+++ b/src/Protocol/ProtocolRecognizer.h
@@ -18,7 +18,7 @@
// Adjust these if a new protocol is added or an old one is removed:
-#define MCS_CLIENT_VERSIONS "1.7.x, 1.8"
+#define MCS_CLIENT_VERSIONS "1.7.x, 1.8.x"
#define MCS_PROTOCOL_VERSIONS "4, 5, 47"
@@ -82,6 +82,7 @@ public:
virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators, unsigned int m_Scale) override;
virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override;
virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount) override;
+ virtual void SendParticleEffect (const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data) override;
virtual void SendPaintingSpawn (const cPainting & a_Painting) override;
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
virtual void SendPlayerAbilities (void) override;
diff --git a/src/RCONServer.cpp b/src/RCONServer.cpp
index 685bd92f5..c5dc9b69b 100644
--- a/src/RCONServer.cpp
+++ b/src/RCONServer.cpp
@@ -134,15 +134,15 @@ cRCONServer::~cRCONServer()
-void cRCONServer::Initialize(cIniFile & a_IniFile)
+void cRCONServer::Initialize(cSettingsRepositoryInterface & a_Settings)
{
- if (!a_IniFile.GetValueSetB("RCON", "Enabled", false))
+ if (!a_Settings.GetValueSetB("RCON", "Enabled", false))
{
return;
}
// Read the password, don't allow an empty one:
- m_Password = a_IniFile.GetValueSet("RCON", "Password", "");
+ m_Password = a_Settings.GetValueSet("RCON", "Password", "");
if (m_Password.empty())
{
LOGWARNING("RCON is requested, but the password is not set. RCON is now disabled.");
@@ -150,7 +150,7 @@ void cRCONServer::Initialize(cIniFile & a_IniFile)
}
// Read the listening ports for RCON from config:
- AStringVector Ports = ReadUpgradeIniPorts(a_IniFile, "RCON", "Ports", "PortsIPv4", "PortsIPv6", "25575");
+ AStringVector Ports = ReadUpgradeIniPorts(a_Settings, "RCON", "Ports", "PortsIPv4", "PortsIPv6", "25575");
// Start listening on each specified port:
for (auto port: Ports)
diff --git a/src/RCONServer.h b/src/RCONServer.h
index 352fa7b50..ecd936eeb 100644
--- a/src/RCONServer.h
+++ b/src/RCONServer.h
@@ -17,7 +17,7 @@
// fwd:
class cServer;
-class cIniFile;
+class cSettingsRepositoryInterface;
@@ -29,7 +29,7 @@ public:
cRCONServer(cServer & a_Server);
virtual ~cRCONServer();
- void Initialize(cIniFile & a_IniFile);
+ void Initialize(cSettingsRepositoryInterface & a_Settings);
protected:
friend class cRCONCommandOutput;
@@ -61,7 +61,7 @@ protected:
// cTCPLink::cCallbacks overrides:
- virtual void OnLinkCreated(cTCPLinkPtr a_Link);
+ virtual void OnLinkCreated(cTCPLinkPtr a_Link) override;
virtual void OnReceivedData(const char * a_Data, size_t a_Length) override;
virtual void OnRemoteClosed(void) override;
virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override;
diff --git a/src/RankManager.cpp b/src/RankManager.cpp
index 451de88e7..ee39026ea 100644
--- a/src/RankManager.cpp
+++ b/src/RankManager.cpp
@@ -414,6 +414,7 @@ void cRankManager::Initialize(cMojangAPI & a_MojangAPI)
m_DB.exec("CREATE TABLE IF NOT EXISTS PermGroup (PermGroupID INTEGER PRIMARY KEY, Name)");
m_DB.exec("CREATE TABLE IF NOT EXISTS RankPermGroup (RankID INTEGER, PermGroupID INTEGER)");
m_DB.exec("CREATE TABLE IF NOT EXISTS PermissionItem (PermGroupID INTEGER, Permission)");
+ m_DB.exec("CREATE TABLE IF NOT EXISTS RestrictionItem (PermGroupID INTEGER, Permission)");
m_DB.exec("CREATE TABLE IF NOT EXISTS DefaultRank (RankID INTEGER)");
m_IsInitialized = true;
@@ -571,6 +572,20 @@ AStringVector cRankManager::GetPlayerPermissions(const AString & a_PlayerUUID)
+AStringVector cRankManager::GetPlayerRestrictions(const AString & a_PlayerUUID)
+{
+ AString Rank = GetPlayerRankName(a_PlayerUUID);
+ if (Rank.empty())
+ {
+ Rank = m_DefaultRank;
+ }
+ return GetRankRestrictions(Rank);
+}
+
+
+
+
+
AStringVector cRankManager::GetRankGroups(const AString & a_RankName)
{
ASSERT(m_IsInitialized);
@@ -632,6 +647,36 @@ AStringVector cRankManager::GetGroupPermissions(const AString & a_GroupName)
+AStringVector cRankManager::GetGroupRestrictions(const AString & a_GroupName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ AStringVector res;
+ try
+ {
+ SQLite::Statement stmt(m_DB,
+ "SELECT RestrictionItem.Permission FROM RestrictionItem "
+ "LEFT JOIN PermGroup ON PermGroup.PermGroupID = RestrictionItem.PermGroupID "
+ "WHERE PermGroup.Name = ?"
+ );
+ stmt.bind(1, a_GroupName);
+ while (stmt.executeStep())
+ {
+ res.push_back(stmt.getColumn(0).getText());
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to get group restrictions from DB: %s", __FUNCTION__, ex.what());
+ }
+ return res;
+}
+
+
+
+
+
AStringVector cRankManager::GetRankPermissions(const AString & a_RankName)
{
ASSERT(m_IsInitialized);
@@ -663,6 +708,37 @@ AStringVector cRankManager::GetRankPermissions(const AString & a_RankName)
+AStringVector cRankManager::GetRankRestrictions(const AString & a_RankName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ AStringVector res;
+ try
+ {
+ SQLite::Statement stmt(m_DB,
+ "SELECT RestrictionItem.Permission FROM RestrictionItem "
+ "LEFT JOIN RankPermGroup ON RankPermGroup.PermGroupID = RestrictionItem.PermGroupID "
+ "LEFT JOIN Rank ON Rank.RankID = RankPermGroup.RankID "
+ "WHERE Rank.Name = ?"
+ );
+ stmt.bind(1, a_RankName);
+ while (stmt.executeStep())
+ {
+ res.push_back(stmt.getColumn(0).getText());
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to get rank restrictions from DB: %s", __FUNCTION__, ex.what());
+ }
+ return res;
+}
+
+
+
+
+
AStringVector cRankManager::GetAllPlayerUUIDs(void)
{
ASSERT(m_IsInitialized);
@@ -764,6 +840,46 @@ AStringVector cRankManager::GetAllPermissions(void)
+AStringVector cRankManager::GetAllRestrictions(void)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ AStringVector res;
+ try
+ {
+ SQLite::Statement stmt(m_DB, "SELECT DISTINCT(Permission) FROM RestrictionItem");
+ while (stmt.executeStep())
+ {
+ res.push_back(stmt.getColumn(0).getText());
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to get restrictions from DB: %s", __FUNCTION__, ex.what());
+ }
+ return res;
+}
+
+
+
+
+
+AStringVector cRankManager::GetAllPermissionsRestrictions(void)
+{
+ AStringVector Permissions = GetAllPermissions();
+ AStringVector Restrictions = GetAllRestrictions();
+ for (auto & restriction: Restrictions)
+ {
+ Permissions.push_back(restriction);
+ }
+ return Permissions;
+}
+
+
+
+
+
bool cRankManager::GetPlayerMsgVisuals(
const AString & a_PlayerUUID,
AString & a_MsgPrefix,
@@ -1063,6 +1179,73 @@ bool cRankManager::AddPermissionToGroup(const AString & a_Permission, const AStr
+bool cRankManager::AddRestrictionToGroup(const AString & a_Restriction, const AString & a_GroupName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ // Get the group's ID:
+ int GroupID;
+ {
+ SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?");
+ stmt.bind(1, a_GroupName);
+ if (!stmt.executeStep())
+ {
+ LOGWARNING("%s: No such group (%s), aborting.", __FUNCTION__, a_GroupName.c_str());
+ return false;
+ }
+ GroupID = stmt.getColumn(0).getInt();
+ }
+
+ // Check if the restriction is already present:
+ {
+ SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM RestrictionItem WHERE PermGroupID = ? AND Permission = ?");
+ stmt.bind(1, GroupID);
+ stmt.bind(2, a_Restriction);
+ if (!stmt.executeStep())
+ {
+ LOGWARNING("%s: Failed to check binding between restriction %s and group %s, aborting.", __FUNCTION__, a_Restriction.c_str(), a_GroupName.c_str());
+ return false;
+ }
+ if (stmt.getColumn(0).getInt() > 0)
+ {
+ LOGD("%s: Restriction %s is already present in group %s, skipping and returning success.",
+ __FUNCTION__, a_Restriction.c_str(), a_GroupName.c_str()
+ );
+ return true;
+ }
+ }
+
+ // Add the restriction:
+ {
+ SQLite::Statement stmt(m_DB, "INSERT INTO RestrictionItem (Permission, PermGroupID) VALUES (?, ?)");
+ stmt.bind(1, a_Restriction);
+ stmt.bind(2, GroupID);
+ if (stmt.exec() <= 0)
+ {
+ LOGWARNING("%s: Failed to add restriction %s to group %s, aborting.", __FUNCTION__, a_Restriction.c_str(), a_GroupName.c_str());
+ return false;
+ }
+ }
+
+ // Adding succeeded:
+ return true;
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to add restriction %s to group %s: %s",
+ __FUNCTION__, a_Restriction.c_str(), a_GroupName.c_str(), ex.what()
+ );
+ }
+ return false;
+}
+
+
+
+
+
bool cRankManager::AddPermissionsToGroup(const AStringVector & a_Permissions, const AString & a_GroupName)
{
ASSERT(m_IsInitialized);
@@ -1133,6 +1316,76 @@ bool cRankManager::AddPermissionsToGroup(const AStringVector & a_Permissions, co
+bool cRankManager::AddRestrictionsToGroup(const AStringVector & a_Restrictions, const AString & a_GroupName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ // Get the group's ID:
+ int GroupID;
+ {
+ SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?");
+ stmt.bind(1, a_GroupName);
+ if (!stmt.executeStep())
+ {
+ LOGWARNING("%s: No such group (%s), aborting.", __FUNCTION__, a_GroupName.c_str());
+ return false;
+ }
+ GroupID = stmt.getColumn(0).getInt();
+ }
+
+ for (auto itr = a_Restrictions.cbegin(), end = a_Restrictions.cend(); itr != end; ++itr)
+ {
+ // Check if the restriction is already present:
+ {
+ SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM RestrictionItem WHERE PermGroupID = ? AND Permission = ?");
+ stmt.bind(1, GroupID);
+ stmt.bind(2, *itr);
+ if (!stmt.executeStep())
+ {
+ LOGWARNING("%s: Failed to check binding between restriction %s and group %s, aborting.", __FUNCTION__, itr->c_str(), a_GroupName.c_str());
+ return false;
+ }
+ if (stmt.getColumn(0).getInt() > 0)
+ {
+ LOGD("%s: Restriction %s is already present in group %s, skipping and returning success.",
+ __FUNCTION__, itr->c_str(), a_GroupName.c_str()
+ );
+ continue;
+ }
+ }
+
+ // Add the permission:
+ {
+ SQLite::Statement stmt(m_DB, "INSERT INTO RestrictionItem (Permission, PermGroupID) VALUES (?, ?)");
+ stmt.bind(1, *itr);
+ stmt.bind(2, GroupID);
+ if (stmt.exec() <= 0)
+ {
+ LOGWARNING("%s: Failed to add restriction %s to group %s, skipping.", __FUNCTION__, itr->c_str(), a_GroupName.c_str());
+ continue;
+ }
+ }
+ } // for itr - a_Restrictions[]
+
+ // Adding succeeded:
+ return true;
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to add restrictions to group %s: %s",
+ __FUNCTION__, a_GroupName.c_str(), ex.what()
+ );
+ }
+ return false;
+}
+
+
+
+
+
void cRankManager::RemoveRank(const AString & a_RankName, const AString & a_ReplacementRankName)
{
ASSERT(m_IsInitialized);
@@ -1362,6 +1615,46 @@ void cRankManager::RemovePermissionFromGroup(const AString & a_Permission, const
+void cRankManager::RemoveRestrictionFromGroup(const AString & a_Restriction, const AString & a_GroupName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ // Get the ID of the group:
+ int GroupID;
+ {
+ SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?");
+ stmt.bind(1, a_GroupName);
+ if (!stmt.executeStep())
+ {
+ LOGINFO("%s: Group %s was not found, skipping.", __FUNCTION__, a_GroupName.c_str());
+ return;
+ }
+ GroupID = stmt.getColumn(0).getInt();
+ }
+
+ // Remove the permission from the group:
+ {
+ SQLite::Statement stmt(m_DB, "DELETE FROM RestrictionItem WHERE PermGroupID = ? AND Permission = ?");
+ stmt.bind(1, GroupID);
+ stmt.bind(2, a_Restriction);
+ stmt.exec();
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to remove restriction %s from group %s in DB: %s",
+ __FUNCTION__, a_Restriction.c_str(), a_GroupName.c_str(), ex.what()
+ );
+ }
+}
+
+
+
+
+
bool cRankManager::RenameRank(const AString & a_OldName, const AString & a_NewName)
{
ASSERT(m_IsInitialized);
@@ -1744,6 +2037,37 @@ bool cRankManager::IsPermissionInGroup(const AString & a_Permission, const AStri
+bool cRankManager::IsRestrictionInGroup(const AString & a_Restriction, const AString & a_GroupName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ SQLite::Statement stmt(m_DB,
+ "SELECT * FROM RestrictionItem "
+ "LEFT JOIN PermGroup ON PermGroup.PermGroupID = RestrictionItem.PermGroupID "
+ "WHERE RestrictionItem.Permission = ? AND PermGroup.Name = ?"
+ );
+ stmt.bind(1, a_Restriction);
+ stmt.bind(2, a_GroupName);
+ if (stmt.executeStep())
+ {
+ // The restriction is in the group
+ return true;
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to query DB: %s", __FUNCTION__, ex.what());
+ }
+ return false;
+}
+
+
+
+
+
void cRankManager::NotifyNameUUID(const AString & a_PlayerName, const AString & a_UUID)
{
ASSERT(m_IsInitialized);
@@ -1829,7 +2153,7 @@ void cRankManager::ClearPlayerRanks(void)
}
catch (SQLite::Exception & ex)
{
- LOGWARNING("%s: Failed to remove/clear all players: %s", __FUNCTION__, ex.what());
+ LOGWARNING("%s: Failed to remove / clear all players: %s", __FUNCTION__, ex.what());
}
}
@@ -1936,3 +2260,58 @@ void cRankManager::CreateDefaults(void)
+bool cRankManager::DoesColumnExist(const char * a_TableName, const char * a_ColumnName)
+{
+ try
+ {
+ SQLite::Statement stmt(m_DB, Printf("PRAGMA table_info(%s)", a_TableName));
+ while (stmt.executeStep()) // Iterate over all table's columns
+ {
+ int NumColumns = stmt.getColumnCount();
+ for (int i = 0; i < NumColumns; i++) // Iterate over all reply's columns (table column's metadata)
+ {
+ auto column = stmt.getColumn(i);
+ if (strcmp(column.getName(), "name") == 0)
+ {
+ if (NoCaseCompare(column.getText(), a_ColumnName) == 0)
+ {
+ // Colun found
+ return true;
+ }
+ }
+ } // for i - stmt.getColumns()
+ } // while (stmt.executeStep())
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to query DB: %s", __FUNCTION__, ex.what());
+ }
+ return false;
+}
+
+
+
+
+
+void cRankManager::CreateColumnIfNotExists(const char * a_TableName, const char * a_ColumnName, const char * a_ColumnType)
+{
+ // If the column already exists, bail out:
+ if (DoesColumnExist(a_TableName, a_ColumnName))
+ {
+ return;
+ }
+
+ // Add the column:
+ try
+ {
+ m_DB.exec(Printf("ALTER TABLE %s ADD COLUMN %s %s", a_TableName, a_ColumnName, a_ColumnType));
+ }
+ catch (const SQLite::Exception & exc)
+ {
+ LOGWARNING("%s: Failed to query DB: %s", __FUNCTION__, exc.what());
+ }
+}
+
+
+
+
diff --git a/src/RankManager.h b/src/RankManager.h
index 5dff634b5..b3431b7d1 100644
--- a/src/RankManager.h
+++ b/src/RankManager.h
@@ -71,6 +71,10 @@ public:
If the player has no rank assigned to them, returns the default rank's permissions. */
AStringVector GetPlayerPermissions(const AString & a_PlayerUUID);
+ /** Returns the restrictions that the specified player has assigned to them.
+ If the player has no rank assigned to them, returns the default rank's restrictions. */
+ AStringVector GetPlayerRestrictions(const AString & a_PlayerUUID);
+
/** Returns the names of groups that the specified rank has assigned to it.
Returns an empty vector if the rank doesn't exist. */
AStringVector GetRankGroups(const AString & a_RankName);
@@ -79,10 +83,18 @@ public:
Returns an empty vector if the group doesn't exist. */
AStringVector GetGroupPermissions(const AString & a_GroupName);
+ /** Returns the restrictions that the specified group has assigned to it.
+ Returns an empty vector if the group doesn't exist. */
+ AStringVector GetGroupRestrictions(const AString & a_GroupName);
+
/** Returns all permissions that the specified rank has assigned to it, through all its groups.
Returns an empty vector if the rank doesn't exist. Any non-existent groups are ignored. */
AStringVector GetRankPermissions(const AString & a_RankName);
+ /** Returns all restrictions that the specified rank has assigned to it, through all its groups.
+ Returns an empty vector if the rank doesn't exist. Any non-existent groups are ignored. */
+ AStringVector GetRankRestrictions(const AString & a_RankName);
+
/** Returns the short uuids of all defined players. The returned players are ordered by their name (NOT their UUIDs). */
AStringVector GetAllPlayerUUIDs(void);
@@ -95,6 +107,12 @@ public:
/** Returns all the distinct permissions that are stored in the DB. */
AStringVector GetAllPermissions(void);
+ /** Returns all the distinct restrictions that are stored in the DB. */
+ AStringVector GetAllRestrictions(void);
+
+ /** Returns all the distinct permissions and restrictions that are stored in the DB. */
+ AStringVector GetAllPermissionsRestrictions(void);
+
/** Returns the message visuals (prefix, postfix, color) for the specified player.
Returns true if the visuals were read from the DB, false if not (player not found etc). */
bool GetPlayerMsgVisuals(
@@ -128,17 +146,27 @@ public:
Returns true if successful, false on error. */
bool AddPermissionToGroup(const AString & a_Permission, const AString & a_GroupName);
+ /** Adds the specified restriction to the specified group.
+ Fails if the group name is not found.
+ Returns true if successful, false on error. */
+ bool AddRestrictionToGroup(const AString & a_Restriction, const AString & a_GroupName);
+
/** Adds the specified permissions to the specified permission group.
Fails if the permission group name is not found.
Returns true if successful, false on error. */
bool AddPermissionsToGroup(const AStringVector & a_Permissions, const AString & a_GroupName);
+ /** Adds the specified restrictions to the specified group.
+ Fails if the group name is not found.
+ Returns true if successful, false on error. */
+ bool AddRestrictionsToGroup(const AStringVector & a_Restrictions, const AString & a_GroupName);
+
/** Removes the specified rank.
All players assigned to that rank will be re-assigned to a_ReplacementRankName.
If a_ReplacementRankName is empty or not a valid rank, the player will be removed from the DB,
which means they will receive the default rank the next time they are queried.
If the rank being removed is the default rank, the default will be changed to the replacement
- rank; the operation fails if there's no replacement. */
+ rank; the operation fails silently if there's no replacement. */
void RemoveRank(const AString & a_RankName, const AString & a_ReplacementRankName);
/** Removes the specified group completely.
@@ -152,6 +180,9 @@ public:
/** Removes the specified permission from the specified group. */
void RemovePermissionFromGroup(const AString & a_Permission, const AString & a_GroupName);
+ /** Removes the specified restriction from the specified group. */
+ void RemoveRestrictionFromGroup(const AString & a_Restriction, const AString & a_GroupName);
+
/** Renames the specified rank. No action if the rank name is not found.
Fails if the new name is already used.
Updates the cached m_DefaultRank if the default rank is being renamed.
@@ -208,6 +239,9 @@ public:
/** Returns true iff the specified group contains the specified permission. */
bool IsPermissionInGroup(const AString & a_Permission, const AString & a_GroupName);
+ /** Returns true iff the specified group contains the specified restriction. */
+ bool IsRestrictionInGroup(const AString & a_Restriction, const AString & a_GroupName);
+
/** Called by cMojangAPI whenever the playername-uuid pairing is discovered. Updates the DB. */
void NotifyNameUUID(const AString & a_PlayerName, const AString & a_UUID);
@@ -253,6 +287,13 @@ protected:
/** Creates a default set of ranks / groups / permissions. */
void CreateDefaults(void);
+
+ /** Returns true if the specified column exists in the specified table. */
+ bool DoesColumnExist(const char * a_TableName, const char * a_ColumnName);
+
+ /** If the specified table doesn't contain the specified column, it is added to the table.
+ The column type is used only when creating the column, it is not used when checking for existence. */
+ void CreateColumnIfNotExists(const char * a_TableName, const char * a_ColumnName, const char * a_ColumnType = "");
} ;
diff --git a/src/Root.cpp b/src/Root.cpp
index 87bc29627..b28e7c894 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -19,6 +19,8 @@
#include "LoggerListeners.h"
#include "BuildInfo.h"
#include "IniFile.h"
+#include "SettingsRepositoryInterface.h"
+#include "OverridesSettingsRepository.h"
#ifdef _WIN32
#include <conio.h>
@@ -33,7 +35,8 @@
-cRoot* cRoot::s_Root = nullptr;
+cRoot * cRoot::s_Root = nullptr;
+bool cRoot::m_ShouldStop = false;
@@ -48,7 +51,6 @@ cRoot::cRoot(void) :
m_WebAdmin(nullptr),
m_PluginManager(nullptr),
m_MojangAPI(nullptr),
- m_bStop(false),
m_bRestart(false)
{
s_Root = this;
@@ -70,8 +72,8 @@ cRoot::~cRoot()
void cRoot::InputThread(cRoot & a_Params)
{
cLogCommandOutputCallback Output;
-
- while (!a_Params.m_bStop && !a_Params.m_bRestart && !m_TerminateEventRaised && std::cin.good())
+
+ while (!cRoot::m_ShouldStop && !a_Params.m_bRestart && !m_TerminateEventRaised && std::cin.good())
{
AString Command;
std::getline(std::cin, Command);
@@ -83,10 +85,11 @@ void cRoot::InputThread(cRoot & a_Params)
if (m_TerminateEventRaised || !std::cin.good())
{
- // We have come here because the std::cin has received an EOF / a terminate signal has been sent, and the server is still running; stop the server:
- if (m_RunAsService) // HACK: Dont kill if running as a service
+ // We have come here because the std::cin has received an EOF / a terminate signal has been sent, and the server is still running
+ // Stop the server:
+ if (!m_RunAsService) // Dont kill if running as a service
{
- a_Params.m_bStop = true;
+ a_Params.m_ShouldStop = true;
}
}
}
@@ -95,19 +98,19 @@ void cRoot::InputThread(cRoot & a_Params)
-void cRoot::Start(void)
+void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo)
{
#ifdef _WIN32
HWND hwnd = GetConsoleWindow();
HMENU hmenu = GetSystemMenu(hwnd, FALSE);
EnableMenuItem(hmenu, SC_CLOSE, MF_GRAYED); // Disable close button when starting up; it causes problems with our CTRL-CLOSE handling
#endif
-
+
cLogger::cListener * consoleLogListener = MakeConsoleListener();
cLogger::cListener * fileLogListener = new cFileListener();
cLogger::GetInstance().AttachListener(consoleLogListener);
cLogger::GetInstance().AttachListener(fileLogListener);
-
+
LOG("--- Started Log ---\n");
#ifdef BUILD_ID
@@ -117,8 +120,8 @@ void cRoot::Start(void)
cDeadlockDetect dd;
- m_bStop = false;
- while (!m_bStop)
+ m_ShouldStop = false;
+ while (!m_ShouldStop)
{
auto BeginTime = std::chrono::steady_clock::now();
m_bRestart = false;
@@ -129,22 +132,24 @@ void cRoot::Start(void)
m_Server = new cServer();
LOG("Reading server config...");
- cIniFile IniFile;
- if (!IniFile.ReadFile("settings.ini"))
+
+ auto IniFile = cpp14::make_unique<cIniFile>();
+ if (!IniFile->ReadFile("settings.ini"))
{
LOGWARN("Regenerating settings.ini, all settings will be reset");
- IniFile.AddHeaderComment(" This is the main server configuration");
- IniFile.AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini");
- IniFile.AddHeaderComment(" See: http://wiki.mc-server.org/doku.php?id=configure:settings.ini for further configuration help");
+ IniFile->AddHeaderComment(" This is the main server configuration");
+ IniFile->AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini");
+ IniFile->AddHeaderComment(" See: http://wiki.mc-server.org/doku.php?id=configure:settings.ini for further configuration help");
}
+ auto settingsRepo = cpp14::make_unique<cOverridesSettingsRepository>(std::move(IniFile), std::move(overridesRepo));
LOG("Starting server...");
m_MojangAPI = new cMojangAPI;
- bool ShouldAuthenticate = IniFile.GetValueSetB("Authentication", "Authenticate", true);
- m_MojangAPI->Start(IniFile, ShouldAuthenticate); // Mojang API needs to be started before plugins, so that plugins may use it for DB upgrades on server init
- if (!m_Server->InitServer(IniFile, ShouldAuthenticate))
+ bool ShouldAuthenticate = settingsRepo->GetValueSetB("Authentication", "Authenticate", true);
+ m_MojangAPI->Start(*settingsRepo, ShouldAuthenticate); // Mojang API needs to be started before plugins, so that plugins may use it for DB upgrades on server init
+ if (!m_Server->InitServer(*settingsRepo, ShouldAuthenticate))
{
- IniFile.WriteFile("settings.ini");
+ settingsRepo->Flush();
LOGERROR("Failure starting server, aborting...");
return;
}
@@ -157,31 +162,31 @@ void cRoot::Start(void)
m_RankManager->Initialize(*m_MojangAPI);
m_CraftingRecipes = new cCraftingRecipes;
m_FurnaceRecipe = new cFurnaceRecipe();
-
+
LOGD("Loading worlds...");
- LoadWorlds(IniFile);
+ LoadWorlds(*settingsRepo);
LOGD("Loading plugin manager...");
m_PluginManager = new cPluginManager();
- m_PluginManager->ReloadPluginsNow(IniFile);
-
+ m_PluginManager->ReloadPluginsNow(*settingsRepo);
+
LOGD("Loading MonsterConfig...");
m_MonsterConfig = new cMonsterConfig;
// This sets stuff in motion
LOGD("Starting Authenticator...");
- m_Authenticator.Start(IniFile);
-
+ m_Authenticator.Start(*settingsRepo);
+
LOGD("Starting worlds...");
StartWorlds();
-
- if (IniFile.GetValueSetB("DeadlockDetect", "Enabled", true))
+
+ if (settingsRepo->GetValueSetB("DeadlockDetect", "Enabled", true))
{
LOGD("Starting deadlock detector...");
- dd.Start(IniFile.GetValueSetI("DeadlockDetect", "IntervalSec", 20));
+ dd.Start(settingsRepo->GetValueSetI("DeadlockDetect", "IntervalSec", 20));
}
-
- IniFile.WriteFile("settings.ini");
+
+ settingsRepo->Flush();
LOGD("Finalising startup...");
if (m_Server->Start())
@@ -202,18 +207,22 @@ void cRoot::Start(void)
#endif
LOG("Startup complete, took %ldms!", static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count()));
+
+ // Save the current time
+ m_StartTime = std::chrono::steady_clock::now();
+
#ifdef _WIN32
EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button
#endif
- while (!m_bStop && !m_bRestart && !m_TerminateEventRaised) // These are modified by external threads
+ while (!m_ShouldStop && !m_bRestart && !m_TerminateEventRaised) // These are modified by external threads
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
if (m_TerminateEventRaised)
{
- m_bStop = true;
+ m_ShouldStop = true;
}
// Stop the server:
@@ -224,7 +233,7 @@ void cRoot::Start(void)
} // if (m_Server->Start())
else
{
- m_bStop = true;
+ m_ShouldStop = true;
}
delete m_MojangAPI; m_MojangAPI = nullptr;
@@ -248,7 +257,7 @@ void cRoot::Start(void)
LOGD("Unloading worlds...");
UnloadWorlds();
-
+
LOGD("Stopping plugin manager...");
delete m_PluginManager; m_PluginManager = nullptr;
@@ -259,9 +268,9 @@ void cRoot::Start(void)
LOG("Shutdown successful!");
}
-
+
LOG("--- Stopped Log ---");
-
+
cLogger::GetInstance().DetachListener(consoleLogListener);
delete consoleLogListener;
cLogger::GetInstance().DetachListener(fileLogListener);
@@ -271,13 +280,6 @@ void cRoot::Start(void)
-void cRoot::SetStopping(bool a_Stopping)
-{
- m_bStop = a_Stopping;
-}
-
-
-
void cRoot::LoadGlobalSettings()
{
@@ -288,45 +290,44 @@ void cRoot::LoadGlobalSettings()
-void cRoot::LoadWorlds(cIniFile & IniFile)
+void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings)
{
// First get the default world
- AString DefaultWorldName = IniFile.GetValueSet("Worlds", "DefaultWorld", "world");
+ AString DefaultWorldName = a_Settings.GetValueSet("Worlds", "DefaultWorld", "world");
m_pDefaultWorld = new cWorld(DefaultWorldName.c_str());
m_WorldsByName[ DefaultWorldName ] = m_pDefaultWorld;
// Then load the other worlds
- int KeyNum = IniFile.FindKey("Worlds");
- int NumWorlds = IniFile.GetNumValues(KeyNum);
- if (NumWorlds <= 0)
+ auto Worlds = a_Settings.GetValues("Worlds");
+ if (Worlds.size() <= 0)
{
return;
}
-
+
bool FoundAdditionalWorlds = false;
- for (int i = 0; i < NumWorlds; i++)
+ for (auto WorldNameValue : Worlds)
{
- AString ValueName = IniFile.GetValueName(KeyNum, i);
+ AString ValueName = WorldNameValue.first;
if (ValueName.compare("World") != 0)
{
continue;
}
- AString WorldName = IniFile.GetValue(KeyNum, i);
+ AString WorldName = WorldNameValue.second;
if (WorldName.empty())
{
continue;
}
FoundAdditionalWorlds = true;
- cWorld* NewWorld = new cWorld( WorldName.c_str());
- m_WorldsByName[ WorldName ] = NewWorld;
+ cWorld * NewWorld = new cWorld(WorldName.c_str());
+ m_WorldsByName[WorldName] = NewWorld;
} // for i - Worlds
if (!FoundAdditionalWorlds)
{
- if (IniFile.GetKeyComment("Worlds", 0) != " World=secondworld")
+ if (a_Settings.GetKeyComment("Worlds", 0) != " World=secondworld")
{
- IniFile.DeleteKeyComment("Worlds", 0);
- IniFile.AddKeyComment("Worlds", " World=secondworld");
+ a_Settings.DeleteKeyComment("Worlds", 0);
+ a_Settings.AddKeyComment("Worlds", " World=secondworld");
}
}
}
@@ -466,7 +467,7 @@ void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCall
// Some commands are built-in:
if (a_Cmd == "stop")
{
- m_bStop = true;
+ m_ShouldStop = true;
}
else if (a_Cmd == "restart")
{
@@ -498,7 +499,7 @@ void cRoot::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback
// cRoot handles stopping and restarting due to our access to controlling variables
if (a_Cmd == "stop")
{
- m_bStop = true;
+ m_ShouldStop = true;
return;
}
else if (a_Cmd == "restart")
@@ -613,7 +614,7 @@ bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallbac
size_t Rating = RateCompareString (m_PlayerName, a_pPlayer->GetName());
if ((Rating > 0) && (Rating >= m_BestRating))
{
- m_BestMatch = a_pPlayer;
+ m_BestMatch = a_pPlayer->GetName();
if (Rating > m_BestRating)
{
m_NumMatches = 0;
@@ -633,18 +634,18 @@ bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallbac
m_BestRating(0),
m_NameLength(a_PlayerName.length()),
m_PlayerName(a_PlayerName),
- m_BestMatch(nullptr),
+ m_BestMatch(),
m_NumMatches(0)
{}
- cPlayer * m_BestMatch;
+ AString m_BestMatch;
unsigned m_NumMatches;
} Callback (a_PlayerName);
ForEachPlayer(Callback);
if (Callback.m_NumMatches == 1)
{
- return a_Callback.Item(Callback.m_BestMatch);
+ return DoWithPlayer(Callback.m_BestMatch, a_Callback);
}
return false;
}
@@ -853,7 +854,3 @@ int cRoot::GetFurnaceFuelBurnTime(const cItem & a_Fuel)
cFurnaceRecipe * FR = Get()->GetFurnaceRecipe();
return FR->GetBurnTime(a_Fuel);
}
-
-
-
-
diff --git a/src/Root.h b/src/Root.h
index d552466dc..ab820427f 100644
--- a/src/Root.h
+++ b/src/Root.h
@@ -24,6 +24,7 @@ class cWorld;
class cPlayer;
class cCommandOutputCallback;
class cCompositeChat;
+class cSettingsRepositoryInterface;
typedef cItemCallback<cPlayer> cPlayerListCallback;
typedef cItemCallback<cWorld> cWorldListCallback;
@@ -47,15 +48,13 @@ public:
static bool m_TerminateEventRaised;
static bool m_RunAsService;
+ static bool m_ShouldStop;
cRoot(void);
~cRoot();
- void Start(void);
-
- // Added so the service handler can request a stop
- void SetStopping(bool a_Stopping);
+ void Start(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo);
// tolua_begin
cServer * GetServer(void) { return m_Server; }
@@ -71,22 +70,31 @@ public:
a_OverworldName should be set for non-overworld dimensions if one wishes that world to link back to an overworld via portals
*/
cWorld * CreateAndInitializeWorld(const AString & a_WorldName, eDimension a_Dimension = dimOverworld, const AString & a_OverworldName = "");
+
+ /** Returns the up time of the server in seconds */
+ int GetServerUpTime(void)
+ {
+ return static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - m_StartTime).count());
+ }
// tolua_end
-
+
/// Calls the callback for each world; returns true if the callback didn't abort (return true)
bool ForEachWorld(cWorldListCallback & a_Callback); // >> Exported in ManualBindings <<
-
+
/// Writes chunkstats, for each world and totals, to the output callback
void LogChunkStats(cCommandOutputCallback & a_Output);
-
+
cMonsterConfig * GetMonsterConfig(void) { return m_MonsterConfig; }
cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export
cFurnaceRecipe * GetFurnaceRecipe (void) { return m_FurnaceRecipe; } // Exported in ManualBindings.cpp with quite a different signature
-
+
/// Returns the number of ticks for how long the item would fuel a furnace. Returns zero if not a fuel
static int GetFurnaceFuelBurnTime(const cItem & a_Fuel); // tolua_export
-
+
+ /** The current time where the startup of the server has been completed */
+ std::chrono::steady_clock::time_point m_StartTime;
+
cWebAdmin * GetWebAdmin (void) { return m_WebAdmin; } // tolua_export
cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export
cAuthenticator & GetAuthenticator (void) { return m_Authenticator; }
@@ -99,32 +107,32 @@ public:
"stop" and "restart" commands have special handling.
*/
void QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output);
-
+
/** Queues a console command for execution through the cServer class.
The command will be executed in the tick thread
The command's output will be sent to console
"stop" and "restart" commands have special handling.
*/
void QueueExecuteConsoleCommand(const AString & a_Cmd); // tolua_export
-
+
/// Executes a console command through the cServer class; does special handling for "stop" and "restart".
void ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output);
-
+
/// Kicks the user, no matter in what world they are. Used from cAuthenticator
void KickUser(int a_ClientID, const AString & a_Reason);
-
+
/// Called by cAuthenticator to auth the specified user
void AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties);
-
+
/// Executes commands queued in the command queue
void TickCommands(void);
/// Returns the number of chunks loaded
int GetTotalChunkCount(void); // tolua_export
-
+
/// Saves all chunks in all worlds
void SaveAllChunks(void); // tolua_export
-
+
/// Calls the callback for each player in all worlds
bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
@@ -133,12 +141,12 @@ public:
/** Finds the player over his uuid and calls the callback */
bool DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
-
+
/** Finds the player using it's complete username and calls the callback */
bool DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback);
-
+
// tolua_begin
-
+
/// Sends a chat message to all connected clients (in all worlds)
void BroadcastChat (const AString & a_Message, eMessageType a_ChatPrefix = mtCustom);
void BroadcastChat (const cCompositeChat & a_Message);
@@ -150,18 +158,18 @@ public:
void BroadcastChatLeave (const AString & a_Message) { BroadcastChat(a_Message, mtLeave); }
void BroadcastChatSuccess(const AString & a_Message) { BroadcastChat(a_Message, mtSuccess); }
void BroadcastChatWarning(const AString & a_Message) { BroadcastChat(a_Message, mtWarning); }
-
+
/// Returns the textual description of the protocol version: 49 -> "1.4.4". Provided specifically for Lua API
static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum);
-
+
/// Returns the amount of virtual RAM used, in KiB. Returns a negative number on error
static int GetVirtualRAMUsage(void);
-
+
/// Returns the amount of virtual RAM used, in KiB. Returns a negative number on error
static int GetPhysicalRAMUsage(void);
-
+
// tolua_end
-
+
private:
class cCommand
{
@@ -171,17 +179,17 @@ private:
m_Output(a_Output)
{
}
-
+
AString m_Command;
cCommandOutputCallback * m_Output;
} ;
-
+
typedef std::map<AString, cWorld *> WorldMap;
typedef std::vector<cCommand> cCommandQueue;
cWorld * m_pDefaultWorld;
WorldMap m_WorldsByName;
-
+
cCriticalSection m_CSPendingCommands;
cCommandQueue m_PendingCommands;
@@ -201,31 +209,26 @@ private:
cHTTPServer m_HTTPServer;
- bool m_bStop;
bool m_bRestart;
void LoadGlobalSettings();
/// Loads the worlds from settings.ini, creates the worldmap
- void LoadWorlds(cIniFile & IniFile);
-
+ void LoadWorlds(cSettingsRepositoryInterface & a_Settings);
+
/// Starts each world's life
void StartWorlds(void);
-
+
/// Stops each world's threads, so that it's safe to unload them
void StopWorlds(void);
-
+
/// Unloads all worlds from memory
void UnloadWorlds(void);
-
+
/// Does the actual work of executing a command
void DoExecuteConsoleCommand(const AString & a_Cmd);
-
- static cRoot* s_Root;
+
+ static cRoot * s_Root;
static void InputThread(cRoot & a_Params);
}; // tolua_export
-
-
-
-
diff --git a/src/Scoreboard.cpp b/src/Scoreboard.cpp
index adce8b549..a4a4c3391 100644
--- a/src/Scoreboard.cpp
+++ b/src/Scoreboard.cpp
@@ -286,7 +286,7 @@ cScoreboard::cScoreboard(cWorld * a_World) : m_World(a_World)
-cObjective* cScoreboard::RegisterObjective(const AString & a_Name, const AString & a_DisplayName, cObjective::eType a_Type)
+cObjective * cScoreboard::RegisterObjective(const AString & a_Name, const AString & a_DisplayName, cObjective::eType a_Type)
{
cObjective Objective(a_Name, a_DisplayName, a_Type, m_World);
@@ -471,7 +471,7 @@ cObjective * cScoreboard::GetObjectiveIn(eDisplaySlot a_Slot)
-bool cScoreboard::ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback& a_Callback)
+bool cScoreboard::ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback & a_Callback)
{
cCSLock Lock(m_CSObjectives);
@@ -493,7 +493,7 @@ bool cScoreboard::ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallb
-bool cScoreboard::ForEachObjective(cObjectiveCallback& a_Callback)
+bool cScoreboard::ForEachObjective(cObjectiveCallback & a_Callback)
{
cCSLock Lock(m_CSObjectives);
@@ -512,7 +512,7 @@ bool cScoreboard::ForEachObjective(cObjectiveCallback& a_Callback)
-bool cScoreboard::ForEachTeam(cTeamCallback& a_Callback)
+bool cScoreboard::ForEachTeam(cTeamCallback & a_Callback)
{
cCSLock Lock(m_CSTeams);
diff --git a/src/Scoreboard.h b/src/Scoreboard.h
index dd4073c89..ab10b20aa 100644
--- a/src/Scoreboard.h
+++ b/src/Scoreboard.h
@@ -262,19 +262,16 @@ public:
cTeam * QueryPlayerTeam(const AString & a_Name); // WARNING: O(n logn)
/** Execute callback for each objective with the specified type
- Returns true if all objectives processed, false if the callback aborted by returning true.
- */
- bool ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback& a_Callback);
+ Returns true if all objectives processed, false if the callback aborted by returning true. */
+ bool ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback & a_Callback);
/** Execute callback for each objective.
- Returns true if all objectives have been processed, false if the callback aborted by returning true.
- */
- bool ForEachObjective(cObjectiveCallback& a_Callback); // Exported in ManualBindings.cpp
+ Returns true if all objectives have been processed, false if the callback aborted by returning true. */
+ bool ForEachObjective(cObjectiveCallback & a_Callback); // Exported in ManualBindings.cpp
/** Execute callback for each team.
- Returns true if all teams have been processed, false if the callback aborted by returning true.
- */
- bool ForEachTeam(cTeamCallback& a_Callback); // Exported in ManualBindings.cpp
+ Returns true if all teams have been processed, false if the callback aborted by returning true. */
+ bool ForEachTeam(cTeamCallback & a_Callback); // Exported in ManualBindings.cpp
void SetDisplay(cObjective * a_Objective, eDisplaySlot a_Slot);
diff --git a/src/Server.cpp b/src/Server.cpp
index df2c7deef..fd3188b18 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -72,7 +72,7 @@ class cServerListenCallbacks:
virtual void OnAccepted(cTCPLink & a_Link) override {}
- virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg)
+ virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
{
LOGWARNING("Cannot listen on port %d: %d (%s).", m_Port, a_ErrorCode, a_ErrorMsg.c_str());
}
@@ -113,7 +113,7 @@ void cServer::cTickThread::Execute(void)
auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(NowTime - LastTime).count();
m_ShouldTerminate = !m_Server.Tick(static_cast<float>(msec));
auto TickTime = std::chrono::steady_clock::now() - NowTime;
-
+
if (TickTime < msPerTick)
{
// Stretch tick time until it's at least msPerTick
@@ -185,12 +185,12 @@ void cServer::PlayerDestroying(const cPlayer * a_Player)
-bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth)
+bool cServer::InitServer(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth)
{
- m_Description = a_SettingsIni.GetValueSet("Server", "Description", "MCServer - in C++!");
- m_MaxPlayers = a_SettingsIni.GetValueSetI("Server", "MaxPlayers", 100);
- m_bIsHardcore = a_SettingsIni.GetValueSetB("Server", "HardcoreEnabled", false);
- m_bAllowMultiLogin = a_SettingsIni.GetValueSetB("Server", "AllowMultiLogin", false);
+ m_Description = a_Settings.GetValueSet("Server", "Description", "MCServer - in C++!");
+ m_MaxPlayers = a_Settings.GetValueSetI("Server", "MaxPlayers", 100);
+ m_bIsHardcore = a_Settings.GetValueSetB("Server", "HardcoreEnabled", false);
+ m_bAllowMultiLogin = a_Settings.GetValueSetB("Server", "AllowMultiLogin", false);
m_PlayerCount = 0;
m_PlayerCountDiff = 0;
@@ -205,9 +205,9 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth)
LOGINFO("Compatible clients: %s", MCS_CLIENT_VERSIONS);
LOGINFO("Compatible protocol versions %s", MCS_PROTOCOL_VERSIONS);
- m_Ports = ReadUpgradeIniPorts(a_SettingsIni, "Server", "Ports", "Port", "PortsIPv6", "25565");
-
- m_RCONServer.Initialize(a_SettingsIni);
+ m_Ports = ReadUpgradeIniPorts(a_Settings, "Server", "Ports", "Port", "PortsIPv6", "25565");
+
+ m_RCONServer.Initialize(a_Settings);
m_bIsConnected = true;
@@ -226,16 +226,16 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth)
}
// Check if both BungeeCord and online mode are on, if so, warn the admin:
- m_ShouldAllowBungeeCord = a_SettingsIni.GetValueSetB("Authentication", "AllowBungeeCord", false);
+ m_ShouldAllowBungeeCord = a_Settings.GetValueSetB("Authentication", "AllowBungeeCord", false);
if (m_ShouldAllowBungeeCord && m_ShouldAuthenticate)
{
LOGWARNING("WARNING: BungeeCord is allowed and server set to online mode. This is unsafe and will not work properly. Disable either authentication or BungeeCord in settings.ini.");
}
-
- m_ShouldLoadOfflinePlayerData = a_SettingsIni.GetValueSetB("PlayerData", "LoadOfflinePlayerData", false);
- m_ShouldLoadNamedPlayerData = a_SettingsIni.GetValueSetB("PlayerData", "LoadNamedPlayerData", true);
-
- m_ClientViewDistance = a_SettingsIni.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE);
+
+ m_ShouldLoadOfflinePlayerData = a_Settings.GetValueSetB("PlayerData", "LoadOfflinePlayerData", false);
+ m_ShouldLoadNamedPlayerData = a_Settings.GetValueSetB("PlayerData", "LoadNamedPlayerData", true);
+
+ m_ClientViewDistance = a_Settings.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE);
if (m_ClientViewDistance < cClientHandle::MIN_VIEW_DISTANCE)
{
m_ClientViewDistance = cClientHandle::MIN_VIEW_DISTANCE;
@@ -246,9 +246,9 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth)
m_ClientViewDistance = cClientHandle::MAX_VIEW_DISTANCE;
LOGINFO("Setting default viewdistance to the maximum of %d", m_ClientViewDistance);
}
-
+
PrepareKeys();
-
+
return true;
}
@@ -320,13 +320,13 @@ bool cServer::Tick(float a_Dt)
cCSLock Lock(m_CSPlayerCount);
m_PlayerCount += PlayerCountDiff;
}
-
+
// Send the tick to the plugins, as well as let the plugin manager reload, if asked to (issue #102):
cPluginManager::Get()->Tick(a_Dt);
-
+
// Let the Root process all the queued commands:
cRoot::Get()->TickCommands();
-
+
// Tick all clients not yet assigned to a world:
TickClients(a_Dt);
@@ -351,7 +351,7 @@ void cServer::TickClients(float a_Dt)
cClientHandlePtrs RemoveClients;
{
cCSLock Lock(m_CSClients);
-
+
// Remove clients that have moved to a world (the world will be ticking them from now on)
for (auto itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr)
{
@@ -365,7 +365,7 @@ void cServer::TickClients(float a_Dt)
}
} // for itr - m_ClientsToRemove[]
m_ClientsToRemove.clear();
-
+
// Tick the remaining clients, take out those that have been destroyed into RemoveClients
for (auto itr = m_Clients.begin(); itr != m_Clients.end();)
{
@@ -380,7 +380,7 @@ void cServer::TickClients(float a_Dt)
++itr;
} // for itr - m_Clients[]
}
-
+
// Delete the clients that have been destroyed
RemoveClients.clear();
}
@@ -439,7 +439,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
}
// "stop" and "restart" are handled in cRoot::ExecuteConsoleCommand, our caller, due to its access to controlling variables
-
+
// "help" and "reload" are to be handled by MCS, so that they work no matter what
if (split[0] == "help")
{
@@ -464,22 +464,12 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
{
if (split.size() > 1)
{
- cPluginManager::PluginMap map = cPluginManager::Get()->GetAllPlugins();
-
- for (auto plugin_entry : map)
- {
- if (plugin_entry.first == split[1])
- {
- a_Output.Out("Error! Plugin is already loaded!");
- a_Output.Finished();
- return;
- }
- }
+ cPluginManager::Get()->RefreshPluginList(); // Refresh the plugin list, so that if the plugin was added just now, it is loadable
a_Output.Out(cPluginManager::Get()->LoadPlugin(split[1]) ? "Plugin loaded" : "Error occurred loading plugin");
}
else
{
- a_Output.Out("Usage: load <pluginname>");
+ a_Output.Out("Usage: load <PluginFolder>");
}
a_Output.Finished();
return;
@@ -488,12 +478,12 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
{
if (split.size() > 1)
{
- cPluginManager::Get()->RemovePlugin(cPluginManager::Get()->GetPlugin(split[1]));
- a_Output.Out("Plugin unloaded");
+ cPluginManager::Get()->UnloadPlugin(split[1]);
+ a_Output.Out("Plugin unload scheduled");
}
else
{
- a_Output.Out("Usage: unload <pluginname>");
+ a_Output.Out("Usage: unload <PluginFolder>");
}
a_Output.Finished();
return;
@@ -539,7 +529,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
DumpUsedMemory(&Output);
return;
}
-
+
else if (split[0].compare("killmem") == 0)
{
for (;;)
@@ -554,7 +544,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
a_Output.Finished();
return;
}
-
+
a_Output.Out("Unknown command, type 'help' for all commands.");
a_Output.Finished();
}
@@ -568,13 +558,13 @@ void cServer::PrintHelp(const AStringVector & a_Split, cCommandOutputCallback &
UNUSED(a_Split);
typedef std::pair<AString, AString> AStringPair;
typedef std::vector<AStringPair> AStringPairs;
-
+
class cCallback :
public cPluginManager::cCommandEnumCallback
{
public:
cCallback(void) : m_MaxLen(0) {}
-
+
virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override
{
UNUSED(a_Plugin);
@@ -589,7 +579,7 @@ void cServer::PrintHelp(const AStringVector & a_Split, cCommandOutputCallback &
}
return false;
}
-
+
AStringPairs m_Commands;
size_t m_MaxLen;
} Callback;
@@ -635,7 +625,7 @@ void cServer::Shutdown(void)
srv->Close();
}
m_ServerHandles.clear();
-
+
// Notify the tick thread and wait for it to terminate:
m_bRestarting = true;
m_RestartEvent.Wait();
diff --git a/src/Server.h b/src/Server.h
index 1f30295b7..4d0bc1c18 100644
--- a/src/Server.h
+++ b/src/Server.h
@@ -38,8 +38,8 @@ class cClientHandle;
typedef SharedPtr<cClientHandle> cClientHandlePtr;
typedef std::list<cClientHandlePtr> cClientHandlePtrs;
typedef std::list<cClientHandle *> cClientHandles;
-class cIniFile;
class cCommandOutputCallback;
+class cSettingsRepositoryInterface;
namespace Json
@@ -58,7 +58,7 @@ public:
// tolua_end
virtual ~cServer() {}
- bool InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth);
+ bool InitServer(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth);
// tolua_begin
diff --git a/src/SetChunkData.cpp b/src/SetChunkData.cpp
index 5a0bea980..c0ae31fd3 100644
--- a/src/SetChunkData.cpp
+++ b/src/SetChunkData.cpp
@@ -33,8 +33,8 @@ cSetChunkData::cSetChunkData(
const NIBBLETYPE * a_SkyLight,
const cChunkDef::HeightMap * a_HeightMap,
const cChunkDef::BiomeMap * a_Biomes,
- cEntityList & a_Entities,
- cBlockEntityList & a_BlockEntities,
+ cEntityList && a_Entities,
+ cBlockEntityList && a_BlockEntities,
bool a_ShouldMarkDirty
) :
m_ChunkX(a_ChunkX),
@@ -84,8 +84,8 @@ cSetChunkData::cSetChunkData(
}
// Move entities and blockentities:
- std::swap(m_Entities, a_Entities);
- std::swap(m_BlockEntities, a_BlockEntities);
+ m_Entities = std::move(a_Entities);
+ m_BlockEntities = std::move(a_BlockEntities);
}
@@ -103,7 +103,7 @@ void cSetChunkData::CalculateHeightMap(void)
int index = cChunkDef::MakeIndexNoCheck(x, y, z);
if (m_BlockTypes[index] != E_BLOCK_AIR)
{
- m_HeightMap[x + z * cChunkDef::Width] = (HEIGHTTYPE)y;
+ m_HeightMap[x + z * cChunkDef::Width] = static_cast<HEIGHTTYPE>(y);
break;
}
} // for y
diff --git a/src/SetChunkData.h b/src/SetChunkData.h
index 1eeb75ca9..bf5283569 100644
--- a/src/SetChunkData.h
+++ b/src/SetChunkData.h
@@ -24,8 +24,10 @@ public:
/** Constructs a new instance based on data existing elsewhere, will copy all the memory. Prefer to use the
other constructor as much as possible.
- Will move the entity and blockentity lists into the internal storage, and empty the a_Entities and
- a_BlockEntities lists.
+ Will move the entity and blockentity lists into the internal storage, and invalidate a_Entities and
+ a_BlockEntities.
+ When passing an lvalue, a_Entities and a_BlockEntities must be explicitly converted to an rvalue beforehand
+ with std::move().
a_BlockTypes and a_BlockMetas must always be valid.
If either of the light arrays are nullptr, the chunk data will be marked as not having any light at all and
will be scheduled for re-lighting once it is set into the chunkmap.
@@ -41,8 +43,8 @@ public:
const NIBBLETYPE * a_SkyLight,
const cChunkDef::HeightMap * a_HeightMap,
const cChunkDef::BiomeMap * a_Biomes,
- cEntityList & a_Entities,
- cBlockEntityList & a_BlockEntities,
+ cEntityList && a_Entities,
+ cBlockEntityList && a_BlockEntities,
bool a_ShouldMarkDirty
);
diff --git a/src/SettingsRepositoryInterface.h b/src/SettingsRepositoryInterface.h
new file mode 100644
index 000000000..443b90ff5
--- /dev/null
+++ b/src/SettingsRepositoryInterface.h
@@ -0,0 +1,61 @@
+
+#pragma once
+
+class cSettingsRepositoryInterface
+{
+public:
+
+ enum errors
+ {
+ noID = -1,
+ };
+
+ virtual ~cSettingsRepositoryInterface() = default;
+
+ /** Returns true iff the specified key exists */
+ virtual bool KeyExists(const AString keyname) const = 0;
+
+ /** Returns true iff the specified value exists. */
+ virtual bool HasValue(const AString & a_KeyName, const AString & a_ValueName) const = 0;
+
+ /** Add a key name. Return value is not required to mean anything **/
+ virtual int AddKeyName(const AString & keyname) = 0;
+
+ /** Add a key comment, will always fail if the repository does not support comments **/
+ virtual bool AddKeyComment(const AString & keyname, const AString & comment) = 0;
+
+ /** Return a key comment, returns "" for repositories that do not return comments **/
+ virtual AString GetKeyComment(const AString & keyname, const int commentID) const = 0;
+
+ /** Delete a key comment, will always fail if the repository does not support comments **/
+ virtual bool DeleteKeyComment(const AString & keyname, const int commentID) = 0;
+
+ /** Adds a new value to the specified key.
+ If a value of the same name already exists, creates another one **/
+ virtual void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) = 0;
+
+ /** returns a vector containing a name, value pair for each value under the key **/
+ virtual std::vector<std::pair<AString, AString>> GetValues(AString a_keyName) = 0;
+
+ /** Get the value at the specified key and value, returns defValue on failure **/
+ virtual AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const = 0;
+
+ /** Gets the value; if not found, write the default to the repository **/
+ virtual AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = "") = 0;
+ virtual int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0) = 0;
+ virtual Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0) = 0;
+ virtual bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) = 0;
+
+ /** Overwrites the value of the key, value pair
+ Specify the optional parameter as false if you do not want the value created if it doesn't exist.
+ Returns true if value set, false otherwise. **/
+ virtual bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true) = 0;
+ virtual bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true) = 0;
+
+ /** Deletes the specified key, value pair **/
+ virtual bool DeleteValue(const AString & keyname, const AString & valuename) = 0;
+
+
+ /** Writes the changes to the backing store, if the repository has one **/
+ virtual bool Flush() = 0;
+};
diff --git a/src/Simulator/FloodyFluidSimulator.h b/src/Simulator/FloodyFluidSimulator.h
index 8e1be5e6b..0eac61a03 100644
--- a/src/Simulator/FloodyFluidSimulator.h
+++ b/src/Simulator/FloodyFluidSimulator.h
@@ -47,7 +47,7 @@ protected:
/** Checks if there are enough neighbors to create a source at the coords specified; turns into source and returns true if so. */
bool CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ);
- /** Checks if the specified block should harden (Water/Lava interaction) and if so, converts it to a suitable block.
+ /** Checks if the specified block should harden (Water / Lava interaction) and if so, converts it to a suitable block.
Returns whether the block was changed or not. */
bool HardenBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta);
diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp
index 40be9c582..7f320af0d 100644
--- a/src/Simulator/IncrementalRedstoneSimulator.cpp
+++ b/src/Simulator/IncrementalRedstoneSimulator.cpp
@@ -366,7 +366,7 @@ void cIncrementalRedstoneSimulator::HandleRedstoneTorch(int a_RelBlockX, int a_R
if (i + 1 < ARRAYCOUNT(gCrossCoords)) // Sides of torch, not top (top is last)
{
if (
- IsMechanism(Type) && // Is it a mechanism? Not block/other torch etc.
+ IsMechanism(Type) && // Is it a mechanism? Not block / other torch etc.
(!Vector3i(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z).Equals(Vector3i(X, Y, Z))) // CAN'T power block is that it is on
)
{
@@ -1600,7 +1600,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB
if ((OtherRepeaterDir & 0x03) == 0x3)
{
return true;
- } // If so, I am latched/locked
+ } // If so, I am latched / locked
}
// Check if western(left) neighbor is a powered on repeater who is facing us
@@ -1612,7 +1612,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB
if ((OtherRepeaterDir & 0x03) == 0x1)
{
return true;
- } // If so, I am latched/locked
+ } // If so, I am latched / locked
}
break;
@@ -1634,7 +1634,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB
if ((OtherRepeaterDir & 0x30) == 0x00)
{
return true;
- } // If so, am latched/locked
+ } // If so, I am latched / locked
}
// Check if northern(up) neighbor is a powered on repeater who is facing us
@@ -1646,7 +1646,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB
if ((OtherRepeaterDir & 0x03) == 0x02)
{
return true;
- } // If so, I am latched/locked
+ } // If so, I am latched / locked
}
break;
diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp
index 4adc6a0a0..12bd3ada1 100644
--- a/src/StringUtils.cpp
+++ b/src/StringUtils.cpp
@@ -150,6 +150,13 @@ AStringVector StringSplitWithQuotes(const AString & str, const AString & delim)
while ((cutAt = str.find_first_of(delim, Prev)) != str.npos)
{
+ if (cutAt == Prev)
+ {
+ // Empty string due to multiple whitespace / whitespace at the beginning of the input
+ // Just skip it
+ Prev = Prev + 1;
+ continue;
+ }
AString current = str.substr(Prev, cutAt - Prev);
if ((current.front() == '"') || (current.front() == '\''))
{
diff --git a/src/StringUtils.h b/src/StringUtils.h
index 785197763..b5fc58a2d 100644
--- a/src/StringUtils.h
+++ b/src/StringUtils.h
@@ -75,7 +75,7 @@ extern int NoCaseCompare(const AString & s1, const AString & s2); // tolua_expo
/** Case-insensitive string comparison that returns a rating of equal-ness between [0 - s1.length()]. */
extern size_t RateCompareString(const AString & s1, const AString & s2);
-/** Replaces *each* occurence of iNeedle in iHayStack with iReplaceWith */
+/** Replaces each occurence of iNeedle in iHayStack with iReplaceWith */
extern void ReplaceString(AString & iHayStack, const AString & iNeedle, const AString & iReplaceWith); // tolua_export
/** Converts a stream of BE shorts into UTF-8 string; returns a_UTF8. */
diff --git a/src/Tracer.cpp b/src/Tracer.cpp
index e604f4a5b..5fdaff15d 100644
--- a/src/Tracer.cpp
+++ b/src/Tracer.cpp
@@ -12,17 +12,32 @@
+const float FLOAT_EPSILON = 0.0001f; // TODO: Stash this in some header where it can be reused
+
+
+const std::array<const Vector3f, 6>& cTracer::m_NormalTable(void)
+{
+ static std::array<const Vector3f, 6>* table =
+ new std::array<const Vector3f, 6>
+ {
+ {
+ Vector3f(-1, 0, 0), // 1: -x
+ Vector3f( 0, 0, -1), // 2: -z
+ Vector3f( 1, 0, 0), // 3: +x
+ Vector3f( 0, 0, 1), // 4: +z
+ Vector3f( 0, 1, 0), // 5: +y
+ Vector3f( 0, -1, 0) // 6: -y
+ }
+ };
+
+ return *table;
+};
+
cTracer::cTracer(cWorld * a_World):
m_World(a_World)
{
- m_NormalTable[0].Set(-1, 0, 0);
- m_NormalTable[1].Set( 0, 0, -1);
- m_NormalTable[2].Set( 1, 0, 0);
- m_NormalTable[3].Set( 0, 0, 1);
- m_NormalTable[4].Set( 0, 1, 0);
- m_NormalTable[5].Set( 0, -1, 0);
}
@@ -37,17 +52,17 @@ cTracer::~cTracer()
-float cTracer::SigNum(float a_Num)
+int cTracer::SigNum(float a_Num)
{
if (a_Num < 0.f)
{
- return -1.f;
+ return -1;
}
if (a_Num > 0.f)
{
- return 1.f;
+ return 1;
}
- return 0.f;
+ return 0;
}
@@ -56,26 +71,28 @@ float cTracer::SigNum(float a_Num)
void cTracer::SetValues(const Vector3f & a_Start, const Vector3f & a_Direction)
{
+ // Since this method should only be called by Trace, zero length vectors should already have been taken care of
+ ASSERT(a_Direction.HasNonZeroLength());
+
// calculate the direction of the ray (linear algebra)
dir = a_Direction;
// decide which direction to start walking in
- step.x = (int) SigNum(dir.x);
- step.y = (int) SigNum(dir.y);
- step.z = (int) SigNum(dir.z);
+ step.x = SigNum(dir.x);
+ step.y = SigNum(dir.y);
+ step.z = SigNum(dir.z);
+
// normalize the direction vector
- if (dir.SqrLength() > 0.f)
- {
- dir.Normalize();
- }
+ dir.Normalize();
+
// how far we must move in the ray direction before
// we encounter a new voxel in x-direction
// same but y-direction
if (dir.x != 0.f)
{
- tDelta.x = 1 / fabs(dir.x);
+ tDelta.x = 1 / std::abs(dir.x);
}
else
{
@@ -83,7 +100,7 @@ void cTracer::SetValues(const Vector3f & a_Start, const Vector3f & a_Direction)
}
if (dir.y != 0.f)
{
- tDelta.y = 1 / fabs(dir.y);
+ tDelta.y = 1 / std::abs(dir.y);
}
else
{
@@ -91,44 +108,45 @@ void cTracer::SetValues(const Vector3f & a_Start, const Vector3f & a_Direction)
}
if (dir.z != 0.f)
{
- tDelta.z = 1 / fabs(dir.z);
+ tDelta.z = 1 / std::abs(dir.z);
}
else
{
tDelta.z = 0;
}
+
// start voxel coordinates
- pos.x = (int)floorf(a_Start.x);
- pos.y = (int)floorf(a_Start.y);
- pos.z = (int)floorf(a_Start.z);
+ pos.x = static_cast<int>(floorf(a_Start.x));
+ pos.y = static_cast<int>(floorf(a_Start.y));
+ pos.z = static_cast<int>(floorf(a_Start.z));
// calculate distance to first intersection in the voxel we start from
if (dir.x < 0)
{
- tMax.x = ((float)pos.x - a_Start.x) / dir.x;
+ tMax.x = (static_cast<float>(pos.x) - a_Start.x) / dir.x;
}
else
{
- tMax.x = (((float)pos.x + 1) - a_Start.x) / dir.x;
+ tMax.x = (static_cast<float>(pos.x + 1) - a_Start.x) / dir.x; // TODO: Possible division by zero
}
if (dir.y < 0)
{
- tMax.y = ((float)pos.y - a_Start.y) / dir.y;
+ tMax.y = (static_cast<float>(pos.y) - a_Start.y) / dir.y;
}
else
{
- tMax.y = (((float)pos.y + 1) - a_Start.y) / dir.y;
+ tMax.y = (static_cast<float>(pos.y + 1) - a_Start.y) / dir.y; // TODO: Possible division by zero
}
if (dir.z < 0)
{
- tMax.z = ((float)pos.z - a_Start.z) / dir.z;
+ tMax.z = (static_cast<float>(pos.z) - a_Start.z) / dir.z;
}
else
{
- tMax.z = (((float)pos.z + 1) - a_Start.z) / dir.z;
+ tMax.z = (static_cast<float>(pos.z + 1) - a_Start.z) / dir.z; // TODO: Possible division by zero
}
}
@@ -138,6 +156,11 @@ void cTracer::SetValues(const Vector3f & a_Start, const Vector3f & a_Direction)
bool cTracer::Trace(const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance, bool a_LineOfSight)
{
+ if (!a_Direction.HasNonZeroLength())
+ {
+ return false;
+ }
+
if ((a_Start.y < 0) || (a_Start.y >= cChunkDef::Height))
{
LOGD("%s: Start Y is outside the world (%.2f), not tracing.", __FUNCTION__, a_Start.y);
@@ -146,18 +169,18 @@ bool cTracer::Trace(const Vector3f & a_Start, const Vector3f & a_Direction, int
SetValues(a_Start, a_Direction);
- Vector3f End = a_Start + (dir * (float)a_Distance);
+ Vector3f End = a_Start + (dir * static_cast<float>(a_Distance));
if (End.y < 0)
{
- float dist = -a_Start.y / dir.y;
+ float dist = -a_Start.y / dir.y; // No division by 0 possible
End = a_Start + (dir * dist);
}
// end voxel coordinates
- end1.x = (int)floorf(End.x);
- end1.y = (int)floorf(End.y);
- end1.z = (int)floorf(End.z);
+ end1.x = static_cast<int>(floorf(End.x));
+ end1.y = static_cast<int>(floorf(End.y));
+ end1.z = static_cast<int>(floorf(End.z));
// check if first is occupied
if (pos.Equals(end1))
@@ -241,7 +264,7 @@ bool cTracer::Trace(const Vector3f & a_Start, const Vector3f & a_Direction, int
int Normal = GetHitNormal(a_Start, End, pos);
if (Normal > 0)
{
- HitNormal = m_NormalTable[Normal-1];
+ HitNormal = m_NormalTable()[Normal - 1];
}
return true;
}
@@ -295,8 +318,7 @@ int cTracer::intersect3D_SegmentPlane(const Vector3f & a_Origin, const Vector3f
float D = a_PlaneNormal.Dot(u); // dot(Pn.n, u);
float N = -(a_PlaneNormal.Dot(w)); // -dot(a_Plane.n, w);
- const float EPSILON = 0.0001f;
- if (fabs(D) < EPSILON)
+ if (std::abs(D) < FLOAT_EPSILON)
{
// segment is parallel to plane
if (N == 0.0)
diff --git a/src/Tracer.h b/src/Tracer.h
index ec87d449e..55c21546b 100644
--- a/src/Tracer.h
+++ b/src/Tracer.h
@@ -3,6 +3,8 @@
#include "Vector3.h"
+#include <array>
+
@@ -61,10 +63,12 @@ private:
/// Return 1 through 6 for the following block faces, repectively: -x, -z, x, z, y, -y
int GetHitNormal( const Vector3f & start, const Vector3f & end, const Vector3i & a_BlockPos);
- float SigNum( float a_Num);
- cWorld* m_World;
+ /// Signum function
+ int SigNum(float a_Num);
+
+ cWorld * m_World;
- Vector3f m_NormalTable[6];
+ static const std::array<const Vector3f, 6> & m_NormalTable(void);
Vector3f dir;
Vector3f tDelta;
diff --git a/src/UI/DropSpenserWindow.cpp b/src/UI/DropSpenserWindow.cpp
index aeb7c64b7..121836e40 100644
--- a/src/UI/DropSpenserWindow.cpp
+++ b/src/UI/DropSpenserWindow.cpp
@@ -1,7 +1,7 @@
// DropSpenserWindow.cpp
-// Representing the UI window for the dropper/dispenser block
+// Representing the UI window for the dropper / dispenser block
#include "Globals.h"
#include "DropSpenserWindow.h"
diff --git a/src/UI/DropSpenserWindow.h b/src/UI/DropSpenserWindow.h
index edff936e5..cfc040493 100644
--- a/src/UI/DropSpenserWindow.h
+++ b/src/UI/DropSpenserWindow.h
@@ -1,7 +1,7 @@
// DropSpenserWindow.h
-// Representing the UI window for the dropper/dispenser block
+// Representing the UI window for the dropper / dispenser block
diff --git a/src/UI/FurnaceWindow.cpp b/src/UI/FurnaceWindow.cpp
index 132439ff3..a4e852fd8 100644
--- a/src/UI/FurnaceWindow.cpp
+++ b/src/UI/FurnaceWindow.cpp
@@ -41,7 +41,7 @@ void cFurnaceWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer &
}
else
{
- // Furnace Input/Fuel Slot
+ // Furnace Input / Fuel Slot
AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */
AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */
super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false);
diff --git a/src/UI/MinecartWithChestWindow.h b/src/UI/MinecartWithChestWindow.h
index a2b5283a6..87e8f6137 100644
--- a/src/UI/MinecartWithChestWindow.h
+++ b/src/UI/MinecartWithChestWindow.h
@@ -33,7 +33,7 @@ public:
a_ChestCart->GetWorld()->BroadcastSoundEffect("random.chestopen", a_ChestCart->GetPosX(), a_ChestCart->GetPosY(), a_ChestCart->GetPosZ(), 1, 1);
}
- virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea* a_ClickedArea, bool a_ShouldApply) override
+ virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override
{
cSlotAreas AreasInOrder;
diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h
index 0e7ba2a50..664c6502c 100644
--- a/src/UI/SlotArea.h
+++ b/src/UI/SlotArea.h
@@ -239,7 +239,7 @@ public:
// cSlotAreaTemporary overrides:
virtual void Clicked (cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
- virtual void DblClicked (cPlayer & a_Player, int a_SlotNum);
+ virtual void DblClicked (cPlayer & a_Player, int a_SlotNum) override;
virtual void OnPlayerRemoved(cPlayer & a_Player) override;
virtual void SetSlot (int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
@@ -306,7 +306,7 @@ public:
void UpdateResult(cPlayer & a_Player);
protected:
- /** The maximum cost of repairing/renaming in the anvil. */
+ /** The maximum cost of repairing / renaming in the anvil. */
int m_MaximumCost;
/** The stack size of the second item where was used for repair */
diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp
index bb2e2a807..d1c08acec 100644
--- a/src/UI/Window.cpp
+++ b/src/UI/Window.cpp
@@ -21,19 +21,23 @@
-char cWindow::m_WindowIDCounter = 1;
+Byte cWindow::m_WindowIDCounter = 0;
cWindow::cWindow(WindowType a_WindowType, const AString & a_WindowTitle) :
- m_WindowID((++m_WindowIDCounter) % 127),
+ m_WindowID(static_cast<char>((++m_WindowIDCounter) % 127)),
m_WindowType(a_WindowType),
m_WindowTitle(a_WindowTitle),
m_IsDestroyed(false),
m_Owner(nullptr)
{
+ // The window ID is signed in protocol 1.7, unsigned in protocol 1.8. Keep out of trouble by using only 7 bits:
+ // Ref.: http://forum.mc-server.org/showthread.php?tid=1876
+ ASSERT((m_WindowID >= 0) && (m_WindowID < 127));
+
if (a_WindowType == wtInventory)
{
m_WindowID = 0;
diff --git a/src/UI/Window.h b/src/UI/Window.h
index 9821aade1..156028465 100644
--- a/src/UI/Window.h
+++ b/src/UI/Window.h
@@ -185,7 +185,7 @@ protected:
cWindowOwner * m_Owner;
- static char m_WindowIDCounter;
+ static Byte m_WindowIDCounter;
/// Sets the internal flag as "destroyed"; notifies the owner that the window is destroying
virtual void Destroy(void);
diff --git a/src/Vector3.h b/src/Vector3.h
index 1f3f6b955..346bc1bbb 100644
--- a/src/Vector3.h
+++ b/src/Vector3.h
@@ -78,6 +78,20 @@ public:
);
}
+ inline bool HasNonZeroLength(void) const
+ {
+ #ifdef __clang__
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wfloat-equal"
+ #endif
+
+ return ((x != 0) || (y != 0) || (z != 0));
+
+ #ifdef __clang__
+ #pragma clang diagnostic pop
+ #endif
+ }
+
inline double Length(void) const
{
return sqrt(static_cast<double>(x * x + y * y + z * z));
@@ -119,13 +133,19 @@ public:
inline bool Equals(const Vector3<T> & a_Rhs) const
{
- // Perform a bitwise comparison of the contents - we want to know whether this object is exactly equal
+ // Perform a strict comparison of the contents - we want to know whether this object is exactly equal
// To perform EPS-based comparison, use the EqualsEps() function
- return (
- (memcmp(&x, &a_Rhs.x, sizeof(x)) == 0) &&
- (memcmp(&y, &a_Rhs.y, sizeof(y)) == 0) &&
- (memcmp(&z, &a_Rhs.z, sizeof(z)) == 0)
- );
+
+ #ifdef __clang__
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wfloat-equal"
+ #endif
+
+ return !((x != a_Rhs.x) || (y != a_Rhs.y) || (z != a_Rhs.z));
+
+ #ifdef __clang__
+ #pragma clang diagnostic pop
+ #endif
}
inline bool EqualsEps(const Vector3<T> & a_Rhs, T a_Eps) const
@@ -244,6 +264,15 @@ public:
);
}
+ inline Vector3<T> operator / (const Vector3<T> & a_Rhs)
+ {
+ return Vector3<T>(
+ x / a_Rhs.x,
+ y / a_Rhs.y,
+ z / a_Rhs.z
+ );
+ }
+
inline Vector3<T> operator * (T a_v) const
{
return Vector3<T>(
@@ -345,6 +374,7 @@ protected:
+
template <> inline Vector3<int> Vector3<int>::Floor(void) const
{
return *this;
diff --git a/src/VoronoiMap.h b/src/VoronoiMap.h
index 56022849e..a04a613d5 100644
--- a/src/VoronoiMap.h
+++ b/src/VoronoiMap.h
@@ -59,7 +59,7 @@ protected:
cNoise m_Noise2;
cNoise m_Noise3;
- /** Size of the Voronoi cells (avg X/Y distance between the seeds). Expected to be at least 2. */
+ /** Size of the Voronoi cells (avg X / Y distance between the seeds). Expected to be at least 2. */
int m_CellSize;
/** The amount that the cell seeds may be offset from the grid.
diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp
index 13cf3cc41..1cb4463e1 100644
--- a/src/WebAdmin.cpp
+++ b/src/WebAdmin.cpp
@@ -234,7 +234,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque
bool ShouldWrapInTemplate = ((BareURL.length() > 1) && (BareURL[1] != '~'));
// Retrieve the request data:
- cWebadminRequestData * Data = (cWebadminRequestData *)(a_Request.GetUserData());
+ cWebadminRequestData * Data = reinterpret_cast<cWebadminRequestData *>(a_Request.GetUserData());
if (Data == nullptr)
{
a_Connection.SendStatusAndReason(500, "Bad UserData");
@@ -244,6 +244,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque
// Wrap it all up for the Lua call:
AString Template;
HTTPTemplateRequest TemplateRequest;
+ TemplateRequest.Request.URL = a_Request.GetURL();
TemplateRequest.Request.Username = a_Request.GetAuthUsername();
TemplateRequest.Request.Method = a_Request.GetMethod();
TemplateRequest.Request.Path = BareURL.substr(1);
@@ -464,10 +465,10 @@ sWebAdminPage cWebAdmin::GetPage(const HTTPRequest & a_Request)
{
if ((*itr)->GetWebTitle() == Split[1])
{
- Page.Content = (*itr)->HandleWebRequest(&a_Request);
+ Page.Content = (*itr)->HandleWebRequest(a_Request);
cWebPlugin * WebPlugin = *itr;
FoundPlugin = WebPlugin->GetWebTitle();
- AString TabName = WebPlugin->GetTabNameForRequest(&a_Request).first;
+ AString TabName = WebPlugin->GetTabNameForRequest(a_Request).first;
Page.PluginName = FoundPlugin;
Page.TabName = TabName;
break;
@@ -489,20 +490,32 @@ AString cWebAdmin::GetDefaultPage(void)
Content += "<h4>Server Name:</h4>";
Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID()) + "</p>";
+ // Display a list of all plugins:
Content += "<h4>Plugins:</h4><ul>";
- cPluginManager * PM = cPluginManager::Get();
- const cPluginManager::PluginMap & List = PM->GetAllPlugins();
- for (cPluginManager::PluginMap::const_iterator itr = List.begin(); itr != List.end(); ++itr)
+ struct cPluginCallback:
+ public cPluginManager::cPluginCallback
{
- if (itr->second == nullptr)
+ AString & m_Content;
+
+ cPluginCallback(AString & a_Content):
+ m_Content(a_Content)
{
- continue;
}
- AppendPrintf(Content, "<li>%s V.%i</li>", itr->second->GetName().c_str(), itr->second->GetVersion());
- }
+
+ virtual bool Item(cPlugin * a_Plugin) override
+ {
+ if (a_Plugin->IsLoaded())
+ {
+ AppendPrintf(m_Content, "<li>%s V.%i</li>", a_Plugin->GetName().c_str(), a_Plugin->GetVersion());
+ }
+ return false;
+ }
+ } Callback(Content);
+ cPluginManager::Get()->ForEachPlugin(Callback);
Content += "</ul>";
- Content += "<h4>Players:</h4><ul>";
+ // Display a list of all players:
+ Content += "<h4>Players:</h4><ul>";
cPlayerAccum PlayerAccum;
cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players
if (World != nullptr)
@@ -518,7 +531,7 @@ AString cWebAdmin::GetDefaultPage(void)
-AString cWebAdmin::GetBaseURL( const AString& a_URL)
+AString cWebAdmin::GetBaseURL(const AString & a_URL)
{
return GetBaseURL(StringSplit(a_URL, "/"));
}
diff --git a/src/WebAdmin.h b/src/WebAdmin.h
index 1e1a9bfa9..4dbcc57a6 100644
--- a/src/WebAdmin.h
+++ b/src/WebAdmin.h
@@ -50,9 +50,18 @@ struct HTTPRequest
typedef std::map< std::string, std::string > StringStringMap;
typedef std::map< std::string, HTTPFormData > FormDataMap;
+ /** The entire URL presented to the HTTP server. */
+ AString URL;
+
+ /** HTTP method used for the request ("GET", "POST" etc.) */
AString Method;
+
+ /** The Path part of the request's URL (excluding GET params). */
AString Path;
+
+ /** Name of the logged-in user. Empty if not logged in. */
AString Username;
+
// tolua_end
/** Parameters given in the URL, after the questionmark */
diff --git a/src/World.cpp b/src/World.cpp
index 4480013c3..3c39de317 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -57,7 +57,7 @@
#include <stdlib.h>
#endif
-
+#include "Broadcaster.h"
@@ -150,7 +150,7 @@ protected:
int m_LastReportChunkCount;
// cChunkCoordCallback override:
- virtual void Call(int a_ChunkX, int a_ChunkZ)
+ virtual void Call(int a_ChunkX, int a_ChunkZ) override
{
// Check if this was the last chunk:
m_NumPrepared += 1;
@@ -176,7 +176,7 @@ protected:
{
float PercentDone = static_cast<float>(m_NumPrepared * 100) / m_MaxIdx;
float ChunkSpeed = static_cast<float>((m_NumPrepared - m_LastReportChunkCount) * 1000) / std::chrono::duration_cast<std::chrono::milliseconds>(Now - m_LastReportTime).count();
- LOG("Preparing spawn (%s): %.02f%% (%d/%d; %.02f chunks/s)",
+ LOG("Preparing spawn (%s): %.02f%% (%d/%d; %.02f chunks / sec)",
m_World.GetName().c_str(), PercentDone, m_NumPrepared, m_MaxIdx, ChunkSpeed
);
m_LastReportTime = Now;
@@ -362,6 +362,7 @@ cWorld::~cWorld()
void cWorld::CastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ)
{
BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ);
+ BroadcastSoundEffect("ambient.weather.thunder", a_BlockX, a_BlockY, a_BlockZ, 50, 1);
}
@@ -621,18 +622,18 @@ void cWorld::Start(void)
InitialiseAndLoadMobSpawningValues(IniFile);
SetTimeOfDay(IniFile.GetValueSetI("General", "TimeInTicks", GetTimeOfDay()));
- m_ChunkMap = make_unique<cChunkMap>(this);
+ m_ChunkMap = cpp14::make_unique<cChunkMap>(this);
// preallocate some memory for ticking blocks so we don't need to allocate that often
m_BlockTickQueue.reserve(1000);
m_BlockTickQueueCopy.reserve(1000);
// Simulators:
- m_SimulatorManager = make_unique<cSimulatorManager>(*this);
+ m_SimulatorManager = cpp14::make_unique<cSimulatorManager>(*this);
m_WaterSimulator = InitializeFluidSimulator(IniFile, "Water", E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER);
m_LavaSimulator = InitializeFluidSimulator(IniFile, "Lava", E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA);
- m_SandSimulator = make_unique<cSandSimulator>(*this, IniFile);
- m_FireSimulator = make_unique<cFireSimulator>(*this, IniFile);
+ m_SandSimulator = cpp14::make_unique<cSandSimulator>(*this, IniFile);
+ m_FireSimulator = cpp14::make_unique<cFireSimulator>(*this, IniFile);
m_RedstoneSimulator = InitializeRedstoneSimulator(IniFile);
// Water, Lava and Redstone simulators get registered in their initialize function.
@@ -767,7 +768,7 @@ eWeather cWorld::ChooseNewWeather()
case eWeather_Rain:
{
- // 1/8 chance of turning into a thunderstorm
+ // 1 / 8 chance of turning into a thunderstorm
return ((m_TickRand.randInt() % 256) < 32) ? eWeather_ThunderStorm : eWeather_Sunny;
}
}
@@ -800,7 +801,7 @@ void cWorld::InitialiseGeneratorDefaults(cIniFile & a_IniFile)
a_IniFile.GetValueSet("Generator", "BiomeGen", "Grown");
a_IniFile.GetValueSet("Generator", "ShapeGen", "BiomalNoise3D");
a_IniFile.GetValueSet("Generator", "CompositionGen", "Biomal");
- a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, NaturalPatches, PreSimulator, Animals");
+ a_IniFile.GetValueSet("Generator", "Finishers", "RoughRavines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, TallGrass, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, NaturalPatches, PreSimulator, Animals");
break;
}
case dimNether:
@@ -812,7 +813,7 @@ void cWorld::InitialiseGeneratorDefaults(cIniFile & a_IniFile)
a_IniFile.GetValueSet("Generator", "HeightGen", "Flat");
a_IniFile.GetValueSet("Generator", "FlatHeight", "128");
a_IniFile.GetValueSet("Generator", "CompositionGen", "Nether");
- a_IniFile.GetValueSet("Generator", "Finishers", "SoulsandRims, WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherOreNests, NetherForts, PreSimulator");
+ a_IniFile.GetValueSet("Generator", "Finishers", "SoulsandRims, WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherOreNests, NetherForts, GlowStone, PreSimulator");
a_IniFile.GetValueSet("Generator", "BottomLavaHeight", "30");
break;
}
@@ -833,7 +834,7 @@ void cWorld::InitialiseAndLoadMobSpawningValues(cIniFile & a_IniFile)
AString DefaultMonsters;
switch (m_Dimension)
{
- case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, horse, mooshroom, ocelot, pig, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break;
+ case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, guardian, horse, mooshroom, ocelot, pig, rabbit, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break;
case dimNether: DefaultMonsters = "blaze, ghast, magmacube, skeleton, zombie, zombiepigman"; break;
case dimEnd: DefaultMonsters = "enderman"; break;
case dimNotSet: ASSERT(!"Dimension not set"); break;
@@ -1019,7 +1020,7 @@ void cWorld::TickWeather(float a_Dt)
// 0.5% chance per tick of thunderbolt
if (m_TickRand.randInt() % 199 == 0)
{
- CastThunderbolt(0, 0, 0); // TODO: find random possitions near players to cast thunderbolts.
+ CastThunderbolt(0, 0, 0); // TODO: find random positions near players to cast thunderbolts.
}
}
}
@@ -1119,7 +1120,7 @@ void cWorld::TickScheduledTasks(void)
auto WorldAge = m_WorldAge;
// Move all the due tasks from m_ScheduledTasks into Tasks:
- for (auto itr = m_ScheduledTasks.begin(); itr != m_ScheduledTasks.end();) // Cannot use range-basd for, we're modifying the container
+ for (auto itr = m_ScheduledTasks.begin(); itr != m_ScheduledTasks.end();) // Cannot use range-based for, we're modifying the container
{
if ((*itr)->m_TargetTick < std::chrono::duration_cast<cTickTimeLong>(WorldAge).count())
{
@@ -1351,7 +1352,7 @@ bool cWorld::DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBloc
-bool cWorld::DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconCallback& a_Callback)
+bool cWorld::DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconCallback & a_Callback)
{
return m_ChunkMap->DoWithBeaconAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
}
@@ -1459,6 +1460,15 @@ bool cWorld::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback
+bool cWorld::DoWithChunkAt(Vector3i a_BlockPos, std::function<bool(cChunk &)> a_Callback)
+{
+ return m_ChunkMap->DoWithChunkAt(a_BlockPos, a_Callback);
+}
+
+
+
+
+
void cWorld::GrowTree(int a_X, int a_Y, int a_Z)
{
if (GetBlock(a_X, a_Y, a_Z) == E_BLOCK_SAPLING)
@@ -2241,14 +2251,6 @@ void cWorld::BroadcastEntityAnimation(const cEntity & a_Entity, char a_Animation
-void cWorld::BroadcastParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount, cClientHandle * a_Exclude)
-{
- m_ChunkMap->BroadcastParticleEffect(a_ParticleName, a_SrcX, a_SrcY, a_SrcZ, a_OffsetX, a_OffsetY, a_OffsetZ, a_ParticleData, a_ParticleAmount, a_Exclude);
-}
-
-
-
-
void cWorld::BroadcastPlayerListAddPlayer(const cPlayer & a_Player, const cClientHandle * a_Exclude)
{
@@ -2679,7 +2681,7 @@ void cWorld::UnloadUnusedChunks(void)
void cWorld::QueueUnloadUnusedChunks(void)
{
- QueueTask(make_unique<cWorld::cTaskUnloadUnusedChunks>());
+ QueueTask(cpp14::make_unique<cWorld::cTaskUnloadUnusedChunks>());
}
@@ -3712,7 +3714,7 @@ void cWorld::cChunkGeneratorCallbacks::OnChunkGenerated(cChunkDesc & a_ChunkDesc
a_ChunkDesc.GetBlockTypes(), BlockMetas,
nullptr, nullptr, // We don't have lighting, chunk will be lighted when needed
&a_ChunkDesc.GetHeightMap(), &a_ChunkDesc.GetBiomeMap(),
- a_ChunkDesc.GetEntities(), a_ChunkDesc.GetBlockEntities(),
+ std::move(a_ChunkDesc.GetEntities()), std::move(a_ChunkDesc.GetBlockEntities()),
true
));
SetChunkData->RemoveInvalidBlockEntities();
@@ -3770,5 +3772,10 @@ void cWorld::cChunkGeneratorCallbacks::CallHookChunkGenerated (cChunkDesc & a_Ch
+cBroadcaster cWorld::GetBroadcaster()
+{
+ return cBroadcaster(this);
+}
+
diff --git a/src/World.h b/src/World.h
index 1de241f60..2ecdd519c 100644
--- a/src/World.h
+++ b/src/World.h
@@ -55,6 +55,7 @@ class cMobHeadEntity;
class cCompositeChat;
class cCuboid;
class cSetChunkData;
+class cBroadcaster;
typedef std::list< cPlayer * > cPlayerList;
@@ -151,10 +152,10 @@ public:
int GetTicksUntilWeatherChange(void) const { return m_WeatherInterval; }
- /** Is the daylight cyclus enabled? */
+ /** Is the daylight cycle enabled? */
virtual bool IsDaylightCycleEnabled(void) const { return m_IsDaylightCycleEnabled; }
- /** Sets the daylight cyclus to true/false. */
+ /** Sets the daylight cycle to true / false. */
virtual void SetDaylightCycleEnabled(bool a_IsDaylightCycleEnabled)
{
m_IsDaylightCycleEnabled = a_IsDaylightCycleEnabled;
@@ -202,10 +203,10 @@ public:
bool VillagersShouldHarvestCrops(void) const { return m_VillagersShouldHarvestCrops; }
- virtual eDimension GetDimension(void) const { return m_Dimension; }
+ virtual eDimension GetDimension(void) const override { return m_Dimension; }
/** Returns the world height at the specified coords; waits for the chunk to get loaded / generated */
- virtual int GetHeight(int a_BlockX, int a_BlockZ);
+ virtual int GetHeight(int a_BlockX, int a_BlockZ) override;
// tolua_end
@@ -243,7 +244,6 @@ public:
void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = nullptr);
void BroadcastEntityVelocity (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
virtual void BroadcastEntityAnimation (const cEntity & a_Entity, char a_Animation, const cClientHandle * a_Exclude = nullptr) override; // tolua_export
- void BroadcastParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount, cClientHandle * a_Exclude = nullptr); // tolua_export
void BroadcastPlayerListAddPlayer (const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
void BroadcastPlayerListRemovePlayer (const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
void BroadcastPlayerListUpdateGameMode (const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
@@ -253,7 +253,7 @@ public:
void BroadcastScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode);
void BroadcastScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode);
void BroadcastDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display);
- void BroadcastSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = nullptr); // tolua_export
+ void BroadcastSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = nullptr) override; // tolua_export
void BroadcastSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = nullptr); // tolua_export
void BroadcastSpawnEntity (cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
void BroadcastTeleportEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
@@ -610,6 +610,9 @@ public:
/** Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback */
bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback);
+ /** Calls the callback for the chunk at the block position specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback **/
+ bool DoWithChunkAt(Vector3i a_BlockPos, std::function<bool(cChunk &)> a_Callback);
+
void GrowTreeImage(const sSetBlockVector & a_Blocks);
// tolua_begin
@@ -785,7 +788,7 @@ public:
bool IsWeatherWet(void) const { return !IsWeatherSunny(); }
/** Returns true if it is raining, stormy or snowing at the specified location. This takes into account biomes. */
- virtual bool IsWeatherWetAt(int a_BlockX, int a_BlockZ)
+ virtual bool IsWeatherWetAt(int a_BlockX, int a_BlockZ) override
{
return (IsWeatherWet() && !IsBiomeNoDownfall(GetBiomeAt(a_BlockX, a_BlockZ)));
}
@@ -828,6 +831,8 @@ public:
This function allows nesting and task-concurrency (multiple separate tasks can request ticking and as long
as at least one requests is active the chunk will be ticked). */
void SetChunkAlwaysTicked(int a_ChunkX, int a_ChunkZ, bool a_AlwaysTicked = true); // tolua_export
+
+ cBroadcaster GetBroadcaster();
private:
@@ -1062,7 +1067,7 @@ private:
/** Handles the weather in each tick */
void TickWeather(float a_Dt);
- /** Handles the mob spawning/moving/destroying each tick */
+ /** Handles the mob spawning / moving / destroying each tick */
void TickMobs(std::chrono::milliseconds a_Dt);
/** Executes all tasks queued onto the tick thread */
diff --git a/src/WorldStorage/EnchantmentSerializer.cpp b/src/WorldStorage/EnchantmentSerializer.cpp
index 56072207f..a6e562956 100644
--- a/src/WorldStorage/EnchantmentSerializer.cpp
+++ b/src/WorldStorage/EnchantmentSerializer.cpp
@@ -5,7 +5,7 @@
#include "Enchantments.h"
#include "FastNBT.h"
-void EnchantmentSerializer::WriteToNBTCompound(cEnchantments const& a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName)
+void EnchantmentSerializer::WriteToNBTCompound(const cEnchantments & a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName)
{
// Write the enchantments into the specified NBT writer
// begin with the LIST tag of the specified name ("ench" or "StoredEnchantments")
@@ -25,7 +25,7 @@ void EnchantmentSerializer::WriteToNBTCompound(cEnchantments const& a_Enchantmen
-void EnchantmentSerializer::ParseFromNBT(cEnchantments& a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx)
+void EnchantmentSerializer::ParseFromNBT(cEnchantments & a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx)
{
// Read the enchantments from the specified NBT list tag (ench or StoredEnchantments)
diff --git a/src/WorldStorage/EnchantmentSerializer.h b/src/WorldStorage/EnchantmentSerializer.h
index 9ed362900..7d6546aad 100644
--- a/src/WorldStorage/EnchantmentSerializer.h
+++ b/src/WorldStorage/EnchantmentSerializer.h
@@ -9,9 +9,9 @@ namespace EnchantmentSerializer
{
/// Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments")
- void WriteToNBTCompound(cEnchantments const& a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName);
+ void WriteToNBTCompound(const cEnchantments & a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName);
/// Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments)
- void ParseFromNBT(cEnchantments& a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx);
+ void ParseFromNBT(cEnchantments & a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx);
};
diff --git a/src/WorldStorage/FastNBT.h b/src/WorldStorage/FastNBT.h
index c6225eacf..35e47c8e4 100644
--- a/src/WorldStorage/FastNBT.h
+++ b/src/WorldStorage/FastNBT.h
@@ -164,7 +164,7 @@ public:
/** Returns the direct child tag of the specified name, or -1 if no such tag. */
int FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLength = 0) const;
- /** Returns the child tag of the specified path (Name1/Name2/Name3...), or -1 if no such tag. */
+ /** Returns the child tag of the specified path (Name1 / Name2 / Name3...), or -1 if no such tag. */
int FindTagByPath(int a_Tag, const AString & a_Path) const;
eTagType GetType(int a_Tag) const { return m_Tags[(size_t)a_Tag].m_Type; }
diff --git a/src/WorldStorage/MapSerializer.cpp b/src/WorldStorage/MapSerializer.cpp
index f2d35b318..da7b9d929 100644
--- a/src/WorldStorage/MapSerializer.cpp
+++ b/src/WorldStorage/MapSerializer.cpp
@@ -15,13 +15,13 @@
-cMapSerializer::cMapSerializer(const AString& a_WorldName, cMap * a_Map)
- : m_Map(a_Map)
+cMapSerializer::cMapSerializer(const AString & a_WorldName, cMap * a_Map):
+ m_Map(a_Map)
{
AString DataPath;
- Printf(DataPath, "%s/data", a_WorldName.c_str());
+ Printf(DataPath, "%s%cdata", a_WorldName.c_str(), cFile::PathSeparator);
- Printf(m_Path, "%s/map_%i.dat", DataPath.c_str(), a_Map->GetID());
+ Printf(m_Path, "%s%cmap_%i.dat", DataPath.c_str(), cFile::PathSeparator, a_Map->GetID());
cFile::CreateFolder(FILE_IO_PREFIX + DataPath);
}
@@ -203,9 +203,9 @@ bool cMapSerializer::LoadMapFromNBT(const cParsedNBT & a_NBT)
cIDCountSerializer::cIDCountSerializer(const AString & a_WorldName) : m_MapCount(0)
{
AString DataPath;
- Printf(DataPath, "%s/data", a_WorldName.c_str());
+ Printf(DataPath, "%s%cdata", a_WorldName.c_str(), cFile::PathSeparator);
- Printf(m_Path, "%s/idcounts.dat", DataPath.c_str());
+ Printf(m_Path, "%s%cidcounts.dat", DataPath.c_str(), cFile::PathSeparator);
cFile::CreateFolder(FILE_IO_PREFIX + DataPath);
}
diff --git a/src/WorldStorage/MapSerializer.h b/src/WorldStorage/MapSerializer.h
index e13a75c8f..007f1347b 100644
--- a/src/WorldStorage/MapSerializer.h
+++ b/src/WorldStorage/MapSerializer.h
@@ -26,7 +26,7 @@ class cMapSerializer
{
public:
- cMapSerializer(const AString& a_WorldName, cMap * a_Map);
+ cMapSerializer(const AString & a_WorldName, cMap * a_Map);
/** Try to load the map */
bool Load(void);
diff --git a/src/WorldStorage/ScoreboardSerializer.cpp b/src/WorldStorage/ScoreboardSerializer.cpp
index 404604382..01e56dbe1 100644
--- a/src/WorldStorage/ScoreboardSerializer.cpp
+++ b/src/WorldStorage/ScoreboardSerializer.cpp
@@ -14,13 +14,13 @@
-cScoreboardSerializer::cScoreboardSerializer(const AString & a_WorldName, cScoreboard* a_ScoreBoard)
- : m_ScoreBoard(a_ScoreBoard)
+cScoreboardSerializer::cScoreboardSerializer(const AString & a_WorldName, cScoreboard * a_ScoreBoard):
+ m_ScoreBoard(a_ScoreBoard)
{
AString DataPath;
- Printf(DataPath, "%s/data", a_WorldName.c_str());
+ Printf(DataPath, "%s%cdata", a_WorldName.c_str(), cFile::PathSeparator);
- m_Path = DataPath + "/scoreboard.dat";
+ m_Path = DataPath + cFile::PathSeparator + "scoreboard.dat";
cFile::CreateFolder(FILE_IO_PREFIX + DataPath);
}
diff --git a/src/WorldStorage/ScoreboardSerializer.h b/src/WorldStorage/ScoreboardSerializer.h
index 048fa3ab4..f9065b35f 100644
--- a/src/WorldStorage/ScoreboardSerializer.h
+++ b/src/WorldStorage/ScoreboardSerializer.h
@@ -25,7 +25,7 @@ class cScoreboardSerializer
{
public:
- cScoreboardSerializer(const AString & a_WorldName, cScoreboard* a_ScoreBoard);
+ cScoreboardSerializer(const AString & a_WorldName, cScoreboard * a_ScoreBoard);
/// Try to load the scoreboard
bool Load(void);
@@ -40,7 +40,7 @@ private:
bool LoadScoreboardFromNBT(const cParsedNBT & a_NBT);
- cScoreboard* m_ScoreBoard;
+ cScoreboard * m_ScoreBoard;
AString m_Path;
diff --git a/src/WorldStorage/StatSerializer.cpp b/src/WorldStorage/StatSerializer.cpp
index 74113941c..99a702c39 100644
--- a/src/WorldStorage/StatSerializer.cpp
+++ b/src/WorldStorage/StatSerializer.cpp
@@ -18,7 +18,7 @@ cStatSerializer::cStatSerializer(const AString & a_WorldName, const AString & a_
// inside the folder of the default world.
AString StatsPath;
- Printf(StatsPath, "%s/stats", a_WorldName.c_str());
+ Printf(StatsPath, "%s%cstats", a_WorldName.c_str(), cFile::PathSeparator);
m_Path = StatsPath + "/" + a_PlayerName + ".json";
diff --git a/src/WorldStorage/StatSerializer.h b/src/WorldStorage/StatSerializer.h
index 72f8d74f1..6b7efddbb 100644
--- a/src/WorldStorage/StatSerializer.h
+++ b/src/WorldStorage/StatSerializer.h
@@ -43,7 +43,7 @@ protected:
private:
- cStatManager* m_Manager;
+ cStatManager * m_Manager;
AString m_Path;
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index 7244bcb73..392b9bf83 100755
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -95,7 +95,7 @@ cWSSAnvil::cWSSAnvil(cWorld * a_World, int a_CompressionFactor) :
{
// Create a level.dat file for mapping tools, if it doesn't already exist:
AString fnam;
- Printf(fnam, "%s/level.dat", a_World->GetName().c_str());
+ Printf(fnam, "%s%clevel.dat", a_World->GetName().c_str(), cFile::PathSeparator);
if (!cFile::Exists(fnam))
{
cFastNBTWriter Writer;
@@ -251,7 +251,7 @@ cWSSAnvil::cMCAFile * cWSSAnvil::LoadMCAFile(const cChunkCoords & a_Chunk)
// Load it anew:
AString FileName;
- Printf(FileName, "%s/region", m_World->GetName().c_str());
+ Printf(FileName, "%s%cregion", m_World->GetName().c_str(), cFile::PathSeparator);
cFile::CreateFolder(FILE_IO_PREFIX + FileName);
AppendPrintf(FileName, "/r.%d.%d.mca", RegionX, RegionZ);
cMCAFile * f = new cMCAFile(FileName, RegionX, RegionZ);
@@ -323,7 +323,7 @@ bool cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data)
bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT)
{
- // The data arrays, in MCA-native y/z/x ordering (will be reordered for the final chunk data)
+ // The data arrays, in MCA-native y / z / x ordering (will be reordered for the final chunk data)
cChunkDef::BlockTypes BlockTypes;
cChunkDef::BlockNibbles MetaData;
cChunkDef::BlockNibbles BlockLight;
@@ -431,7 +431,7 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT
IsLightValid ? BlockLight : nullptr,
IsLightValid ? SkyLight : nullptr,
nullptr, Biomes,
- Entities, BlockEntities,
+ std::move(Entities), std::move(BlockEntities),
false
));
m_World->QueueSetChunkData(SetChunkData);
@@ -879,7 +879,7 @@ cBlockEntity * cWSSAnvil::LoadBeaconFromNBT(const cParsedNBT & a_NBT, int a_TagI
Beacon->SetSecondaryEffect((cEntityEffect::eType)a_NBT.GetInt(CurrentLine));
}
- // We are better than mojang, we load/save the beacon inventory!
+ // We are better than mojang, we load / save the beacon inventory!
int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items >= 0) && (a_NBT.GetType(Items) == TAG_List))
{
@@ -3148,7 +3148,7 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri
}
// Store the header:
- ChunkSize = ((u_long)a_Data.size() + MCA_CHUNK_HEADER_LENGTH + 4095) / 4096; // Round data size *up* to nearest 4KB sector, make it a sector number
+ ChunkSize = ((u_long)a_Data.size() + MCA_CHUNK_HEADER_LENGTH + 4095) / 4096; // Round data size up to nearest 4KB sector, make it a sector number
if (ChunkSize > 255)
{
LOGWARNING("Cannot save chunk [%d, %d], the data is too large (%u KiB, maximum is 1024 KiB). Remove some entities and retry.",
diff --git a/src/XMLParser.h b/src/XMLParser.h
index ea341bce6..90e499785 100644
--- a/src/XMLParser.h
+++ b/src/XMLParser.h
@@ -168,9 +168,9 @@ public:
protected:
- // Parser callback enable/disable methods:
+ // Parser callback enable / disable methods:
- // @cmember Enable/Disable the start element handler
+ // @cmember Enable / Disable the start element handler
void EnableStartElementHandler (bool fEnable = true)
{
@@ -178,7 +178,7 @@ protected:
XML_SetStartElementHandler (m_p, fEnable ? StartElementHandler : nullptr);
}
- // @cmember Enable/Disable the end element handler
+ // @cmember Enable / Disable the end element handler
void EnableEndElementHandler (bool fEnable = true)
{
@@ -186,7 +186,7 @@ protected:
XML_SetEndElementHandler (m_p, fEnable ? EndElementHandler : nullptr);
}
- // @cmember Enable/Disable the element handlers
+ // @cmember Enable / Disable the element handlers
void EnableElementHandler (bool fEnable = true)
{
@@ -195,7 +195,7 @@ protected:
EnableEndElementHandler (fEnable);
}
- // @cmember Enable/Disable the character data handler
+ // @cmember Enable / Disable the character data handler
void EnableCharacterDataHandler (bool fEnable = true)
{
@@ -203,7 +203,7 @@ protected:
XML_SetCharacterDataHandler (m_p, fEnable ? CharacterDataHandler : nullptr);
}
- // @cmember Enable/Disable the processing instruction handler
+ // @cmember Enable / Disable the processing instruction handler
void EnableProcessingInstructionHandler (bool fEnable = true)
{
@@ -211,7 +211,7 @@ protected:
XML_SetProcessingInstructionHandler (m_p, fEnable ? ProcessingInstructionHandler : nullptr);
}
- // @cmember Enable/Disable the comment handler
+ // @cmember Enable / Disable the comment handler
void EnableCommentHandler (bool fEnable = true)
{
@@ -219,7 +219,7 @@ protected:
XML_SetCommentHandler (m_p, fEnable ? CommentHandler : nullptr);
}
- // @cmember Enable/Disable the start CDATA section handler
+ // @cmember Enable / Disable the start CDATA section handler
void EnableStartCdataSectionHandler (bool fEnable = true)
{
@@ -227,7 +227,7 @@ protected:
XML_SetStartCdataSectionHandler (m_p, fEnable ? StartCdataSectionHandler : nullptr);
}
- // @cmember Enable/Disable the end CDATA section handler
+ // @cmember Enable / Disable the end CDATA section handler
void EnableEndCdataSectionHandler (bool fEnable = true)
{
@@ -235,7 +235,7 @@ protected:
XML_SetEndCdataSectionHandler (m_p, fEnable ? EndCdataSectionHandler : nullptr);
}
- // @cmember Enable/Disable the CDATA section handlers
+ // @cmember Enable / Disable the CDATA section handlers
void EnableCdataSectionHandler (bool fEnable = true)
{
@@ -244,7 +244,7 @@ protected:
EnableEndCdataSectionHandler (fEnable);
}
- // @cmember Enable/Disable default handler
+ // @cmember Enable / Disable default handler
void EnableDefaultHandler (bool fEnable = true, bool fExpand = true)
{
@@ -259,7 +259,7 @@ protected:
}
}
- // @cmember Enable/Disable external entity ref handler
+ // @cmember Enable / Disable external entity ref handler
void EnableExternalEntityRefHandler (bool fEnable = true)
{
@@ -267,7 +267,7 @@ protected:
XML_SetExternalEntityRefHandler (m_p, fEnable ? ExternalEntityRefHandler : nullptr);
}
- // @cmember Enable/Disable unknown encoding handler
+ // @cmember Enable / Disable unknown encoding handler
void EnableUnknownEncodingHandler (bool fEnable = true)
{
@@ -275,7 +275,7 @@ protected:
XML_SetUnknownEncodingHandler (m_p, fEnable ? UnknownEncodingHandler : nullptr);
}
- // @cmember Enable/Disable start namespace handler
+ // @cmember Enable / Disable start namespace handler
void EnableStartNamespaceDeclHandler (bool fEnable = true)
{
@@ -283,7 +283,7 @@ protected:
XML_SetStartNamespaceDeclHandler (m_p, fEnable ? StartNamespaceDeclHandler : nullptr);
}
- // @cmember Enable/Disable end namespace handler
+ // @cmember Enable / Disable end namespace handler
void EnableEndNamespaceDeclHandler (bool fEnable = true)
{
@@ -291,7 +291,7 @@ protected:
XML_SetEndNamespaceDeclHandler (m_p, fEnable ? EndNamespaceDeclHandler : nullptr);
}
- // @cmember Enable/Disable namespace handlers
+ // @cmember Enable / Disable namespace handlers
void EnableNamespaceDeclHandler (bool fEnable = true)
{
@@ -299,7 +299,7 @@ protected:
EnableEndNamespaceDeclHandler (fEnable);
}
- // @cmember Enable/Disable the XML declaration handler
+ // @cmember Enable / Disable the XML declaration handler
void EnableXmlDeclHandler (bool fEnable = true)
{
@@ -307,7 +307,7 @@ protected:
XML_SetXmlDeclHandler (m_p, fEnable ? XmlDeclHandler : nullptr);
}
- // @cmember Enable/Disable the start DOCTYPE declaration handler
+ // @cmember Enable / Disable the start DOCTYPE declaration handler
void EnableStartDoctypeDeclHandler (bool fEnable = true)
{
@@ -315,7 +315,7 @@ protected:
XML_SetStartDoctypeDeclHandler (m_p, fEnable ? StartDoctypeDeclHandler : nullptr);
}
- // @cmember Enable/Disable the end DOCTYPE declaration handler
+ // @cmember Enable / Disable the end DOCTYPE declaration handler
void EnableEndDoctypeDeclHandler (bool fEnable = true)
{
@@ -324,7 +324,7 @@ protected:
fEnable ? EndDoctypeDeclHandler : nullptr);
}
- // @cmember Enable/Disable the DOCTYPE declaration handler
+ // @cmember Enable / Disable the DOCTYPE declaration handler
void EnableDoctypeDeclHandler (bool fEnable = true)
{
diff --git a/src/main.cpp b/src/main.cpp
index da8eb75d4..5cd057278 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -2,6 +2,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Root.h"
+#include "tclap/CmdLine.h"
#include <exception>
#include <csignal>
@@ -12,12 +13,11 @@
#endif // _MSC_VER
#include "OSSupport/NetworkSingleton.h"
+#include "BuildInfo.h"
+#include "MemorySettingsRepository.h"
-/** Make the Root instance global, so it can be terminated from the worker threads */
-cRoot Root;
-
/** If something has told the server to stop; checked periodically in cRoot */
bool cRoot::m_TerminateEventRaised = false;
@@ -35,14 +35,20 @@ bool g_ShouldLogCommOut;
bool cRoot::m_RunAsService = false;
+
+
+
#if defined(_WIN32)
-SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
-HANDLE g_ServiceThread = INVALID_HANDLE_VALUE;
-#define SERVICE_NAME "MCServerService"
+ SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
+ HANDLE g_ServiceThread = INVALID_HANDLE_VALUE;
+ #define SERVICE_NAME "MCServerService"
#endif
-/// If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window
+
+
+
+/** If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window */
// _X 2014_02_20: Disabled for canon repo, it makes the debug version too slow in MSVC2013
// and we haven't had a memory leak for over a year anyway.
// #define ENABLE_LEAK_FINDER
@@ -74,6 +80,10 @@ void NonCtrlHandler(int a_Signal)
std::signal(SIGSEGV, SIG_DFL);
LOGERROR(" D: | MCServer has encountered an error and needs to close");
LOGERROR("Details | SIGSEGV: Segmentation fault");
+ #ifdef BUILD_ID
+ LOGERROR("MCServer " BUILD_SERIES_NAME " build id: " BUILD_ID);
+ LOGERROR("from commit id: " BUILD_COMMIT_ID " built at: " BUILD_DATETIME);
+ #endif
PrintStackTrace();
abort();
}
@@ -85,6 +95,10 @@ void NonCtrlHandler(int a_Signal)
std::signal(a_Signal, SIG_DFL);
LOGERROR(" D: | MCServer has encountered an error and needs to close");
LOGERROR("Details | SIGABRT: Server self-terminated due to an internal fault");
+ #ifdef BUILD_ID
+ LOGERROR("MCServer " BUILD_SERIES_NAME " build id: " BUILD_ID);
+ LOGERROR("from commit id: " BUILD_COMMIT_ID " built at: " BUILD_DATETIME);
+ #endif
PrintStackTrace();
abort();
}
@@ -169,6 +183,7 @@ LONG WINAPI LastChanceExceptionFilter(__in struct _EXCEPTION_POINTERS * a_Except
+
#ifdef _WIN32
// Handle CTRL events in windows, including console window close
BOOL CtrlHandler(DWORD fdwCtrlType)
@@ -188,10 +203,11 @@ BOOL CtrlHandler(DWORD fdwCtrlType)
+
////////////////////////////////////////////////////////////////////////////////
// universalMain - Main startup logic for both standard running and as a service
-void universalMain()
+void universalMain(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo)
{
#ifdef _WIN32
if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE))
@@ -210,7 +226,8 @@ void universalMain()
try
#endif
{
- Root.Start();
+ cRoot Root;
+ Root.Start(std::move(overridesRepo));
}
#if !defined(ANDROID_NDK)
catch (std::exception & e)
@@ -241,7 +258,7 @@ DWORD WINAPI serviceWorkerThread(LPVOID lpParam)
UNREFERENCED_PARAMETER(lpParam);
// Do the normal startup
- universalMain();
+ universalMain(cpp14::make_unique<cMemorySettingsRepository>());
return ERROR_SUCCESS;
}
@@ -282,7 +299,7 @@ void WINAPI serviceCtrlHandler(DWORD CtrlCode)
{
case SERVICE_CONTROL_STOP:
{
- Root.SetStopping(true);
+ cRoot::m_ShouldStop = true;
serviceSetState(0, SERVICE_STOP_PENDING, 0);
break;
}
@@ -309,10 +326,10 @@ void WINAPI serviceMain(DWORD argc, TCHAR *argv[])
char applicationFilename[MAX_PATH];
char applicationDirectory[MAX_PATH];
- GetModuleFileName(NULL, applicationFilename, MAX_PATH); // This binaries fill path.
+ GetModuleFileName(NULL, applicationFilename, sizeof(applicationFilename)); // This binary's file path.
- // GetModuleFileName() returns the path and filename. Strip off the filename.
- strncpy(applicationDirectory, applicationFilename, (strrchr(applicationFilename, '\\') - applicationFilename));
+ // Strip off the filename, keep only the path:
+ strncpy_s(applicationDirectory, sizeof(applicationDirectory), applicationFilename, (strrchr(applicationFilename, '\\') - applicationFilename));
applicationDirectory[strlen(applicationDirectory)] = '\0'; // Make sure new path is null terminated
// Services are run by the SCM, and inherit its working directory - usually System32.
@@ -323,7 +340,7 @@ void WINAPI serviceMain(DWORD argc, TCHAR *argv[])
if (g_StatusHandle == NULL)
{
- OutputDebugString("RegisterServiceCtrlHandler() failed\n");
+ OutputDebugStringA("RegisterServiceCtrlHandler() failed\n");
serviceSetState(0, SERVICE_STOPPED, GetLastError());
return;
}
@@ -333,7 +350,7 @@ void WINAPI serviceMain(DWORD argc, TCHAR *argv[])
g_ServiceThread = CreateThread(NULL, 0, serviceWorkerThread, NULL, 0, NULL);
if (g_ServiceThread == NULL)
{
- OutputDebugString("CreateThread() failed\n");
+ OutputDebugStringA("CreateThread() failed\n");
serviceSetState(0, SERVICE_STOPPED, GetLastError());
return;
}
@@ -347,14 +364,79 @@ void WINAPI serviceMain(DWORD argc, TCHAR *argv[])
+std::unique_ptr<cMemorySettingsRepository> parseArguments(int argc, char **argv)
+{
+ try
+ {
+ TCLAP::CmdLine cmd("MCServer");
+
+ TCLAP::ValueArg<int> slotsArg("s", "max-players", "Maximum number of slots for the server to use, overrides setting in setting.ini", false, -1, "number", cmd);
+
+ TCLAP::MultiArg<int> portsArg("p", "port", "The port number the server should listen to", false, "port", cmd);
+
+ TCLAP::SwitchArg commLogArg("", "log-comm", "Log server client communications to file", cmd);
+
+ TCLAP::SwitchArg commLogInArg("", "log-comm-in", "Log inbound server client communications to file", cmd);
+
+ TCLAP::SwitchArg commLogOutArg("", "log-comm-out", "Log outbound server client communications to file", cmd);
+
+ TCLAP::SwitchArg noBufArg("", "no-output-buffering", "Disable output buffering", cmd);
+
+ cmd.parse(argc, argv);
+
+ auto repo = cpp14::make_unique<cMemorySettingsRepository>();
+
+ if (slotsArg.isSet())
+ {
+
+ int slots = slotsArg.getValue();
+
+ repo->AddValue("Server", "MaxPlayers", static_cast<Int64>(slots));
+
+ }
+
+ if (portsArg.isSet())
+ {
+ std::vector<int> ports = portsArg.getValue();
+ for (auto port : ports)
+ {
+ repo->AddValue("Server", "Port", static_cast<Int64>(port));
+ }
+ }
+
+ if (commLogArg.getValue())
+ {
+ g_ShouldLogCommIn = true;
+ g_ShouldLogCommOut = true;
+ }
+ else
+ {
+ g_ShouldLogCommIn = commLogInArg.getValue();
+ g_ShouldLogCommOut = commLogOutArg.getValue();
+ }
+
+ if (noBufArg.getValue())
+ {
+ setvbuf(stdout, nullptr, _IONBF, 0);
+ }
+
+ repo->SetReadOnly();
+
+ return repo;
+ }
+ catch (TCLAP::ArgException &e)
+ {
+ printf("error reading command line %s for arg %s", e.error().c_str(), e.argId().c_str());
+ return cpp14::make_unique<cMemorySettingsRepository>();
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
// main:
-int main( int argc, char **argv)
+int main(int argc, char **argv)
{
- UNUSED(argc);
- UNUSED(argv);
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
InitLeakFinder();
@@ -409,39 +491,13 @@ int main( int argc, char **argv)
// DEBUG: test the dumpfile creation:
// *((int *)0) = 0;
+ auto argsRepo = parseArguments(argc, argv);
+
// Check if comm logging is to be enabled:
for (int i = 0; i < argc; i++)
{
AString Arg(argv[i]);
- if (
- (NoCaseCompare(Arg, "/commlog") == 0) ||
- (NoCaseCompare(Arg, "/logcomm") == 0)
- )
- {
- g_ShouldLogCommIn = true;
- g_ShouldLogCommOut = true;
- }
- else if (
- (NoCaseCompare(Arg, "/commlogin") == 0) ||
- (NoCaseCompare(Arg, "/comminlog") == 0) ||
- (NoCaseCompare(Arg, "/logcommin") == 0)
- )
- {
- g_ShouldLogCommIn = true;
- }
- else if (
- (NoCaseCompare(Arg, "/commlogout") == 0) ||
- (NoCaseCompare(Arg, "/commoutlog") == 0) ||
- (NoCaseCompare(Arg, "/logcommout") == 0)
- )
- {
- g_ShouldLogCommOut = true;
- }
- else if (NoCaseCompare(Arg, "nooutbuf") == 0)
- {
- setvbuf(stdout, nullptr, _IONBF, 0);
- }
- else if (NoCaseCompare(Arg, "/service") == 0)
+ if (NoCaseCompare(Arg, "/service") == 0)
{
cRoot::m_RunAsService = true;
}
@@ -467,7 +523,7 @@ int main( int argc, char **argv)
#endif
{
// Not running as a service, do normal startup
- universalMain();
+ universalMain(std::move(argsRepo));
}
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)