summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHowaner <franzi.moos@googlemail.com>2015-01-25 00:34:19 +0100
committerHowaner <franzi.moos@googlemail.com>2015-01-25 00:34:19 +0100
commit1eedccc56a1a80b42adbea8dbbe968d42c7fe712 (patch)
treea5fe3fb72a3c3918b2c4ba413c686f718f5d403c
parentC++11 and function rename. (diff)
parentGamosocm support (diff)
downloadcuberite-1eedccc56a1a80b42adbea8dbbe968d42c7fe712.tar
cuberite-1eedccc56a1a80b42adbea8dbbe968d42c7fe712.tar.gz
cuberite-1eedccc56a1a80b42adbea8dbbe968d42c7fe712.tar.bz2
cuberite-1eedccc56a1a80b42adbea8dbbe968d42c7fe712.tar.lz
cuberite-1eedccc56a1a80b42adbea8dbbe968d42c7fe712.tar.xz
cuberite-1eedccc56a1a80b42adbea8dbbe968d42c7fe712.tar.zst
cuberite-1eedccc56a1a80b42adbea8dbbe968d42c7fe712.zip
Diffstat (limited to '')
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules3
-rw-r--r--.travis.yml3
-rwxr-xr-xCIbuild.sh3
-rw-r--r--CMakeLists.txt25
-rw-r--r--COMPILING.md4
-rw-r--r--MCServer/Plugins/APIDump/APIDesc.lua2
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnPlayerPlacedBlock.lua10
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnPlayerPlacingBlock.lua10
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnServerPing.lua5
-rw-r--r--MCServer/monsters.ini195
-rw-r--r--MCServer/webadmin/template.lua2
-rw-r--r--README.md3
-rw-r--r--Tools/AnvilStats/Utils.cpp2
-rw-r--r--Tools/AnvilStats/Utils.h2
-rw-r--r--Tools/MCADefrag/CMakeLists.txt2
-rw-r--r--Tools/MCADefrag/Globals.h1
-rw-r--r--Tools/MCADefrag/MCADefrag.cpp2
-rw-r--r--Tools/ProtoProxy/CMakeLists.txt2
-rw-r--r--Tools/ProtoProxy/Connection.cpp223
-rw-r--r--Tools/ProtoProxy/Connection.h2
-rw-r--r--Tools/ProtoProxy/Server.cpp34
-rw-r--r--app.yml8
-rwxr-xr-xeasyinstall.sh2
m---------lib/libevent0
-rw-r--r--src/Bindings/LuaState.cpp7
-rw-r--r--src/Bindings/LuaState.h1
-rw-r--r--src/Bindings/Plugin.h6
-rw-r--r--src/Bindings/PluginLua.cpp20
-rw-r--r--src/Bindings/PluginLua.h6
-rw-r--r--src/Bindings/PluginManager.cpp14
-rw-r--r--src/Bindings/PluginManager.h10
-rw-r--r--src/BlockEntities/BeaconEntity.cpp2
-rw-r--r--src/BlockEntities/BeaconEntity.h2
-rw-r--r--src/BlockEntities/BlockEntity.h2
-rw-r--r--src/BlockEntities/CommandBlockEntity.cpp2
-rw-r--r--src/BlockEntities/CommandBlockEntity.h2
-rw-r--r--src/BlockEntities/DropSpenserEntity.cpp2
-rw-r--r--src/BlockEntities/DropSpenserEntity.h2
-rw-r--r--src/BlockEntities/FurnaceEntity.cpp2
-rw-r--r--src/BlockEntities/FurnaceEntity.h2
-rw-r--r--src/BlockEntities/HopperEntity.cpp2
-rw-r--r--src/BlockEntities/HopperEntity.h2
-rw-r--r--src/BlockEntities/MobSpawnerEntity.cpp2
-rw-r--r--src/BlockEntities/MobSpawnerEntity.h2
-rw-r--r--src/BlockID.h2
-rw-r--r--src/Blocks/BlockBed.cpp20
-rw-r--r--src/Blocks/BlockBed.h1
-rw-r--r--src/Blocks/BlockBigFlower.h12
-rw-r--r--src/Blocks/BlockChest.h65
-rw-r--r--src/Blocks/BlockDoor.cpp40
-rw-r--r--src/Blocks/BlockDoor.h40
-rw-r--r--src/Blocks/BlockFire.h3
-rw-r--r--src/Blocks/BlockHandler.cpp2
-rw-r--r--src/Blocks/BlockHandler.h21
-rw-r--r--src/Blocks/BlockMobHead.h204
-rw-r--r--src/Blocks/BlockPumpkin.h64
-rw-r--r--src/Blocks/BlockSignPost.h11
-rw-r--r--src/Blocks/BlockWallSign.h11
-rw-r--r--src/ByteBuffer.cpp50
-rw-r--r--src/ByteBuffer.h2
-rw-r--r--src/CMakeLists.txt3
-rw-r--r--src/Chunk.cpp18
-rw-r--r--src/Chunk.h2
-rw-r--r--src/ChunkDef.h49
-rw-r--r--src/ChunkMap.cpp107
-rw-r--r--src/ChunkMap.h27
-rw-r--r--src/ClientHandle.cpp162
-rw-r--r--src/ClientHandle.h7
-rw-r--r--src/Enchantments.cpp29
-rw-r--r--src/Enchantments.h7
-rw-r--r--src/Entities/ArrowEntity.cpp8
-rw-r--r--src/Entities/ArrowEntity.h6
-rw-r--r--src/Entities/Boat.cpp2
-rw-r--r--src/Entities/Boat.h2
-rw-r--r--src/Entities/EnderCrystal.cpp2
-rw-r--r--src/Entities/EnderCrystal.h2
-rw-r--r--src/Entities/Entity.cpp30
-rw-r--r--src/Entities/Entity.h4
-rw-r--r--src/Entities/ExpOrb.cpp8
-rw-r--r--src/Entities/ExpOrb.h8
-rw-r--r--src/Entities/FallingBlock.cpp4
-rw-r--r--src/Entities/FallingBlock.h2
-rw-r--r--src/Entities/FireworkEntity.cpp8
-rw-r--r--src/Entities/FireworkEntity.h4
-rw-r--r--src/Entities/Floater.cpp2
-rw-r--r--src/Entities/Floater.h2
-rw-r--r--src/Entities/HangingEntity.h2
-rw-r--r--src/Entities/Minecart.cpp12
-rw-r--r--src/Entities/Minecart.h10
-rw-r--r--src/Entities/Painting.cpp2
-rw-r--r--src/Entities/Painting.h2
-rw-r--r--src/Entities/Pawn.cpp2
-rw-r--r--src/Entities/Pawn.h2
-rw-r--r--src/Entities/Pickup.cpp16
-rw-r--r--src/Entities/Pickup.h8
-rw-r--r--src/Entities/Player.cpp98
-rw-r--r--src/Entities/Player.h23
-rw-r--r--src/Entities/ProjectileEntity.cpp4
-rw-r--r--src/Entities/ProjectileEntity.h4
-rw-r--r--src/Entities/SplashPotionEntity.h2
-rw-r--r--src/Entities/TNTEntity.cpp2
-rw-r--r--src/Entities/TNTEntity.h2
-rw-r--r--src/Entities/ThrownEggEntity.cpp2
-rw-r--r--src/Entities/ThrownEggEntity.h2
-rw-r--r--src/Entities/ThrownEnderPearlEntity.cpp2
-rw-r--r--src/Entities/ThrownEnderPearlEntity.h2
-rw-r--r--src/Entities/ThrownSnowballEntity.cpp2
-rw-r--r--src/Entities/ThrownSnowballEntity.h2
-rw-r--r--src/Generating/BioGen.cpp14
-rw-r--r--src/Generating/EndGen.cpp8
-rw-r--r--src/Generating/Noise3DGenerator.cpp15
-rw-r--r--src/Generating/StructGen.cpp14
-rw-r--r--src/Generating/Trees.cpp14
-rw-r--r--src/Globals.h30
-rw-r--r--src/HTTPServer/HTTPMessage.cpp2
-rw-r--r--src/ItemGrid.cpp2
-rw-r--r--src/Items/CMakeLists.txt7
-rw-r--r--src/Items/ItemBed.h28
-rw-r--r--src/Items/ItemBigFlower.h56
-rw-r--r--src/Items/ItemBucket.h12
-rw-r--r--src/Items/ItemChest.h167
-rw-r--r--src/Items/ItemDoor.h96
-rw-r--r--src/Items/ItemDye.h25
-rw-r--r--src/Items/ItemHandler.cpp200
-rw-r--r--src/Items/ItemHandler.h39
-rw-r--r--src/Items/ItemMobHead.h308
-rw-r--r--src/Items/ItemPumpkin.h156
-rw-r--r--src/Items/ItemRedstoneDust.h37
-rw-r--r--src/Items/ItemSign.h22
-rw-r--r--src/Items/ItemSlab.h93
-rw-r--r--src/Items/ItemSpawnEgg.h2
-rw-r--r--src/LightingThread.cpp2
-rw-r--r--src/LineBlockTracer.cpp18
-rw-r--r--src/MobProximityCounter.cpp2
-rw-r--r--src/MobSpawner.cpp8
-rw-r--r--src/Mobs/AggressiveMonster.cpp8
-rw-r--r--src/Mobs/AggressiveMonster.h6
-rw-r--r--src/Mobs/Blaze.cpp4
-rw-r--r--src/Mobs/Blaze.h2
-rw-r--r--src/Mobs/CMakeLists.txt4
-rw-r--r--src/Mobs/CaveSpider.cpp4
-rw-r--r--src/Mobs/CaveSpider.h4
-rw-r--r--src/Mobs/Chicken.cpp2
-rw-r--r--src/Mobs/Chicken.h2
-rw-r--r--src/Mobs/Creeper.cpp4
-rw-r--r--src/Mobs/Creeper.h4
-rw-r--r--src/Mobs/Enderman.cpp2
-rw-r--r--src/Mobs/Enderman.h2
-rw-r--r--src/Mobs/Ghast.cpp4
-rw-r--r--src/Mobs/Ghast.h2
-rw-r--r--src/Mobs/Guardian.cpp65
-rw-r--r--src/Mobs/Guardian.h31
-rw-r--r--src/Mobs/Horse.cpp2
-rw-r--r--src/Mobs/Horse.h2
-rw-r--r--src/Mobs/IncludeAllMonsters.h2
-rw-r--r--src/Mobs/Monster.cpp28
-rw-r--r--src/Mobs/Monster.h14
-rw-r--r--src/Mobs/MonsterTypes.h3
-rw-r--r--src/Mobs/PassiveMonster.cpp2
-rw-r--r--src/Mobs/PassiveMonster.h2
-rw-r--r--src/Mobs/Pig.cpp2
-rw-r--r--src/Mobs/Pig.h2
-rw-r--r--src/Mobs/Rabbit.cpp34
-rw-r--r--src/Mobs/Rabbit.h24
-rw-r--r--src/Mobs/Sheep.cpp2
-rw-r--r--src/Mobs/Sheep.h2
-rw-r--r--src/Mobs/Skeleton.cpp4
-rw-r--r--src/Mobs/Skeleton.h2
-rw-r--r--src/Mobs/Slime.cpp2
-rw-r--r--src/Mobs/Slime.h2
-rw-r--r--src/Mobs/SnowGolem.cpp2
-rw-r--r--src/Mobs/SnowGolem.h2
-rw-r--r--src/Mobs/Squid.cpp2
-rw-r--r--src/Mobs/Squid.h2
-rw-r--r--src/Mobs/Villager.cpp2
-rw-r--r--src/Mobs/Villager.h2
-rw-r--r--src/Mobs/Wither.cpp2
-rw-r--r--src/Mobs/Wither.h2
-rw-r--r--src/Mobs/Wolf.cpp4
-rw-r--r--src/Mobs/Wolf.h4
-rw-r--r--src/OSSupport/CMakeLists.txt11
-rw-r--r--src/OSSupport/CriticalSection.cpp1
-rw-r--r--src/OSSupport/HostnameLookup.cpp124
-rw-r--r--src/OSSupport/HostnameLookup.h47
-rw-r--r--src/OSSupport/IPLookup.cpp111
-rw-r--r--src/OSSupport/IPLookup.h49
-rw-r--r--src/OSSupport/IsThread.cpp18
-rw-r--r--src/OSSupport/IsThread.h19
-rw-r--r--src/OSSupport/Network.h246
-rw-r--r--src/OSSupport/NetworkSingleton.cpp245
-rw-r--r--src/OSSupport/NetworkSingleton.h134
-rw-r--r--src/OSSupport/Queue.h4
-rw-r--r--src/OSSupport/ServerHandleImpl.cpp334
-rw-r--r--src/OSSupport/ServerHandleImpl.h105
-rw-r--r--src/OSSupport/StackTrace.cpp2
-rw-r--r--src/OSSupport/TCPLinkImpl.cpp331
-rw-r--r--src/OSSupport/TCPLinkImpl.h122
-rw-r--r--src/Protocol/Protocol17x.cpp249
-rw-r--r--src/Protocol/Protocol18x.cpp22
-rw-r--r--src/Root.cpp18
-rw-r--r--src/Root.h4
-rw-r--r--src/Server.cpp2
-rw-r--r--src/SetChunkData.cpp2
-rw-r--r--src/Simulator/DelayedFluidSimulator.cpp2
-rw-r--r--src/Simulator/DelayedFluidSimulator.h2
-rw-r--r--src/Simulator/FireSimulator.cpp4
-rw-r--r--src/Simulator/FireSimulator.h2
-rw-r--r--src/Simulator/FloodyFluidSimulator.cpp2
-rw-r--r--src/Simulator/IncrementalRedstoneSimulator.cpp96
-rw-r--r--src/Simulator/IncrementalRedstoneSimulator.h2
-rw-r--r--src/Simulator/NoopRedstoneSimulator.h2
-rw-r--r--src/Simulator/SandSimulator.cpp2
-rw-r--r--src/Simulator/SandSimulator.h2
-rw-r--r--src/Simulator/Simulator.h2
-rw-r--r--src/Simulator/SimulatorManager.cpp2
-rw-r--r--src/Simulator/SimulatorManager.h2
-rw-r--r--src/StringUtils.cpp57
-rw-r--r--src/StringUtils.h51
-rw-r--r--src/Vector3.h16
-rw-r--r--src/World.cpp115
-rw-r--r--src/World.h33
-rw-r--r--src/WorldStorage/FastNBT.cpp88
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp4
-rw-r--r--src/WorldStorage/SchematicFileSerializer.cpp4
-rwxr-xr-xsrc/WorldStorage/WSSAnvil.cpp54
-rwxr-xr-xsrc/WorldStorage/WSSAnvil.h2
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/Network/CMakeLists.txt60
-rw-r--r--tests/Network/EchoServer.cpp132
-rw-r--r--tests/Network/Google.cpp118
-rw-r--r--tests/Network/NameLookup.cpp80
232 files changed, 5177 insertions, 1534 deletions
diff --git a/.gitignore b/.gitignore
index b85b172cb..c7f032b00 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@ nbproject/
ipch/
Win32/
MCServer/MCServer
+MCServer/itemblacklist
ChunkWorxSave.ini
doxy/
Profiling
diff --git a/.gitmodules b/.gitmodules
index d2ce2c855..93fac9d1f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -25,3 +25,6 @@
[submodule "lib/SQLiteCpp"]
path = lib/SQLiteCpp
url = https://github.com/mc-server/SQLiteCpp.git
+[submodule "lib/libevent"]
+ path = lib/libevent
+ url = https://github.com/mc-server/libevent.git
diff --git a/.travis.yml b/.travis.yml
index 26e74c1b2..84b963a89 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,6 +13,9 @@ before_install:
install:
# g++4.8 and clang
- sudo apt-get install -qq g++-4.8
+
+ # 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
diff --git a/CIbuild.sh b/CIbuild.sh
index 5d95f88a6..755dc7daa 100755
--- a/CIbuild.sh
+++ b/CIbuild.sh
@@ -7,6 +7,9 @@ export MCSERVER_BUILD_ID=$TRAVIS_JOB_NUMBER
export MCSERVER_BUILD_DATETIME=`date`
cmake . -DBUILD_TOOLS=1 -DSELF_TEST=1;
+cd src
+lua CheckBasicStyle.lua
+cd ..
make -j 2;
make -j 2 test;
cd MCServer/;
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d4e9d41e8..5b3fd5e7d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -95,6 +95,24 @@ set(SQLITECPP_BUILD_EXAMPLES OFF CACHE BOOL "Build examples."
set(SQLITECPP_BUILD_TESTS OFF CACHE BOOL "Build and run tests." FORCE)
set(SQLITECPP_INTERNAL_SQLITE OFF CACHE BOOL "Add the internal SQLite3 source to the project." FORCE)
+# Set options for LibEvent, disable all their tests and benchmarks:
+set(EVENT__DISABLE_OPENSSL YES CACHE BOOL "Disable OpenSSL in LibEvent" FORCE)
+set(EVENT__DISABLE_BENCHMARK YES CACHE BOOL "Disable LibEvent benchmarks" FORCE)
+set(EVENT__DISABLE_TESTS YES CACHE BOOL "Disable LibEvent tests" FORCE)
+set(EVENT__DISABLE_REGRESS YES CACHE BOOL "Disable LibEvent regression tests" FORCE)
+set(EVENT__DISABLE_SAMPLES YES CACHE BOOL "Disable LibEvent samples" FORCE)
+
+# Check that the libraries are present:
+if (NOT EXISTS ${CMAKE_SOURCE_DIR}/lib/SQLiteCpp/CMakeLists.txt)
+ message(FATAL_ERROR "SQLiteCpp is missing in folder lib/SQLiteCpp. Have you initialized the submodules / downloaded the extra libraries?")
+endif()
+if (NOT EXISTS ${CMAKE_SOURCE_DIR}/lib/polarssl/CMakeLists.txt)
+ message(FATAL_ERROR "PolarSSL is missing in folder lib/polarssl. Have you initialized the submodules / downloaded the extra libraries?")
+endif()
+if (NOT EXISTS ${CMAKE_SOURCE_DIR}/lib/libevent/CMakeLists.txt)
+ message(FATAL_ERROR "LibEvent is missing in folder lib/libevent. Have you initialized and updated the submodules / downloaded the extra libraries?")
+endif()
+
# Include all the libraries:
add_subdirectory(lib/jsoncpp/)
add_subdirectory(lib/zlib/)
@@ -104,6 +122,7 @@ add_subdirectory(lib/sqlite/)
add_subdirectory(lib/SQLiteCpp/)
add_subdirectory(lib/expat/)
add_subdirectory(lib/luaexpat/)
+add_subdirectory(lib/libevent/)
# Add proper include directories so that SQLiteCpp can find SQLite3:
get_property(SQLITECPP_INCLUDES DIRECTORY "lib/SQLiteCpp/" PROPERTY INCLUDE_DIRECTORIES)
@@ -111,6 +130,9 @@ set(SQLITECPP_INCLUDES "${SQLITECPP_INCLUDES}" "${CMAKE_CURRENT_SOURCE_DIR}/lib/
set_property(DIRECTORY lib/SQLiteCpp/ PROPERTY INCLUDE_DIRECTORIES "${SQLITECPP_INCLUDES}")
set_property(TARGET SQLiteCpp PROPERTY INCLUDE_DIRECTORIES "${SQLITECPP_INCLUDES}")
+# Add proper includes for LibEvent's event-config.h header:
+include_directories(SYSTEM ${LIBEVENT_INCLUDE_DIRS})
+
if (WIN32)
add_subdirectory(lib/luaproxy/)
endif()
@@ -118,13 +140,14 @@ endif()
# We use EXCLUDE_FROM_ALL so that only the explicit dependencies are used
# (PolarSSL also has test and example programs in their CMakeLists.txt, we don't want those)
-include(lib/polarssl.cmake)
+include(lib/polarssl.cmake EXCLUDE_FROM_ALL)
set_exe_flags()
add_subdirectory (src)
if(${SELF_TEST})
+ message("Tests enabled")
enable_testing()
add_subdirectory (tests)
endif()
diff --git a/COMPILING.md b/COMPILING.md
index 731d64653..34b1e976f 100644
--- a/COMPILING.md
+++ b/COMPILING.md
@@ -69,9 +69,9 @@ After doing so, run the command `xcodebuild lib/polarssl/POLARSSL.xcodeproj` in
## Linux, FreeBSD etc. ##
-Install git, cmake and gcc or clang, using your platform's package manager:
+Install git, make, cmake and gcc or clang, using your platform's package manager:
```
-sudo apt-get install git cmake gcc g++
+sudo apt-get install git make cmake gcc g++
```
### Getting the sources ###
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua
index ba3763724..b2c7108e9 100644
--- a/MCServer/Plugins/APIDump/APIDesc.lua
+++ b/MCServer/Plugins/APIDump/APIDesc.lua
@@ -1840,7 +1840,9 @@ a_Player:OpenWindow(Window);
MoveToWorld = { Params = "WorldName", Return = "bool", Return = "Moves the player to the specified world. Returns true if successful." },
OpenWindow = { Params = "{{cWindow|Window}}", Return = "", Notes = "Opens the specified UI window for the player." },
PermissionMatches = { Params = "Permission, Template", Return = "bool", Notes = "(STATIC) Returns true if the specified permission matches the specified template. The template may contain wildcards." },
+ PlaceBlock = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta", Return = "bool", Notes = "Places a block while impersonating the player. The {{OnPlayerPlacingBlock|HOOK_PLAYER_PLACING_BLOCK}} hook is called before the placement, and if it succeeds, the block is placed and the {{OnPlayerPlacedBlock|HOOK_PLAYER_PLACED_BLOCK}} hook is called. Returns true iff the block is successfully placed. Assumes that the block is in a currently loaded chunk." },
Respawn = { Params = "", Return = "", Notes = "Restores the health, extinguishes fire, makes visible and sends the Respawn packet." },
+ SendBlocksAround = { Params = "BlockX, BlockY, BlockZ, [Range]", Return = "", Notes = "Sends all the world's blocks in Range from the specified coords to the player, as a BlockChange packet. Range defaults to 1 (only one block sent)." },
SendMessage = { Params = "Message", Return = "", Notes = "Sends the specified message to the player." },
SendMessageFailure = { Params = "Message", Return = "", Notes = "Prepends Rose [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and sends message to player. For a command that failed to run because of insufficient permissions, etc." },
SendMessageFatal = { Params = "Message", Return = "", Notes = "Prepends Red [FATAL] / colours entire text (depending on ShouldUseChatPrefixes()) and sends message to player. For something serious, such as a plugin crash, etc." },
diff --git a/MCServer/Plugins/APIDump/Hooks/OnPlayerPlacedBlock.lua b/MCServer/Plugins/APIDump/Hooks/OnPlayerPlacedBlock.lua
index 54888a6db..6445a76b4 100644
--- a/MCServer/Plugins/APIDump/Hooks/OnPlayerPlacedBlock.lua
+++ b/MCServer/Plugins/APIDump/Hooks/OnPlayerPlacedBlock.lua
@@ -12,7 +12,11 @@ return
Use the {{cPlayer}}:GetWorld() function to get the world to which the block belongs.</p>
<p>
See also the {{OnPlayerPlacingBlock|HOOK_PLAYER_PLACING_BLOCK}} hook for a similar hook called
- before the placement.
+ before the placement.</p>
+ <p>
+ If the client action results in multiple blocks being placed (such as a bed or a door), each separate
+ block is reported through this hook. All the blocks are already present in the world before the first
+ instance of this hook is called.
]],
Params =
{
@@ -20,10 +24,6 @@ return
{ Name = "BlockX", Type = "number", Notes = "X-coord of the block" },
{ Name = "BlockY", Type = "number", Notes = "Y-coord of the block" },
{ Name = "BlockZ", Type = "number", Notes = "Z-coord of the block" },
- { Name = "BlockFace", Type = "number", Notes = "Face of the existing block upon which the player interacted. One of the BLOCK_FACE_ constants" },
- { Name = "CursorX", Type = "number", Notes = "X-coord of the cursor within the block face (0 .. 15)" },
- { Name = "CursorY", Type = "number", Notes = "Y-coord of the cursor within the block face (0 .. 15)" },
- { Name = "CursorZ", Type = "number", Notes = "Z-coord of the cursor within the block face (0 .. 15)" },
{ Name = "BlockType", Type = "BLOCKTYPE", Notes = "The block type of the block" },
{ Name = "BlockMeta", Type = "NIBBLETYPE", Notes = "The block meta of the block" },
},
diff --git a/MCServer/Plugins/APIDump/Hooks/OnPlayerPlacingBlock.lua b/MCServer/Plugins/APIDump/Hooks/OnPlayerPlacingBlock.lua
index 2a928390b..4241a09aa 100644
--- a/MCServer/Plugins/APIDump/Hooks/OnPlayerPlacingBlock.lua
+++ b/MCServer/Plugins/APIDump/Hooks/OnPlayerPlacingBlock.lua
@@ -15,7 +15,11 @@ return
Use the {{cPlayer}}:GetWorld() function to get the world to which the block belongs.</p>
<p>
See also the {{OnPlayerPlacedBlock|HOOK_PLAYER_PLACED_BLOCK}} hook for a similar hook called after
- the placement.
+ the placement.</p>
+ <p>
+ If the client action results in multiple blocks being placed (such as a bed or a door), each separate
+ block is reported through this hook and only if all of them succeed, all the blocks are placed. If
+ any one of the calls are refused by the plugin, all the blocks are refused and reverted on the client.
]],
Params =
{
@@ -23,10 +27,6 @@ return
{ Name = "BlockX", Type = "number", Notes = "X-coord of the block" },
{ Name = "BlockY", Type = "number", Notes = "Y-coord of the block" },
{ Name = "BlockZ", Type = "number", Notes = "Z-coord of the block" },
- { Name = "BlockFace", Type = "number", Notes = "Face of the existing block upon which the player is interacting. One of the BLOCK_FACE_ constants" },
- { Name = "CursorX", Type = "number", Notes = "X-coord of the cursor within the block face (0 .. 15)" },
- { Name = "CursorY", Type = "number", Notes = "Y-coord of the cursor within the block face (0 .. 15)" },
- { Name = "CursorZ", Type = "number", Notes = "Z-coord of the cursor within the block face (0 .. 15)" },
{ Name = "BlockType", Type = "BLOCKTYPE", Notes = "The block type of the block" },
{ Name = "BlockMeta", Type = "NIBBLETYPE", Notes = "The block meta of the block" },
},
diff --git a/MCServer/Plugins/APIDump/Hooks/OnServerPing.lua b/MCServer/Plugins/APIDump/Hooks/OnServerPing.lua
index 76b6d1517..430465786 100644
--- a/MCServer/Plugins/APIDump/Hooks/OnServerPing.lua
+++ b/MCServer/Plugins/APIDump/Hooks/OnServerPing.lua
@@ -7,7 +7,10 @@ return
Desc = [[
A plugin may implement an OnServerPing() function and register it as a Hook to process pings from
clients in the server server list. It can change the logged in players and player capacity, as well
- as the server description and the favicon, that are displayed to the client in the server list.
+ as the server description and the favicon, that are displayed to the client in the server list.</p>
+ <p>
+ The client handle already has its protocol version assigned to it, so the plugin can check that; however,
+ there's no username associated with the client yet, and no player object.
]],
Params = {
{ Name = "ClientHandle", Type = "{{cClientHandle}}", Notes = "The client handle that pinged the server" },
diff --git a/MCServer/monsters.ini b/MCServer/monsters.ini
index c4bc8c810..af1938e3e 100644
--- a/MCServer/monsters.ini
+++ b/MCServer/monsters.ini
@@ -1,44 +1,52 @@
-[Spider]
+[Bat]
AttackRange=2.0
AttackRate=1
-AttackDamage=2.0
+AttackDamage=0.0
SightDistance=25.0
-MaxHealth=16
+MaxHealth=6
-[Chicken]
+[Blaze]
+AttackRange=15.0
+AttackRate=1
+AttackDamage=6.0
+SightDistance=25.0
+MaxHealth=20
+IsFireproof=1
+
+[CaveSpider]
AttackRange=2.0
AttackRate=1
-AttackDamage=1.0
+AttackDamage=2.0
SightDistance=25.0
-MaxHealth=4
+MaxHealth=12
-[Cow]
+[Chicken]
AttackRange=2.0
AttackRate=1
-AttackDamage=1.0
+AttackDamage=0.0
SightDistance=25.0
-MaxHealth=10
+MaxHealth=4
-[Pig]
+[Cow]
AttackRange=2.0
AttackRate=1
-AttackDamage=1.0
+AttackDamage=0.0
SightDistance=25.0
MaxHealth=10
-[Sheep]
-AttackRange=2.0
+[Creeper]
+AttackRange=3.0
AttackRate=1
-AttackDamage=1.0
+AttackDamage=0.0
SightDistance=25.0
-MaxHealth=8
+MaxHealth=20
-[Squid]
+[EnderDragon]
AttackRange=2.0
AttackRate=1
-AttackDamage=1.0
+AttackDamage=6.0
SightDistance=25.0
-MaxHealth=10
+MaxHealth=200
[Enderman]
AttackRange=2.0
@@ -47,27 +55,6 @@ AttackDamage=4.0
SightDistance=64.0
MaxHealth=40
-[ZombiePigman]
-AttackRange=2.0
-AttackRate=1
-AttackDamage=7.0
-SightDistance=25.0
-MaxHealth=20
-IsFireproof=1
-
-[CaveSpider]
-AttackRange=2.0
-AttackRate=1
-AttackDamage=2.0
-SightDistance=25.0
-MaxHealth=12
-
-[Creeper]
-AttackRange=3.0
-AttackRate=1
-AttackDamage=0.0
-SightDistance=25.0
-MaxHealth=20
[Ghast]
AttackRange=50.0
@@ -77,118 +64,150 @@ SightDistance=50.0
MaxHealth=10
IsFireproof=1
-[Silverfish]
+[Giant]
AttackRange=2.0
AttackRate=1
-AttackDamage=1.0
+AttackDamage=6.0
SightDistance=25.0
-MaxHealth=8
-
-[Skeleton]
-AttackRange=15.0
-AttackRate=1
-SightDistance=40.0
-MaxHealth=20
+MaxHealth=100
-[Slime]
+[Guardian]
AttackRange=2.0
-AttackRate=1
-AttackDamage=4.0
+AttackRate=1
+AttackDamage=9.0
SightDistance=25.0
-MaxHealth=16
+MaxHealth=30
-[Zombie]
+[Horse]
AttackRange=2.0
AttackRate=1
-AttackDamage=4.0
+AttackDamage=6.0
SightDistance=25.0
-MaxHealth=20
+MaxHealth=30
-[Wolf]
+[IronGolem]
AttackRange=2.0
AttackRate=1
-AttackDamage=4.0
+AttackDamage=6.0
SightDistance=25.0
-MaxHealth=20
+MaxHealth=100
-[Blaze]
-AttackRange=15.0
+[MagmaCube]
+AttackRange=2.0
AttackRate=1
AttackDamage=6.0
SightDistance=25.0
-MaxHealth=20
+MaxHealth=16
IsFireproof=1
-[Villager]
+[Mooshroom]
AttackRange=2.0
AttackRate=1
AttackDamage=0.0
SightDistance=25.0
-MaxHealth=20
-IsFireproof=0
+MaxHealth=10
-[Witch]
+[Ocelot]
AttackRange=2.0
AttackRate=1
AttackDamage=0.0
SightDistance=25.0
-MaxHealth=26
-
+MaxHealth=10
-[Ocelot]
+[Pig]
AttackRange=2.0
AttackRate=1
AttackDamage=0.0
SightDistance=25.0
MaxHealth=10
-[Mooshroom]
+[Rabbit]
AttackRange=2.0
AttackRate=1
AttackDamage=0.0
SightDistance=25.0
MaxHealth=10
-[MagmaCube]
+[Sheep]
AttackRange=2.0
AttackRate=1
-AttackDamage=6.0
+AttackDamage=0.0
SightDistance=25.0
-MaxHealth=16
-IsFireproof=1
+MaxHealth=8
-[Horse]
+[Silverfish]
AttackRange=2.0
AttackRate=1
-AttackDamage=6.0
+AttackDamage=1.0
SightDistance=25.0
-MaxHealth=30
+MaxHealth=8
-[EnderDragon]
+[Skeleton]
+AttackRange=15.0
+AttackRate=1
+SightDistance=40.0
+MaxHealth=20
+
+[Slime]
AttackRange=2.0
AttackRate=1
-AttackDamage=6.0
+AttackDamage=4.0
SightDistance=25.0
-MaxHealth=200
+MaxHealth=16
-[Giant]
+[SnowGolem]
AttackRange=2.0
AttackRate=1
-AttackDamage=6.0
+AttackDamage=0.0
SightDistance=25.0
-MaxHealth=100
+MaxHealth=4
-[IronGolem]
+[Spider]
+AttackRange=2.0
+AttackRate=1
+AttackDamage=2.0
+SightDistance=25.0
+MaxHealth=16
+
+[Squid]
AttackRange=2.0
AttackRate=1
-AttackDamage=6.0
+AttackDamage=0.0
SightDistance=25.0
-MaxHealth=100
+MaxHealth=10
-[Bat]
+[Villager]
AttackRange=2.0
-AttackRate=1
+AttackRate=1
AttackDamage=0.0
SightDistance=25.0
-MaxHealth=6
+MaxHealth=20
+[Witch]
+AttackRange=2.0
+AttackRate=1
+AttackDamage=0.0
+SightDistance=25.0
+MaxHealth=26
+
+[Wolf]
+AttackRange=2.0
+AttackRate=1
+AttackDamage=4.0
+SightDistance=25.0
+MaxHealth=20
+
+[Zombie]
+AttackRange=2.0
+AttackRate=1
+AttackDamage=4.0
+SightDistance=25.0
+MaxHealth=20
+
+[ZombiePigman]
+AttackRange=2.0
+AttackRate=1
+AttackDamage=7.0
+SightDistance=25.0
+MaxHealth=20
+IsFireproof=1
diff --git a/MCServer/webadmin/template.lua b/MCServer/webadmin/template.lua
index 4d3934da8..6ea7b69bc 100644
--- a/MCServer/webadmin/template.lua
+++ b/MCServer/webadmin/template.lua
@@ -58,6 +58,7 @@ function ShowPage(WebAdmin, TemplateRequest)
SiteContent = {}
local BaseURL = WebAdmin:GetBaseURL(TemplateRequest.Request.Path)
local Title = "MCServer WebAdmin"
+ local NumPlayers = cRoot:Get():GetServer():GetNumPlayers()
local MemoryUsageKiB = cRoot:GetPhysicalRAMUsage()
local NumChunks = cRoot:Get():GetTotalChunkCount()
local PluginPage = WebAdmin:GetPage(TemplateRequest.Request)
@@ -102,6 +103,7 @@ function ShowPage(WebAdmin, TemplateRequest)
<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>Chunks: <strong>]] .. NumChunks .. [[</strong></a></li>
</ul>
diff --git a/README.md b/README.md
index 88080a52f..87c4ee93e 100644
--- a/README.md
+++ b/README.md
@@ -9,8 +9,7 @@ We currently support Release 1.7 and 1.8 (not beta) Minecraft protocol versions.
Installation
------------
-
-[![Install on DigitalOcean](http://doinstall.bearbin.net/button.svg)](http://doinstall.bearbin.net/install?url=https://github.com/mc-server/MCServer)
+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.
For Linux there is an easy installation method, just run this in your terminal:
diff --git a/Tools/AnvilStats/Utils.cpp b/Tools/AnvilStats/Utils.cpp
index d7543cb4c..34374647b 100644
--- a/Tools/AnvilStats/Utils.cpp
+++ b/Tools/AnvilStats/Utils.cpp
@@ -26,11 +26,13 @@ struct
{entEnderman, "Enderman"},
{entGhast, "Ghast"},
{entGiant, "Giant"},
+ {entGuardian, "Guardian"},
{entLavaSlime, "LavaSlime"},
{entMushroomCow, "MushroomCow"},
{entOzelot, "Ozelot"},
{entPig, "Pig"},
{entPigZombie, "PigZombie"},
+ {entRabbit, "Rabbit"},
{entSheep, "Sheep"},
{entSilverfish, "Slverfish"},
{entSkeleton, "Skeleton"},
diff --git a/Tools/AnvilStats/Utils.h b/Tools/AnvilStats/Utils.h
index ab2d7166c..e3172649c 100644
--- a/Tools/AnvilStats/Utils.h
+++ b/Tools/AnvilStats/Utils.h
@@ -25,11 +25,13 @@ enum eEntityType
entEnderman,
entGhast,
entGiant,
+ entGuardian,
entLavaSlime,
entMushroomCow,
entOzelot,
entPig,
entPigZombie,
+ entRabbit,
entSheep,
entSilverfish,
entSkeleton,
diff --git a/Tools/MCADefrag/CMakeLists.txt b/Tools/MCADefrag/CMakeLists.txt
index 82e048671..700310edc 100644
--- a/Tools/MCADefrag/CMakeLists.txt
+++ b/Tools/MCADefrag/CMakeLists.txt
@@ -53,6 +53,7 @@ source_group("Shared" FILES ${SHARED_SRC} ${SHARED_HDR})
set(SHARED_OSS_SRC
../../src/OSSupport/CriticalSection.cpp
+ ../../src/OSSupport/Event.cpp
../../src/OSSupport/File.cpp
../../src/OSSupport/IsThread.cpp
../../src/OSSupport/StackTrace.cpp
@@ -60,6 +61,7 @@ set(SHARED_OSS_SRC
set(SHARED_OSS_HDR
../../src/OSSupport/CriticalSection.h
+ ../../src/OSSupport/Event.h
../../src/OSSupport/File.h
../../src/OSSupport/IsThread.h
../../src/OSSupport/StackTrace.h
diff --git a/Tools/MCADefrag/Globals.h b/Tools/MCADefrag/Globals.h
index 288069599..f13a06566 100644
--- a/Tools/MCADefrag/Globals.h
+++ b/Tools/MCADefrag/Globals.h
@@ -201,6 +201,7 @@ typedef unsigned char Byte;
// Common headers (without macros):
#include "StringUtils.h"
#include "OSSupport/CriticalSection.h"
+#include "OSSupport/Event.h"
#include "OSSupport/IsThread.h"
#include "OSSupport/File.h"
diff --git a/Tools/MCADefrag/MCADefrag.cpp b/Tools/MCADefrag/MCADefrag.cpp
index d5d233fd2..0d38a87f1 100644
--- a/Tools/MCADefrag/MCADefrag.cpp
+++ b/Tools/MCADefrag/MCADefrag.cpp
@@ -269,7 +269,7 @@ bool cMCADefrag::cThread::ReadChunk(cFile & a_File, const Byte * a_LocationRaw)
return false;
}
m_CompressedChunkDataSize = (Buf[0] << 24) | (Buf[1] << 16) | (Buf[2] << 8) | Buf[3];
- if (m_CompressedChunkDataSize > SizeInSectors)
+ if ((m_CompressedChunkDataSize > SizeInSectors) || (m_CompressedChunkDataSize < 0))
{
LOGWARNING("Invalid chunk data - SizeInSectors (%d) smaller that RealSize (%d)", SizeInSectors, m_CompressedChunkDataSize);
return false;
diff --git a/Tools/ProtoProxy/CMakeLists.txt b/Tools/ProtoProxy/CMakeLists.txt
index cca0c1b8b..132a14f78 100644
--- a/Tools/ProtoProxy/CMakeLists.txt
+++ b/Tools/ProtoProxy/CMakeLists.txt
@@ -55,12 +55,14 @@ set(SHARED_HDR
)
set(SHARED_OSS_SRC
../../src/OSSupport/CriticalSection.cpp
+ ../../src/OSSupport/Event.cpp
../../src/OSSupport/File.cpp
../../src/OSSupport/IsThread.cpp
../../src/OSSupport/StackTrace.cpp
)
set(SHARED_OSS_HDR
../../src/OSSupport/CriticalSection.h
+ ../../src/OSSupport/Event.h
../../src/OSSupport/File.h
../../src/OSSupport/IsThread.h
../../src/OSSupport/StackTrace.h
diff --git a/Tools/ProtoProxy/Connection.cpp b/Tools/ProtoProxy/Connection.cpp
index fb2d40e5b..6d347e07d 100644
--- a/Tools/ProtoProxy/Connection.cpp
+++ b/Tools/ProtoProxy/Connection.cpp
@@ -100,13 +100,11 @@
CLIENTENCRYPTSEND(ToClient.data(), ToClient.size()); \
break; \
} \
- /* case csWaitingForEncryption: \
+ case csWaitingForEncryption: \
{ \
- Log("Waiting for client encryption, queued %u bytes", ToClient.size()); \
- m_ClientEncryptionBuffer.append(ToClient.data(), ToClient.size()); \
break; \
} \
- */ \
+ \
} \
DebugSleep(50); \
}
@@ -141,8 +139,14 @@ typedef unsigned char Byte;
+// fwd declarations, to avoid clang warnings:
+AString PrintableAbsIntTriplet(int a_X, int a_Y, int a_Z, double a_Divisor = 32);
+
+
+
-AString PrintableAbsIntTriplet(int a_X, int a_Y, int a_Z, double a_Divisor = 32)
+
+AString PrintableAbsIntTriplet(int a_X, int a_Y, int a_Z, double a_Divisor)
{
return Printf("<%d, %d, %d> ~ {%.02f, %.02f, %.02f}",
a_X, a_Y, a_Z,
@@ -298,7 +302,7 @@ void cConnection::Log(const char * a_Format, ...)
-void cConnection::DataLog(const void * a_Data, int a_Size, const char * a_Format, ...)
+void cConnection::DataLog(const void * a_Data, size_t a_Size, const char * a_Format, ...)
{
va_list args;
va_start(args, a_Format);
@@ -359,14 +363,14 @@ bool cConnection::ConnectToServer(void)
bool cConnection::RelayFromServer(void)
{
char Buffer[64 KiB];
- int res = recv(m_ServerSocket, Buffer, sizeof(Buffer), 0);
+ int res = static_cast<int>(recv(m_ServerSocket, Buffer, sizeof(Buffer), 0)); // recv returns int on windows, ssize_t on linux
if (res <= 0)
{
Log("Server closed the socket: %d; %d; aborting connection", res, SocketError);
return false;
}
- DataLog(Buffer, res, "Received %d bytes from the SERVER", res);
+ DataLog(Buffer, static_cast<size_t>(res), "Received %d bytes from the SERVER", res);
switch (m_ServerState)
{
@@ -377,15 +381,15 @@ bool cConnection::RelayFromServer(void)
}
case csEncryptedUnderstood:
{
- m_ServerDecryptor.ProcessData((Byte *)Buffer, (Byte *)Buffer, res);
- DataLog(Buffer, res, "Decrypted %d bytes from the SERVER", res);
+ m_ServerDecryptor.ProcessData(reinterpret_cast<Byte *>(Buffer), reinterpret_cast<Byte *>(Buffer), static_cast<size_t>(res));
+ DataLog(Buffer, static_cast<size_t>(res), "Decrypted %d bytes from the SERVER", res);
return DecodeServersPackets(Buffer, res);
}
case csEncryptedUnknown:
{
- m_ServerDecryptor.ProcessData((Byte *)Buffer, (Byte *)Buffer, res);
- DataLog(Buffer, res, "Decrypted %d bytes from the SERVER", res);
- return CLIENTSEND(Buffer, res);
+ m_ServerDecryptor.ProcessData(reinterpret_cast<Byte *>(Buffer), reinterpret_cast<Byte *>(Buffer), static_cast<size_t>(res));
+ DataLog(Buffer, static_cast<size_t>(res), "Decrypted %d bytes from the SERVER", res);
+ return CLIENTSEND(Buffer, static_cast<size_t>(res));
}
}
ASSERT(!"Unhandled server state while relaying from server");
@@ -399,14 +403,14 @@ bool cConnection::RelayFromServer(void)
bool cConnection::RelayFromClient(void)
{
char Buffer[64 KiB];
- int res = recv(m_ClientSocket, Buffer, sizeof(Buffer), 0);
+ int res = static_cast<int>(recv(m_ClientSocket, Buffer, sizeof(Buffer), 0)); // recv returns int on Windows, ssize_t on Linux
if (res <= 0)
{
Log("Client closed the socket: %d; %d; aborting connection", res, SocketError);
return false;
}
- DataLog(Buffer, res, "Received %d bytes from the CLIENT", res);
+ DataLog(Buffer, static_cast<size_t>(res), "Received %d bytes from the CLIENT", res);
switch (m_ClientState)
{
@@ -421,9 +425,9 @@ bool cConnection::RelayFromClient(void)
}
case csEncryptedUnknown:
{
- DataLog(Buffer, res, "Decrypted %d bytes from the CLIENT", res);
- m_ServerEncryptor.ProcessData((Byte *)Buffer, (Byte *)Buffer, res);
- return SERVERSEND(Buffer, res);
+ DataLog(Buffer, static_cast<size_t>(res), "Decrypted %d bytes from the CLIENT", res);
+ m_ServerEncryptor.ProcessData(reinterpret_cast<Byte *>(Buffer), reinterpret_cast<Byte *>(Buffer), static_cast<size_t>(res));
+ return SERVERSEND(Buffer, static_cast<size_t>(res));
}
}
ASSERT(!"Unhandled server state while relaying from client");
@@ -446,9 +450,9 @@ double cConnection::GetRelativeTime(void)
bool cConnection::SendData(SOCKET a_Socket, const char * a_Data, size_t a_Size, const char * a_Peer)
{
- DataLog(a_Data, a_Size, "Sending data to %s, %u bytes", a_Peer, (unsigned)a_Size);
+ DataLog(a_Data, a_Size, "Sending data to %s, %u bytes", a_Peer, static_cast<unsigned>(a_Size));
- int res = send(a_Socket, a_Data, (int)a_Size, 0);
+ int res = static_cast<int>(send(a_Socket, a_Data, a_Size, 0)); // Windows uses int for a_Size, Linux uses size_t; but Windows doesn't complain. Return type is int on Windows and ssize_t on Linux
if (res <= 0)
{
Log("%s closed the socket: %d, %d; aborting connection", a_Peer, res, SocketError);
@@ -511,7 +515,7 @@ bool cConnection::SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Enc
bool cConnection::DecodeClientsPackets(const char * a_Data, int a_Size)
{
- if (!m_ClientBuffer.Write(a_Data, a_Size))
+ if (!m_ClientBuffer.Write(a_Data, static_cast<size_t>(a_Size)))
{
Log("Too much queued data for the server, aborting connection");
return false;
@@ -529,10 +533,12 @@ bool cConnection::DecodeClientsPackets(const char * a_Data, int a_Size)
break;
}
UInt32 PacketType, PacketReadSoFar;
- PacketReadSoFar = m_ClientBuffer.GetReadableSpace();
+ PacketReadSoFar = static_cast<UInt32>(m_ClientBuffer.GetReadableSpace());
VERIFY(m_ClientBuffer.ReadVarInt(PacketType));
PacketReadSoFar -= m_ClientBuffer.GetReadableSpace();
- Log("Decoding client's packets, there are now %d bytes in the queue; next packet is 0x%0x, %u bytes long", m_ClientBuffer.GetReadableSpace(), PacketType, PacketLen);
+ Log("Decoding client's packets, there are now %u bytes in the queue; next packet is 0x%02x, %u bytes long",
+ static_cast<unsigned>(m_ClientBuffer.GetReadableSpace()), PacketType, PacketLen
+ );
switch (m_ClientProtocolState)
{
case -1:
@@ -619,7 +625,7 @@ bool cConnection::DecodeClientsPackets(const char * a_Data, int a_Size)
bool cConnection::DecodeServersPackets(const char * a_Data, int a_Size)
{
- if (!m_ServerBuffer.Write(a_Data, a_Size))
+ if (!m_ServerBuffer.Write(a_Data, static_cast<size_t>(a_Size)))
{
Log("Too much queued data for the client, aborting connection");
return false;
@@ -655,7 +661,7 @@ bool cConnection::DecodeServersPackets(const char * a_Data, int a_Size)
break;
}
UInt32 PacketType, PacketReadSoFar;
- PacketReadSoFar = m_ServerBuffer.GetReadableSpace();
+ PacketReadSoFar = static_cast<UInt32>(m_ServerBuffer.GetReadableSpace());
VERIFY(m_ServerBuffer.ReadVarInt(PacketType));
PacketReadSoFar -= m_ServerBuffer.GetReadableSpace();
Log("Decoding server's packets, there are now %d bytes in the queue; next packet is 0x%0x, %u bytes long", m_ServerBuffer.GetReadableSpace(), PacketType, PacketLen);
@@ -1111,7 +1117,7 @@ bool cConnection::HandleClientPlayerPositionLook(void)
bool cConnection::HandleClientPluginMessage(void)
{
HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, ChannelName);
- HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, Length);
+ HANDLE_CLIENT_PACKET_READ(ReadBEUInt16, UInt16, Length);
AString Data;
if (!m_ClientBuffer.ReadString(Data, Length))
{
@@ -1119,7 +1125,7 @@ bool cConnection::HandleClientPluginMessage(void)
}
Log("Received a PACKET_PLUGIN_MESSAGE from the client");
Log(" ChannelName = \"%s\"", ChannelName.c_str());
- DataLog(Data.data(), Length, " Data: %d bytes", Length);
+ DataLog(Data.data(), Length, " Data: %u bytes", Length);
COPY_TO_SERVER();
return true;
}
@@ -1288,13 +1294,13 @@ bool cConnection::HandleServerLoginEncryptionKeyRequest(void)
{
// Read the packet from the server:
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, ServerID);
- HANDLE_SERVER_PACKET_READ(ReadBEShort, short, PublicKeyLength);
+ HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, PublicKeyLength);
AString PublicKey;
if (!m_ServerBuffer.ReadString(PublicKey, PublicKeyLength))
{
return false;
}
- HANDLE_SERVER_PACKET_READ(ReadBEShort, short, NonceLength);
+ HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, NonceLength);
AString Nonce;
if (!m_ServerBuffer.ReadString(Nonce, NonceLength))
{
@@ -1392,6 +1398,9 @@ bool cConnection::HandleServerBlockChange(void)
HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, BlockType);
HANDLE_SERVER_PACKET_READ(ReadChar, char, BlockMeta);
Log("Received a PACKET_BLOCK_CHANGE from the server");
+ Log(" Pos = {%d, %d, %d}", BlockX, BlockY, BlockZ);
+ Log(" BlockType = %d (0x%x", BlockType, BlockType);
+ Log(" BlockMeta = %d", BlockMeta);
COPY_TO_CLIENT();
return true;
}
@@ -1461,12 +1470,12 @@ bool cConnection::HandleServerCompass(void)
bool cConnection::HandleServerDestroyEntities(void)
{
HANDLE_SERVER_PACKET_READ(ReadByte, Byte, NumEntities);
- if (!m_ServerBuffer.SkipRead((int)NumEntities * 4))
+ if (!m_ServerBuffer.SkipRead(static_cast<size_t>(NumEntities) * 4))
{
return false;
}
Log("Received PACKET_DESTROY_ENTITIES from the server:");
- Log(" NumEntities = %d", NumEntities);
+ Log(" NumEntities = %u", NumEntities);
COPY_TO_CLIENT();
return true;
}
@@ -1687,15 +1696,15 @@ bool cConnection::HandleServerEntityVelocity(void)
bool cConnection::HandleServerExplosion(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PosX);
- HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PosY);
- HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PosZ);
- HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Force);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, NumRecords);
+ HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PosX);
+ HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PosY);
+ HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PosZ);
+ HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Force);
+ HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, NumRecords);
std::vector<sCoords> Records;
Records.reserve(NumRecords);
int PosXI = (int)PosX, PosYI = (int)PosY, PosZI = (int)PosZ;
- for (int i = 0; i < NumRecords; i++)
+ for (UInt32 i = 0; i < NumRecords; i++)
{
HANDLE_SERVER_PACKET_READ(ReadChar, char, rx);
HANDLE_SERVER_PACKET_READ(ReadChar, char, ry);
@@ -1708,10 +1717,10 @@ bool cConnection::HandleServerExplosion(void)
Log("Received a PACKET_EXPLOSION from the server:");
Log(" Pos = {%.02f, %.02f, %.02f}", PosX, PosY, PosZ);
Log(" Force = %.02f", Force);
- Log(" NumRecords = %d", NumRecords);
- for (int i = 0; i < NumRecords; i++)
+ Log(" NumRecords = %u", NumRecords);
+ for (UInt32 i = 0; i < NumRecords; i++)
{
- Log(" Records[%d] = {%d, %d, %d}", i, Records[i].x, Records[i].y, Records[i].z);
+ Log(" Records[%u] = {%d, %d, %d}", i, Records[i].x, Records[i].y, Records[i].z);
}
Log(" Player motion = <%.02f, %.02f, %.02f>", PlayerMotionX, PlayerMotionY, PlayerMotionZ);
COPY_TO_CLIENT();
@@ -1822,8 +1831,8 @@ bool cConnection::HandleServerKick(void)
Reason.append(Split[5]);
AString ReasonBE16 = UTF8ToRawBEUTF16(Reason.data(), Reason.size());
AString PacketStart("\xff");
- PacketStart.push_back((ReasonBE16.size() / 2) / 256);
- PacketStart.push_back((ReasonBE16.size() / 2) % 256);
+ PacketStart.push_back(static_cast<char>((ReasonBE16.size() / 2) / 256));
+ PacketStart.push_back(static_cast<char>((ReasonBE16.size() / 2) % 256));
CLIENTSEND(PacketStart.data(), PacketStart.size());
CLIENTSEND(ReasonBE16.data(), ReasonBE16.size());
return true;
@@ -1847,12 +1856,12 @@ bool cConnection::HandleServerKick(void)
bool cConnection::HandleServerMapChunk(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkX);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkZ);
- HANDLE_SERVER_PACKET_READ(ReadChar, char, IsContiguous);
- HANDLE_SERVER_PACKET_READ(ReadBEShort, short, PrimaryBitmap);
- HANDLE_SERVER_PACKET_READ(ReadBEShort, short, AdditionalBitmap);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, CompressedSize);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkX);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkZ);
+ HANDLE_SERVER_PACKET_READ(ReadChar, char, IsContiguous);
+ HANDLE_SERVER_PACKET_READ(ReadBEShort, short, PrimaryBitmap);
+ HANDLE_SERVER_PACKET_READ(ReadBEShort, short, AdditionalBitmap);
+ HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, CompressedSize);
AString CompressedData;
if (!m_ServerBuffer.ReadString(CompressedData, CompressedSize))
{
@@ -1860,7 +1869,7 @@ bool cConnection::HandleServerMapChunk(void)
}
Log("Received a PACKET_MAP_CHUNK from the server:");
Log(" ChunkPos = [%d, %d]", ChunkX, ChunkZ);
- Log(" Compressed size = %d (0x%x)", CompressedSize, CompressedSize);
+ Log(" Compressed size = %u (0x%x)", CompressedSize, CompressedSize);
// TODO: Save the compressed data into a file for later analysis
@@ -1874,9 +1883,9 @@ bool cConnection::HandleServerMapChunk(void)
bool cConnection::HandleServerMapChunkBulk(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEShort, short, ChunkCount);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, CompressedSize);
- HANDLE_SERVER_PACKET_READ(ReadBool, bool, IsSkyLightSent);
+ HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, ChunkCount);
+ HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, CompressedSize);
+ HANDLE_SERVER_PACKET_READ(ReadBool, bool, IsSkyLightSent);
AString CompressedData;
if (!m_ServerBuffer.ReadString(CompressedData, CompressedSize))
{
@@ -1898,8 +1907,8 @@ bool cConnection::HandleServerMapChunkBulk(void)
}
Log("Received a PACKET_MAP_CHUNK_BULK from the server:");
- Log(" ChunkCount = %d", ChunkCount);
- Log(" Compressed size = %d (0x%x)", CompressedSize, CompressedSize);
+ Log(" ChunkCount = %u", ChunkCount);
+ Log(" Compressed size = %u (0x%x)", CompressedSize, CompressedSize);
Log(" IsSkyLightSent = %s", IsSkyLightSent ? "true" : "false");
// Log individual chunk coords:
@@ -1923,10 +1932,10 @@ bool cConnection::HandleServerMapChunkBulk(void)
bool cConnection::HandleServerMultiBlockChange(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkX);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkZ);
- HANDLE_SERVER_PACKET_READ(ReadBEShort, short, NumBlocks);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, DataSize);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkX);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkZ);
+ HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, NumBlocks);
+ HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, DataSize);
AString BlockChangeData;
if (!m_ServerBuffer.ReadString(BlockChangeData, DataSize))
{
@@ -1934,7 +1943,7 @@ bool cConnection::HandleServerMultiBlockChange(void)
}
Log("Received a PACKET_MULTI_BLOCK_CHANGE packet from the server:");
Log(" Chunk = [%d, %d]", ChunkX, ChunkZ);
- Log(" NumBlocks = %d", NumBlocks);
+ Log(" NumBlocks = %u", NumBlocks);
COPY_TO_CLIENT();
return true;
}
@@ -2035,7 +2044,7 @@ bool cConnection::HandleServerPlayerPositionLook(void)
bool cConnection::HandleServerPluginMessage(void)
{
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, ChannelName);
- HANDLE_SERVER_PACKET_READ(ReadBEShort, short, Length);
+ HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, Length);
AString Data;
if (!m_ServerBuffer.ReadString(Data, Length))
{
@@ -2043,7 +2052,7 @@ bool cConnection::HandleServerPluginMessage(void)
}
Log("Received a PACKET_PLUGIN_MESSAGE from the server");
Log(" ChannelName = \"%s\"", ChannelName.c_str());
- DataLog(Data.data(), Length, " Data: %d bytes", Length);
+ DataLog(Data.data(), Length, " Data: %u bytes", Length);
COPY_TO_CLIENT();
return true;
}
@@ -2530,11 +2539,11 @@ bool cConnection::HandleServerUpdateSign(void)
bool cConnection::HandleServerUpdateTileEntity(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX);
- HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockY);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ);
- HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Action);
- HANDLE_SERVER_PACKET_READ(ReadBEShort, short, DataLength);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX);
+ HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockY);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ);
+ HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Action);
+ HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, DataLength);
AString Data;
if ((DataLength > 0) && !m_ServerBuffer.ReadString(Data, DataLength))
@@ -2548,7 +2557,7 @@ bool cConnection::HandleServerUpdateTileEntity(void)
// Save metadata to a file:
AString fnam;
- Printf(fnam, "%s_item_%08x.nbt", m_LogNameBase.c_str(), m_ItemIdx++);
+ Printf(fnam, "%s_tile_%08x.nbt", m_LogNameBase.c_str(), m_ItemIdx++);
FILE * f = fopen(fnam.c_str(), "wb");
if (f != NULL)
{
@@ -2686,10 +2695,10 @@ bool cConnection::ParseSlot(cByteBuffer & a_Buffer, AString & a_ItemDesc)
}
char ItemCount;
short ItemDamage;
- short MetadataLength;
- a_Buffer.ReadChar(ItemCount);
+ UInt16 MetadataLength;
+ a_Buffer.ReadChar(ItemCount); // We already know we can read these bytes - we checked before.
a_Buffer.ReadBEShort(ItemDamage);
- a_Buffer.ReadBEShort(MetadataLength);
+ a_Buffer.ReadBEUInt16(MetadataLength);
Printf(a_ItemDesc, "%d:%d * %d", ItemType, ItemDamage, ItemCount);
if (MetadataLength <= 0)
{
@@ -2697,13 +2706,13 @@ bool cConnection::ParseSlot(cByteBuffer & a_Buffer, AString & a_ItemDesc)
}
AString Metadata;
Metadata.resize(MetadataLength);
- if (!a_Buffer.ReadBuf((void *)Metadata.data(), MetadataLength))
+ if (!a_Buffer.ReadBuf(const_cast<char *>(Metadata.data()), MetadataLength))
{
return false;
}
AString MetaHex;
CreateHexDump(MetaHex, Metadata.data(), Metadata.size(), 16);
- AppendPrintf(a_ItemDesc, "; %d bytes of meta:\n%s", MetadataLength, MetaHex.c_str());
+ AppendPrintf(a_ItemDesc, "; %u bytes of meta:\n%s", MetadataLength, MetaHex.c_str());
// Save metadata to a file:
AString fnam;
@@ -2725,17 +2734,19 @@ bool cConnection::ParseSlot(cByteBuffer & a_Buffer, AString & a_ItemDesc)
bool cConnection::ParseMetadata(cByteBuffer & a_Buffer, AString & a_Metadata)
{
- char x;
- if (!a_Buffer.ReadChar(x))
+ Byte x;
+ if (!a_Buffer.ReadByte(x))
{
return false;
}
- a_Metadata.push_back(x);
+ a_Metadata.push_back(static_cast<char>(x));
while (x != 0x7f)
{
- // int Index = ((unsigned)((unsigned char)x)) & 0x1f; // Lower 5 bits = index
- int Type = ((unsigned)((unsigned char)x)) >> 5; // Upper 3 bits = type
- int Length = 0;
+ // int Index = static_cast<unsigned>(x) & 0x1f; // Lower 5 bits = index
+ int Type = static_cast<unsigned>(x) >> 5; // Upper 3 bits = type
+
+ // Get the length of the data for this item:
+ UInt32 Length = 0;
switch (Type)
{
case 0: Length = 1; break; // Byte
@@ -2745,12 +2756,12 @@ bool cConnection::ParseMetadata(cByteBuffer & a_Buffer, AString & a_Metadata)
case 4: // UTF-8 string with VarInt length
{
UInt32 Len;
- int rs = a_Buffer.GetReadableSpace();
+ int rs = static_cast<int>(a_Buffer.GetReadableSpace());
if (!a_Buffer.ReadVarInt(Len))
{
return false;
}
- rs = rs - a_Buffer.GetReadableSpace();
+ rs = rs - static_cast<int>(a_Buffer.GetReadableSpace());
cByteBuffer LenBuf(8);
LenBuf.WriteVarInt(Len);
AString VarLen;
@@ -2759,18 +2770,18 @@ bool cConnection::ParseMetadata(cByteBuffer & a_Buffer, AString & a_Metadata)
Length = Len;
break;
}
- case 5:
+ case 5: // Item, in "slot" format
{
- int Before = a_Buffer.GetReadableSpace();
+ size_t Before = a_Buffer.GetReadableSpace();
AString ItemDesc;
if (!ParseSlot(a_Buffer, ItemDesc))
{
return false;
}
- int After = a_Buffer.GetReadableSpace();
+ size_t After = a_Buffer.GetReadableSpace();
a_Buffer.ResetRead();
a_Buffer.SkipRead(a_Buffer.GetReadableSpace() - Before);
- Length = Before - After;
+ Length = static_cast<UInt32>(Before - After);
break;
}
case 6: Length = 12; break; // 3 * int
@@ -2781,17 +2792,19 @@ bool cConnection::ParseMetadata(cByteBuffer & a_Buffer, AString & a_Metadata)
break;
}
} // switch (Type)
+
+ // Read the data in this item:
AString data;
if (!a_Buffer.ReadString(data, Length))
{
return false;
}
a_Metadata.append(data);
- if (!a_Buffer.ReadChar(x))
+ if (!a_Buffer.ReadByte(x))
{
return false;
}
- a_Metadata.push_back(x);
+ a_Metadata.push_back(static_cast<char>(x));
} // while (x != 0x7f)
return true;
}
@@ -2803,58 +2816,62 @@ bool cConnection::ParseMetadata(cByteBuffer & a_Buffer, AString & a_Metadata)
void cConnection::LogMetadata(const AString & a_Metadata, size_t a_IndentCount)
{
AString Indent(a_IndentCount, ' ');
- int pos = 0;
+ size_t pos = 0;
while (a_Metadata[pos] != 0x7f)
{
- int Index = ((unsigned)((unsigned char)a_Metadata[pos])) & 0x1f; // Lower 5 bits = index
- int Type = ((unsigned)((unsigned char)a_Metadata[pos])) >> 5; // Upper 3 bits = type
+ unsigned Index = static_cast<unsigned>(static_cast<unsigned char>(a_Metadata[pos])) & 0x1f; // Lower 5 bits = index
+ unsigned Type = static_cast<unsigned>(static_cast<unsigned char>(a_Metadata[pos])) >> 5; // Upper 3 bits = type
// int Length = 0;
switch (Type)
{
case 0:
{
- Log("%sbyte[%d] = %d", Indent.c_str(), Index, a_Metadata[pos + 1]);
+ Log("%sbyte[%u] = %d", Indent.c_str(), Index, a_Metadata[pos + 1]);
pos += 1;
break;
}
case 1:
{
- Log("%sshort[%d] = %d", Indent.c_str(), Index, (a_Metadata[pos + 1] << 8) | a_Metadata[pos + 2]);
+ Log("%sshort[%u] = %d", Indent.c_str(), Index, (a_Metadata[pos + 1] << 8) | a_Metadata[pos + 2]);
pos += 2;
break;
}
case 2:
{
- Log("%sint[%d] = %d", Indent.c_str(), Index, (a_Metadata[pos + 1] << 24) | (a_Metadata[pos + 2] << 16) | (a_Metadata[pos + 3] << 8) | a_Metadata[pos + 4]);
+ Log("%sint[%u] = %d", Indent.c_str(), Index, (a_Metadata[pos + 1] << 24) | (a_Metadata[pos + 2] << 16) | (a_Metadata[pos + 3] << 8) | a_Metadata[pos + 4]);
pos += 4;
break;
}
case 3:
{
- Log("%sfloat[%d] = 0x%x", Indent.c_str(), Index, (a_Metadata[pos + 1] << 24) | (a_Metadata[pos + 2] << 16) | (a_Metadata[pos + 3] << 8) | a_Metadata[pos + 4]);
+ Log("%sfloat[%u] = 0x%x", Indent.c_str(), Index, (a_Metadata[pos + 1] << 24) | (a_Metadata[pos + 2] << 16) | (a_Metadata[pos + 3] << 8) | a_Metadata[pos + 4]);
pos += 4;
break;
}
case 4: // UTF-8 string with VarInt length
{
cByteBuffer bb(10);
- int RestLen = (int)a_Metadata.size() - pos - 1;
+ size_t RestLen = a_Metadata.size() - pos - 1;
if (RestLen > 8)
{
RestLen = 8;
}
bb.Write(a_Metadata.data() + pos + 1, RestLen);
UInt32 Length;
- int rs = bb.GetReadableSpace();
- bb.ReadVarInt(Length);
+ size_t rs = bb.GetReadableSpace();
+ if (!bb.ReadVarInt(Length))
+ {
+ Log("Invalid metadata value, was supposed to be a varint-prefixed string, but cannot read the varint");
+ break;
+ }
rs = rs - bb.GetReadableSpace();
- Log("%sstring[%d] = \"%*s\"", Indent.c_str(), Index, Length, a_Metadata.c_str() + pos + rs + 1);
+ Log("%sstring[%u] = \"%*s\"", Indent.c_str(), Index, Length, a_Metadata.c_str() + pos + rs + 1);
pos += Length + rs + 2;
break;
}
case 5:
{
- int BytesLeft = a_Metadata.size() - pos - 1;
+ size_t BytesLeft = a_Metadata.size() - pos - 1;
cByteBuffer bb(BytesLeft);
bb.Write(a_Metadata.data() + pos + 1, BytesLeft);
AString ItemDesc;
@@ -2863,16 +2880,16 @@ void cConnection::LogMetadata(const AString & a_Metadata, size_t a_IndentCount)
ASSERT(!"Cannot parse item description from metadata");
return;
}
- // int After = bb.GetReadableSpace();
- int BytesConsumed = BytesLeft - bb.GetReadableSpace();
+ // size_t After = bb.GetReadableSpace();
+ size_t BytesConsumed = BytesLeft - bb.GetReadableSpace();
- Log("%sslot[%d] = %s (%d bytes)", Indent.c_str(), Index, ItemDesc.c_str(), BytesConsumed);
+ Log("%sslot[%u] = %s (%u bytes)", Indent.c_str(), Index, ItemDesc.c_str(), static_cast<unsigned>(BytesConsumed));
pos += BytesConsumed;
break;
}
case 6:
{
- Log("%spos[%d] = <%d, %d, %d>", Indent.c_str(), Index,
+ Log("%spos[%u] = <%d, %d, %d>", Indent.c_str(), Index,
(a_Metadata[pos + 1] << 24) | (a_Metadata[pos + 2] << 16) | (a_Metadata[pos + 3] << 8) | a_Metadata[pos + 4],
(a_Metadata[pos + 5] << 24) | (a_Metadata[pos + 6] << 16) | (a_Metadata[pos + 7] << 8) | a_Metadata[pos + 8],
(a_Metadata[pos + 9] << 24) | (a_Metadata[pos + 10] << 16) | (a_Metadata[pos + 11] << 8) | a_Metadata[pos + 12]
@@ -2931,7 +2948,7 @@ void cConnection::SendEncryptionKeyResponse(const AString & a_ServerPublicKey, c
DataLog(EncryptedSecret, sizeof(EncryptedSecret), "Encrypted secret (%u bytes)", (unsigned)sizeof(EncryptedSecret));
DataLog(EncryptedNonce, sizeof(EncryptedNonce), "Encrypted nonce (%u bytes)", (unsigned)sizeof(EncryptedNonce));
cByteBuffer Len(5);
- Len.WriteVarInt(ToServer.GetReadableSpace());
+ Len.WriteVarInt(static_cast<UInt32>(ToServer.GetReadableSpace()));
SERVERSEND(Len);
SERVERSEND(ToServer);
m_ServerState = csEncryptedUnderstood;
diff --git a/Tools/ProtoProxy/Connection.h b/Tools/ProtoProxy/Connection.h
index c79273f8a..8aacaaa7f 100644
--- a/Tools/ProtoProxy/Connection.h
+++ b/Tools/ProtoProxy/Connection.h
@@ -58,7 +58,7 @@ public:
void Run(void);
void Log(const char * a_Format, ...);
- void DataLog(const void * a_Data, int a_Size, const char * a_Format, ...);
+ void DataLog(const void * a_Data, size_t a_Size, const char * a_Format, ...);
void LogFlush(void);
protected:
diff --git a/Tools/ProtoProxy/Server.cpp b/Tools/ProtoProxy/Server.cpp
index bb042b259..9545af852 100644
--- a/Tools/ProtoProxy/Server.cpp
+++ b/Tools/ProtoProxy/Server.cpp
@@ -38,13 +38,41 @@ int cServer::Init(short a_ListenPort, short a_ConnectPort)
m_PublicKeyDER = m_PrivateKey.GetPubKeyDER();
m_ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (m_ListenSocket < 0)
+ {
+ #ifdef _WIN32
+ int err = WSAGetLastError();
+ #else
+ int err = errno;
+ #endif
+ printf("Failed to create listener socket: %d\n", err);
+ return err;
+ }
sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
- local.sin_addr.s_addr = 0; // All interfaces
+ local.sin_addr.s_addr = 130; // INADDR_ANY; // All interfaces
local.sin_port = htons(a_ListenPort);
- bind(m_ListenSocket, (sockaddr *)&local, sizeof(local));
- listen(m_ListenSocket, 1);
+ if (!bind(m_ListenSocket, (sockaddr *)&local, sizeof(local)))
+ {
+ #ifdef _WIN32
+ int err = WSAGetLastError();
+ #else
+ int err = errno;
+ #endif
+ printf("Failed to bind listener socket: %d\n", err);
+ return err;
+ }
+ if (listen(m_ListenSocket, 1) != 0)
+ {
+ #ifdef _WIN32
+ int err = WSAGetLastError();
+ #else
+ int err = errno;
+ #endif
+ printf("Failed to listen on socket: %d\n", err);
+ return err;
+ }
printf("Listening on port %d, connecting to localhost:%d\n", a_ListenPort, a_ConnectPort);
diff --git a/app.yml b/app.yml
index 44edf0852..51f9b0f58 100644
--- a/app.yml
+++ b/app.yml
@@ -3,9 +3,7 @@ image: ubuntu-14-04-x64
config:
#cloud-config
packages:
- - curl
- - screen
+ - git
runcmd:
- - mkdir /minecraft
- - cd /minecraft && curl -s https://raw.githubusercontent.com/mc-server/MCServer/master/easyinstall.sh | sh
- - cd /minecraft/MCServer && screen -S mcserver -d -m ./MCServer
+ - cd /tmp && git clone https://github.com/cuberite/mcserver-ocean.git
+ - cd /tmp/mcserver-ocean && ./initialinstall.sh
diff --git a/easyinstall.sh b/easyinstall.sh
index 77ffb2290..9b4007144 100755
--- a/easyinstall.sh
+++ b/easyinstall.sh
@@ -7,7 +7,7 @@ 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" ;;
# Assume that all arm devices are a raspi for now.
- "arm*") DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20armhf/lastSuccessfulBuild/artifact/MCServer.tar"
+ "arm*") DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20armhf/lastSuccessfulBuild/artifact/MCServer/MCServer.tar"
esac
echo "Downloading precompiled binaries."
diff --git a/lib/libevent b/lib/libevent
new file mode 160000
+Subproject 0b49ae34594533daa82c06a506078de9e336a01
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index 2f5d173fd..01d3ac687 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -839,6 +839,13 @@ void cLuaState::Push(void * a_Ptr)
m_NumCurrentFunctionArgs += 1;
}
+void cLuaState::Push(std::chrono::milliseconds a_Value)
+{
+ ASSERT(IsValid());
+
+ tolua_pushnumber(m_LuaState, static_cast<lua_Number>(a_Value.count()));
+ m_NumCurrentFunctionArgs += 1;
+}
diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h
index c13e36188..7ac4120e1 100644
--- a/src/Bindings/LuaState.h
+++ b/src/Bindings/LuaState.h
@@ -217,6 +217,7 @@ public:
void Push(Vector3d * a_Vector);
void Push(Vector3i * a_Vector);
void Push(void * a_Ptr);
+ void Push(std::chrono::milliseconds a_time);
/** 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);
diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h
index 08677553c..6210dbed4 100644
--- a/src/Bindings/Plugin.h
+++ b/src/Bindings/Plugin.h
@@ -76,8 +76,8 @@ public:
virtual bool OnPlayerJoined (cPlayer & a_Player) = 0;
virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) = 0;
virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0;
- virtual bool OnPlayerPlacedBlock (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) = 0;
- virtual bool OnPlayerPlacingBlock (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) = 0;
+ virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) = 0;
+ virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) = 0;
virtual bool 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) = 0;
virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) = 0;
virtual bool OnPlayerShooting (cPlayer & a_Player) = 0;
@@ -104,7 +104,7 @@ public:
virtual bool OnWeatherChanged (cWorld & a_World) = 0;
virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather) = 0;
virtual bool OnWorldStarted (cWorld & a_World) = 0;
- virtual bool OnWorldTick (cWorld & a_World, float a_Dt, int a_LastTickDurationMSec) = 0;
+ virtual bool OnWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) = 0;
/** Handles the command split into a_Split, issued by player a_Player.
Command permissions have already been checked.
diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp
index ea782ea3f..500913e76 100644
--- a/src/Bindings/PluginLua.cpp
+++ b/src/Bindings/PluginLua.cpp
@@ -857,14 +857,19 @@ bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPositi
-bool cPluginLua::OnPlayerPlacedBlock(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)
+bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange)
{
cCSLock Lock(m_CriticalSection);
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACED_BLOCK];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
{
- m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res);
+ m_LuaState.Call((int)(**itr), &a_Player,
+ a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(),
+ a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta,
+ cLuaState::Return,
+ res
+ );
if (res)
{
return true;
@@ -877,14 +882,19 @@ bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_Blo
-bool cPluginLua::OnPlayerPlacingBlock(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)
+bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange)
{
cCSLock Lock(m_CriticalSection);
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACING_BLOCK];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
{
- m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res);
+ m_LuaState.Call((int)(**itr), &a_Player,
+ a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(),
+ a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta,
+ cLuaState::Return,
+ res
+ );
if (res)
{
return true;
@@ -1420,7 +1430,7 @@ bool cPluginLua::OnWorldStarted(cWorld & a_World)
-bool cPluginLua::OnWorldTick(cWorld & a_World, float a_Dt, int a_LastTickDurationMSec)
+bool cPluginLua::OnWorldTick(cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec)
{
cCSLock Lock(m_CriticalSection);
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WORLD_TICK];
diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h
index 7de5ffec4..f443f5fc0 100644
--- a/src/Bindings/PluginLua.h
+++ b/src/Bindings/PluginLua.h
@@ -100,8 +100,8 @@ public:
virtual bool OnPlayerJoined (cPlayer & a_Player) override;
virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) override;
virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) override;
- virtual bool OnPlayerPlacedBlock (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) override;
- virtual bool OnPlayerPlacingBlock (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) override;
+ virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) override;
+ virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) override;
virtual bool 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) override;
virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) override;
virtual bool OnPlayerShooting (cPlayer & a_Player) override;
@@ -128,7 +128,7 @@ public:
virtual bool OnWeatherChanged (cWorld & a_World) override;
virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather) override;
virtual bool OnWorldStarted (cWorld & a_World) override;
- virtual bool OnWorldTick (cWorld & a_World, float a_Dt, int a_LastTickDurationMSec) override;
+ virtual bool OnWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) override;
virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player) override;
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index 406a540f4..9d86c64a2 100644
--- a/src/Bindings/PluginManager.cpp
+++ b/src/Bindings/PluginManager.cpp
@@ -771,7 +771,7 @@ bool cPluginManager::CallHookPlayerFoodLevelChange(cPlayer & a_Player, int a_New
-bool cPluginManager::CallHookPlayerFished(cPlayer & a_Player, const cItems a_Reward)
+bool cPluginManager::CallHookPlayerFished(cPlayer & a_Player, const cItems & a_Reward)
{
FIND_HOOK(HOOK_PLAYER_FISHED);
VERIFY_HOOK;
@@ -847,7 +847,7 @@ bool cPluginManager::CallHookPlayerLeftClick(cPlayer & a_Player, int a_BlockX, i
-bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition)
+bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition)
{
FIND_HOOK(HOOK_PLAYER_MOVING);
VERIFY_HOOK;
@@ -866,14 +866,14 @@ bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player, const Vector3d a_O
-bool cPluginManager::CallHookPlayerPlacedBlock(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)
+bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange)
{
FIND_HOOK(HOOK_PLAYER_PLACED_BLOCK);
VERIFY_HOOK;
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
{
- if ((*itr)->OnPlayerPlacedBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta))
+ if ((*itr)->OnPlayerPlacedBlock(a_Player, a_BlockChange))
{
return true;
}
@@ -885,14 +885,14 @@ bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX,
-bool cPluginManager::CallHookPlayerPlacingBlock(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)
+bool cPluginManager::CallHookPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange)
{
FIND_HOOK(HOOK_PLAYER_PLACING_BLOCK);
VERIFY_HOOK;
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
{
- if ((*itr)->OnPlayerPlacingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta))
+ if ((*itr)->OnPlayerPlacingBlock(a_Player, a_BlockChange))
{
return true;
}
@@ -1394,7 +1394,7 @@ bool cPluginManager::CallHookWorldStarted(cWorld & a_World)
-bool cPluginManager::CallHookWorldTick(cWorld & a_World, float a_Dt, int a_LastTickDurationMSec)
+bool cPluginManager::CallHookWorldTick(cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec)
{
FIND_HOOK(HOOK_WORLD_TICK);
VERIFY_HOOK;
diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h
index 3a2aecc92..97e91c1df 100644
--- a/src/Bindings/PluginManager.h
+++ b/src/Bindings/PluginManager.h
@@ -203,14 +203,14 @@ public:
bool CallHookPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
bool CallHookPlayerDestroyed (cPlayer & a_Player);
bool CallHookPlayerEating (cPlayer & a_Player);
- bool CallHookPlayerFished (cPlayer & a_Player, const cItems a_Reward);
+ bool CallHookPlayerFished (cPlayer & a_Player, const cItems & a_Reward);
bool CallHookPlayerFishing (cPlayer & a_Player, cItems a_Reward);
bool CallHookPlayerFoodLevelChange (cPlayer & a_Player, int a_NewFoodLevel);
bool CallHookPlayerJoined (cPlayer & a_Player);
- bool CallHookPlayerMoving (cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition);
bool CallHookPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status);
- bool CallHookPlayerPlacedBlock (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);
- bool CallHookPlayerPlacingBlock (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);
+ bool CallHookPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition);
+ bool CallHookPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange);
+ bool CallHookPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange);
bool CallHookPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
bool CallHookPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity);
bool CallHookPlayerShooting (cPlayer & a_Player);
@@ -237,7 +237,7 @@ public:
bool CallHookWeatherChanged (cWorld & a_World);
bool CallHookWeatherChanging (cWorld & a_World, eWeather & a_NewWeather);
bool CallHookWorldStarted (cWorld & a_World);
- bool CallHookWorldTick (cWorld & a_World, float a_Dt, int a_LastTickDurationMSec);
+ 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
diff --git a/src/BlockEntities/BeaconEntity.cpp b/src/BlockEntities/BeaconEntity.cpp
index 18e697b19..7038a98c7 100644
--- a/src/BlockEntities/BeaconEntity.cpp
+++ b/src/BlockEntities/BeaconEntity.cpp
@@ -267,7 +267,7 @@ void cBeaconEntity::GiveEffects(void)
-bool cBeaconEntity::Tick(float a_Dt, cChunk & a_Chunk)
+bool cBeaconEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
// Update the beacon every 4 seconds
if ((GetWorld()->GetWorldAge() % 80) == 0)
diff --git a/src/BlockEntities/BeaconEntity.h b/src/BlockEntities/BeaconEntity.h
index bc27e92b0..4f723c617 100644
--- a/src/BlockEntities/BeaconEntity.h
+++ b/src/BlockEntities/BeaconEntity.h
@@ -30,7 +30,7 @@ public:
// cBlockEntity overrides:
virtual void SendTo(cClientHandle & a_Client) override;
- virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void UsedBy(cPlayer * a_Player) override;
/** Modify the beacon level. (It is needed to load the beacon corectly) */
diff --git a/src/BlockEntities/BlockEntity.h b/src/BlockEntities/BlockEntity.h
index 056a88721..85f75a523 100644
--- a/src/BlockEntities/BlockEntity.h
+++ b/src/BlockEntities/BlockEntity.h
@@ -110,7 +110,7 @@ public:
virtual void SendTo(cClientHandle & a_Client) = 0;
/// Ticks the entity; returns true if the chunk should be marked as dirty as a result of this ticking. By default does nothing.
- virtual bool Tick(float a_Dt, cChunk & a_Chunk)
+ virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
UNUSED(a_Dt);
return false;
diff --git a/src/BlockEntities/CommandBlockEntity.cpp b/src/BlockEntities/CommandBlockEntity.cpp
index b7f938ffd..13c2637dc 100644
--- a/src/BlockEntities/CommandBlockEntity.cpp
+++ b/src/BlockEntities/CommandBlockEntity.cpp
@@ -125,7 +125,7 @@ void cCommandBlockEntity::SetRedstonePower(bool a_IsPowered)
-bool cCommandBlockEntity::Tick(float a_Dt, cChunk & a_Chunk)
+bool cCommandBlockEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
UNUSED(a_Dt);
UNUSED(a_Chunk);
diff --git a/src/BlockEntities/CommandBlockEntity.h b/src/BlockEntities/CommandBlockEntity.h
index d8ac054f0..c525ebc26 100644
--- a/src/BlockEntities/CommandBlockEntity.h
+++ b/src/BlockEntities/CommandBlockEntity.h
@@ -33,7 +33,7 @@ public:
/// Creates a new empty command block entity
cCommandBlockEntity(int a_X, int a_Y, int a_Z, cWorld * a_World);
- virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void SendTo(cClientHandle & a_Client) override;
virtual void UsedBy(cPlayer * a_Player) override;
diff --git a/src/BlockEntities/DropSpenserEntity.cpp b/src/BlockEntities/DropSpenserEntity.cpp
index 97a3aecb0..039f5d360 100644
--- a/src/BlockEntities/DropSpenserEntity.cpp
+++ b/src/BlockEntities/DropSpenserEntity.cpp
@@ -126,7 +126,7 @@ void cDropSpenserEntity::SetRedstonePower(bool a_IsPowered)
-bool cDropSpenserEntity::Tick(float a_Dt, cChunk & a_Chunk)
+bool cDropSpenserEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
UNUSED(a_Dt);
if (!m_ShouldDropSpense)
diff --git a/src/BlockEntities/DropSpenserEntity.h b/src/BlockEntities/DropSpenserEntity.h
index 6c23a402f..83500addb 100644
--- a/src/BlockEntities/DropSpenserEntity.h
+++ b/src/BlockEntities/DropSpenserEntity.h
@@ -47,7 +47,7 @@ public:
virtual ~cDropSpenserEntity();
// cBlockEntity overrides:
- virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void SendTo(cClientHandle & a_Client) override;
virtual void UsedBy(cPlayer * a_Player) override;
diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp
index c6d575eac..2621b560b 100644
--- a/src/BlockEntities/FurnaceEntity.cpp
+++ b/src/BlockEntities/FurnaceEntity.cpp
@@ -90,7 +90,7 @@ bool cFurnaceEntity::ContinueCooking(void)
-bool cFurnaceEntity::Tick(float a_Dt, cChunk & a_Chunk)
+bool cFurnaceEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
UNUSED(a_Dt);
diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h
index fbe9d6c75..8b3ba3e36 100644
--- a/src/BlockEntities/FurnaceEntity.h
+++ b/src/BlockEntities/FurnaceEntity.h
@@ -42,7 +42,7 @@ public:
// cBlockEntity overrides:
virtual void SendTo(cClientHandle & a_Client) override;
- virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void UsedBy(cPlayer * a_Player) override;
virtual void Destroy() override
{
diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp
index 7884639bd..bfd4b8322 100644
--- a/src/BlockEntities/HopperEntity.cpp
+++ b/src/BlockEntities/HopperEntity.cpp
@@ -55,7 +55,7 @@ bool cHopperEntity::GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, i
-bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk)
+bool cHopperEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
UNUSED(a_Dt);
Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge();
diff --git a/src/BlockEntities/HopperEntity.h b/src/BlockEntities/HopperEntity.h
index da65aa671..af99c526d 100644
--- a/src/BlockEntities/HopperEntity.h
+++ b/src/BlockEntities/HopperEntity.h
@@ -48,7 +48,7 @@ protected:
Int64 m_LastMoveItemsOutTick;
// cBlockEntity overrides:
- virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void SendTo(cClientHandle & a_Client) override;
virtual void UsedBy(cPlayer * a_Player) override;
diff --git a/src/BlockEntities/MobSpawnerEntity.cpp b/src/BlockEntities/MobSpawnerEntity.cpp
index a7d29638a..9b3f605f9 100644
--- a/src/BlockEntities/MobSpawnerEntity.cpp
+++ b/src/BlockEntities/MobSpawnerEntity.cpp
@@ -73,7 +73,7 @@ void cMobSpawnerEntity::UpdateActiveState(void)
-bool cMobSpawnerEntity::Tick(float a_Dt, cChunk & a_Chunk)
+bool cMobSpawnerEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
// Update the active flag every 5 seconds
if ((m_World->GetWorldAge() % 100) == 0)
diff --git a/src/BlockEntities/MobSpawnerEntity.h b/src/BlockEntities/MobSpawnerEntity.h
index 594b5301e..b572e6657 100644
--- a/src/BlockEntities/MobSpawnerEntity.h
+++ b/src/BlockEntities/MobSpawnerEntity.h
@@ -29,7 +29,7 @@ public:
virtual void SendTo(cClientHandle & a_Client) override;
virtual void UsedBy(cPlayer * a_Player) override;
- virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
// tolua_begin
diff --git a/src/BlockID.h b/src/BlockID.h
index 8f2cee02e..41ccf90b5 100644
--- a/src/BlockID.h
+++ b/src/BlockID.h
@@ -959,6 +959,7 @@ enum
E_META_SPAWN_EGG_WITHER = 64,
E_META_SPAWN_EGG_BAT = 65,
E_META_SPAWN_EGG_WITCH = 66,
+ E_META_SPAWN_EGG_GUARDIAN = 68,
E_META_SPAWN_EGG_PIG = 90,
E_META_SPAWN_EGG_SHEEP = 91,
E_META_SPAWN_EGG_COW = 92,
@@ -970,6 +971,7 @@ enum
E_META_SPAWN_EGG_OCELOT = 98,
E_META_SPAWN_EGG_IRON_GOLEM = 99,
E_META_SPAWN_EGG_HORSE = 100,
+ E_META_SPAWN_EGG_RABBIT = 101,
E_META_SPAWN_EGG_VILLAGER = 120,
E_META_SPAWN_EGG_ENDER_CRYSTAL = 200,
} ;
diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp
index 3b6328b38..57b9855d0 100644
--- a/src/Blocks/BlockBed.cpp
+++ b/src/Blocks/BlockBed.cpp
@@ -14,24 +14,6 @@
-void cBlockBedHandler::OnPlacedByPlayer(
- 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,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
-)
-{
- if (a_BlockMeta < 8)
- {
- Vector3i Direction = MetaDataToDirection(a_BlockMeta);
- a_ChunkInterface.SetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, a_BlockMeta | 0x8);
- }
-}
-
-
-
-
-
void cBlockBedHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
{
NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
@@ -151,7 +133,7 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface
cPlayerBedStateUnsetter Unsetter(Vector3i(a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z), a_WorldInterface);
a_WorldInterface.ForEachPlayer(Unsetter);
a_WorldInterface.SetTimeOfDay(0);
- a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0xB); // Where 0xB = 1011, and zero is to make sure 'occupied' bit is always unset
+ a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0x0b); // Clear the "occupied" bit of the bed's block
}
}
}
diff --git a/src/Blocks/BlockBed.h b/src/Blocks/BlockBed.h
index a8b5be899..5b746110a 100644
--- a/src/Blocks/BlockBed.h
+++ b/src/Blocks/BlockBed.h
@@ -23,7 +23,6 @@ public:
}
- virtual void OnPlacedByPlayer(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, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override;
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;
diff --git a/src/Blocks/BlockBigFlower.h b/src/Blocks/BlockBigFlower.h
index 3577bdd40..5240ddf53 100644
--- a/src/Blocks/BlockBigFlower.h
+++ b/src/Blocks/BlockBigFlower.h
@@ -85,18 +85,6 @@ public:
}
- virtual void OnPlacedByPlayer(
- 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,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
- ) override
- {
- int Meta = (((int)floor(a_Player->GetYaw() * 4.0 / 360.0 + 0.5) & 0x3) + 2) % 4;
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, 0x8 | Meta);
- }
-
-
virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override
{
NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
diff --git a/src/Blocks/BlockChest.h b/src/Blocks/BlockChest.h
index 201f2309b..01fec7f8b 100644
--- a/src/Blocks/BlockChest.h
+++ b/src/Blocks/BlockChest.h
@@ -62,50 +62,11 @@ public:
}
// Single chest, get meta from rotation only
- a_BlockMeta = RotationToMetaData(yaw);
+ a_BlockMeta = PlayerYawToMetaData(yaw);
return true;
}
- virtual void OnPlacedByPlayer(
- 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,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
- ) override
- {
- // Check if this forms a doublechest, if so, need to adjust the meta:
- cBlockArea Area;
- if (!Area.Read(&a_ChunkInterface, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1))
- {
- return;
- }
-
- double rot = a_Player->GetYaw(); // FIXME: Rename rot to yaw
- // Choose meta from player rotation, choose only between 2 or 3
- NIBBLETYPE NewMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3;
- if (
- CheckAndAdjustNeighbor(a_ChunkInterface, Area, 0, 1, NewMeta) ||
- CheckAndAdjustNeighbor(a_ChunkInterface, Area, 2, 1, NewMeta)
- )
- {
- // Forming a double chest in the X direction
- return;
- }
- // Choose meta from player rotation, choose only between 4 or 5
- NewMeta = (rot < 0) ? 4 : 5;
- if (
- CheckAndAdjustNeighbor(a_ChunkInterface, Area, 1, 0, NewMeta) ||
- CheckAndAdjustNeighbor(a_ChunkInterface, Area, 2, 2, NewMeta)
- )
- {
- // Forming a double chest in the Z direction
- return;
- }
-
- // Single chest, no further processing needed
- }
-
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
{
int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
@@ -180,30 +141,30 @@ public:
}
- /// Translates player rotation when placing a chest into the chest block metadata. Valid for single chests only
- static NIBBLETYPE RotationToMetaData(double a_Rotation)
+ /** Translates player yaw when placing a chest into the chest block metadata. Valid for single chests only */
+ static NIBBLETYPE PlayerYawToMetaData(double a_Yaw)
{
- a_Rotation += 90 + 45; // So its not aligned with axis
+ a_Yaw += 90 + 45; // So its not aligned with axis
- if (a_Rotation > 360.f)
+ if (a_Yaw > 360.f)
{
- a_Rotation -= 360.f;
+ a_Yaw -= 360.f;
}
- if ((a_Rotation >= 0.f) && (a_Rotation < 90.f))
+ if ((a_Yaw >= 0.f) && (a_Yaw < 90.f))
{
- return 0x4;
+ return 0x04;
}
- else if ((a_Rotation >= 180) && (a_Rotation < 270))
+ else if ((a_Yaw >= 180) && (a_Yaw < 270))
{
- return 0x5;
+ return 0x05;
}
- else if ((a_Rotation >= 90) && (a_Rotation < 180))
+ else if ((a_Yaw >= 90) && (a_Yaw < 180))
{
- return 0x2;
+ return 0x02;
}
else
{
- return 0x3;
+ return 0x03;
}
}
diff --git a/src/Blocks/BlockDoor.cpp b/src/Blocks/BlockDoor.cpp
index 90b7b15c2..d2bf180be 100644
--- a/src/Blocks/BlockDoor.cpp
+++ b/src/Blocks/BlockDoor.cpp
@@ -23,7 +23,7 @@ void cBlockDoorHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldIn
if (OldMeta & 8)
{
// Was upper part of door
- if (IsDoor(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)))
+ if (IsDoorBlockType(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)))
{
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
}
@@ -31,7 +31,7 @@ void cBlockDoorHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldIn
else
{
// Was lower part
- if (IsDoor(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)))
+ if (IsDoorBlockType(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)))
{
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_AIR, 0);
}
@@ -84,52 +84,34 @@ void cBlockDoorHandler::OnCancelRightClick(cChunkInterface & a_ChunkInterface, c
-void cBlockDoorHandler::OnPlacedByPlayer(
- 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,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
-)
-{
- NIBBLETYPE a_TopBlockMeta = 8;
- if (
- ((a_BlockMeta == 0) && (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1) == m_BlockType)) ||
- ((a_BlockMeta == 1) && (a_ChunkInterface.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ) == m_BlockType)) ||
- ((a_BlockMeta == 2) && (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1) == m_BlockType)) ||
- ((a_BlockMeta == 3) && (a_ChunkInterface.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ) == m_BlockType))
- )
- {
- a_TopBlockMeta = 9;
- }
- a_ChunkInterface.SetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, a_TopBlockMeta);
-}
-
-
-
-
-
NIBBLETYPE cBlockDoorHandler::MetaRotateCCW(NIBBLETYPE a_Meta)
{
if (a_Meta & 0x08)
{
+ // The meta doesn't change for the top block
return a_Meta;
}
else
{
+ // Rotate the bottom block
return super::MetaRotateCCW(a_Meta);
}
}
+
+
NIBBLETYPE cBlockDoorHandler::MetaRotateCW(NIBBLETYPE a_Meta)
{
if (a_Meta & 0x08)
{
+ // The meta doesn't change for the top block
return a_Meta;
}
else
{
+ // Rotate the bottom block
return super::MetaRotateCW(a_Meta);
}
}
@@ -138,8 +120,10 @@ NIBBLETYPE cBlockDoorHandler::MetaRotateCW(NIBBLETYPE a_Meta)
NIBBLETYPE cBlockDoorHandler::MetaMirrorXY(NIBBLETYPE a_Meta)
{
- // 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)
+ /*
+ Top bit (0x08) contains door block position (Top / Bottom). Only Bottom blocks 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
// in only the bottom tile while the hinge position is in the top tile. This function only operates on one tile at a time,
diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h
index 92ad8da12..53f84b553 100644
--- a/src/Blocks/BlockDoor.h
+++ b/src/Blocks/BlockDoor.h
@@ -101,14 +101,6 @@ public:
}
- virtual void OnPlacedByPlayer(
- 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,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
- ) override;
-
-
virtual bool IsUseable(void) override
{
return true;
@@ -117,11 +109,20 @@ 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) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR));
+ return ((a_RelY > 0) && CanBeOn(a_Chunk.GetBlock(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)
+ {
+ // Vanilla refuses to place doors on transparent blocks
+ // 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);
}
- bool CanReplaceBlock(BLOCKTYPE a_BlockType)
+ static bool CanReplaceBlock(BLOCKTYPE a_BlockType)
{
switch (a_BlockType)
{
@@ -170,8 +171,21 @@ public:
}
+ /** Returns a vector pointing one block in the direction the door is facing (where the outside is). */
+ inline static Vector3i GetRelativeDirectionToOutside(NIBBLETYPE a_BlockMeta)
+ {
+ switch (a_BlockMeta & 0x03)
+ {
+ case 0: return Vector3i(-1, 0, 0); // Facing West / XM
+ case 1: return Vector3i( 0, 0, -1); // Facing North / ZM
+ case 2: return Vector3i( 1, 0, 0); // Facing East / XP
+ default: return Vector3i( 0, 0, 1); // Facing South / ZP
+ }
+ }
+
+
/** Returns true if the specified blocktype is any kind of door */
- inline static bool IsDoor(BLOCKTYPE a_Block)
+ inline static bool IsDoorBlockType(BLOCKTYPE a_Block)
{
switch (a_Block)
{
@@ -193,6 +207,8 @@ public:
}
+ /** Returns true iff the door at the specified coords is open.
+ The coords may point to either the top part or the bottom part of the door. */
static NIBBLETYPE IsOpen(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
{
NIBBLETYPE Meta = GetCompleteDoorMeta(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ);
@@ -237,7 +253,7 @@ public:
static void SetOpen(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ, bool a_Open)
{
BLOCKTYPE Block = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ);
- if (!IsDoor(Block))
+ if (!IsDoorBlockType(Block))
{
return;
}
diff --git a/src/Blocks/BlockFire.h b/src/Blocks/BlockFire.h
index 07fcefe16..bafd385ab 100644
--- a/src/Blocks/BlockFire.h
+++ b/src/Blocks/BlockFire.h
@@ -12,7 +12,8 @@ class cBlockFireHandler :
{
public:
cBlockFireHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
+ : cBlockHandler(a_BlockType),
+ XZP(0), XZM(0), Dir(0)
{
}
diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp
index d532aa1dc..2de4a3e4c 100644
--- a/src/Blocks/BlockHandler.cpp
+++ b/src/Blocks/BlockHandler.cpp
@@ -369,7 +369,7 @@ void cBlockHandler::OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface
-void cBlockHandler::OnPlacedByPlayer(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, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+void cBlockHandler::OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, const sSetBlock & a_BlockChange)
{
}
diff --git a/src/Blocks/BlockHandler.h b/src/Blocks/BlockHandler.h
index f2298afb5..4dec0dc95 100644
--- a/src/Blocks/BlockHandler.h
+++ b/src/Blocks/BlockHandler.h
@@ -42,15 +42,12 @@ public:
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
);
- /// Called by cWorld::SetBlock() after the block has been set
+ /** Called by cWorld::SetBlock() after the block has been set */
virtual void OnPlaced(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
- /// Called by cClientHandle::HandlePlaceBlock() after the player has placed a new block. Called after OnPlaced().
+ /** Called by cPlayer::PlaceBlocks() for each block after it has been set to the world. Called after OnPlaced(). */
virtual void OnPlacedByPlayer(
- 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,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
+ cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, const sSetBlock & a_BlockChange
);
/// Called before the player has destroyed a block
@@ -96,7 +93,8 @@ public:
*/
// virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir);
- /// Called to check whether this block supports a rclk action. If it returns true, OnUse() is called
+ /** Called to check whether this block supports a rclk action.
+ If it returns true, OnUse() is called */
virtual bool IsUseable(void);
/** Indicates whether the client will click through this block.
@@ -109,20 +107,21 @@ public:
*/
virtual bool DoesIgnoreBuildCollision(void);
- /// <summary>Similar to DoesIgnoreBuildCollision(void), but is used for cases where block meta/player item-in-hand is needed to determine collision (thin snow)</summary>
+ /** Similar to DoesIgnoreBuildCollision(void), but is used for cases where block's meta or
+ player's item-in-hand is needed to determine collision (thin snow) */
virtual bool DoesIgnoreBuildCollision(cPlayer *, NIBBLETYPE a_Meta)
{
UNUSED(a_Meta);
return DoesIgnoreBuildCollision();
}
- /// <summary>Returns if this block drops if it gets destroyed by an unsuitable situation. Default: true</summary>
+ /** Returns if this block drops if it gets destroyed by an unsuitable situation.
+ Default: true */
virtual bool DoesDropOnUnsuitable(void);
/** Called when one of the neighbors gets set; equivalent to MC block update.
By default drops if position no more suitable (CanBeAt(), DoesDropOnUnsuitable(), Drop()),
- and wakes up all simulators on the block.
- */
+ and wakes up all simulators on the block. */
virtual void Check(cChunkInterface & ChunkInterface, cBlockPluginInterface & a_PluginInterface, int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk);
/// <summary>Rotates a given block meta counter-clockwise. Default: no change</summary>
diff --git a/src/Blocks/BlockMobHead.h b/src/Blocks/BlockMobHead.h
index e21e42334..cb8143749 100644
--- a/src/Blocks/BlockMobHead.h
+++ b/src/Blocks/BlockMobHead.h
@@ -12,16 +12,18 @@ class cBlockMobHeadHandler :
public cBlockEntityHandler
{
public:
- cBlockMobHeadHandler(BLOCKTYPE a_BlockType)
- : cBlockEntityHandler(a_BlockType)
+ cBlockMobHeadHandler(BLOCKTYPE a_BlockType):
+ cBlockEntityHandler(a_BlockType)
{
}
+
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
- // The drop spawn is in OnDestroyed method
+ // The drop spawn is in the OnDestroyedByPlayer method
}
+
virtual void OnDestroyedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override
{
if (a_Player->IsGameModeCreative())
@@ -61,202 +63,6 @@ public:
a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback);
}
-
- bool TrySpawnWither(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
- {
- if (a_BlockY < 2)
- {
- return false;
- }
-
- class cCallback : public cBlockEntityCallback
- {
- bool m_IsWither;
-
- virtual bool Item(cBlockEntity * a_BlockEntity)
- {
- if (a_BlockEntity->GetBlockType() != E_BLOCK_HEAD)
- {
- return false;
- }
- cMobHeadEntity * MobHeadEntity = static_cast<cMobHeadEntity*>(a_BlockEntity);
-
- m_IsWither = (MobHeadEntity->GetType() == SKULL_TYPE_WITHER);
- return false;
- }
-
- public:
- cCallback () : m_IsWither(false) {}
-
- bool IsWither(void) const { return m_IsWither; }
-
- void Reset(void) { m_IsWither = false; }
-
- } CallbackA, CallbackB;
-
- class cPlayerCallback : public cPlayerListCallback
- {
- Vector3f m_Pos;
-
- virtual bool Item(cPlayer * a_Player)
- {
- // TODO 2014-05-21 xdot: Vanilla minecraft uses an AABB check instead of a radius one
- double Dist = (a_Player->GetPosition() - m_Pos).Length();
- if (Dist < 50.0)
- {
- // If player is close, award achievement
- a_Player->AwardAchievement(achSpawnWither);
- }
- return false;
- }
-
- public:
- cPlayerCallback(const Vector3f & a_Pos) : m_Pos(a_Pos) {}
-
- } PlayerCallback(Vector3f((float)a_BlockX, (float)a_BlockY, (float)a_BlockZ));
-
- a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, CallbackA);
-
- if (!CallbackA.IsWither())
- {
- return false;
- }
-
- CallbackA.Reset();
-
- BLOCKTYPE BlockY1 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
- BLOCKTYPE BlockY2 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ);
-
- if ((BlockY1 != E_BLOCK_SOULSAND) || (BlockY2 != E_BLOCK_SOULSAND))
- {
- return false;
- }
-
- a_WorldInterface.DoWithBlockEntityAt(a_BlockX - 1, a_BlockY, a_BlockZ, CallbackA);
- a_WorldInterface.DoWithBlockEntityAt(a_BlockX + 1, a_BlockY, a_BlockZ, CallbackB);
-
- BLOCKTYPE Block1 = a_ChunkInterface.GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ);
- BLOCKTYPE Block2 = a_ChunkInterface.GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ);
-
- if ((Block1 == E_BLOCK_SOULSAND) && (Block2 == E_BLOCK_SOULSAND) && CallbackA.IsWither() && CallbackB.IsWither())
- {
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0);
-
- // Block entities
- a_ChunkInterface.SetBlock(a_BlockX + 1, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.SetBlock(a_BlockX - 1, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
-
- // Spawn the wither:
- a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtWither);
-
- // Award Achievement
- a_WorldInterface.ForEachPlayer(PlayerCallback);
-
- return true;
- }
-
- CallbackA.Reset();
- CallbackB.Reset();
-
- a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ - 1, CallbackA);
- a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ + 1, CallbackB);
-
- Block1 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1);
- Block2 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1);
-
- if ((Block1 == E_BLOCK_SOULSAND) && (Block2 == E_BLOCK_SOULSAND) && CallbackA.IsWither() && CallbackB.IsWither())
- {
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0);
-
- // Block entities
- a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ + 1, E_BLOCK_AIR, 0);
- a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ - 1, E_BLOCK_AIR, 0);
-
- // Spawn the wither:
- a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtWither);
-
- // Award Achievement
- a_WorldInterface.ForEachPlayer(PlayerCallback);
-
- return true;
- }
-
- return false;
- }
-
- virtual void OnPlacedByPlayer(
- 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,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
- ) override
- {
- class cCallback : public cBlockEntityCallback
- {
- cPlayer * m_Player;
- NIBBLETYPE m_OldBlockMeta;
- NIBBLETYPE m_NewBlockMeta;
-
- virtual bool Item(cBlockEntity * a_BlockEntity)
- {
- if (a_BlockEntity->GetBlockType() != E_BLOCK_HEAD)
- {
- return false;
- }
- cMobHeadEntity * MobHeadEntity = static_cast<cMobHeadEntity*>(a_BlockEntity);
-
- int Rotation = 0;
- if (m_NewBlockMeta == 1)
- {
- Rotation = (int) floor(m_Player->GetYaw() * 16.0F / 360.0F + 0.5) & 0xF;
- }
-
- MobHeadEntity->SetType(static_cast<eMobHeadType>(m_OldBlockMeta));
- MobHeadEntity->SetRotation(static_cast<eMobHeadRotation>(Rotation));
- MobHeadEntity->GetWorld()->BroadcastBlockEntity(MobHeadEntity->GetPosX(), MobHeadEntity->GetPosY(), MobHeadEntity->GetPosZ());
- return false;
- }
-
- public:
- cCallback (cPlayer * a_CBPlayer, NIBBLETYPE a_OldBlockMeta, NIBBLETYPE a_NewBlockMeta) :
- m_Player(a_CBPlayer),
- m_OldBlockMeta(a_OldBlockMeta),
- m_NewBlockMeta(a_NewBlockMeta)
- {}
- };
- cCallback Callback(a_Player, a_BlockMeta, static_cast<NIBBLETYPE>(a_BlockFace));
-
- a_BlockMeta = (NIBBLETYPE)a_BlockFace;
- a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback);
- a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta);
-
- if (a_BlockMeta == SKULL_TYPE_WITHER)
- {
- static const Vector3i Coords[] =
- {
- Vector3i( 0, 0, 0),
- Vector3i( 1, 0, 0),
- Vector3i(-1, 0, 0),
- Vector3i( 0, 0, 1),
- Vector3i( 0, 0, -1),
- };
- for (size_t i = 0; i < ARRAYCOUNT(Coords); ++i)
- {
- if (TrySpawnWither(a_ChunkInterface, a_WorldInterface, a_BlockX + Coords[i].x, a_BlockY, a_BlockZ + Coords[i].z))
- {
- break;
- }
- } // for i - Coords[]
- }
- }
} ;
diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h
index 275d1422a..af00fbe8e 100644
--- a/src/Blocks/BlockPumpkin.h
+++ b/src/Blocks/BlockPumpkin.h
@@ -17,70 +17,6 @@ public:
}
- virtual void OnPlacedByPlayer(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, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override
- {
- // Check whether the pumpkin is a part of a golem or a snowman
-
- if (a_BlockY < 2)
- {
- // The pumpkin is too low for a golem / snowman
- return;
- }
-
- BLOCKTYPE BlockY1 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
- BLOCKTYPE BlockY2 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ);
-
- // Check for a snow golem:
- if ((BlockY1 == E_BLOCK_SNOW_BLOCK) && (BlockY2 == E_BLOCK_SNOW_BLOCK))
- {
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0);
- a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtSnowGolem);
- return;
- }
-
- // Check for an iron golem. First check only the body and legs, since those are the same for both orientations:
- if ((BlockY1 != E_BLOCK_IRON_BLOCK) || (BlockY2 != E_BLOCK_IRON_BLOCK))
- {
- // One of the blocks is not an iron, no chance of a golem here
- return;
- }
-
- // Now check both orientations for hands:
- if (
- (a_ChunkInterface.GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) &&
- (a_ChunkInterface.GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK)
- )
- {
- // Remove the iron blocks:
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0);
-
- // Spawn the golem:
- a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtIronGolem);
- }
- else if (
- (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) &&
- (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK)
- )
- {
- // Remove the iron blocks:
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0);
-
- // Spawn the golem:
- a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtIronGolem);
- }
- }
-
-
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
diff --git a/src/Blocks/BlockSignPost.h b/src/Blocks/BlockSignPost.h
index d97501651..99c000633 100644
--- a/src/Blocks/BlockSignPost.h
+++ b/src/Blocks/BlockSignPost.h
@@ -53,17 +53,6 @@ public:
}
- virtual void OnPlacedByPlayer(
- 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,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
- ) override
- {
- a_Player->GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
- }
-
-
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
{
return (a_Meta + 4) & 0x0f;
diff --git a/src/Blocks/BlockWallSign.h b/src/Blocks/BlockWallSign.h
index 0abe9c52c..b6599d033 100644
--- a/src/Blocks/BlockWallSign.h
+++ b/src/Blocks/BlockWallSign.h
@@ -27,17 +27,6 @@ public:
}
- virtual void OnPlacedByPlayer(
- 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,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
- ) override
- {
- a_Player->GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
- }
-
-
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
{
int BlockX = (a_Chunk.GetPosX() * cChunkDef::Width) + a_RelX;
diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp
index 080176dcd..f3dc44d91 100644
--- a/src/ByteBuffer.cpp
+++ b/src/ByteBuffer.cpp
@@ -13,6 +13,15 @@
+/** When defined, each access to a cByteBuffer object is checked whether it's done in the same thread.
+cByteBuffer assumes that it is not used by multiple threads at once, this macro adds a runtime check for that.
+Unfortunately it is very slow, so it is disabled even for regular DEBUG builds. */
+// #define DEBUG_SINGLE_THREAD_ACCESS
+
+
+
+
+
// Try to determine endianness:
#if ( \
defined(__i386__) || defined(__alpha__) || \
@@ -109,7 +118,7 @@ public:
-#ifdef _DEBUG
+#ifdef DEBUG_SINGLE_THREAD_ACCESS
/** Simple RAII class that is used for checking that no two threads are using an object simultanously.
It requires the monitored object to provide the storage for a thread ID.
@@ -122,7 +131,7 @@ public:
{
ASSERT(
(*a_ThreadID == std::this_thread::get_id()) || // Either the object is used by current thread...
- (*a_ThreadID == std::thread::id()) // ... or by no thread at all
+ (*a_ThreadID == m_EmptyThreadID) // ... or by no thread at all
);
// Mark as being used by this thread:
@@ -138,8 +147,13 @@ public:
protected:
/** Points to the storage used for ID of the thread using the object. */
std::thread::id * m_ThreadID;
+
+ /** The value of an unassigned thread ID, used to speed up checking. */
+ static std::thread::id m_EmptyThreadID;
};
+ std::thread::id cSingleThreadAccessChecker::m_EmptyThreadID;
+
#define CHECK_THREAD cSingleThreadAccessChecker Checker(&m_ThreadID);
#else
@@ -334,8 +348,24 @@ bool cByteBuffer::ReadBEShort(short & a_Value)
CHECK_THREAD
CheckValid();
NEEDBYTES(2);
+ Int16 val;
+ ReadBuf(&val, 2);
+ val = ntohs(val);
+ a_Value = *(reinterpret_cast<short *>(&val));
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadBEUInt16(UInt16 & a_Value)
+{
+ CHECK_THREAD
+ CheckValid();
+ NEEDBYTES(2);
ReadBuf(&a_Value, 2);
- a_Value = (short)ntohs((u_short)a_Value);
+ a_Value = ntohs(a_Value);
return true;
}
@@ -357,6 +387,20 @@ bool cByteBuffer::ReadBEInt(int & a_Value)
+bool cByteBuffer::ReadBEUInt32(UInt32 & a_Value)
+{
+ CHECK_THREAD
+ CheckValid();
+ NEEDBYTES(4);
+ ReadBuf(&a_Value, 4);
+ a_Value = ntohl(a_Value);
+ return true;
+}
+
+
+
+
+
bool cByteBuffer::ReadBEInt64(Int64 & a_Value)
{
CHECK_THREAD
diff --git a/src/ByteBuffer.h b/src/ByteBuffer.h
index 2a316fa32..f480ad557 100644
--- a/src/ByteBuffer.h
+++ b/src/ByteBuffer.h
@@ -55,7 +55,9 @@ public:
bool ReadChar (char & a_Value);
bool ReadByte (unsigned char & a_Value);
bool ReadBEShort (short & a_Value);
+ bool ReadBEUInt16 (UInt16 & a_Value);
bool ReadBEInt (int & a_Value);
+ bool ReadBEUInt32 (UInt32 & a_Value);
bool ReadBEInt64 (Int64 & a_Value);
bool ReadBEFloat (float & a_Value);
bool ReadBEDouble (double & a_Value);
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 997326cc7..c39f5f6e6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -5,6 +5,7 @@ project (MCServer)
include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/")
include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/jsoncpp/include")
include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/polarssl/include")
+include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/libevent/include")
set(FOLDERS
OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++ Bindings
@@ -323,4 +324,4 @@ 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)
+target_link_libraries(${EXECUTABLE} luaexpat jsoncpp polarssl zlib sqlite lua SQLiteCpp event_core event_extra)
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index 39e97f5cf..979492b46 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -49,14 +49,14 @@
////////////////////////////////////////////////////////////////////////////////
// sSetBlock:
-sSetBlock::sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) // absolute block position
- : x( a_BlockX)
- , y( a_BlockY)
- , z( a_BlockZ)
- , BlockType( a_BlockType)
- , BlockMeta( a_BlockMeta)
+sSetBlock::sSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta):
+ m_RelX(a_BlockX),
+ m_RelY(a_BlockY),
+ m_RelZ(a_BlockZ),
+ m_BlockType(a_BlockType),
+ m_BlockMeta(a_BlockMeta)
{
- cChunkDef::AbsoluteToRelative(x, y, z, ChunkX, ChunkZ);
+ cChunkDef::AbsoluteToRelative(m_RelX, m_RelY, m_RelZ, m_ChunkX, m_ChunkZ);
}
@@ -73,6 +73,7 @@ cChunk::cChunk(
cAllocationPool<cChunkData::sChunkSection> & a_Pool
) :
m_Presence(cpInvalid),
+ m_ShouldGenerateIfLoadFailed(false),
m_IsLightValid(false),
m_IsDirty(false),
m_IsSaving(false),
@@ -93,6 +94,7 @@ cChunk::cChunk(
m_WaterSimulatorData(a_World->GetWaterSimulator()->CreateChunkData()),
m_LavaSimulatorData (a_World->GetLavaSimulator ()->CreateChunkData()),
m_RedstoneSimulatorData(a_World->GetRedstoneSimulator()->CreateChunkData()),
+ m_IsRedstoneDirty(false),
m_AlwaysTicked(0)
{
if (a_NeighborXM != nullptr)
@@ -599,7 +601,7 @@ void cChunk::SpawnMobs(cMobSpawner& a_MobSpawner)
-void cChunk::Tick(float a_Dt)
+void cChunk::Tick(std::chrono::milliseconds a_Dt)
{
BroadcastPendingBlockChanges();
diff --git a/src/Chunk.h b/src/Chunk.h
index 6f4ac5cac..1ce862371 100644
--- a/src/Chunk.h
+++ b/src/Chunk.h
@@ -160,7 +160,7 @@ public:
/** Try to Spawn Monsters inside chunk */
void SpawnMobs(cMobSpawner& a_MobSpawner);
- void Tick(float a_Dt);
+ void Tick(std::chrono::milliseconds a_Dt);
/** Ticks a single block. Used by cWorld::TickQueuedBlocks() to tick the queued blocks */
void TickBlock(int a_RelX, int a_RelY, int a_RelZ);
diff --git a/src/ChunkDef.h b/src/ChunkDef.h
index 8f1d416ad..2bfa2949c 100644
--- a/src/ChunkDef.h
+++ b/src/ChunkDef.h
@@ -347,18 +347,32 @@ public:
struct sSetBlock
{
- int x, y, z;
- int ChunkX, ChunkZ;
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
-
- sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // absolute block position
- sSetBlock(int a_ChunkX, int a_ChunkZ, int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) :
- x(a_X), y(a_Y), z(a_Z),
- ChunkX(a_ChunkX), ChunkZ(a_ChunkZ),
- BlockType(a_BlockType),
- BlockMeta(a_BlockMeta)
- {}
+ int m_RelX, m_RelY, m_RelZ;
+ int m_ChunkX, m_ChunkZ;
+ BLOCKTYPE m_BlockType;
+ NIBBLETYPE m_BlockMeta;
+
+ sSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
+ sSetBlock(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) :
+ m_RelX(a_RelX), m_RelY(a_RelY), m_RelZ(a_RelZ),
+ m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ),
+ m_BlockType(a_BlockType),
+ m_BlockMeta(a_BlockMeta)
+ {
+ ASSERT((a_RelX >= 0) && (a_RelX < cChunkDef::Width));
+ ASSERT((a_RelZ >= 0) && (a_RelZ < cChunkDef::Width));
+ }
+
+ /** Returns the absolute X coord of the stored block. */
+ int GetX(void) const { return m_RelX + cChunkDef::Width * m_ChunkX; }
+
+ /** Returns the absolute Y coord of the stored block.
+ Is the same as relative Y coords, because there's no Y relativization. */
+ int GetY(void) const { return m_RelY; }
+
+ /** Returns the absolute Z coord of the stored block. */
+ int GetZ(void) const { return m_RelZ + cChunkDef::Width * m_ChunkZ; }
};
typedef std::list<sSetBlock> sSetBlockList;
@@ -385,6 +399,17 @@ public:
typedef std::list<cChunkCoords> cChunkCoordsList;
typedef std::vector<cChunkCoords> cChunkCoordsVector;
+/** A simple hash function for chunk coords, we assume that chunk coords won't use more than 16 bits, so the hash is almost an identity.
+Used for std::unordered_map<cChunkCoords, ...> */
+class cChunkCoordsHash
+{
+public:
+ size_t operator () (const cChunkCoords & a_Coords) const
+ {
+ return (static_cast<size_t>(a_Coords.m_ChunkX) << 16) ^ static_cast<size_t>(a_Coords.m_ChunkZ);
+ }
+};
+
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index 6aff3a754..2dd04f801 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -1101,17 +1101,17 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList)
// Process all items from a_BlockList, either successfully or by placing into Failed
while (!a_BlockList.empty())
{
- int ChunkX = a_BlockList.front().ChunkX;
- int ChunkZ = a_BlockList.front().ChunkZ;
+ int ChunkX = a_BlockList.front().m_ChunkX;
+ int ChunkZ = a_BlockList.front().m_ChunkZ;
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk != nullptr) && Chunk->IsValid())
{
for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();)
{
- if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ))
+ if ((itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ))
{
- Chunk->FastSetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
+ Chunk->FastSetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
itr = a_BlockList.erase(itr);
}
else
@@ -1125,7 +1125,7 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList)
// The chunk is not valid, move all blocks within this chunk to Failed
for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();)
{
- if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ))
+ if ((itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ))
{
Failed.push_back(*itr);
itr = a_BlockList.erase(itr);
@@ -1146,6 +1146,34 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList)
+void cChunkMap::SetBlocks(const sSetBlockVector & a_Blocks)
+{
+ cCSLock lock(m_CSLayers);
+ cChunkPtr chunk = nullptr;
+ int lastChunkX = 0x7fffffff; // Bogus coords so that chunk is updated on first pass
+ int lastChunkZ = 0x7fffffff;
+ for (auto block: a_Blocks)
+ {
+ // Update the chunk, if different from last time:
+ if ((block.m_ChunkX != lastChunkX) || (block.m_ChunkZ != lastChunkZ))
+ {
+ lastChunkX = block.m_ChunkX;
+ lastChunkZ = block.m_ChunkZ;
+ chunk = GetChunk(lastChunkX, lastChunkZ);
+ }
+
+ // If the chunk is valid, set the block:
+ if (chunk != nullptr)
+ {
+ chunk->SetBlock(block.m_RelX, block.m_RelY, block.m_RelZ, block.m_BlockType, block.m_BlockMeta);
+ }
+ } // for block - a_Blocks[]
+}
+
+
+
+
+
void cChunkMap::CollectPickupsByPlayer(cPlayer & a_Player)
{
int BlockX = (int)(a_Player.GetPosX()); // Truncating doesn't matter much; we're scanning entire chunks anyway
@@ -1175,28 +1203,28 @@ void cChunkMap::CollectPickupsByPlayer(cPlayer & a_Player)
BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
{
+ int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ);
+
// First check if it isn't queued in the m_FastSetBlockQueue:
{
- int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
- int ChunkX, ChunkZ;
- cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ);
cCSLock Lock(m_CSFastSetBlock);
for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr)
{
- if ((itr->x == X) && (itr->y == Y) && (itr->z == Z) && (itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ))
+ if ((itr->m_RelX == X) && (itr->m_RelY == Y) && (itr->m_RelZ == Z) && (itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ))
{
- return itr->BlockType;
+ return itr->m_BlockType;
}
} // for itr - m_FastSetBlockQueue[]
}
- int ChunkX, ChunkZ;
- cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
-
+
+ // Not in the queue, query the chunk, if loaded:
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ);
if ((Chunk != nullptr) && Chunk->IsValid())
{
- return Chunk->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ return Chunk->GetBlock(X, Y, Z);
}
return 0;
}
@@ -1207,25 +1235,28 @@ BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
NIBBLETYPE cChunkMap::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ)
{
+ int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ);
+
// First check if it isn't queued in the m_FastSetBlockQueue:
{
cCSLock Lock(m_CSFastSetBlock);
for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr)
{
- if ((itr->x == a_BlockX) && (itr->y == a_BlockY) && (itr->z == a_BlockZ))
+ if ((itr->m_RelX == X) && (itr->m_RelY == Y) && (itr->m_RelZ == Z) && (itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ))
{
- return itr->BlockMeta;
+ return itr->m_BlockMeta;
}
} // for itr - m_FastSetBlockQueue[]
}
- int ChunkX, ChunkZ;
- cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
-
+
+ // Not in the queue, query the chunk, if loaded:
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ);
if ((Chunk != nullptr) && Chunk->IsValid())
{
- return Chunk->GetMeta(a_BlockX, a_BlockY, a_BlockZ);
+ return Chunk->GetMeta(X, Y, Z);
}
return 0;
}
@@ -1373,14 +1404,14 @@ void cChunkMap::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_Filt
cCSLock Lock(m_CSLayers);
for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
{
- cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ);
+ cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkZ);
if ((Chunk == nullptr) || !Chunk->IsValid())
{
continue;
}
- if (Chunk->GetBlock(itr->x, itr->y, itr->z) == a_FilterBlockType)
+ if (Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ) == a_FilterBlockType)
{
- Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
+ Chunk->SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
}
}
}
@@ -1394,24 +1425,24 @@ void cChunkMap::ReplaceTreeBlocks(const sSetBlockVector & a_Blocks)
cCSLock Lock(m_CSLayers);
for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
{
- cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ);
+ cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkZ);
if ((Chunk == nullptr) || !Chunk->IsValid())
{
continue;
}
- switch (Chunk->GetBlock(itr->x, itr->y, itr->z))
+ switch (Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ))
{
CASE_TREE_OVERWRITTEN_BLOCKS:
{
- Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
+ Chunk->SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
break;
}
case E_BLOCK_LEAVES:
case E_BLOCK_NEW_LEAVES:
{
- if ((itr->BlockType == E_BLOCK_LOG) || (itr->BlockType == E_BLOCK_NEW_LOG))
+ if ((itr->m_BlockType == E_BLOCK_LOG) || (itr->m_BlockType == E_BLOCK_NEW_LOG))
{
- Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
+ Chunk->SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
}
break;
}
@@ -1507,7 +1538,7 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
cCSLock Lock(m_CSLayers);
for (sSetBlockVector::iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
{
- cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ);
+ cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkZ);
if ((Chunk == nullptr) || !Chunk->IsValid())
{
if (!a_ContinueOnFailure)
@@ -1517,8 +1548,8 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
res = false;
continue;
}
- itr->BlockType = Chunk->GetBlock(itr->x, itr->y, itr->z);
- itr->BlockMeta = Chunk->GetMeta(itr->x, itr->y, itr->z);
+ itr->m_BlockType = Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ);
+ itr->m_BlockMeta = Chunk->GetMeta(itr->m_RelX, itr->m_RelY, itr->m_RelZ);
}
return res;
}
@@ -1527,11 +1558,11 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
-bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z)
+bool cChunkMap::DigBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
{
- int PosX = a_X, PosY = a_Y, PosZ = a_Z, ChunkX, ChunkZ;
+ int PosX = a_BlockX, PosY = a_BlockY, PosZ = a_BlockZ, ChunkX, ChunkZ;
- cChunkDef::AbsoluteToRelative( PosX, PosY, PosZ, ChunkX, ChunkZ);
+ cChunkDef::AbsoluteToRelative(PosX, PosY, PosZ, ChunkX, ChunkZ);
{
cCSLock Lock(m_CSLayers);
@@ -1542,7 +1573,7 @@ bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z)
}
DestChunk->SetBlock(PosX, PosY, PosZ, E_BLOCK_AIR, 0);
- m_World->GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z, DestChunk);
+ m_World->GetSimulatorManager()->WakeUp(a_BlockX, a_BlockY, a_BlockZ, DestChunk);
}
return true;
@@ -2689,7 +2720,7 @@ void cChunkMap::SpawnMobs(cMobSpawner& a_MobSpawner)
-void cChunkMap::Tick(float a_Dt)
+void cChunkMap::Tick(std::chrono::milliseconds a_Dt)
{
cCSLock Lock(m_CSLayers);
for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
@@ -2917,7 +2948,7 @@ void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner& a_MobSpawner)
-void cChunkMap::cChunkLayer::Tick(float a_Dt)
+void cChunkMap::cChunkLayer::Tick(std::chrono::milliseconds a_Dt)
{
for (size_t i = 0; i < ARRAYCOUNT(m_Chunks); i++)
{
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
index 6a858b06d..f08d02337 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -143,7 +143,13 @@ public:
void FastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
void FastSetQueuedBlocks();
- void FastSetBlocks (sSetBlockList & a_BlockList);
+ void FastSetBlocks(sSetBlockList & a_BlockList);
+
+ /** Performs the specified single-block set operations simultaneously, as if SetBlock() was called for each item.
+ Is more efficient than calling SetBlock() multiple times.
+ If the chunk for any of the blocks is not loaded, the set operation is ignored silently. */
+ void SetBlocks(const sSetBlockVector & a_Blocks);
+
void CollectPickupsByPlayer(cPlayer & a_Player);
BLOCKTYPE GetBlock (int a_BlockX, int a_BlockY, int a_BlockZ);
@@ -173,11 +179,20 @@ public:
(Re)sends the chunks to their relevant clients if successful. */
bool SetAreaBiome(int a_MinX, int a_MaxX, int a_MinZ, int a_MaxZ, EMCSBiome a_Biome);
- /** Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read. */
+ /** Retrieves block types and metas of the specified blocks.
+ If a chunk is not loaded, doesn't modify the block and consults a_ContinueOnFailure whether to process the rest of the array.
+ Returns true if all blocks were read, false if any one failed. */
bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure);
- bool DigBlock (int a_X, int a_Y, int a_Z);
- void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player);
+ /** Removes the block at the specified coords and wakes up simulators.
+ Returns false if the chunk is not loaded (and the block is not dug).
+ Returns true if successful. */
+ bool DigBlock(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /** Sends the block at the specified coords to the specified player.
+ Uses a blockchange packet to send the block.
+ If the relevant chunk isn't loaded, doesn't do anything. */
+ void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, cPlayer * a_Player);
/** Compares clients of two chunks, calls the callback accordingly */
void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback);
@@ -333,7 +348,7 @@ public:
/** Try to Spawn Monsters inside all Chunks */
void SpawnMobs(cMobSpawner& a_MobSpawner);
- void Tick(float a_Dt);
+ void Tick(std::chrono::milliseconds a_Dt);
/** Ticks a single block. Used by cWorld::TickQueuedBlocks() to tick the queued blocks */
void TickBlock(int a_BlockX, int a_BlockY, int a_BlockZ);
@@ -400,7 +415,7 @@ private:
/** Try to Spawn Monsters inside all Chunks */
void SpawnMobs(cMobSpawner& a_MobSpawner);
- void Tick(float a_Dt);
+ void Tick(std::chrono::milliseconds a_Dt);
void RemoveClient(cClientHandle * a_Client);
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 974b75556..09e1d76b7 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -1349,12 +1349,19 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
if (ItemHandler->IsPlaceable() && (a_BlockFace != BLOCK_FACE_NONE))
{
- HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler);
+ if (!ItemHandler->OnPlayerPlace(*World, *m_Player, Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
+ {
+ // Placement failed, bail out
+ return;
+ }
}
else if ((ItemHandler->IsFood() || ItemHandler->IsDrinkable(EquippedDamage)))
{
- if ((m_Player->IsSatiated() || m_Player->IsGameModeCreative()) &&
- ItemHandler->IsFood() && (Equipped.m_ItemType != E_ITEM_GOLDEN_APPLE))
+ if (
+ (m_Player->IsSatiated() || m_Player->IsGameModeCreative()) && // Only creative or hungry players can eat
+ ItemHandler->IsFood() &&
+ (Equipped.m_ItemType != E_ITEM_GOLDEN_APPLE) // Golden apple is a special case, it is used instead of eaten
+ )
{
// The player is satiated or in creative, and trying to eat
return;
@@ -1382,151 +1389,6 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
-void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler)
-{
- BLOCKTYPE EquippedBlock = (BLOCKTYPE)(m_Player->GetEquippedItem().m_ItemType);
- if (a_BlockFace < 0)
- {
- // Clicked in air
- return;
- }
-
- cWorld * World = m_Player->GetWorld();
-
- BLOCKTYPE ClickedBlock;
- NIBBLETYPE ClickedBlockMeta;
- NIBBLETYPE EquippedBlockDamage = (NIBBLETYPE)(m_Player->GetEquippedItem().m_ItemDamage);
-
- if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
- {
- // The block is being placed outside the world, ignore this packet altogether (#128)
- return;
- }
-
- World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta);
-
- // Special slab handling - placing a slab onto another slab produces a dblslab instead:
- if (
- cBlockSlabHandler::IsAnySlabType(ClickedBlock) && // Is there a slab already?
- cBlockSlabHandler::IsAnySlabType(EquippedBlock) && // Is the player placing another slab?
- ((ClickedBlockMeta & 0x07) == EquippedBlockDamage) && // Is it the same slab type?
- (
- (a_BlockFace == BLOCK_FACE_TOP) || // Clicking the top of a bottom slab
- (a_BlockFace == BLOCK_FACE_BOTTOM) // Clicking the bottom of a top slab
- )
- )
- {
- // Coordinates at clicked block, which was an eligible slab, and either top or bottom faces were clicked
- // If clicked top face and slab occupies the top voxel, we want a slab to be placed above it (therefore increment Y)
- // Else if clicked bottom face and slab occupies the bottom voxel, decrement Y for the same reason
- // Don't touch coordinates if anything else because a dblslab opportunity is present
- if ((ClickedBlockMeta & 0x08) && (a_BlockFace == BLOCK_FACE_TOP))
- {
- ++a_BlockY;
- }
- else if (!(ClickedBlockMeta & 0x08) && (a_BlockFace == BLOCK_FACE_BOTTOM))
- {
- --a_BlockY;
- }
- World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta);
- }
- else
- {
- // Check if the block ignores build collision (water, grass etc.):
- if (
- BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() ||
- BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(m_Player, ClickedBlockMeta)
- )
- {
- cChunkInterface ChunkInterface(World->GetChunkMap());
- BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ);
- }
- else
- {
- AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
-
- if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
- {
- // The block is being placed outside the world, ignore this packet altogether (#128)
- return;
- }
-
- NIBBLETYPE PlaceMeta;
- BLOCKTYPE PlaceBlock;
- World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlock, PlaceMeta);
-
- // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed.
- // No need to do combinability (dblslab) checks, client will do that here.
- if (cBlockSlabHandler::IsAnySlabType(PlaceBlock))
- {
- // It's a slab, don't do checks and proceed to double-slabbing
- }
- else
- {
- if (
- !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision() &&
- !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(m_Player, PlaceMeta)
- )
- {
- // 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;
- }
- }
- }
- }
-
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- if (!a_ItemHandler.GetPlacementBlockTypeMeta(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
- {
- // Handler refused the placement, send that information back to the client:
- World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
- m_Player->GetInventory().SendEquippedSlot();
- return;
- }
-
- cBlockHandler * NewBlock = BlockHandler(BlockType);
-
- if (cRoot::Get()->GetPluginManager()->CallHookPlayerPlacingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
- {
- // A plugin doesn't agree with placing the block, revert the block on the client:
- World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
- m_Player->GetInventory().SendEquippedSlot();
- return;
- }
-
- // The actual block placement:
- World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
- if (!m_Player->IsGameModeCreative())
- {
- m_Player->GetInventory().RemoveOneEquippedItem();
- }
-
- cChunkInterface ChunkInterface(World->GetChunkMap());
- NewBlock->OnPlacedByPlayer(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);
-
- AString PlaceSound = cBlockInfo::GetPlaceSound(BlockType);
- float Volume = 1.0f, Pitch = 0.8f;
- if (PlaceSound == "dig.metal")
- {
- Pitch = 1.2f;
- PlaceSound = "dig.stone";
- }
- else if (PlaceSound == "random.anvil_land")
- {
- Volume = 0.65f;
- }
-
- World->BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch);
-
- cRoot::Get()->GetPluginManager()->CallHookPlayerPlacedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);
-}
-
-
-
-
-
void cClientHandle::HandleChat(const AString & a_Message)
{
// We no longer need to postpone message processing, because the messages already arrive in the Tick thread
@@ -1586,7 +1448,7 @@ void cClientHandle::HandlePlayerMoveLook(double a_PosX, double a_PosY, double a_
-void cClientHandle::HandleAnimation(char a_Animation)
+void cClientHandle::HandleAnimation(int a_Animation)
{
if (cPluginManager::Get()->CallHookPlayerAnimation(*m_Player, a_Animation))
{
@@ -2973,7 +2835,7 @@ void cClientHandle::PacketUnknown(UInt32 a_PacketType)
-void cClientHandle::PacketError(unsigned char a_PacketType)
+void cClientHandle::PacketError(UInt32 a_PacketType)
{
LOGERROR("Protocol error while parsing packet type 0x%02x; disconnecting client \"%s\"", a_PacketType, m_Username.c_str());
SendDisconnect("Protocol error");
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index 25dd250d9..03ae38cfd 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -251,10 +251,10 @@ public:
// Calls that cProtocol descendants use to report state:
void PacketBufferFull(void);
void PacketUnknown(UInt32 a_PacketType);
- void PacketError(unsigned char a_PacketType);
+ void PacketError(UInt32 a_PacketType);
// Calls that cProtocol descendants use for handling packets:
- void HandleAnimation(char a_Animation);
+ void HandleAnimation(int a_Animation);
/** Called when the protocol receives a MC|ItemName plugin message, indicating that the player named
an item in the anvil UI. */
@@ -459,9 +459,6 @@ private:
UInt32 m_ProtocolVersion;
- /** Handles the block placing packet when it is a real block placement (not block-using, item-using or eating) */
- void HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler);
-
/** Returns true if the rate block interactions is within a reasonable limit (bot protection) */
bool CheckBlockInteractionsRate(void);
diff --git a/src/Enchantments.cpp b/src/Enchantments.cpp
index 36c451b81..5ed18de6b 100644
--- a/src/Enchantments.cpp
+++ b/src/Enchantments.cpp
@@ -183,6 +183,7 @@ int cEnchantments::StringToEnchantmentID(const AString & a_EnchantmentName)
{ enchRespiration, "Respiration"},
{ enchAquaAffinity, "AquaAffinity"},
{ enchThorns, "Thorns"},
+ { enchDepthStrider, "DepthStrider"},
{ enchSharpness, "Sharpness"},
{ enchSmite, "Smite"},
{ enchBaneOfArthropods, "BaneOfArthropods"},
@@ -506,6 +507,20 @@ void cEnchantments::AddItemEnchantmentWeights(cWeightedEnchantments & a_Enchantm
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchFeatherFalling, 1);
}
+
+ // Depth Strider
+ if ((a_EnchantmentLevel >= 30) && (a_EnchantmentLevel <= 45))
+ {
+ AddEnchantmentWeightToVector(a_Enchantments, 2, enchDepthStrider, 3);
+ }
+ else if ((a_EnchantmentLevel >= 20) && (a_EnchantmentLevel <= 35))
+ {
+ AddEnchantmentWeightToVector(a_Enchantments, 2, enchDepthStrider, 2);
+ }
+ else if ((a_EnchantmentLevel >= 10) && (a_EnchantmentLevel <= 25))
+ {
+ AddEnchantmentWeightToVector(a_Enchantments, 2, enchDepthStrider, 1);
+ }
}
}
@@ -1021,26 +1036,34 @@ cEnchantments cEnchantments::GetRandomEnchantmentFromVector(cWeightedEnchantment
-cEnchantments cEnchantments::GenerateEnchantmentFromVector(cWeightedEnchantments & a_Enchantments, int a_Seed)
+cEnchantments cEnchantments::SelectEnchantmentFromVector(const cWeightedEnchantments & a_Enchantments, int a_Seed)
{
+ // Sum up all the enchantments' weights:
int AllWeights = 0;
for (const auto Enchantment : a_Enchantments)
{
AllWeights += Enchantment.m_Weight;
}
+ // If there's no weight for any of the enchantments, return an empty enchantment
+ if (AllWeights <= 0)
+ {
+ return cEnchantments();
+ }
+
+ // Pick a random enchantment:
cNoise Noise(a_Seed);
int RandomNumber = Noise.IntNoise1DInt(AllWeights) / 7 % AllWeights;
-
for (const auto Enchantment : a_Enchantments)
{
RandomNumber -= Enchantment.m_Weight;
- if (RandomNumber < 0)
+ if (RandomNumber <= 0)
{
return Enchantment.m_Enchantments;
}
}
+ // No enchantment picked, return an empty one (we probably shouldn't ever get here):
return cEnchantments();
}
diff --git a/src/Enchantments.h b/src/Enchantments.h
index e4390a5f2..e8e84d43c 100644
--- a/src/Enchantments.h
+++ b/src/Enchantments.h
@@ -53,6 +53,7 @@ public:
enchRespiration = 5,
enchAquaAffinity = 6,
enchThorns = 7,
+ enchDepthStrider = 8,
enchSharpness = 16,
enchSmite = 17,
enchBaneOfArthropods = 18,
@@ -128,8 +129,10 @@ public:
/** Gets random enchantment from Vector and returns it */
static cEnchantments GetRandomEnchantmentFromVector(cWeightedEnchantments & a_Enchantments);
- /** Returns an enchantment from a Vector using cNoise. Mostly used for generators.*/
- static cEnchantments GenerateEnchantmentFromVector(cWeightedEnchantments & a_Enchantments, int a_Seed);
+ /** Selects one enchantment from a Vector using cNoise. Mostly used for generators.
+ Uses the enchantments' weights for the random distribution.
+ If a_Enchantments is empty, returns an empty enchantment. */
+ static cEnchantments SelectEnchantmentFromVector(const cWeightedEnchantments & a_Enchantments, int a_Seed);
/** Returns true if a_Other doesn't contain exactly the same enchantments and levels */
bool operator !=(const cEnchantments & a_Other) const;
diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp
index 30f18f677..0fbbfb681 100644
--- a/src/Entities/ArrowEntity.cpp
+++ b/src/Entities/ArrowEntity.cpp
@@ -174,20 +174,20 @@ void cArrowEntity::CollectedBy(cPlayer & a_Dest)
-void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk)
+void cArrowEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
m_Timer += a_Dt;
if (m_bIsCollected)
{
- if (m_Timer > 500.f) // 0.5 seconds
+ if (m_Timer > std::chrono::milliseconds(500))
{
Destroy();
return;
}
}
- else if (m_Timer > 1000 * 60 * 5) // 5 minutes
+ else if (m_Timer > std::chrono::minutes(5))
{
Destroy();
return;
@@ -202,7 +202,7 @@ void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk)
if (!m_HasTeleported) // Sent a teleport already, don't do again
{
- if (m_HitGroundTimer > 500.f) // Send after half a second, could be less, but just in case
+ if (m_HitGroundTimer > std::chrono::milliseconds(500))
{
m_World->BroadcastTeleportEntity(*this);
m_HasTeleported = true;
diff --git a/src/Entities/ArrowEntity.h b/src/Entities/ArrowEntity.h
index 436ec0293..2ecc16d1c 100644
--- a/src/Entities/ArrowEntity.h
+++ b/src/Entities/ArrowEntity.h
@@ -85,10 +85,10 @@ protected:
bool m_IsCritical;
/** Timer for pickup collection animation or five minute timeout */
- float m_Timer;
+ std::chrono::milliseconds m_Timer;
/** Timer for client arrow position confirmation via TeleportEntity */
- float m_HitGroundTimer;
+ std::chrono::milliseconds m_HitGroundTimer;
// Whether the arrow has already been teleported into the proper position in the ground.
bool m_HasTeleported;
@@ -103,6 +103,6 @@ protected:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
virtual void CollectedBy(cPlayer & a_Player) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
}; // tolua_export
diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp
index 953213bca..6d8b4ef31 100644
--- a/src/Entities/Boat.cpp
+++ b/src/Entities/Boat.cpp
@@ -91,7 +91,7 @@ void cBoat::OnRightClicked(cPlayer & a_Player)
-void cBoat::Tick(float a_Dt, cChunk & a_Chunk)
+void cBoat::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
BroadcastMovementUpdate();
diff --git a/src/Entities/Boat.h b/src/Entities/Boat.h
index 8de88d165..a873ff822 100644
--- a/src/Entities/Boat.h
+++ b/src/Entities/Boat.h
@@ -27,7 +27,7 @@ public:
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
virtual void OnRightClicked(cPlayer & a_Player) override;
virtual bool DoTakeDamage(TakeDamageInfo & TDI) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void HandleSpeedFromAttachee(float a_Forward, float a_Sideways) override;
cBoat(double a_X, double a_Y, double a_Z);
diff --git a/src/Entities/EnderCrystal.cpp b/src/Entities/EnderCrystal.cpp
index 30df2c110..7a911d4db 100644
--- a/src/Entities/EnderCrystal.cpp
+++ b/src/Entities/EnderCrystal.cpp
@@ -29,7 +29,7 @@ void cEnderCrystal::SpawnOn(cClientHandle & a_ClientHandle)
-void cEnderCrystal::Tick(float a_Dt, cChunk & a_Chunk)
+void cEnderCrystal::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
UNUSED(a_Dt);
// No further processing (physics e.t.c.) is needed
diff --git a/src/Entities/EnderCrystal.h b/src/Entities/EnderCrystal.h
index c98c3b681..8f7e2e9b9 100644
--- a/src/Entities/EnderCrystal.h
+++ b/src/Entities/EnderCrystal.h
@@ -23,7 +23,7 @@ private:
// cEntity overrides:
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void KilledBy(TakeDamageInfo & a_TDI) override;
}; // tolua_export
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 54b9f2a20..c51a27961 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -772,7 +772,7 @@ void cEntity::SetHealth(int a_Health)
-void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
+void cEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
m_TicksAlive++;
@@ -841,7 +841,7 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
-void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
+void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
int BlockX = POSX_TOINT;
int BlockY = POSY_TOINT;
@@ -851,15 +851,15 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
GET_AND_VERIFY_CURRENT_CHUNK(NextChunk, BlockX, BlockZ)
// TODO Add collision detection with entities.
- a_Dt /= 1000; // Convert from msec to sec
+ auto DtSec = std::chrono::duration_cast<std::chrono::duration<double>>(a_Dt);
Vector3d NextPos = Vector3d(GetPosX(), GetPosY(), GetPosZ());
Vector3d NextSpeed = Vector3d(GetSpeedX(), GetSpeedY(), GetSpeedZ());
if ((BlockY >= cChunkDef::Height) || (BlockY < 0))
{
// Outside of the world
- AddSpeedY(m_Gravity * a_Dt);
- AddPosition(GetSpeed() * a_Dt);
+ AddSpeedY(m_Gravity * DtSec.count());
+ AddPosition(GetSpeed() * DtSec.count());
return;
}
@@ -927,11 +927,11 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
if (!m_bOnGround)
{
- float fallspeed;
+ double fallspeed;
if (IsBlockWater(BlockIn))
{
- fallspeed = m_Gravity * a_Dt / 3; // Fall 3x slower in water
- ApplyFriction(NextSpeed, 0.7, a_Dt);
+ fallspeed = m_Gravity * DtSec.count() / 3; // Fall 3x slower in water
+ ApplyFriction(NextSpeed, 0.7, static_cast<float>(DtSec.count()));
}
else if (BlockIn == E_BLOCK_COBWEB)
{
@@ -941,13 +941,13 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
else
{
// Normal gravity
- fallspeed = m_Gravity * a_Dt;
+ fallspeed = m_Gravity * DtSec.count();
}
- NextSpeed.y += fallspeed;
+ NextSpeed.y += static_cast<float>(fallspeed);
}
else
{
- ApplyFriction(NextSpeed, 0.7, a_Dt);
+ ApplyFriction(NextSpeed, 0.7, static_cast<float>(DtSec.count()));
}
// Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we
@@ -1002,14 +1002,14 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
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
- int DistanceToTrace = (int)(ceil((NextSpeed * a_Dt).SqrLength()) * 2);
+ int DistanceToTrace = (int)(ceil((NextSpeed * DtSec.count()).SqrLength()) * 2);
bool HasHit = Tracer.Trace(NextPos, NextSpeed, DistanceToTrace);
if (HasHit)
{
// Oh noez! We hit something: verify that the (hit position - current) was smaller or equal to the (position that we should travel without obstacles - current)
// This is because previously, we traced with a length that was rounded up (due to integer limitations), and in the case that something was hit, we don't want to overshoot our projected movement
- if ((Tracer.RealHit - NextPos).SqrLength() <= (NextSpeed * a_Dt).SqrLength())
+ if ((Tracer.RealHit - NextPos).SqrLength() <= (NextSpeed * DtSec.count()).SqrLength())
{
// 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.
@@ -1044,13 +1044,13 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
// and that this piece of software will come to be hailed as the epitome of performance and functionality in C++, never before seen, and of such a like that will never
// be henceforth seen again in the time of programmers and man alike
// </&sensationalist>
- NextPos += (NextSpeed * a_Dt);
+ NextPos += (NextSpeed * DtSec.count());
}
}
else
{
// We didn't hit anything, so move =]
- NextPos += (NextSpeed * a_Dt);
+ NextPos += (NextSpeed * DtSec.count());
}
}
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
index af545fe4a..de9b88dfb 100644
--- a/src/Entities/Entity.h
+++ b/src/Entities/Entity.h
@@ -326,10 +326,10 @@ public:
// tolua_end
- virtual void Tick(float a_Dt, cChunk & a_Chunk);
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
/// Handles the physics of the entity - updates position based on speed, updates speed based on environment
- virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk);
+ virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
/// Updates the state related to this entity being on fire
virtual void TickBurning(cChunk & a_Chunk);
diff --git a/src/Entities/ExpOrb.cpp b/src/Entities/ExpOrb.cpp
index 751308661..db7f6f2c8 100644
--- a/src/Entities/ExpOrb.cpp
+++ b/src/Entities/ExpOrb.cpp
@@ -8,7 +8,7 @@
cExpOrb::cExpOrb(double a_X, double a_Y, double a_Z, int a_Reward)
: cEntity(etExpOrb, a_X, a_Y, a_Z, 0.98, 0.98)
, m_Reward(a_Reward)
- , m_Timer(0.f)
+ , m_Timer(0)
{
SetMaxHealth(5);
SetHealth(5);
@@ -21,7 +21,7 @@ cExpOrb::cExpOrb(double a_X, double a_Y, double a_Z, int a_Reward)
cExpOrb::cExpOrb(const Vector3d & a_Pos, int a_Reward)
: cEntity(etExpOrb, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98)
, m_Reward(a_Reward)
- , m_Timer(0.f)
+ , m_Timer(0)
{
SetMaxHealth(5);
SetHealth(5);
@@ -42,7 +42,7 @@ void cExpOrb::SpawnOn(cClientHandle & a_Client)
-void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk)
+void cExpOrb::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
cPlayer * a_ClosestPlayer(m_World->FindClosestPlayer(Vector3f(GetPosition()), 5));
if (a_ClosestPlayer != nullptr)
@@ -70,7 +70,7 @@ void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk)
HandlePhysics(a_Dt, a_Chunk);
m_Timer += a_Dt;
- if (m_Timer >= 1000 * 60 * 5) // 5 minutes
+ if (m_Timer >= std::chrono::minutes(5))
{
Destroy(true);
}
diff --git a/src/Entities/ExpOrb.h b/src/Entities/ExpOrb.h
index bdb9a5b19..9aac4f748 100644
--- a/src/Entities/ExpOrb.h
+++ b/src/Entities/ExpOrb.h
@@ -22,14 +22,14 @@ public:
cExpOrb(const Vector3d & a_Pos, int a_Reward);
// Override functions
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void SpawnOn(cClientHandle & a_Client) override;
/** Returns the number of ticks that this entity has existed */
- int GetAge(void) const { return (int)(m_Timer / 50); } // tolua_export
+ int GetAge(void) const { return std::chrono::duration_cast<cTickTime>(m_Timer).count(); } // tolua_export
/** Set the number of ticks that this entity has existed */
- void SetAge(int a_Age) { m_Timer = (float)(a_Age * 50); } // tolua_export
+ void SetAge(int a_Age) { m_Timer = cTickTime(a_Age); } // tolua_export
/** Get the exp amount */
int GetReward(void) const { return m_Reward; } // tolua_export
@@ -41,5 +41,5 @@ protected:
int m_Reward;
/** The number of ticks that the entity has existed / timer between collect and destroy; in msec */
- float m_Timer;
+ std::chrono::milliseconds m_Timer;
} ; // tolua_export
diff --git a/src/Entities/FallingBlock.cpp b/src/Entities/FallingBlock.cpp
index 111c5fa84..75105a0cd 100644
--- a/src/Entities/FallingBlock.cpp
+++ b/src/Entities/FallingBlock.cpp
@@ -31,7 +31,7 @@ void cFallingBlock::SpawnOn(cClientHandle & a_ClientHandle)
-void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk)
+void cFallingBlock::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
// GetWorld()->BroadcastTeleportEntity(*this); // Test position
@@ -82,7 +82,7 @@ void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk)
return;
}
- float MilliDt = a_Dt * 0.001f;
+ float MilliDt = a_Dt.count() * 0.001f;
AddSpeedY(MilliDt * -9.8f);
AddPosition(GetSpeed() * MilliDt);
diff --git a/src/Entities/FallingBlock.h b/src/Entities/FallingBlock.h
index c20fe8eb9..884938f4d 100644
--- a/src/Entities/FallingBlock.h
+++ b/src/Entities/FallingBlock.h
@@ -30,7 +30,7 @@ public:
// cEntity overrides:
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
private:
BLOCKTYPE m_BlockType;
diff --git a/src/Entities/FireworkEntity.cpp b/src/Entities/FireworkEntity.cpp
index 68d02640a..32eaf669a 100644
--- a/src/Entities/FireworkEntity.cpp
+++ b/src/Entities/FireworkEntity.cpp
@@ -19,7 +19,7 @@ cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, do
-void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
+void cFireworkEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
int RelX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width;
int RelZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width;
@@ -28,7 +28,7 @@ void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
if ((PosY < 0) || (PosY >= cChunkDef::Height))
{
AddSpeedY(1);
- AddPosition(GetSpeed() * (a_Dt / 1000));
+ AddPosition(GetSpeed() * (static_cast<double>(a_Dt.count()) / 1000));
return;
}
@@ -53,14 +53,14 @@ void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
}
AddSpeedY(1);
- AddPosition(GetSpeed() * (a_Dt / 1000));
+ AddPosition(GetSpeed() * (static_cast<double>(a_Dt.count()) / 1000));
}
-void cFireworkEntity::Tick(float a_Dt, cChunk & a_Chunk)
+void cFireworkEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
diff --git a/src/Entities/FireworkEntity.h b/src/Entities/FireworkEntity.h
index 300ec571e..c0a38a943 100644
--- a/src/Entities/FireworkEntity.h
+++ b/src/Entities/FireworkEntity.h
@@ -49,8 +49,8 @@ public:
protected:
// cProjectileEntity overrides:
- virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
private:
diff --git a/src/Entities/Floater.cpp b/src/Entities/Floater.cpp
index 5fe6a1238..cf8dd6c6f 100644
--- a/src/Entities/Floater.cpp
+++ b/src/Entities/Floater.cpp
@@ -125,7 +125,7 @@ void cFloater::SpawnOn(cClientHandle & a_Client)
-void cFloater::Tick(float a_Dt, cChunk & a_Chunk)
+void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
HandlePhysics(a_Dt, a_Chunk);
if (IsBlockWater(m_World->GetBlock((int) GetPosX(), (int) GetPosY(), (int) GetPosZ())) && m_World->GetBlockMeta((int) GetPosX(), (int) GetPosY(), (int) GetPosZ()) == 0)
diff --git a/src/Entities/Floater.h b/src/Entities/Floater.h
index 96d77ac82..d5715f89e 100644
--- a/src/Entities/Floater.h
+++ b/src/Entities/Floater.h
@@ -21,7 +21,7 @@ public:
cFloater(double a_X, double a_Y, double a_Z, Vector3d a_Speed, int a_PlayerID, int a_CountDownTime);
virtual void SpawnOn(cClientHandle & a_Client) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
// tolua_begin
bool CanPickup(void) const { return m_CanPickupItem; }
diff --git a/src/Entities/HangingEntity.h b/src/Entities/HangingEntity.h
index 67146a20b..d1ef79a68 100644
--- a/src/Entities/HangingEntity.h
+++ b/src/Entities/HangingEntity.h
@@ -43,7 +43,7 @@ public:
private:
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override {}
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override {}
eBlockFace m_Facing;
diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp
index a5ce0ac94..a32926838 100644
--- a/src/Entities/Minecart.cpp
+++ b/src/Entities/Minecart.cpp
@@ -112,7 +112,7 @@ void cMinecart::SpawnOn(cClientHandle & a_ClientHandle)
-void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
+void cMinecart::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
if (IsDestroyed()) // Mainly to stop detector rails triggering again after minecart is dead
{
@@ -178,7 +178,7 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
default: VERIFY(!"Unhandled rail type despite checking if block was rail!"); break;
}
- AddPosition(GetSpeed() * (a_Dt / 1000)); // Commit changes; as we use our own engine when on rails, this needs to be done, whereas it is normally in Entity.cpp
+ AddPosition(GetSpeed() * (static_cast<double>(a_Dt.count()) / 1000)); // Commit changes; as we use our own engine when on rails, this needs to be done, whereas it is normally in Entity.cpp
}
else
{
@@ -206,7 +206,7 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
-void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
+void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt)
{
/*
NOTE: Please bear in mind that taking away from negatives make them even more negative,
@@ -566,7 +566,7 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
-void cMinecart::HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
+void cMinecart::HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt)
{
m_World->SetBlockMeta(m_DetectorRailPosition, a_RailMeta | 0x08);
@@ -577,7 +577,7 @@ void cMinecart::HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
-void cMinecart::HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
+void cMinecart::HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt)
{
HandleRailPhysics(a_RailMeta & 0x07, a_Dt);
}
@@ -1214,7 +1214,7 @@ void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player)
-void cMinecartWithFurnace::Tick(float a_Dt, cChunk & a_Chunk)
+void cMinecartWithFurnace::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h
index f7d0d5dda..898776e71 100644
--- a/src/Entities/Minecart.h
+++ b/src/Entities/Minecart.h
@@ -38,7 +38,7 @@ public:
// cEntity overrides:
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
- virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
+ virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual bool DoTakeDamage(TakeDamageInfo & TDI) override;
virtual void Destroyed() override;
@@ -56,7 +56,7 @@ protected:
/** 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
*/
- void HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt);
+ void HandleRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt);
/** Handles powered rail physics
Each tick, speed up or slow down cart, depending on metadata of rail (powered or not)
@@ -66,10 +66,10 @@ protected:
/** Handles detector rail activation
Activates detector rails when a minecart is on them. Calls HandleRailPhysics() for physics simulations
*/
- void HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt);
+ void HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt);
/** Handles activator rails - placeholder for future implementation */
- void HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt);
+ void HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt);
/** Snaps a mincecart to a rail's axis, resetting its speed
For curved rails, it changes the cart's direction as well as snapping it to axis */
@@ -171,7 +171,7 @@ public:
// cEntity overrides:
virtual void OnRightClicked(cPlayer & a_Player) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
// Set functions.
void SetIsFueled(bool a_IsFueled, int a_FueledTimeLeft = -1) {m_IsFueled = a_IsFueled; m_FueledTimeLeft = a_FueledTimeLeft;}
diff --git a/src/Entities/Painting.cpp b/src/Entities/Painting.cpp
index 1aa6da0a1..6f6277f28 100644
--- a/src/Entities/Painting.cpp
+++ b/src/Entities/Painting.cpp
@@ -31,7 +31,7 @@ void cPainting::SpawnOn(cClientHandle & a_Client)
-void cPainting::Tick(float a_Dt, cChunk & a_Chunk)
+void cPainting::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
UNUSED(a_Dt);
UNUSED(a_Chunk);
diff --git a/src/Entities/Painting.h b/src/Entities/Painting.h
index 078270b42..6e8a382fc 100644
--- a/src/Entities/Painting.h
+++ b/src/Entities/Painting.h
@@ -31,7 +31,7 @@ public:
private:
virtual void SpawnOn(cClientHandle & a_Client) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void GetDrops(cItems & a_Items, cEntity * a_Killer) override;
virtual void KilledBy(TakeDamageInfo & a_TDI) override
{
diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp
index fc8ca3d47..baf8a2f3b 100644
--- a/src/Entities/Pawn.cpp
+++ b/src/Entities/Pawn.cpp
@@ -19,7 +19,7 @@ cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) :
-void cPawn::Tick(float a_Dt, cChunk & a_Chunk)
+void cPawn::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
// Iterate through this entity's applied effects
for (tEffectMap::iterator iter = m_EntityEffects.begin(); iter != m_EntityEffects.end();)
diff --git a/src/Entities/Pawn.h b/src/Entities/Pawn.h
index d50bcd8af..e3e99651d 100644
--- a/src/Entities/Pawn.h
+++ b/src/Entities/Pawn.h
@@ -20,7 +20,7 @@ public:
cPawn(eEntityType a_EntityType, double a_Width, double a_Height);
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void KilledBy(TakeDamageInfo & a_TDI) override;
// tolua_begin
diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp
index e5e28446d..9f2609894 100644
--- a/src/Entities/Pickup.cpp
+++ b/src/Entities/Pickup.cpp
@@ -86,7 +86,7 @@ protected:
cPickup::cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_Item, bool IsPlayerCreated, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */)
: cEntity(etPickup, a_PosX, a_PosY, a_PosZ, 0.2, 0.2)
- , m_Timer(0.f)
+ , m_Timer(0)
, m_Item(a_Item)
, m_bCollected(false)
, m_bIsPlayerCreated(IsPlayerCreated)
@@ -110,7 +110,7 @@ void cPickup::SpawnOn(cClientHandle & a_Client)
-void cPickup::Tick(float a_Dt, cChunk & a_Chunk)
+void cPickup::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
BroadcastMovementUpdate(); // Notify clients of position
@@ -141,9 +141,9 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk)
)
{
m_bCollected = true;
- m_Timer = 0; // We have to reset the timer.
+ m_Timer = std::chrono::milliseconds(0); // We have to reset the timer.
m_Timer += a_Dt; // In case we have to destroy the pickup in the same tick.
- if (m_Timer > 500.f)
+ if (m_Timer > std::chrono::milliseconds(500))
{
Destroy(true);
return;
@@ -167,14 +167,14 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk)
}
else
{
- if (m_Timer > 500.f) // 0.5 second
+ if (m_Timer > std::chrono::milliseconds(500)) // 0.5 second
{
Destroy(true);
return;
}
}
- if (m_Timer > 1000 * 60 * 5) // 5 minutes
+ if (m_Timer > std::chrono::minutes(5)) // 5 minutes
{
Destroy(true);
return;
@@ -200,7 +200,7 @@ bool cPickup::CollectedBy(cPlayer & a_Dest)
}
// Two seconds if player created the pickup (vomiting), half a second if anything else
- if (m_Timer < (m_bIsPlayerCreated ? 2000.f : 500.f))
+ if (m_Timer < (m_bIsPlayerCreated ? std::chrono::seconds(2) : std::chrono::milliseconds(500)))
{
// LOG("Pickup %d cannot be collected by \"%s\", because it is not old enough.", m_UniqueID, a_Dest->GetName().c_str());
return false; // Not old enough
@@ -234,7 +234,7 @@ bool cPickup::CollectedBy(cPlayer & a_Dest)
// All of the pickup has been collected, schedule the pickup for destroying
m_bCollected = true;
}
- m_Timer = 0;
+ m_Timer = std::chrono::milliseconds(0);
return true;
}
diff --git a/src/Entities/Pickup.h b/src/Entities/Pickup.h
index d1176a7cf..ed5949f37 100644
--- a/src/Entities/Pickup.h
+++ b/src/Entities/Pickup.h
@@ -34,13 +34,13 @@ public:
bool CollectedBy(cPlayer & a_Dest); // tolua_export
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
/** Returns the number of ticks that this entity has existed */
- int GetAge(void) const { return static_cast<int>(m_Timer / 50); } // tolua_export
+ int GetAge(void) const { return std::chrono::duration_cast<cTickTime>(m_Timer).count(); } // tolua_export
/** Set the number of ticks that this entity has existed */
- void SetAge(int a_Age) { m_Timer = static_cast<float>(a_Age * 50); } // tolua_export
+ void SetAge(int a_Age) { m_Timer = cTickTime(a_Age); } // tolua_export
/** Returns true if the pickup has already been collected */
bool IsCollected(void) const { return m_bCollected; } // tolua_export
@@ -51,7 +51,7 @@ public:
private:
/** The number of ticks that the entity has existed / timer between collect and destroy; in msec */
- float m_Timer;
+ std::chrono::milliseconds m_Timer;
cItem m_Item;
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 716e20663..67de2e23a 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -2,6 +2,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Player.h"
+#include <unordered_map>
#include "../ChatColor.h"
#include "../Server.h"
#include "../UI/InventoryWindow.h"
@@ -19,6 +20,10 @@
#include "../WorldStorage/StatSerializer.h"
#include "../CompositeChat.h"
+#include "../Blocks/BlockHandler.h"
+#include "../Blocks/BlockSlab.h"
+#include "../Blocks/ChunkInterface.h"
+
#include "../IniFile.h"
#include "json/json.h"
@@ -186,7 +191,7 @@ void cPlayer::SpawnOn(cClientHandle & a_Client)
-void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
+void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
if (m_ClientHandle != nullptr)
{
@@ -2168,6 +2173,97 @@ void cPlayer::LoadRank(void)
+bool cPlayer::PlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ sSetBlockVector blk{{a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta}};
+ return PlaceBlocks(blk);
+}
+
+
+
+
+
+void cPlayer::SendBlocksAround(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Range)
+{
+ // Collect the coords of all the blocks to send:
+ sSetBlockVector blks;
+ for (int y = a_BlockY - a_Range + 1; y < a_BlockY + a_Range; y++)
+ {
+ for (int z = a_BlockZ - a_Range + 1; z < a_BlockZ + a_Range; z++)
+ {
+ for (int x = a_BlockX - a_Range + 1; x < a_BlockX + a_Range; x++)
+ {
+ blks.emplace_back(x, y, z, E_BLOCK_AIR, 0); // Use fake blocktype, it will get set later on.
+ };
+ };
+ } // for y
+
+ // Get the values of all the blocks:
+ if (!m_World->GetBlocks(blks, false))
+ {
+ LOGD("%s: Cannot query all blocks, not sending an update", __FUNCTION__);
+ return;
+ }
+
+ // Divide the block changes by their respective chunks:
+ std::unordered_map<cChunkCoords, sSetBlockVector, cChunkCoordsHash> Changes;
+ for (const auto & blk: blks)
+ {
+ Changes[cChunkCoords(blk.m_ChunkX, blk.m_ChunkZ)].push_back(blk);
+ } // for blk - blks[]
+ blks.clear();
+
+ // Send the blocks for each affected chunk:
+ for (auto itr = Changes.cbegin(), end = Changes.cend(); itr != end; ++itr)
+ {
+ m_ClientHandle->SendBlockChanges(itr->first.m_ChunkX, itr->first.m_ChunkZ, itr->second);
+ }
+}
+
+
+
+
+
+bool cPlayer::PlaceBlocks(const sSetBlockVector & a_Blocks)
+{
+ // Call the "placing" hooks; if any fail, abort:
+ cPluginManager * pm = cPluginManager::Get();
+ for (auto blk: a_Blocks)
+ {
+ if (pm->CallHookPlayerPlacingBlock(*this, blk))
+ {
+ // Abort - re-send all the current blocks in the a_Blocks' coords to the client:
+ for (auto blk2: a_Blocks)
+ {
+ m_World->SendBlockTo(blk2.GetX(), blk2.GetY(), blk2.GetZ(), this);
+ }
+ return false;
+ }
+ } // for blk - a_Blocks[]
+
+ // Set the blocks:
+ m_World->SetBlocks(a_Blocks);
+
+ // Notify the blockhandlers:
+ cChunkInterface ChunkInterface(m_World->GetChunkMap());
+ for (auto blk: a_Blocks)
+ {
+ cBlockHandler * newBlock = BlockHandler(blk.m_BlockType);
+ newBlock->OnPlacedByPlayer(ChunkInterface, *m_World, this, blk);
+ }
+
+ // Call the "placed" hooks:
+ for (auto blk: a_Blocks)
+ {
+ pm->CallHookPlayerPlacedBlock(*this, blk);
+ }
+ return true;
+}
+
+
+
+
+
void cPlayer::Detach()
{
super::Detach();
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index c643aaa8e..d3ed46db6 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -46,9 +46,9 @@ public:
virtual void SpawnOn(cClientHandle & a_Client) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
- virtual void HandlePhysics(float a_Dt, cChunk &) override { UNUSED(a_Dt); }
+ 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(); }
@@ -440,8 +440,27 @@ public:
Loads the m_Rank, m_Permissions, m_MsgPrefix, m_MsgSuffix and m_MsgNameColorCode members. */
void LoadRank(void);
+ /** Calls the block-placement hook and places the block in the world, unless refused by the hook.
+ If the hook prevents the placement, sends the current block at the specified coords back to the client.
+ Assumes that the block is in a currently loaded chunk.
+ Returns true if the block is successfully placed. */
+ bool PlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
+ /** Sends the block in the specified range around the specified coord to the client
+ as a block change packet.
+ The blocks in range (a_BlockX - a_Range, a_BlockX + a_Range) are sent (NY-metric). */
+ void SendBlocksAround(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Range = 1);
+
// tolua_end
+ /** Calls the block placement hooks and places the blocks in the world.
+ First the "placing" hooks for all the blocks are called, then the blocks are placed, and finally
+ the "placed" hooks are called.
+ If the any of the "placing" hooks aborts, none of the blocks are placed and the function returns false.
+ Returns true if all the blocks are placed.
+ Assumes that all the blocks are in currently loaded chunks. */
+ bool PlaceBlocks(const sSetBlockVector & a_Blocks);
+
// cEntity overrides:
virtual bool IsCrouched (void) const { return m_IsCrouched; }
virtual bool IsSprinting(void) const { return m_IsSprinting; }
diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp
index 1768714f8..4f20bfae6 100644
--- a/src/Entities/ProjectileEntity.cpp
+++ b/src/Entities/ProjectileEntity.cpp
@@ -331,7 +331,7 @@ AString cProjectileEntity::GetMCAClassName(void) const
-void cProjectileEntity::Tick(float a_Dt, cChunk & a_Chunk)
+void cProjectileEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
@@ -346,7 +346,7 @@ void cProjectileEntity::Tick(float a_Dt, cChunk & a_Chunk)
-void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
+void cProjectileEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
if (m_IsInGround)
{
diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h
index 2a98e31c7..93e442d8c 100644
--- a/src/Entities/ProjectileEntity.h
+++ b/src/Entities/ProjectileEntity.h
@@ -118,8 +118,8 @@ protected:
bool m_IsInGround;
// cEntity overrides:
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
- virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void SpawnOn(cClientHandle & a_Client) override;
} ; // tolua_export
diff --git a/src/Entities/SplashPotionEntity.h b/src/Entities/SplashPotionEntity.h
index 9302d8292..264dc0eb9 100644
--- a/src/Entities/SplashPotionEntity.h
+++ b/src/Entities/SplashPotionEntity.h
@@ -58,7 +58,7 @@ protected:
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
- virtual void Tick (float a_Dt, cChunk & a_Chunk) override
+ virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override
{
if (m_DestroyTimer > 0)
{
diff --git a/src/Entities/TNTEntity.cpp b/src/Entities/TNTEntity.cpp
index 53af446cc..a89d2f300 100644
--- a/src/Entities/TNTEntity.cpp
+++ b/src/Entities/TNTEntity.cpp
@@ -50,7 +50,7 @@ void cTNTEntity::Explode(void)
-void cTNTEntity::Tick(float a_Dt, cChunk & a_Chunk)
+void cTNTEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
BroadcastMovementUpdate();
diff --git a/src/Entities/TNTEntity.h b/src/Entities/TNTEntity.h
index 48503cf76..9f894338e 100644
--- a/src/Entities/TNTEntity.h
+++ b/src/Entities/TNTEntity.h
@@ -21,7 +21,7 @@ public:
// cEntity overrides:
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
// tolua_begin
diff --git a/src/Entities/ThrownEggEntity.cpp b/src/Entities/ThrownEggEntity.cpp
index 24c946a9c..e9ef29239 100644
--- a/src/Entities/ThrownEggEntity.cpp
+++ b/src/Entities/ThrownEggEntity.cpp
@@ -44,7 +44,7 @@ void cThrownEggEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_Hit
-void cThrownEggEntity::Tick(float a_Dt, cChunk & a_Chunk)
+void cThrownEggEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
if (m_DestroyTimer > 0)
{
diff --git a/src/Entities/ThrownEggEntity.h b/src/Entities/ThrownEggEntity.h
index 6ffedf5b5..620927c5d 100644
--- a/src/Entities/ThrownEggEntity.h
+++ b/src/Entities/ThrownEggEntity.h
@@ -35,7 +35,7 @@ protected:
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
// Randomly decides whether to spawn a chicken where the egg lands.
void TrySpawnChicken(const Vector3d & a_HitPos);
diff --git a/src/Entities/ThrownEnderPearlEntity.cpp b/src/Entities/ThrownEnderPearlEntity.cpp
index 8f1b62934..f01cdc18c 100644
--- a/src/Entities/ThrownEnderPearlEntity.cpp
+++ b/src/Entities/ThrownEnderPearlEntity.cpp
@@ -46,7 +46,7 @@ void cThrownEnderPearlEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d
-void cThrownEnderPearlEntity::Tick(float a_Dt, cChunk & a_Chunk)
+void cThrownEnderPearlEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
if (m_DestroyTimer > 0)
{
diff --git a/src/Entities/ThrownEnderPearlEntity.h b/src/Entities/ThrownEnderPearlEntity.h
index 475ebde87..94f3ab5cb 100644
--- a/src/Entities/ThrownEnderPearlEntity.h
+++ b/src/Entities/ThrownEnderPearlEntity.h
@@ -35,7 +35,7 @@ protected:
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
/** Teleports the creator where the ender pearl lands */
void TeleportCreator(const Vector3d & a_HitPos);
diff --git a/src/Entities/ThrownSnowballEntity.cpp b/src/Entities/ThrownSnowballEntity.cpp
index 88e39d22e..24db1e7ee 100644
--- a/src/Entities/ThrownSnowballEntity.cpp
+++ b/src/Entities/ThrownSnowballEntity.cpp
@@ -48,7 +48,7 @@ void cThrownSnowballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d &
-void cThrownSnowballEntity::Tick(float a_Dt, cChunk & a_Chunk)
+void cThrownSnowballEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
if (m_DestroyTimer > 0)
{
diff --git a/src/Entities/ThrownSnowballEntity.h b/src/Entities/ThrownSnowballEntity.h
index f806996cc..391b0c40b 100644
--- a/src/Entities/ThrownSnowballEntity.h
+++ b/src/Entities/ThrownSnowballEntity.h
@@ -35,7 +35,7 @@ protected:
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
private:
diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp
index 2a4dbe794..2cc810d3b 100644
--- a/src/Generating/BioGen.cpp
+++ b/src/Generating/BioGen.cpp
@@ -733,7 +733,19 @@ cBioGenTwoLevel::cBioGenTwoLevel(int a_Seed) :
m_Noise3(a_Seed + 5003),
m_Noise4(a_Seed + 5004),
m_Noise5(a_Seed + 5005),
- m_Noise6(a_Seed + 5006)
+ m_Noise6(a_Seed + 5006),
+ m_FreqX1(0.0),
+ m_AmpX1(0.0),
+ m_FreqX2(0.0),
+ m_AmpX2(0.0),
+ m_FreqX3(0.0),
+ m_AmpX3(0.0),
+ m_FreqZ1(0.0),
+ m_AmpZ1(0.0),
+ m_FreqZ2(0.0),
+ m_AmpZ2(0.0),
+ m_FreqZ3(0.0),
+ m_AmpZ3(0.0)
{
}
diff --git a/src/Generating/EndGen.cpp b/src/Generating/EndGen.cpp
index 89d6117bb..c559e765f 100644
--- a/src/Generating/EndGen.cpp
+++ b/src/Generating/EndGen.cpp
@@ -39,7 +39,13 @@ cEndGen::cEndGen(int a_Seed) :
m_IslandSizeZ(256),
m_FrequencyX(80),
m_FrequencyY(80),
- m_FrequencyZ(80)
+ m_FrequencyZ(80),
+ m_MinChunkX(0),
+ m_MaxChunkX(0),
+ m_MinChunkZ(0),
+ m_MaxChunkZ(0),
+ m_LastChunkX(0x7fffffff), // Use dummy coords that won't ever be used by real chunks
+ m_LastChunkZ(0x7fffffff)
{
m_Perlin.AddOctave(1, 1);
m_Perlin.AddOctave(2, 0.5);
diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp
index b43a1a6de..eadc66a4e 100644
--- a/src/Generating/Noise3DGenerator.cpp
+++ b/src/Generating/Noise3DGenerator.cpp
@@ -379,7 +379,20 @@ cNoise3DComposable::cNoise3DComposable(int a_Seed) :
m_ChoiceNoise(a_Seed),
m_DensityNoiseA(a_Seed + 1),
m_DensityNoiseB(a_Seed + 2),
- m_BaseNoise(a_Seed + 3)
+ m_BaseNoise(a_Seed + 3),
+ m_HeightAmplification(0.0),
+ m_MidPoint(0.0),
+ m_FrequencyX(0.0),
+ m_FrequencyY(0.0),
+ m_FrequencyZ(0.0),
+ m_BaseFrequencyX(0.0),
+ m_BaseFrequencyZ(0.0),
+ m_ChoiceFrequencyX(0.0),
+ m_ChoiceFrequencyY(0.0),
+ m_ChoiceFrequencyZ(0.0),
+ m_AirThreshold(0.0),
+ m_LastChunkX(0x7fffffff), // Use dummy coords that won't ever be used by real chunks
+ m_LastChunkZ(0x7fffffff)
{
}
diff --git a/src/Generating/StructGen.cpp b/src/Generating/StructGen.cpp
index 2f685c808..2d5a73739 100644
--- a/src/Generating/StructGen.cpp
+++ b/src/Generating/StructGen.cpp
@@ -123,18 +123,18 @@ void cStructGenTrees::GenerateSingleTree(
// Check if the generated image fits the terrain. Only the logs are checked:
for (sSetBlockVector::const_iterator itr = TreeLogs.begin(); itr != TreeLogs.end(); ++itr)
{
- if ((itr->ChunkX != a_ChunkX) || (itr->ChunkZ != a_ChunkZ))
+ if ((itr->m_ChunkX != a_ChunkX) || (itr->m_ChunkZ != a_ChunkZ))
{
// Outside the chunk
continue;
}
- if (itr->y >= cChunkDef::Height)
+ if (itr->m_RelY >= cChunkDef::Height)
{
// Above the chunk, cut off (this shouldn't happen too often, we're limiting trees to y < 230)
continue;
}
- BLOCKTYPE Block = a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z);
+ BLOCKTYPE Block = a_ChunkDesc.GetBlockType(itr->m_RelX, itr->m_RelY, itr->m_RelZ);
switch (Block)
{
CASE_TREE_ALLOWED_BLOCKS:
@@ -167,14 +167,14 @@ void cStructGenTrees::ApplyTreeImage(
// Put the generated image into a_BlockTypes, push things outside this chunk into a_Blocks
for (sSetBlockVector::const_iterator itr = a_Image.begin(), end = a_Image.end(); itr != end; ++itr)
{
- if ((itr->ChunkX == a_ChunkX) && (itr->ChunkZ == a_ChunkZ) && (itr->y < cChunkDef::Height))
+ if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ) && (itr->m_RelY < cChunkDef::Height))
{
// Inside this chunk, integrate into a_ChunkDesc:
- switch (a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z))
+ switch (a_ChunkDesc.GetBlockType(itr->m_RelX, itr->m_RelY, itr->m_RelZ))
{
case E_BLOCK_LEAVES:
{
- if (itr->BlockType != E_BLOCK_LOG)
+ if (itr->m_BlockType != E_BLOCK_LOG)
{
break;
}
@@ -182,7 +182,7 @@ void cStructGenTrees::ApplyTreeImage(
}
CASE_TREE_OVERWRITTEN_BLOCKS:
{
- a_ChunkDesc.SetBlockTypeMeta(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
+ a_ChunkDesc.SetBlockTypeMeta(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
break;
}
diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp
index be8b0cd6b..a10e0f4f1 100644
--- a/src/Generating/Trees.cpp
+++ b/src/Generating/Trees.cpp
@@ -403,17 +403,17 @@ void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a
for (auto itr : a_LogBlocks)
{
// Get the log's X and Z coordinates
- int X = itr.ChunkX * 16 + itr.x;
- int Z = itr.ChunkZ * 16 + itr.z;
+ int X = itr.GetX();
+ int Z = itr.GetZ();
- a_OtherBlocks.push_back(sSetBlock(X, itr.y - 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
- PushCoordBlocks(X, itr.y - 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ a_OtherBlocks.push_back(sSetBlock(X, itr.m_RelY - 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
+ PushCoordBlocks(X, itr.m_RelY - 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
for (int y = -1; y <= 1; y++)
{
- PushCoordBlocks (X, itr.y + y, Z, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ PushCoordBlocks (X, itr.m_RelY + y, Z, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
}
- PushCoordBlocks(X, itr.y + 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
- a_OtherBlocks.push_back(sSetBlock(X, itr.y + 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
+ PushCoordBlocks(X, itr.m_RelY + 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ a_OtherBlocks.push_back(sSetBlock(X, itr.m_RelY + 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
}
// Trunk:
diff --git a/src/Globals.h b/src/Globals.h
index 61f500db9..654ede95f 100644
--- a/src/Globals.h
+++ b/src/Globals.h
@@ -268,33 +268,47 @@ template class SizeChecker<UInt16, 2>;
#include "OSSupport/StackTrace.h"
#else
// Logging functions
-void inline LOGERROR(const char* a_Format, ...) FORMATSTRING(1, 2);
+void inline LOGERROR(const char * a_Format, ...) FORMATSTRING(1, 2);
-void inline LOGERROR(const char* a_Format, ...)
+void inline LOGERROR(const char * a_Format, ...)
{
va_list argList;
va_start(argList, a_Format);
vprintf(a_Format, argList);
+ putchar('\n');
va_end(argList);
}
-void inline LOGWARNING(const char* a_Format, ...) FORMATSTRING(1, 2);
+void inline LOGWARNING(const char * a_Format, ...) FORMATSTRING(1, 2);
-void inline LOGWARNING(const char* a_Format, ...)
+void inline LOGWARNING(const char * a_Format, ...)
{
va_list argList;
va_start(argList, a_Format);
vprintf(a_Format, argList);
+ putchar('\n');
va_end(argList);
}
-void inline LOGD(const char* a_Format, ...) FORMATSTRING(1, 2);
+void inline LOGD(const char * a_Format, ...) FORMATSTRING(1, 2);
-void inline LOGD(const char* a_Format, ...)
+void inline LOGD(const char * a_Format, ...)
{
va_list argList;
va_start(argList, a_Format);
vprintf(a_Format, argList);
+ putchar('\n');
+ va_end(argList);
+}
+
+void inline LOG(const char * a_Format, ...) FORMATSTRING(1, 2);
+
+void inline LOG(const char * a_Format, ...)
+{
+ va_list argList;
+ va_start(argList, a_Format);
+ vprintf(a_Format, argList);
+ putchar('\n');
va_end(argList);
}
@@ -419,6 +433,9 @@ std::unique_ptr<T> make_unique(Args&&... args)
return std::unique_ptr<T>(new T(args...));
}
+// a tick is 50 ms
+using cTickTime = std::chrono::duration<int, std::ratio_multiply<std::chrono::milliseconds::period, std::ratio<50>>>;
+using cTickTimeLong = std::chrono::duration<Int64, cTickTime::period>;
#ifndef TOLUA_TEMPLATE_BIND
#define TOLUA_TEMPLATE_BIND(x)
@@ -436,3 +453,4 @@ std::unique_ptr<T> make_unique(Args&&... args)
+
diff --git a/src/HTTPServer/HTTPMessage.cpp b/src/HTTPServer/HTTPMessage.cpp
index f6c0204ae..d59ca438e 100644
--- a/src/HTTPServer/HTTPMessage.cpp
+++ b/src/HTTPServer/HTTPMessage.cpp
@@ -55,7 +55,7 @@ void cHTTPMessage::AddHeader(const AString & a_Key, const AString & a_Value)
}
else if (Key == "content-length")
{
- m_ContentLength = atoi(m_Headers[Key].c_str());
+ m_ContentLength = static_cast<size_t>(atol(m_Headers[Key].c_str()));
}
}
diff --git a/src/ItemGrid.cpp b/src/ItemGrid.cpp
index d49ea9df1..06971a1ac 100644
--- a/src/ItemGrid.cpp
+++ b/src/ItemGrid.cpp
@@ -646,7 +646,7 @@ void cItemGrid::GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, s
for (int j = 0; j <= NumEnchantments; j++)
{
- cEnchantments Enchantment = cEnchantments::GenerateEnchantmentFromVector(Enchantments, Noise.IntNoise2DInt(NumEnchantments, i));
+ cEnchantments Enchantment = cEnchantments::SelectEnchantmentFromVector(Enchantments, Noise.IntNoise2DInt(NumEnchantments, i));
CurrentLoot.m_Enchantments.Add(Enchantment);
cEnchantments::RemoveEnchantmentWeightFromVector(Enchantments, Enchantment);
cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment);
diff --git a/src/Items/CMakeLists.txt b/src/Items/CMakeLists.txt
index 12a467672..c50ddb372 100644
--- a/src/Items/CMakeLists.txt
+++ b/src/Items/CMakeLists.txt
@@ -10,12 +10,14 @@ SET (SRCS
SET (HDRS
ItemArmor.h
ItemBed.h
+ ItemBigFlower.h
ItemBoat.h
ItemBow.h
ItemBrewingStand.h
ItemBucket.h
ItemCake.h
ItemCauldron.h
+ ItemChest.h
ItemCloth.h
ItemComparator.h
ItemDoor.h
@@ -38,18 +40,21 @@ SET (HDRS
ItemPainting.h
ItemPickaxe.h
ItemPotion.h
+ ItemPumpkin.h
ItemRedstoneDust.h
ItemRedstoneRepeater.h
ItemSapling.h
ItemSeeds.h
ItemShears.h
ItemShovel.h
+ ItemSlab.h
ItemSign.h
ItemSpawnEgg.h
ItemString.h
ItemSugarcane.h
ItemSword.h
- ItemThrowable.h)
+ ItemThrowable.h
+)
if(NOT MSVC)
add_library(Items ${SRCS} ${HDRS})
diff --git a/src/Items/ItemBed.h b/src/Items/ItemBed.h
index 94a14cf16..77d51d744 100644
--- a/src/Items/ItemBed.h
+++ b/src/Items/ItemBed.h
@@ -24,30 +24,36 @@ public:
return true;
}
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
+
+ virtual bool OnPlayerPlace(
+ 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,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
+ int a_CursorX, int a_CursorY, int a_CursorZ
) override
{
+ // Can only be placed on the floor:
if (a_BlockFace != BLOCK_FACE_TOP)
{
- // Can only be placed on the floor
return false;
}
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
- a_BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player->GetYaw());
+ // The "foot" block:
+ sSetBlockVector blks;
+ NIBBLETYPE BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player.GetYaw());
+ blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BED, BlockMeta);
- // Check if there is empty space for the foot section:
- Vector3i Direction = cBlockBedHandler::MetaDataToDirection(a_BlockMeta);
- if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) != E_BLOCK_AIR)
+ // Check if there is empty space for the "head" block:
+ // (Vanilla only allows beds to be placed into air)
+ Vector3i Direction = cBlockBedHandler::MetaDataToDirection(BlockMeta);
+ if (a_World.GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) != E_BLOCK_AIR)
{
return false;
}
+ blks.emplace_back(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, BlockMeta | 0x08);
- a_BlockType = E_BLOCK_BED;
- return true;
+ // Place both bed blocks:
+ return a_Player.PlaceBlocks(blks);
}
} ;
diff --git a/src/Items/ItemBigFlower.h b/src/Items/ItemBigFlower.h
new file mode 100644
index 000000000..4341a1a17
--- /dev/null
+++ b/src/Items/ItemBigFlower.h
@@ -0,0 +1,56 @@
+
+// ItemBigFlower.h
+
+// Declares the cItemBigFlower class representing the cItemHandler for big flowers
+
+
+
+
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemBigFlowerHandler:
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+public:
+ cItemBigFlowerHandler(void):
+ super(E_BLOCK_BIG_FLOWER)
+ {
+ }
+
+
+ virtual bool OnPlayerPlace(
+ 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
+ {
+ // Can only be placed on the floor:
+ if (a_BlockFace != BLOCK_FACE_TOP)
+ {
+ return false;
+ }
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+
+ // Place both blocks atomically:
+ sSetBlockVector blks;
+ blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BIG_FLOWER, a_EquippedItem.m_ItemDamage & 0x07);
+ if (a_BlockY < cChunkDef::Height - 1)
+ {
+ blks.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_BIG_FLOWER, (a_EquippedItem.m_ItemDamage & 0x07) | 0x08);
+ }
+ return a_Player.PlaceBlocks(blks);
+ }
+};
+
+
+
+
diff --git a/src/Items/ItemBucket.h b/src/Items/ItemBucket.h
index 3a533958f..871db821c 100644
--- a/src/Items/ItemBucket.h
+++ b/src/Items/ItemBucket.h
@@ -199,16 +199,16 @@ public:
Vector3i m_Pos;
BLOCKTYPE m_ReplacedBlock;
- virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override
+ virtual bool OnNextBlock(int a_CBBlockX, int a_CBBlockY, int a_CBBlockZ, BLOCKTYPE a_CBBlockType, NIBBLETYPE a_CBBlockMeta, char a_CBEntryFace) override
{
- if (a_BlockType != E_BLOCK_AIR)
+ if (a_CBBlockType != E_BLOCK_AIR)
{
- m_ReplacedBlock = a_BlockType;
- if (!cFluidSimulator::CanWashAway(a_BlockType) && !IsBlockLiquid(a_BlockType))
+ m_ReplacedBlock = a_CBBlockType;
+ if (!cFluidSimulator::CanWashAway(a_CBBlockType) && !IsBlockLiquid(a_CBBlockType))
{
- AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, (eBlockFace)a_EntryFace); // Was an unwashawayable block, can't overwrite it!
+ AddFaceDirection(a_CBBlockX, a_CBBlockY, a_CBBlockZ, (eBlockFace)a_CBEntryFace); // Was an unwashawayable block, can't overwrite it!
}
- m_Pos.Set(a_BlockX, a_BlockY, a_BlockZ); // (Block could be washed away, replace it)
+ m_Pos.Set(a_CBBlockX, a_CBBlockY, a_CBBlockZ); // (Block could be washed away, replace it)
return true; // Abort tracing
}
return false;
diff --git a/src/Items/ItemChest.h b/src/Items/ItemChest.h
new file mode 100644
index 000000000..b6579c423
--- /dev/null
+++ b/src/Items/ItemChest.h
@@ -0,0 +1,167 @@
+
+// ItemChest.h
+
+// Declares the cItemChestHandler class representing the cItemHandler descendant responsible for chests
+
+
+
+
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../Blocks/BlockChest.h"
+
+
+
+
+
+class cItemChestHandler:
+ public cItemHandler
+{
+ typedef cItemHandler super;
+public:
+ cItemChestHandler(int a_ItemType):
+ super(a_ItemType)
+ {
+ }
+
+
+ virtual bool OnPlayerPlace(
+ 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 (a_BlockFace < 0)
+ {
+ // Clicked in air
+ return false;
+ }
+
+ if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
+ {
+ // The clicked block is outside the world, ignore this call altogether (#128)
+ return false;
+ }
+
+ // Check if the block ignores build collision (water, grass etc.):
+ BLOCKTYPE ClickedBlock;
+ NIBBLETYPE ClickedBlockMeta;
+ a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta);
+ 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);
+ }
+ else
+ {
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+
+ if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
+ {
+ // The block is being placed outside the world, ignore this packet altogether (#128)
+ return false;
+ }
+
+ NIBBLETYPE PlaceMeta;
+ BLOCKTYPE PlaceBlock;
+ a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlock, PlaceMeta);
+
+ // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed.
+ // No need to do combinability (dblslab) checks, client will do that here.
+ if (
+ !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision() &&
+ !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(&a_Player, PlaceMeta)
+ )
+ {
+ // 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;
+ }
+ }
+
+ // Check that there is at most one single neighbor of the same chest type:
+ static const Vector3i CrossCoords[] =
+ {
+ {-1, 0, 0},
+ { 0, 0, -1},
+ { 1, 0, 0},
+ { 0, 0, 1},
+ };
+ int NeighborIdx = -1;
+ for (size_t i = 0; i < ARRAYCOUNT(CrossCoords); i++)
+ {
+ if (a_World.GetBlock(a_BlockX + CrossCoords[i].x, a_BlockY, a_BlockZ + CrossCoords[i].z) != m_ItemType)
+ {
+ continue;
+ }
+ if (NeighborIdx >= 0)
+ {
+ // Can't place here, there are already two neighbors, this would form a 3-block chest
+ return false;
+ }
+ NeighborIdx = static_cast<int>(i);
+
+ // Check that this neighbor is a single chest:
+ int bx = a_BlockX + CrossCoords[i].x;
+ int bz = a_BlockZ + CrossCoords[i].z;
+ for (size_t j = 0; j < ARRAYCOUNT(CrossCoords); j++)
+ {
+ if (a_World.GetBlock(bx + CrossCoords[j].x, a_BlockY, bz + CrossCoords[j].z) == m_ItemType)
+ {
+ return false;
+ }
+ } // for j
+ } // for i
+
+ // If there's no chest neighbor, place the single block chest and bail out:
+ BLOCKTYPE ChestBlockType = static_cast<BLOCKTYPE>(m_ItemType);
+ if (NeighborIdx < 0)
+ {
+ NIBBLETYPE Meta = cBlockChestHandler::PlayerYawToMetaData(a_Player.GetYaw());
+ return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta);
+ }
+
+ // There is a neighbor to which we need to adjust
+ double yaw = a_Player.GetYaw();
+ if ((NeighborIdx == 0) || (NeighborIdx == 2))
+ {
+ // The neighbor is in the X axis, form a X-axis-aligned dblchest:
+ NIBBLETYPE Meta = ((yaw >= -90) && (yaw < 90)) ? E_META_CHEST_FACING_ZM : E_META_CHEST_FACING_ZP;
+
+ // Place the new chest:
+ if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta))
+ {
+ return false;
+ }
+
+ // Adjust the existing chest:
+ a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta);
+ return true;
+ }
+
+ // The neighbor is in the Z axis, form a Z-axis-aligned dblchest:
+ NIBBLETYPE Meta = (yaw < 0) ? E_META_CHEST_FACING_XM : E_META_CHEST_FACING_XP;
+
+ // Place the new chest:
+ if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta))
+ {
+ return false;
+ }
+
+ // Adjust the existing chest:
+ a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta);
+ return true;
+ }
+
+private:
+ cItemChestHandler(const cItemChestHandler &) = delete;
+};
+
+
+
+
diff --git a/src/Items/ItemDoor.h b/src/Items/ItemDoor.h
index cd5baf44f..dacf286e5 100644
--- a/src/Items/ItemDoor.h
+++ b/src/Items/ItemDoor.h
@@ -3,6 +3,7 @@
#include "ItemHandler.h"
#include "../World.h"
+#include "../Blocks/BlockDoor.h"
@@ -18,27 +19,43 @@ public:
}
- virtual bool IsPlaceable(void) override
- {
- return true;
- }
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
+ virtual bool OnPlayerPlace(
+ 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,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
+ int a_CursorX, int a_CursorY, int a_CursorZ
) override
{
+ // Vanilla only allows door placement while clicking on the top face of the block below the door:
+ if (a_BlockFace != BLOCK_FACE_TOP)
+ {
+ return false;
+ }
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+
+ // Door (bottom block) can be placed in Y range of [1, 254]:
+ if ((a_BlockY < 1) || (a_BlockY + 2 >= cChunkDef::Height))
+ {
+ return false;
+ }
+
+ // The door needs a compatible block below it:
+ if ((a_BlockY > 0) && !cBlockDoorHandler::CanBeOn(a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)))
+ {
+ return false;
+ }
+
+ // Get the block type of the door to place:
+ BLOCKTYPE BlockType;
switch (m_ItemType)
{
- case E_ITEM_WOODEN_DOOR: a_BlockType = E_BLOCK_WOODEN_DOOR; break;
- case E_ITEM_IRON_DOOR: a_BlockType = E_BLOCK_IRON_DOOR; break;
- case E_ITEM_SPRUCE_DOOR: a_BlockType = E_BLOCK_SPRUCE_DOOR; break;
- case E_ITEM_BIRCH_DOOR: a_BlockType = E_BLOCK_BIRCH_DOOR; break;
- case E_ITEM_JUNGLE_DOOR: a_BlockType = E_BLOCK_JUNGLE_DOOR; break;
- case E_ITEM_DARK_OAK_DOOR: a_BlockType = E_BLOCK_DARK_OAK_DOOR; break;
- case E_ITEM_ACACIA_DOOR: a_BlockType = E_BLOCK_ACACIA_DOOR; break;
+ case E_ITEM_WOODEN_DOOR: BlockType = E_BLOCK_WOODEN_DOOR; break;
+ case E_ITEM_IRON_DOOR: BlockType = E_BLOCK_IRON_DOOR; break;
+ case E_ITEM_SPRUCE_DOOR: BlockType = E_BLOCK_SPRUCE_DOOR; break;
+ case E_ITEM_BIRCH_DOOR: BlockType = E_BLOCK_BIRCH_DOOR; break;
+ case E_ITEM_JUNGLE_DOOR: BlockType = E_BLOCK_JUNGLE_DOOR; break;
+ case E_ITEM_DARK_OAK_DOOR: BlockType = E_BLOCK_DARK_OAK_DOOR; break;
+ case E_ITEM_ACACIA_DOOR: BlockType = E_BLOCK_ACACIA_DOOR; break;
default:
{
ASSERT(!"Unhandled door type");
@@ -46,14 +63,47 @@ public:
}
}
- cChunkInterface ChunkInterface(a_World->GetChunkMap());
- bool Meta = BlockHandler(a_BlockType)->GetPlacementBlockTypeMeta(
- ChunkInterface, a_Player,
- a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
- a_CursorX, a_CursorY, a_CursorZ,
- a_BlockType, a_BlockMeta
- );
- return Meta;
+ // Check the two blocks that will get replaced by the door:
+ BLOCKTYPE LowerBlockType = a_World.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ);
+ BLOCKTYPE UpperBlockType = a_World.GetBlock(a_BlockX, a_BlockY + 2, a_BlockZ);
+ if (
+ !cBlockDoorHandler::CanReplaceBlock(LowerBlockType) ||
+ !cBlockDoorHandler::CanReplaceBlock(UpperBlockType))
+ {
+ return false;
+ }
+
+ // Get the coords of the neighboring blocks:
+ NIBBLETYPE LowerBlockMeta = cBlockDoorHandler::PlayerYawToMetaData(a_Player.GetYaw());
+ Vector3i RelDirToOutside = cBlockDoorHandler::GetRelativeDirectionToOutside(LowerBlockMeta);
+ Vector3i LeftNeighborPos = RelDirToOutside;
+ LeftNeighborPos.TurnCCW();
+ LeftNeighborPos.Move(a_BlockX, a_BlockY, a_BlockZ);
+ Vector3i RightNeighborPos = RelDirToOutside;
+ RightNeighborPos.TurnCW();
+ RightNeighborPos.Move(a_BlockX, a_BlockY, a_BlockZ);
+
+ // Decide whether the hinge is on the left (default) or on the right:
+ NIBBLETYPE UpperBlockMeta = 0x08;
+ if (
+ cBlockDoorHandler::IsDoorBlockType(a_World.GetBlock(LeftNeighborPos)) || // The block to the left is a door block
+ cBlockInfo::IsSolid(a_World.GetBlock(RightNeighborPos)) // The block to the right is solid
+ )
+ {
+ UpperBlockMeta = 0x09; // Upper block | hinge on right
+ }
+
+ // Set the blocks:
+ sSetBlockVector blks;
+ blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, BlockType, LowerBlockMeta);
+ blks.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, BlockType, UpperBlockMeta);
+ return a_Player.PlaceBlocks(blks);
+ }
+
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
}
} ;
diff --git a/src/Items/ItemDye.h b/src/Items/ItemDye.h
index da978040d..bfcd0bac4 100644
--- a/src/Items/ItemDye.h
+++ b/src/Items/ItemDye.h
@@ -55,25 +55,16 @@ public:
return false;
}
- // Check plugins
- if (cRoot::Get()->GetPluginManager()->CallHookPlayerPlacingBlock(*a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, 0, 0, 0, E_BLOCK_COCOA_POD, BlockMeta))
+ // Place the cocoa pod:
+ if (a_Player->PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COCOA_POD, BlockMeta))
{
- a_World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player);
- a_Player->GetInventory().SendEquippedSlot();
- return false;
- }
-
- // Set block and broadcast place sound
- a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COCOA_POD, BlockMeta);
- a_World->BroadcastSoundEffect("dig.stone", a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 1.0f, 0.8f);
-
- // Remove one cocoa pod from the inventory
- if (!a_Player->IsGameModeCreative())
- {
- a_Player->GetInventory().RemoveOneEquippedItem();
+ a_World->BroadcastSoundEffect("dig.stone", a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 1.0f, 0.8f);
+ if (a_Player->IsGameModeSurvival())
+ {
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ }
+ return true;
}
- cRoot::Get()->GetPluginManager()->CallHookPlayerPlacedBlock(*a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, 0, 0, 0, E_BLOCK_COCOA_POD, BlockMeta);
- return true;
}
return false;
}
diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp
index 9272a723d..621cf9501 100644
--- a/src/Items/ItemHandler.cpp
+++ b/src/Items/ItemHandler.cpp
@@ -6,39 +6,43 @@
#include "../Entities/Player.h"
#include "../FastRandom.h"
#include "../BlockInServerPluginInterface.h"
+#include "../Chunk.h"
// Handlers:
#include "ItemArmor.h"
#include "ItemBed.h"
+#include "ItemBigFlower.h"
#include "ItemBoat.h"
#include "ItemBow.h"
#include "ItemBrewingStand.h"
#include "ItemBucket.h"
#include "ItemCake.h"
#include "ItemCauldron.h"
+#include "ItemChest.h"
#include "ItemCloth.h"
#include "ItemComparator.h"
#include "ItemDoor.h"
-#include "ItemMilk.h"
#include "ItemDye.h"
#include "ItemEmptyMap.h"
#include "ItemFishingRod.h"
#include "ItemFlowerPot.h"
#include "ItemFood.h"
#include "ItemGoldenApple.h"
-#include "ItemItemFrame.h"
#include "ItemHoe.h"
+#include "ItemItemFrame.h"
#include "ItemLeaves.h"
#include "ItemLighter.h"
#include "ItemLilypad.h"
#include "ItemMap.h"
+#include "ItemMilk.h"
#include "ItemMinecart.h"
+#include "ItemMobHead.h"
#include "ItemMushroomSoup.h"
#include "ItemNetherWart.h"
#include "ItemPainting.h"
#include "ItemPickaxe.h"
#include "ItemPotion.h"
-#include "ItemThrowable.h"
+#include "ItemPumpkin.h"
#include "ItemRedstoneDust.h"
#include "ItemRedstoneRepeater.h"
#include "ItemSapling.h"
@@ -46,11 +50,12 @@
#include "ItemShears.h"
#include "ItemShovel.h"
#include "ItemSign.h"
-#include "ItemMobHead.h"
+#include "ItemSlab.h"
#include "ItemSpawnEgg.h"
#include "ItemString.h"
#include "ItemSugarcane.h"
#include "ItemSword.h"
+#include "ItemThrowable.h"
#include "../Blocks/BlockHandler.h"
@@ -94,52 +99,59 @@ cItemHandler * cItemHandler::GetItemHandler(int a_ItemType)
-cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
+cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType)
{
switch (a_ItemType)
{
default: return new cItemHandler(a_ItemType);
// Single item per handler, alphabetically sorted:
- case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType);
- case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType);
- case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType);
- case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType);
- case E_ITEM_BED: return new cItemBedHandler(a_ItemType);
- case E_ITEM_BOAT: return new cItemBoatHandler(a_ItemType);
+ case E_BLOCK_BIG_FLOWER: return new cItemBigFlowerHandler;
+ case E_BLOCK_CHEST: return new cItemChestHandler(a_ItemType);
+ case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType);
+ case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType);
+ case E_BLOCK_HEAD: return new cItemMobHeadHandler(a_ItemType);
+ case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType);
+ case E_BLOCK_PUMPKIN: return new cItemPumpkinHandler;
+ case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType);
+ case E_BLOCK_STONE_SLAB: return new cItemSlabHandler(E_BLOCK_STONE_SLAB, E_BLOCK_DOUBLE_STONE_SLAB);
+ case E_BLOCK_TRAPPED_CHEST: return new cItemChestHandler(a_ItemType);
+ case E_BLOCK_WOODEN_SLAB: return new cItemSlabHandler(E_BLOCK_WOODEN_SLAB, E_BLOCK_DOUBLE_WOODEN_SLAB);
+ case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType);
+ case E_ITEM_BED: return new cItemBedHandler(a_ItemType);
+ case E_ITEM_BOAT: return new cItemBoatHandler(a_ItemType);
case E_ITEM_BOTTLE_O_ENCHANTING: return new cItemBottleOEnchantingHandler();
- case E_ITEM_BOW: return new cItemBowHandler();
- case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType);
- case E_ITEM_CAKE: return new cItemCakeHandler(a_ItemType);
- case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType);
- case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType);
- case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType);
- case E_ITEM_EGG: return new cItemEggHandler();
- case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler();
- case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler();
- case E_ITEM_FIRE_CHARGE: return new cItemLighterHandler(a_ItemType);
- case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler();
- case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType);
- case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
- case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType);
- case E_ITEM_GOLDEN_APPLE: return new cItemGoldenAppleHandler();
- case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType);
- case E_ITEM_MAP: return new cItemMapHandler();
- case E_ITEM_MILK: return new cItemMilkHandler();
- case E_ITEM_MUSHROOM_SOUP: return new cItemMushroomSoupHandler(a_ItemType);
- case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType);
- case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType);
- case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType);
- case E_ITEM_POTIONS: return new cItemPotionHandler();
- case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType);
- case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType);
- case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType);
- case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType);
- case E_ITEM_HEAD: return new cItemMobHeadHandler(a_ItemType);
- case E_ITEM_SNOWBALL: return new cItemSnowballHandler();
- case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType);
- case E_ITEM_STRING: return new cItemStringHandler(a_ItemType);
- case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType);
+ case E_ITEM_BOW: return new cItemBowHandler();
+ case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType);
+ case E_ITEM_CAKE: return new cItemCakeHandler(a_ItemType);
+ case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType);
+ case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType);
+ case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType);
+ case E_ITEM_EGG: return new cItemEggHandler();
+ case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler();
+ case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler();
+ case E_ITEM_FIRE_CHARGE: return new cItemLighterHandler(a_ItemType);
+ case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler();
+ case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType);
+ case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
+ case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType);
+ case E_ITEM_GOLDEN_APPLE: return new cItemGoldenAppleHandler();
+ case E_ITEM_MAP: return new cItemMapHandler();
+ case E_ITEM_MILK: return new cItemMilkHandler();
+ case E_ITEM_MUSHROOM_SOUP: return new cItemMushroomSoupHandler(a_ItemType);
+ case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType);
+ case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType);
+ case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType);
+ case E_ITEM_POTIONS: return new cItemPotionHandler();
+ case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType);
+ case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType);
+ case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType);
+ case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType);
+ case E_ITEM_HEAD: return new cItemMobHeadHandler(a_ItemType);
+ case E_ITEM_SNOWBALL: return new cItemSnowballHandler();
+ case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType);
+ case E_ITEM_STRING: return new cItemStringHandler(a_ItemType);
+ case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType);
case E_ITEM_WOODEN_HOE:
case E_ITEM_STONE_HOE:
@@ -297,6 +309,108 @@ cItemHandler::cItemHandler(int a_ItemType)
+bool cItemHandler::OnPlayerPlace(
+ 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
+)
+{
+ if (a_BlockFace < 0)
+ {
+ // Clicked in air
+ return false;
+ }
+
+ if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
+ {
+ // The clicked block is outside the world, ignore this call altogether (#128)
+ return false;
+ }
+
+ BLOCKTYPE ClickedBlock;
+ NIBBLETYPE ClickedBlockMeta;
+
+ a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta);
+
+ // Check if the block ignores build collision (water, grass etc.):
+ 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);
+ }
+ else
+ {
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+
+ if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
+ {
+ // The block is being placed outside the world, ignore this packet altogether (#128)
+ return false;
+ }
+
+ NIBBLETYPE PlaceMeta;
+ BLOCKTYPE PlaceBlock;
+ a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlock, PlaceMeta);
+
+ // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed.
+ // No need to do combinability (dblslab) checks, client will do that here.
+ if (
+ !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision() &&
+ !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(&a_Player, PlaceMeta)
+ )
+ {
+ // 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;
+ }
+ }
+
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (!GetPlacementBlockTypeMeta(&a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
+ {
+ // Handler refused the placement, send that information back to the client:
+ a_World.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, &a_Player);
+ a_Player.GetInventory().SendEquippedSlot();
+ return false;
+ }
+
+ if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta))
+ {
+ // The placement failed, the block has already been re-sent, re-send inventory:
+ a_Player.GetInventory().SendEquippedSlot();
+ return false;
+ }
+
+ AString PlaceSound = cBlockInfo::GetPlaceSound(BlockType);
+ float Volume = 1.0f, Pitch = 0.8f;
+ if (PlaceSound == "dig.metal")
+ {
+ Pitch = 1.2f;
+ PlaceSound = "dig.stone";
+ }
+ else if (PlaceSound == "random.anvil_land")
+ {
+ Volume = 0.65f;
+ }
+
+ a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch);
+
+ // Remove the "placed" item:
+ if (a_Player.IsGameModeSurvival())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+ return true;
+}
+
+
+
+
+
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)
{
UNUSED(a_World);
diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h
index 67c250a97..3ac664798 100644
--- a/src/Items/ItemHandler.h
+++ b/src/Items/ItemHandler.h
@@ -31,10 +31,35 @@ public:
/** Force virtual destructor */
virtual ~cItemHandler() {}
+
+
+ /** Called when the player tries to place the item (right mouse button, IsPlaceable() == true).
+ The default handler uses GetPlacementBlockTypeMeta and places the returned block.
+ Override this function for advanced behavior such as placing multiple blocks.
+ If the block placement is refused inside this call, it will automatically revert the client-side changes.
+ Returns true if the placement succeeded, false if the placement was aborted for any reason. */
+ virtual bool OnPlayerPlace(
+ 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
+ );
+
+ /** Called when the player right-clicks with this item and IsPlaceable() == true, and OnPlace() is not overridden.
+ This function should provide the block type and meta for the placed block, or refuse the placement.
+ Returns true to allow placement, false to refuse. */
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, cPlayer * a_Player,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
+ int a_CursorX, int a_CursorY, int a_CursorZ,
+ BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
+ );
+
+
/** 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 client sends the SHOOT status in the lclk packet */
virtual void OnItemShoot(cPlayer *, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace)
{
@@ -106,18 +131,8 @@ public:
/** Can the anvil repair this item, when a_Item is the second input? */
virtual bool CanRepairWithRawMaterial(short a_ItemType);
- /** Called before a block is placed into a world.
- The handler should return true to allow placement, false to refuse.
- Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block.
- */
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
- int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
- int a_CursorX, int a_CursorY, int a_CursorZ,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
- );
-
- /** Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can't) DEFAULT: False */
+ /** Returns whether this tool / item can harvest a specific block (e.g. iron pickaxe can harvest diamond ore, but wooden one can't).
+ Defaults to false unless overridden. */
virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType);
static cItemHandler * GetItemHandler(int a_ItemType);
diff --git a/src/Items/ItemMobHead.h b/src/Items/ItemMobHead.h
index 4c36fe8d8..8780f7e4b 100644
--- a/src/Items/ItemMobHead.h
+++ b/src/Items/ItemMobHead.h
@@ -3,6 +3,7 @@
#include "ItemHandler.h"
#include "../World.h"
+#include "../BlockEntities/MobHeadEntity.h"
@@ -18,6 +19,313 @@ public:
}
+ virtual bool OnPlayerPlace(
+ 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
+ {
+ // Cannot place a head at "no face" and from the bottom:
+ if ((a_BlockFace == BLOCK_FACE_NONE) || (a_BlockFace == BLOCK_FACE_BOTTOM))
+ {
+ return true;
+ }
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+
+ // If the placed head is a wither, try to spawn the wither first:
+ if (a_EquippedItem.m_ItemDamage == E_META_HEAD_WITHER)
+ {
+ if (TrySpawnWitherAround(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ))
+ {
+ return true;
+ }
+ // Wither not created, proceed with regular head placement
+ }
+
+ return PlaceRegularHead(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ }
+
+
+ /** Places a regular head block with no mob spawning checking. */
+ bool PlaceRegularHead(
+ cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ )
+ {
+ // Place the block:
+ if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_HEAD, BlockFaceToBlockMeta(a_BlockFace)))
+ {
+ return false;
+ }
+
+ // Use a callback to set the properties of the mob head block entity:
+ class cCallback : public cBlockEntityCallback
+ {
+ cPlayer & m_Player;
+ eMobHeadType m_HeadType;
+ NIBBLETYPE m_BlockMeta;
+
+ virtual bool Item(cBlockEntity * a_BlockEntity)
+ {
+ if (a_BlockEntity->GetBlockType() != E_BLOCK_HEAD)
+ {
+ return false;
+ }
+ cMobHeadEntity * MobHeadEntity = static_cast<cMobHeadEntity *>(a_BlockEntity);
+
+ int Rotation = 0;
+ if (m_BlockMeta == 1)
+ {
+ Rotation = FloorC(m_Player.GetYaw() * 16.0f / 360.0f + 0.5f) & 0x0f;
+ }
+
+ MobHeadEntity->SetType(m_HeadType);
+ MobHeadEntity->SetRotation(static_cast<eMobHeadRotation>(Rotation));
+ MobHeadEntity->GetWorld()->BroadcastBlockEntity(MobHeadEntity->GetPosX(), MobHeadEntity->GetPosY(), MobHeadEntity->GetPosZ());
+ return false;
+ }
+
+ public:
+ cCallback (cPlayer & a_CBPlayer, eMobHeadType a_HeadType, NIBBLETYPE a_BlockMeta) :
+ m_Player(a_CBPlayer),
+ m_HeadType(a_HeadType),
+ m_BlockMeta(a_BlockMeta)
+ {}
+ };
+ cCallback Callback(a_Player, static_cast<eMobHeadType>(a_EquippedItem.m_ItemDamage), static_cast<NIBBLETYPE>(a_BlockFace));
+ a_World.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback);
+ return true;
+ }
+
+
+ /** Spawns a wither if the wither skull placed at the specified coords completes wither's spawning formula.
+ Returns true if the wither was created. */
+ bool TrySpawnWitherAround(
+ cWorld & a_World, cPlayer & a_Player,
+ int a_BlockX, int a_BlockY, int a_BlockZ
+ )
+ {
+ // No wither can be created at Y < 2 - not enough space for the formula:
+ if (a_BlockY < 2)
+ {
+ return false;
+ }
+
+ // Check for all relevant wither locations:
+ static const Vector3i RelCoords[] =
+ {
+ { 0, 0, 0},
+ { 1, 0, 0},
+ {-1, 0, 0},
+ { 0, 0, 1},
+ { 0, 0, -1},
+ };
+ for (size_t i = 0; i < ARRAYCOUNT(RelCoords); ++i)
+ {
+ if (TrySpawnWitherAt(
+ a_World, a_Player,
+ a_BlockX, a_BlockY, a_BlockZ,
+ RelCoords[i].x, RelCoords[i].z
+ ))
+ {
+ return true;
+ }
+ } // for i - Coords[]
+
+ return false;
+ }
+
+
+ /** Tries to spawn a wither at the specified offset from the placed head block.
+ PlacedHead coords are used to override the block query - at those coords the block is not queried from the world,
+ but assumed to be a head instead.
+ Offset is used to shift the image around the X and Z axis.
+ Returns true iff the wither was created successfully. */
+ bool TrySpawnWitherAt(
+ cWorld & a_World, cPlayer & a_Player,
+ int a_PlacedHeadX, int a_PlacedHeadY, int a_PlacedHeadZ,
+ int a_OffsetX, int a_OffsetZ
+ )
+ {
+ // Image for the wither at the X axis:
+ static const sSetBlock ImageWitherX[] =
+ {
+ {-1, 0, 0, E_BLOCK_HEAD, 0},
+ { 0, 0, 0, E_BLOCK_HEAD, 0},
+ { 1, 0, 0, E_BLOCK_HEAD, 0},
+ {-1, -1, 0, E_BLOCK_SOULSAND, 0},
+ { 0, -1, 0, E_BLOCK_SOULSAND, 0},
+ { 1, -1, 0, E_BLOCK_SOULSAND, 0},
+ {-1, -2, 0, E_BLOCK_AIR, 0},
+ { 0, -2, 0, E_BLOCK_SOULSAND, 0},
+ { 1, -2, 0, E_BLOCK_AIR, 0},
+ };
+
+ // Image for the wither at the Z axis:
+ static const sSetBlock ImageWitherZ[] =
+ {
+ { 0, 0, -1, E_BLOCK_HEAD, 0},
+ { 0, 0, 0, E_BLOCK_HEAD, 0},
+ { 0, 0, 1, E_BLOCK_HEAD, 0},
+ { 0, -1, -1, E_BLOCK_SOULSAND, 0},
+ { 0, -1, 0, E_BLOCK_SOULSAND, 0},
+ { 0, -1, 1, E_BLOCK_SOULSAND, 0},
+ { 0, -2, -1, E_BLOCK_AIR, 0},
+ { 0, -2, 0, E_BLOCK_SOULSAND, 0},
+ { 0, -2, 1, E_BLOCK_AIR, 0},
+ };
+
+ // Try to spawn the wither from each image:
+ return (
+ TrySpawnWitherFromImage(
+ a_World, a_Player, ImageWitherX, ARRAYCOUNT(ImageWitherX),
+ a_PlacedHeadX, a_PlacedHeadY, a_PlacedHeadZ,
+ a_OffsetX, a_OffsetZ
+ ) ||
+ TrySpawnWitherFromImage(
+ a_World, a_Player, ImageWitherZ, ARRAYCOUNT(ImageWitherZ),
+ a_PlacedHeadX, a_PlacedHeadY, a_PlacedHeadZ,
+ a_OffsetX, a_OffsetZ
+ )
+ );
+ }
+
+
+ /** Tries to spawn a wither from the specified image at the specified offset from the placed head block.
+ PlacedHead coords are used to override the block query - at those coords the block is not queried from the world,
+ but assumed to be a head instead.
+ Offset is used to shift the image around the X and Z axis.
+ Returns true iff the wither was created successfully. */
+ bool TrySpawnWitherFromImage(
+ cWorld & a_World, cPlayer & a_Player, const sSetBlock * a_Image, size_t a_ImageCount,
+ int a_PlacedHeadX, int a_PlacedHeadY, int a_PlacedHeadZ,
+ int a_OffsetX, int a_OffsetZ
+ )
+ {
+ // Check each block individually; simultaneously build the SetBlockVector for clearing the blocks:
+ sSetBlockVector AirBlocks;
+ AirBlocks.reserve(a_ImageCount);
+ for (size_t i = 0; i < a_ImageCount; i++)
+ {
+ // Get the absolute coords of the image:
+ int BlockX = a_PlacedHeadX + a_OffsetX + a_Image[i].GetX();
+ int BlockY = a_PlacedHeadY + a_Image[i].GetY();
+ int BlockZ = a_PlacedHeadZ + a_OffsetZ + a_Image[i].GetZ();
+
+ // If the query is for the placed head, short-circuit-evaluate it:
+ if ((BlockX == a_PlacedHeadX) && (BlockY == a_PlacedHeadY) && (BlockZ == a_PlacedHeadZ))
+ {
+ if (a_Image[i].m_BlockType != E_BLOCK_HEAD)
+ {
+ return false; // Didn't match
+ }
+ continue; // Matched, continue checking the rest of the image
+ }
+
+ // Query the world block:
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (!a_World.GetBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta))
+ {
+ // Cannot query block, assume unloaded chunk, fail to spawn the wither
+ return false;
+ }
+
+ // Compare the world block:
+ if (BlockType != a_Image[i].m_BlockType)
+ {
+ return false;
+ }
+
+ // If it is a mob head, check the correct head type using the block entity:
+ if (BlockType == E_BLOCK_HEAD)
+ {
+ class cHeadCallback: public cBlockEntityCallback
+ {
+ virtual bool Item(cBlockEntity * a_Entity) override
+ {
+ ASSERT(a_Entity->GetBlockType() == E_BLOCK_HEAD);
+ cMobHeadEntity * MobHead = static_cast<cMobHeadEntity *>(a_Entity);
+ m_IsWitherHead = (MobHead->GetType() == SKULL_TYPE_WITHER);
+ return true;
+ }
+ public:
+ cHeadCallback(void):
+ m_IsWitherHead(false)
+ {
+ }
+ bool m_IsWitherHead;
+ } callback;
+ a_World.DoWithBlockEntityAt(BlockX, BlockY, BlockZ, callback);
+ if (!callback.m_IsWitherHead)
+ {
+ return false;
+ }
+ }
+ // Matched, continue checking
+ AirBlocks.emplace_back(BlockX, BlockY, BlockZ, E_BLOCK_AIR, 0);
+ } // for i - a_Image
+
+ // All image blocks matched, try replace the image with air blocks:
+ if (!a_Player.PlaceBlocks(AirBlocks))
+ {
+ return false;
+ }
+
+ // Spawn the wither:
+ int BlockX = a_PlacedHeadX + a_OffsetX;
+ int BlockZ = a_PlacedHeadZ + a_OffsetZ;
+ a_World.SpawnMob(static_cast<double>(BlockX) + 0.5, a_PlacedHeadY - 2, static_cast<double>(BlockZ) + 0.5, mtWither);
+ AwardSpawnWitherAchievement(a_World, BlockX, a_PlacedHeadY - 2, BlockZ);
+ return true;
+ }
+
+
+ /** Awards the achievement to all players close to the specified point. */
+ void AwardSpawnWitherAchievement(cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ class cPlayerCallback : public cPlayerListCallback
+ {
+ Vector3f m_Pos;
+
+ virtual bool Item(cPlayer * a_Player)
+ {
+ // If player is close, award achievement:
+ double Dist = (a_Player->GetPosition() - m_Pos).Length();
+ if (Dist < 50.0)
+ {
+ a_Player->AwardAchievement(achSpawnWither);
+ }
+ return false;
+ }
+
+ public:
+ cPlayerCallback(const Vector3f & a_Pos) : m_Pos(a_Pos) {}
+ } PlayerCallback(Vector3f(static_cast<float>(a_BlockX), static_cast<float>(a_BlockY), static_cast<float>(a_BlockZ)));
+ a_World.ForEachPlayer(PlayerCallback);
+ }
+
+
+ /** Converts the block face of the placement (which face of the block was clicked to place the head)
+ into the block's metadata value. */
+ static NIBBLETYPE BlockFaceToBlockMeta(int a_BlockFace)
+ {
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_TOP: return 0x01; // On ground (rotation provided in block entity)
+ case BLOCK_FACE_XM: return 0x04; // west wall, facing east
+ case BLOCK_FACE_XP: return 0x05; // east wall, facing west
+ case BLOCK_FACE_ZM: return 0x02; // north wall, facing south
+ case BLOCK_FACE_ZP: return 0x03; // south wall, facing north
+ default:
+ {
+ ASSERT(!"Unhandled block face");
+ return 0;
+ }
+ }
+ }
+
+
virtual bool IsPlaceable(void) override
{
return true;
diff --git a/src/Items/ItemPumpkin.h b/src/Items/ItemPumpkin.h
new file mode 100644
index 000000000..fa00179d3
--- /dev/null
+++ b/src/Items/ItemPumpkin.h
@@ -0,0 +1,156 @@
+
+// ItemPumpkin.h
+
+// Declares the cItemPumpkinHandler class representing the pumpkin block in its item form
+
+
+
+
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemPumpkinHandler:
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+public:
+ cItemPumpkinHandler(void):
+ super(E_BLOCK_PUMPKIN)
+ {
+ }
+
+
+ virtual bool OnPlayerPlace(
+ 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
+ {
+ // First try spawning a snow golem or an iron golem:
+ int PlacedBlockX = a_BlockX;
+ int PlacedBlockY = a_BlockY;
+ int PlacedBlockZ = a_BlockZ;
+ AddFaceDirection(PlacedBlockX, PlacedBlockY, PlacedBlockZ, a_BlockFace);
+ if (TrySpawnGolem(a_World, a_Player, PlacedBlockX, PlacedBlockY, PlacedBlockZ))
+ {
+ // The client thinks that they placed the pumpkin, let them know it's been replaced:
+ a_Player.SendBlocksAround(PlacedBlockX, PlacedBlockY, PlacedBlockZ);
+ return true;
+ }
+
+ // No golem at these coords, place the block normally:
+ return super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
+ }
+
+
+ /** Spawns a snow / iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin.
+ Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched. */
+ bool TrySpawnGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ // A golem can't form with a pumpkin below level 2 or above level 255
+ if ((a_BlockY < 2) || (a_BlockY >= cChunkDef::Height))
+ {
+ return false;
+ }
+
+ // Decide which golem to try spawning based on the block below the placed pumpkin:
+ switch (a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))
+ {
+ case E_BLOCK_SNOW_BLOCK: return TrySpawnSnowGolem(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ);
+ case E_BLOCK_IRON_BLOCK: return TrySpawnIronGolem(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ);
+ default:
+ {
+ // No golem here
+ return false;
+ }
+ }
+ }
+
+
+ /** Spawns a snow golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin.
+ Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched.
+ Assumes that the block below the specified block has already been checked and is a snow block. */
+ bool TrySpawnSnowGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ // Need one more snow block 2 blocks below the pumpkin:
+ if (a_World.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) != E_BLOCK_SNOW_BLOCK)
+ {
+ return false;
+ }
+
+ // Try to place air blocks where the original recipe blocks were:
+ sSetBlockVector AirBlocks;
+ AirBlocks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); // Head
+ AirBlocks.emplace_back(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); // Torso
+ AirBlocks.emplace_back(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Legs
+ if (!a_Player.PlaceBlocks(AirBlocks))
+ {
+ return false;
+ }
+
+ // Spawn the golem:
+ a_World.SpawnMob(static_cast<double>(a_BlockX) + 0.5, a_BlockY - 2, static_cast<double>(a_BlockZ) + 0.5, mtSnowGolem);
+ return true;
+ }
+
+
+ /** Spawns an iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin.
+ Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched.
+ Assumes that the block below the specified block has already been checked and is an iron block. */
+ bool TrySpawnIronGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ // Need one more iron block 2 blocks below the pumpkin:
+ if (a_World.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) != E_BLOCK_IRON_BLOCK)
+ {
+ return false;
+ }
+
+ // Check the two arm directions (X, Z) using a loop over two sets of offset vectors:
+ static const Vector3i ArmOffsets[] =
+ {
+ {1, 0, 0},
+ {0, 0, 1},
+ };
+ for (size_t i = 0; i < ARRAYCOUNT(ArmOffsets); i++)
+ {
+ // If the arm blocks don't match, bail out of this loop repetition:
+ if (
+ (a_World.GetBlock(a_BlockX + ArmOffsets[i].x, a_BlockY - 1, a_BlockZ + ArmOffsets[i].z) != E_BLOCK_IRON_BLOCK) ||
+ (a_World.GetBlock(a_BlockX - ArmOffsets[i].x, a_BlockY - 1, a_BlockZ - ArmOffsets[i].z) != E_BLOCK_IRON_BLOCK)
+ )
+ {
+ continue;
+ }
+
+ // Try to place air blocks where the original recipe blocks were:
+ sSetBlockVector AirBlocks;
+ AirBlocks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); // Head
+ AirBlocks.emplace_back(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); // Torso
+ AirBlocks.emplace_back(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Legs
+ AirBlocks.emplace_back(a_BlockX + ArmOffsets[i].x, a_BlockY - 1, a_BlockZ + ArmOffsets[i].z, E_BLOCK_AIR, 0); // Arm
+ AirBlocks.emplace_back(a_BlockX - ArmOffsets[i].x, a_BlockY - 1, a_BlockZ - ArmOffsets[i].z, E_BLOCK_AIR, 0); // Arm
+ if (!a_Player.PlaceBlocks(AirBlocks))
+ {
+ return false;
+ }
+
+ // Spawn the golem:
+ a_World.SpawnMob(static_cast<double>(a_BlockX) + 0.5, a_BlockY - 2, static_cast<double>(a_BlockZ) + 0.5, mtIronGolem);
+ return true;
+ } // for i - ArmOffsets[]
+
+ // Neither arm offset matched, this thing is not a complete golem
+ return false;
+ }
+};
+
+
+
+
diff --git a/src/Items/ItemRedstoneDust.h b/src/Items/ItemRedstoneDust.h
index a2289239c..6d5fb521f 100644
--- a/src/Items/ItemRedstoneDust.h
+++ b/src/Items/ItemRedstoneDust.h
@@ -27,7 +27,20 @@ public:
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
- if (!cBlockInfo::FullyOccupiesVoxel(a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) // Some solid blocks, such as cocoa beans, are not suitable for dust
+ // Check if coords are out of range:
+ if ((a_BlockY <= 0) || (a_BlockY >= cChunkDef::Height))
+ {
+ return false;
+ }
+
+ // Check the block below, if it supports dust on top of it:
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (!a_World->GetBlockTypeMeta(a_BlockX, a_BlockY - 1, a_BlockZ, BlockType, BlockMeta))
+ {
+ return false;
+ }
+ if (!IsBlockTypeUnderSuitable(BlockType, BlockMeta))
{
return false;
}
@@ -36,6 +49,28 @@ public:
a_BlockMeta = 0;
return true;
}
+
+
+ /** Returns true if the specified block type / meta is suitable to have redstone dust on top of it. */
+ static bool IsBlockTypeUnderSuitable(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+ {
+ if (cBlockInfo::FullyOccupiesVoxel(a_BlockType))
+ {
+ return true;
+ }
+
+ switch (a_BlockType)
+ {
+ case E_BLOCK_NEW_STONE_SLAB:
+ case E_BLOCK_WOODEN_SLAB:
+ case E_BLOCK_STONE_SLAB:
+ {
+ // Slabs can support redstone if they're upside down:
+ return ((a_BlockMeta & 0x08) != 0);
+ }
+ }
+ return false;
+ }
} ;
diff --git a/src/Items/ItemSign.h b/src/Items/ItemSign.h
index 0fa0fa0be..dabbdbba1 100644
--- a/src/Items/ItemSign.h
+++ b/src/Items/ItemSign.h
@@ -13,13 +13,33 @@
class cItemSignHandler :
public cItemHandler
{
+ typedef cItemHandler super;
public:
cItemSignHandler(int a_ItemType) :
- cItemHandler(a_ItemType)
+ super(a_ItemType)
{
}
+ virtual bool OnPlayerPlace(
+ 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
+ )
+ {
+ // 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))
+ {
+ return false;
+ }
+
+ // After successfully placing the sign, open the sign editor for the player:
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ a_Player.GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
+ return true;
+ }
+
+
virtual bool IsPlaceable(void) override
{
return true;
diff --git a/src/Items/ItemSlab.h b/src/Items/ItemSlab.h
new file mode 100644
index 000000000..1b68b9d0c
--- /dev/null
+++ b/src/Items/ItemSlab.h
@@ -0,0 +1,93 @@
+
+// ItemSlab.h
+
+// Declares the cItemSlabHandler responsible for handling slabs, when in their item form.
+
+
+
+
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../Blocks/BlockSlab.h"
+
+
+
+
+
+class cItemSlabHandler:
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+public:
+
+ /** Creates a new handler for the specified slab item type.
+ Sets the handler to use the specified doubleslab block type for combining self into doubleslabs. */
+ cItemSlabHandler(int a_ItemType, BLOCKTYPE a_DoubleSlabBlockType):
+ super(a_ItemType),
+ m_DoubleSlabBlockType(a_DoubleSlabBlockType)
+ {
+ }
+
+
+ // cItemHandler overrides:
+ virtual bool OnPlayerPlace(
+ 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
+ {
+ // 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 (
+ (ClickedBlockType == m_ItemType) && // Placing the same slab material
+ (ClickedBlockMeta == a_EquippedItem.m_ItemDamage) // Placing the same slab sub-kind (and existing slab is single)
+ )
+ {
+ // If clicking the top side of a bottom-half slab, combine into a doubleslab:
+ if (
+ (a_BlockFace == BLOCK_FACE_TOP) &&
+ ((ClickedBlockMeta & 0x08) == 0)
+ )
+ {
+ return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07);
+ }
+
+ // If clicking the bottom side of a top-half slab, combine into a doubleslab:
+ if (
+ (a_BlockFace == BLOCK_FACE_BOTTOM) &&
+ ((ClickedBlockMeta & 0x08) != 0)
+ )
+ {
+ return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07);
+ }
+ }
+
+ // The slabs didn't combine, use the default handler to place the slab:
+ bool res = super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
+
+ /*
+ The client has a bug when a slab replaces snow and there's a slab above it.
+ The client then combines the slab above, rather than replacing the snow.
+ We send the block above the currently placed block back to the client to fix the bug.
+ Ref.: http://forum.mc-server.org/showthread.php?tid=434&pid=17388#pid17388
+ */
+ if ((a_BlockFace == BLOCK_FACE_TOP) && (a_BlockY < cChunkDef::Height - 1))
+ {
+ a_Player.SendBlocksAround(a_BlockX, a_BlockY + 1, a_BlockZ, 1);
+ }
+ return res;
+ }
+
+protected:
+ /** The block type to use when the slab combines into a doubleslab block. */
+ BLOCKTYPE m_DoubleSlabBlockType;
+};
+
+
+
+
diff --git a/src/Items/ItemSpawnEgg.h b/src/Items/ItemSpawnEgg.h
index 617ecd808..dee8a9057 100644
--- a/src/Items/ItemSpawnEgg.h
+++ b/src/Items/ItemSpawnEgg.h
@@ -64,11 +64,13 @@ public:
case E_META_SPAWN_EGG_CREEPER: return mtCreeper;
case E_META_SPAWN_EGG_ENDERMAN: return mtEnderman;
case E_META_SPAWN_EGG_GHAST: return mtGhast;
+ case E_META_SPAWN_EGG_GUARDIAN: return mtGuardian;
case E_META_SPAWN_EGG_HORSE: return mtHorse;
case E_META_SPAWN_EGG_MAGMA_CUBE: return mtMagmaCube;
case E_META_SPAWN_EGG_MOOSHROOM: return mtMooshroom;
case E_META_SPAWN_EGG_OCELOT: return mtOcelot;
case E_META_SPAWN_EGG_PIG: return mtPig;
+ case E_META_SPAWN_EGG_RABBIT: return mtRabbit;
case E_META_SPAWN_EGG_SHEEP: return mtSheep;
case E_META_SPAWN_EGG_SILVERFISH: return mtSilverfish;
case E_META_SPAWN_EGG_SKELETON: return mtSkeleton;
diff --git a/src/LightingThread.cpp b/src/LightingThread.cpp
index ced95d4e1..effde03d0 100644
--- a/src/LightingThread.cpp
+++ b/src/LightingThread.cpp
@@ -197,7 +197,7 @@ void cLightingThread::Execute(void)
{
{
cCSLock Lock(m_CS);
- if (m_Queue.size() == 0)
+ if (m_Queue.empty())
{
cCSUnlock Unlock(Lock);
m_evtItemAdded.Wait();
diff --git a/src/LineBlockTracer.cpp b/src/LineBlockTracer.cpp
index 90f97cd23..e43a79566 100644
--- a/src/LineBlockTracer.cpp
+++ b/src/LineBlockTracer.cpp
@@ -15,7 +15,23 @@
cLineBlockTracer::cLineBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks) :
- super(a_World, a_Callbacks)
+ super(a_World, a_Callbacks),
+ m_StartX(0.0),
+ m_StartY(0.0),
+ m_StartZ(0.0),
+ m_EndX(0.0),
+ m_EndY(0.0),
+ m_EndZ(0.0),
+ m_DiffX(0.0),
+ m_DiffY(0.0),
+ m_DiffZ(0.0),
+ m_DirX(0),
+ m_DirY(0),
+ m_DirZ(0),
+ m_CurrentX(0),
+ m_CurrentY(0),
+ m_CurrentZ(0),
+ m_CurrentFace(0)
{
}
diff --git a/src/MobProximityCounter.cpp b/src/MobProximityCounter.cpp
index 82ba771ff..cfd52440b 100644
--- a/src/MobProximityCounter.cpp
+++ b/src/MobProximityCounter.cpp
@@ -50,7 +50,7 @@ cMobProximityCounter::sIterablePair cMobProximityCounter::getMobWithinThosesDist
a_DistanceMin *= a_DistanceMin;// this is because is use square distance
a_DistanceMax *= a_DistanceMax;
- if (m_DistanceToMonster.size() <= 0)
+ if (m_DistanceToMonster.empty())
{
convertMaps();
}
diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp
index ee9e569a7..0a32d17ef 100644
--- a/src/MobSpawner.cpp
+++ b/src/MobSpawner.cpp
@@ -83,6 +83,7 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
addIfAllowed(mtSkeleton, allowedMobs);
addIfAllowed(mtCreeper, allowedMobs);
addIfAllowed(mtSquid, allowedMobs);
+ addIfAllowed(mtGuardian, allowedMobs);
if ((a_Biome != biDesert) && (a_Biome != biBeach) && (a_Biome != biOcean))
{
@@ -91,6 +92,7 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
addIfAllowed(mtCow, allowedMobs);
addIfAllowed(mtChicken, allowedMobs);
addIfAllowed(mtEnderman, allowedMobs);
+ addIfAllowed(mtRabbit, allowedMobs);
addIfAllowed(mtSlime, allowedMobs); // MG TODO : much more complicated rule
if ((a_Biome == biForest) || (a_Biome == biForestHills) || (a_Biome == biTaiga) || (a_Biome == biTaigaHills))
@@ -144,6 +146,11 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
switch (a_MobType)
{
+ case mtGuardian:
+ {
+ return IsBlockWater(TargetBlock) && (a_RelY >= 45) && (a_RelY <= 62);
+ }
+
case mtSquid:
{
return IsBlockWater(TargetBlock) && (a_RelY >= 45) && (a_RelY <= 62);
@@ -158,6 +165,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
case mtCow:
case mtPig:
case mtHorse:
+ case mtRabbit:
case mtSheep:
{
return (
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp
index 7ca7a9d66..72317d66b 100644
--- a/src/Mobs/AggressiveMonster.cpp
+++ b/src/Mobs/AggressiveMonster.cpp
@@ -22,7 +22,7 @@ cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, eMonsterTyp
// What to do if in Chasing State
-void cAggressiveMonster::InStateChasing(float a_Dt)
+void cAggressiveMonster::InStateChasing(std::chrono::milliseconds a_Dt)
{
super::InStateChasing(a_Dt);
@@ -61,7 +61,7 @@ void cAggressiveMonster::EventSeePlayer(cEntity * a_Entity)
-void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk)
+void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
@@ -93,9 +93,9 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk)
-void cAggressiveMonster::Attack(float a_Dt)
+void cAggressiveMonster::Attack(std::chrono::milliseconds a_Dt)
{
- m_AttackInterval += a_Dt * m_AttackRate;
+ m_AttackInterval += a_Dt.count() * m_AttackRate;
if ((m_Target == nullptr) || (m_AttackInterval < 3.0))
{
diff --git a/src/Mobs/AggressiveMonster.h b/src/Mobs/AggressiveMonster.h
index 2549ba2d3..f64c1103f 100644
--- a/src/Mobs/AggressiveMonster.h
+++ b/src/Mobs/AggressiveMonster.h
@@ -16,11 +16,11 @@ public:
cAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
- virtual void Tick (float a_Dt, cChunk & a_Chunk) override;
- virtual void InStateChasing(float a_Dt) override;
+ virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ virtual void InStateChasing(std::chrono::milliseconds a_Dt) override;
virtual void EventSeePlayer(cEntity *) override;
- virtual void Attack(float a_Dt);
+ virtual void Attack(std::chrono::milliseconds a_Dt);
protected:
/** Whether this mob's destination is the same as its target's position. */
diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp
index 1fa9d2c37..172ccd071 100644
--- a/src/Mobs/Blaze.cpp
+++ b/src/Mobs/Blaze.cpp
@@ -30,9 +30,9 @@ void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-void cBlaze::Attack(float a_Dt)
+void cBlaze::Attack(std::chrono::milliseconds a_Dt)
{
- m_AttackInterval += a_Dt * m_AttackRate;
+ m_AttackInterval += a_Dt.count() * m_AttackRate;
if ((m_Target != nullptr) && (m_AttackInterval > 3.0))
{
diff --git a/src/Mobs/Blaze.h b/src/Mobs/Blaze.h
index e2a4ad9f1..493953a14 100644
--- a/src/Mobs/Blaze.h
+++ b/src/Mobs/Blaze.h
@@ -18,5 +18,5 @@ public:
CLASS_PROTODEF(cBlaze)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual void Attack(float a_Dt) override;
+ virtual void Attack(std::chrono::milliseconds a_Dt) override;
} ;
diff --git a/src/Mobs/CMakeLists.txt b/src/Mobs/CMakeLists.txt
index bbbb9287a..7a291dcf2 100644
--- a/src/Mobs/CMakeLists.txt
+++ b/src/Mobs/CMakeLists.txt
@@ -16,6 +16,7 @@ SET (SRCS
Enderman.cpp
Ghast.cpp
Giant.cpp
+ Guardian.cpp
Horse.cpp
IronGolem.cpp
MagmaCube.cpp
@@ -24,6 +25,7 @@ SET (SRCS
PassiveAggressiveMonster.cpp
PassiveMonster.cpp
Pig.cpp
+ Rabbit.cpp
Sheep.cpp
Skeleton.cpp
Slime.cpp
@@ -49,6 +51,7 @@ SET (HDRS
Enderman.h
Ghast.h
Giant.h
+ Guardian.h
Horse.h
IncludeAllMonsters.h
IronGolem.h
@@ -60,6 +63,7 @@ SET (HDRS
PassiveAggressiveMonster.h
PassiveMonster.h
Pig.h
+ Rabbit.h
Sheep.h
Silverfish.h
Skeleton.h
diff --git a/src/Mobs/CaveSpider.cpp b/src/Mobs/CaveSpider.cpp
index 045b47e73..fa530db82 100644
--- a/src/Mobs/CaveSpider.cpp
+++ b/src/Mobs/CaveSpider.cpp
@@ -16,7 +16,7 @@ cCaveSpider::cCaveSpider(void) :
-void cCaveSpider::Tick(float a_Dt, cChunk & a_Chunk)
+void cCaveSpider::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
@@ -27,7 +27,7 @@ void cCaveSpider::Tick(float a_Dt, cChunk & a_Chunk)
-void cCaveSpider::Attack(float a_Dt)
+void cCaveSpider::Attack(std::chrono::milliseconds a_Dt)
{
super::Attack(a_Dt);
diff --git a/src/Mobs/CaveSpider.h b/src/Mobs/CaveSpider.h
index 494ba1360..d3e56fd2b 100644
--- a/src/Mobs/CaveSpider.h
+++ b/src/Mobs/CaveSpider.h
@@ -16,8 +16,8 @@ public:
CLASS_PROTODEF(cCaveSpider)
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
- virtual void Attack(float a_Dt) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ virtual void Attack(std::chrono::milliseconds a_Dt) override;
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
} ;
diff --git a/src/Mobs/Chicken.cpp b/src/Mobs/Chicken.cpp
index 634867cfa..b2b21d4ae 100644
--- a/src/Mobs/Chicken.cpp
+++ b/src/Mobs/Chicken.cpp
@@ -18,7 +18,7 @@ cChicken::cChicken(void) :
-void cChicken::Tick(float a_Dt, cChunk & a_Chunk)
+void cChicken::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
diff --git a/src/Mobs/Chicken.h b/src/Mobs/Chicken.h
index 07b921884..9349187c6 100644
--- a/src/Mobs/Chicken.h
+++ b/src/Mobs/Chicken.h
@@ -17,7 +17,7 @@ public:
CLASS_PROTODEF(cChicken)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_SEEDS); }
diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp
index a073224cf..c4ae47f2f 100644
--- a/src/Mobs/Creeper.cpp
+++ b/src/Mobs/Creeper.cpp
@@ -23,7 +23,7 @@ cCreeper::cCreeper(void) :
-void cCreeper::Tick(float a_Dt, cChunk & a_Chunk)
+void cCreeper::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
@@ -119,7 +119,7 @@ bool cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI)
-void cCreeper::Attack(float a_Dt)
+void cCreeper::Attack(std::chrono::milliseconds a_Dt)
{
UNUSED(a_Dt);
diff --git a/src/Mobs/Creeper.h b/src/Mobs/Creeper.h
index bf3272e22..1827c416e 100644
--- a/src/Mobs/Creeper.h
+++ b/src/Mobs/Creeper.h
@@ -19,8 +19,8 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
- virtual void Attack(float a_Dt) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Attack(std::chrono::milliseconds a_Dt) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void OnRightClicked(cPlayer & a_Player) override;
bool IsBlowing(void) const {return m_bIsBlowing; }
diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp
index 56ea10245..42c33884a 100644
--- a/src/Mobs/Enderman.cpp
+++ b/src/Mobs/Enderman.cpp
@@ -186,7 +186,7 @@ bool cEnderman::CheckLight()
-void cEnderman::Tick(float a_Dt, cChunk & a_Chunk)
+void cEnderman::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
diff --git a/src/Mobs/Enderman.h b/src/Mobs/Enderman.h
index 28bbceb84..8ccbf2ce7 100644
--- a/src/Mobs/Enderman.h
+++ b/src/Mobs/Enderman.h
@@ -21,7 +21,7 @@ public:
virtual void CheckEventSeePlayer(void) override;
virtual void CheckEventLostPlayer(void) override;
virtual void EventLosePlayer(void) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
bool IsScreaming(void) const {return m_bIsScreaming; }
BLOCKTYPE GetCarriedBlock(void) const {return CarriedBlock; }
diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp
index fc8de8362..ea0295102 100644
--- a/src/Mobs/Ghast.cpp
+++ b/src/Mobs/Ghast.cpp
@@ -32,9 +32,9 @@ void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-void cGhast::Attack(float a_Dt)
+void cGhast::Attack(std::chrono::milliseconds a_Dt)
{
- m_AttackInterval += a_Dt * m_AttackRate;
+ m_AttackInterval += a_Dt.count() * m_AttackRate;
if ((m_Target != nullptr) && (m_AttackInterval > 3.0))
{
diff --git a/src/Mobs/Ghast.h b/src/Mobs/Ghast.h
index a28940a01..431edaf6d 100644
--- a/src/Mobs/Ghast.h
+++ b/src/Mobs/Ghast.h
@@ -18,7 +18,7 @@ public:
CLASS_PROTODEF(cGhast)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual void Attack(float a_Dt) override;
+ virtual void Attack(std::chrono::milliseconds a_Dt) override;
bool IsCharging(void) const {return false; }
} ;
diff --git a/src/Mobs/Guardian.cpp b/src/Mobs/Guardian.cpp
new file mode 100644
index 000000000..5eb30785b
--- /dev/null
+++ b/src/Mobs/Guardian.cpp
@@ -0,0 +1,65 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Guardian.h"
+#include "../Vector3.h"
+#include "../Chunk.h"
+
+
+
+
+
+cGuardian::cGuardian(void) :
+ super("Guardian", mtGuardian, "mob.guardian.idle", "mob.guardian.death", 0.875, 0.8)
+{
+}
+
+
+
+
+
+void cGuardian::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ // Drops 0-3 Ink Sacs
+ int LootingLevel = 0;
+ if (a_Killer != nullptr)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_PRISMARINE_SHARD);
+ AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_RAW_FISH);
+ AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_PRISMARINE_CRYSTALS); // TODO: Prismarine Crystals only drop if the raw fish drop is 0
+}
+
+
+
+
+
+void cGuardian::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ // We must first process current location, and only then tick, otherwise we risk processing a location in a chunk
+ // that is not where the entity currently resides (FS #411)
+
+ Vector3d Pos = GetPosition();
+
+ // TODO: Not a real behavior, but cool :D
+ int RelY = (int)floor(Pos.y);
+ if ((RelY < 0) || (RelY >= cChunkDef::Height))
+ {
+ return;
+ }
+ int RelX = (int)floor(Pos.x) - a_Chunk.GetPosX() * cChunkDef::Width;
+ int RelZ = (int)floor(Pos.z) - a_Chunk.GetPosZ() * cChunkDef::Width;
+ BLOCKTYPE BlockType;
+ if (a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockType) && !IsBlockWater(BlockType) && !IsOnFire())
+ {
+ // Burn for 10 ticks, then decide again
+ StartBurning(10);
+ }
+
+ super::Tick(a_Dt, a_Chunk);
+}
+
+
+
+
diff --git a/src/Mobs/Guardian.h b/src/Mobs/Guardian.h
new file mode 100644
index 000000000..6bc17947c
--- /dev/null
+++ b/src/Mobs/Guardian.h
@@ -0,0 +1,31 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cGuardian :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cGuardian();
+
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+
+ CLASS_PROTODEF(cGuardian)
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
+
+ // Guardians do not drown (or float)
+ virtual void HandleAir(void) override {}
+ virtual void SetSwimState(cChunk & a_Chunk) override {}
+} ;
+
+
+
+
diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp
index d92f0d023..5b4c78bfc 100644
--- a/src/Mobs/Horse.cpp
+++ b/src/Mobs/Horse.cpp
@@ -30,7 +30,7 @@ cHorse::cHorse(int Type, int Color, int Style, int TameTimes) :
-void cHorse::Tick(float a_Dt, cChunk & a_Chunk)
+void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
diff --git a/src/Mobs/Horse.h b/src/Mobs/Horse.h
index 4c644e512..be283705e 100644
--- a/src/Mobs/Horse.h
+++ b/src/Mobs/Horse.h
@@ -18,7 +18,7 @@ public:
CLASS_PROTODEF(cHorse)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void OnRightClicked(cPlayer & a_Player) override;
bool IsSaddled (void) const {return m_bIsSaddled; }
diff --git a/src/Mobs/IncludeAllMonsters.h b/src/Mobs/IncludeAllMonsters.h
index 3460db993..53c709c2b 100644
--- a/src/Mobs/IncludeAllMonsters.h
+++ b/src/Mobs/IncludeAllMonsters.h
@@ -8,12 +8,14 @@
#include "EnderDragon.h"
#include "Ghast.h"
#include "Giant.h"
+#include "Guardian.h"
#include "Horse.h"
#include "IronGolem.h"
#include "MagmaCube.h"
#include "Mooshroom.h"
#include "Ocelot.h"
#include "Pig.h"
+#include "Rabbit.h"
#include "Sheep.h"
#include "Silverfish.h"
#include "Skeleton.h"
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index 7b8f763af..6e07bfbb6 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -38,12 +38,14 @@ static const struct
{mtEnderman, "enderman", "Enderman"},
{mtEnderDragon, "enderdragon", "EnderDragon"},
{mtGhast, "ghast", "Ghast"},
+ {mtGuardian, "guardian", "Guardian"},
{mtHorse, "horse", "EntityHorse"},
{mtIronGolem, "irongolem", "VillagerGolem"},
{mtMagmaCube, "magmacube", "LavaSlime"},
{mtMooshroom, "mooshroom", "MushroomCow"},
{mtOcelot, "ocelot", "Ozelot"},
{mtPig, "pig", "Pig"},
+ {mtRabbit, "rabbit", "Rabbit"},
{mtSheep, "sheep", "Sheep"},
{mtSilverfish, "silverfish", "Silverfish"},
{mtSkeleton, "skeleton", "Skeleton"},
@@ -250,15 +252,15 @@ bool cMonster::ReachedFinalDestination()
-void cMonster::Tick(float a_Dt, cChunk & a_Chunk)
+void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
if (m_Health <= 0)
{
// The mob is dead, but we're still animating the "puff" they leave when they die
- m_DestroyTimer += a_Dt / 1000;
- if (m_DestroyTimer > 1)
+ m_DestroyTimer += a_Dt;
+ if (m_DestroyTimer > std::chrono::seconds(1))
{
Destroy(true);
}
@@ -273,8 +275,6 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk)
// Burning in daylight
HandleDaylightBurning(a_Chunk);
- a_Dt /= 1000;
-
if (m_bMovingToDestination)
{
if (m_bOnGround)
@@ -498,6 +498,7 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI)
case mtCow:
case mtHorse:
case mtPig:
+ case mtRabbit:
case mtSheep:
case mtSquid:
case mtMooshroom:
@@ -513,6 +514,7 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI)
case mtCreeper:
case mtEnderman:
case mtGhast:
+ case mtGuardian:
case mtSilverfish:
case mtSkeleton:
case mtSpider:
@@ -553,7 +555,7 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI)
{
m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), Reward);
}
- m_DestroyTimer = 0;
+ m_DestroyTimer = std::chrono::milliseconds(0);
}
@@ -636,7 +638,7 @@ void cMonster::EventLosePlayer(void)
-void cMonster::InStateIdle(float a_Dt)
+void cMonster::InStateIdle(std::chrono::milliseconds a_Dt)
{
if (m_bMovingToDestination)
{
@@ -645,11 +647,11 @@ void cMonster::InStateIdle(float a_Dt)
m_IdleInterval += a_Dt;
- if (m_IdleInterval > 1)
+ if (m_IdleInterval > std::chrono::seconds(1))
{
// At this interval the results are predictable
int rem = m_World->GetTickRandomNumber(6) + 1;
- m_IdleInterval -= 1; // So nothing gets dropped when the server hangs for a few seconds
+ m_IdleInterval -= std::chrono::seconds(1); // So nothing gets dropped when the server hangs for a few seconds
Vector3d Dist;
Dist.x = (double)m_World->GetTickRandomNumber(10) - 5;
@@ -676,7 +678,7 @@ void cMonster::InStateIdle(float a_Dt)
// What to do if in Chasing State
// This state should always be defined in each child class
-void cMonster::InStateChasing(float a_Dt)
+void cMonster::InStateChasing(std::chrono::milliseconds a_Dt)
{
UNUSED(a_Dt);
}
@@ -686,7 +688,7 @@ void cMonster::InStateChasing(float a_Dt)
// What to do if in Escaping State
-void cMonster::InStateEscaping(float a_Dt)
+void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt)
{
UNUSED(a_Dt);
@@ -842,12 +844,14 @@ cMonster::eFamily cMonster::FamilyFromType(eMonsterType a_Type)
case mtEnderman: return mfHostile;
case mtGhast: return mfHostile;
case mtGiant: return mfNoSpawn;
+ case mtGuardian: return mfWater; // Just because they have special spawning conditions. If Watertemples have been added, this needs to be edited!
case mtHorse: return mfPassive;
case mtIronGolem: return mfPassive;
case mtMagmaCube: return mfHostile;
case mtMooshroom: return mfHostile;
case mtOcelot: return mfPassive;
case mtPig: return mfPassive;
+ case mtRabbit: return mfPassive;
case mtSheep: return mfPassive;
case mtSilverfish: return mfHostile;
case mtSkeleton: return mfHostile;
@@ -955,10 +959,12 @@ cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType)
case mtEnderman: toReturn = new cEnderman(); break;
case mtGhast: toReturn = new cGhast(); break;
case mtGiant: toReturn = new cGiant(); break;
+ case mtGuardian: toReturn = new cGuardian(); break;
case mtIronGolem: toReturn = new cIronGolem(); break;
case mtMooshroom: toReturn = new cMooshroom(); break;
case mtOcelot: toReturn = new cOcelot(); break;
case mtPig: toReturn = new cPig(); break;
+ case mtRabbit: toReturn = new cRabbit(); break;
case mtSheep: toReturn = new cSheep(); break;
case mtSilverfish: toReturn = new cSilverfish(); break;
case mtSnowGolem: toReturn = new cSnowGolem(); break;
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
index f04e45ac6..21ed0c25a 100644
--- a/src/Mobs/Monster.h
+++ b/src/Mobs/Monster.h
@@ -30,7 +30,7 @@ public:
mfHostile = 0, // Spider, Zombies ...
mfPassive = 1, // Cows, Pigs
mfAmbient = 2, // Bats
- mfWater = 3, // Squid
+ mfWater = 3, // Squid, Guardian
mfNoSpawn,
mfUnhandled, // Nothing. Be sure this is the last and the others are in order
@@ -52,7 +52,7 @@ public:
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
@@ -80,9 +80,9 @@ public:
virtual void EventLosePlayer(void);
virtual void CheckEventLostPlayer(void);
- virtual void InStateIdle (float a_Dt);
- virtual void InStateChasing (float a_Dt);
- virtual void InStateEscaping(float a_Dt);
+ virtual void InStateIdle (std::chrono::milliseconds a_Dt);
+ virtual void InStateChasing (std::chrono::milliseconds a_Dt);
+ virtual void InStateEscaping(std::chrono::milliseconds a_Dt);
int GetAttackRate() { return static_cast<int>(m_AttackRate); }
void SetAttackRate(float a_AttackRate) { m_AttackRate = a_AttackRate; }
@@ -217,8 +217,8 @@ protected:
/* =========================== */
- float m_IdleInterval;
- float m_DestroyTimer;
+ std::chrono::milliseconds m_IdleInterval;
+ std::chrono::milliseconds m_DestroyTimer;
eMonsterType m_MobType;
AString m_CustomName;
diff --git a/src/Mobs/MonsterTypes.h b/src/Mobs/MonsterTypes.h
index dc6dd3992..02bec267b 100644
--- a/src/Mobs/MonsterTypes.h
+++ b/src/Mobs/MonsterTypes.h
@@ -18,12 +18,14 @@ enum eMonsterType
mtEnderman = E_META_SPAWN_EGG_ENDERMAN,
mtGhast = E_META_SPAWN_EGG_GHAST,
mtGiant = E_META_SPAWN_EGG_GIANT,
+ mtGuardian = E_META_SPAWN_EGG_GUARDIAN,
mtHorse = E_META_SPAWN_EGG_HORSE,
mtIronGolem = E_META_SPAWN_EGG_IRON_GOLEM,
mtMagmaCube = E_META_SPAWN_EGG_MAGMA_CUBE,
mtMooshroom = E_META_SPAWN_EGG_MOOSHROOM,
mtOcelot = E_META_SPAWN_EGG_OCELOT,
mtPig = E_META_SPAWN_EGG_PIG,
+ mtRabbit = E_META_SPAWN_EGG_RABBIT,
mtSheep = E_META_SPAWN_EGG_SHEEP,
mtSilverfish = E_META_SPAWN_EGG_SILVERFISH,
mtSkeleton = E_META_SPAWN_EGG_SKELETON,
@@ -37,6 +39,7 @@ enum eMonsterType
mtWolf = E_META_SPAWN_EGG_WOLF,
mtZombie = E_META_SPAWN_EGG_ZOMBIE,
mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN,
+ mtMax = 120, // This is just a hotfix for http://forum.mc-server.org/showthread.php?tid=1616. Tolua is too bad to find the highest value, so this is needed.
} ;
// tolua_end
diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp
index 1048616d0..012ca9949 100644
--- a/src/Mobs/PassiveMonster.cpp
+++ b/src/Mobs/PassiveMonster.cpp
@@ -35,7 +35,7 @@ bool cPassiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
-void cPassiveMonster::Tick(float a_Dt, cChunk & a_Chunk)
+void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
diff --git a/src/Mobs/PassiveMonster.h b/src/Mobs/PassiveMonster.h
index 9221d9a6e..2ed2cd21d 100644
--- a/src/Mobs/PassiveMonster.h
+++ b/src/Mobs/PassiveMonster.h
@@ -15,7 +15,7 @@ class cPassiveMonster :
public:
cPassiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
/// When hit by someone, run away
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp
index 1e4c35acd..edd4d9de4 100644
--- a/src/Mobs/Pig.cpp
+++ b/src/Mobs/Pig.cpp
@@ -80,7 +80,7 @@ void cPig::OnRightClicked(cPlayer & a_Player)
-void cPig::Tick(float a_Dt, cChunk & a_Chunk)
+void cPig::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
diff --git a/src/Mobs/Pig.h b/src/Mobs/Pig.h
index 0e026933a..0fe4b4fed 100644
--- a/src/Mobs/Pig.h
+++ b/src/Mobs/Pig.h
@@ -22,7 +22,7 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
virtual void OnRightClicked(cPlayer & a_Player) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_CARROT); }
diff --git a/src/Mobs/Rabbit.cpp b/src/Mobs/Rabbit.cpp
new file mode 100644
index 000000000..cf49d2744
--- /dev/null
+++ b/src/Mobs/Rabbit.cpp
@@ -0,0 +1,34 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Rabbit.h"
+#include "../Entities/Player.h"
+#include "../World.h"
+
+
+
+
+
+cRabbit::cRabbit(void) :
+ super("Rabbit", mtRabbit, "mob.rabbit.idle", "mob.rabbit.death", 0.82, 0.68)
+{
+}
+
+
+
+
+
+void cRabbit::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ int LootingLevel = 0;
+ if (a_Killer != nullptr)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, IsOnFire() ? E_ITEM_COOKED_RABBIT : E_ITEM_RAW_RABBIT);
+ AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_RABBIT_HIDE);
+ cItems RareDrops;
+ RareDrops.Add(cItem(E_ITEM_RABBITS_FOOT));
+ AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel);
+}
+
diff --git a/src/Mobs/Rabbit.h b/src/Mobs/Rabbit.h
new file mode 100644
index 000000000..e86c85579
--- /dev/null
+++ b/src/Mobs/Rabbit.h
@@ -0,0 +1,24 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cRabbit :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cRabbit();
+
+ CLASS_PROTODEF(cRabbit)
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
+
+ virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_CARROT); }
+
+} ;
diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp
index c46404391..e4d1760e0 100644
--- a/src/Mobs/Sheep.cpp
+++ b/src/Mobs/Sheep.cpp
@@ -84,7 +84,7 @@ void cSheep::OnRightClicked(cPlayer & a_Player)
-void cSheep::Tick(float a_Dt, cChunk & a_Chunk)
+void cSheep::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
int PosX = POSX_TOINT;
diff --git a/src/Mobs/Sheep.h b/src/Mobs/Sheep.h
index 16d5fddd3..b6c99ac2a 100644
--- a/src/Mobs/Sheep.h
+++ b/src/Mobs/Sheep.h
@@ -24,7 +24,7 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
virtual void OnRightClicked(cPlayer & a_Player) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_WHEAT); }
diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp
index da5ddc670..dd59d6454 100644
--- a/src/Mobs/Skeleton.cpp
+++ b/src/Mobs/Skeleton.cpp
@@ -67,9 +67,9 @@ void cSkeleton::MoveToPosition(const Vector3d & a_Position)
-void cSkeleton::Attack(float a_Dt)
+void cSkeleton::Attack(std::chrono::milliseconds a_Dt)
{
- m_AttackInterval += a_Dt * m_AttackRate;
+ m_AttackInterval += a_Dt.count() * m_AttackRate;
if ((m_Target != nullptr) && (m_AttackInterval > 3.0))
{
diff --git a/src/Mobs/Skeleton.h b/src/Mobs/Skeleton.h
index cd1c6c3f0..9c49c52fb 100644
--- a/src/Mobs/Skeleton.h
+++ b/src/Mobs/Skeleton.h
@@ -19,7 +19,7 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
virtual void MoveToPosition(const Vector3d & a_Position) override;
- virtual void Attack(float a_Dt) override;
+ virtual void Attack(std::chrono::milliseconds a_Dt) override;
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
virtual bool IsUndead(void) override { return true; }
diff --git a/src/Mobs/Slime.cpp b/src/Mobs/Slime.cpp
index 1c68c5189..e42501e47 100644
--- a/src/Mobs/Slime.cpp
+++ b/src/Mobs/Slime.cpp
@@ -46,7 +46,7 @@ void cSlime::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-void cSlime::Attack(float a_Dt)
+void cSlime::Attack(std::chrono::milliseconds a_Dt)
{
if (m_Size > 1)
{
diff --git a/src/Mobs/Slime.h b/src/Mobs/Slime.h
index a177a279c..29605992d 100644
--- a/src/Mobs/Slime.h
+++ b/src/Mobs/Slime.h
@@ -20,7 +20,7 @@ public:
// cAggressiveMonster overrides:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual void Attack(float a_Dt) override;
+ virtual void Attack(std::chrono::milliseconds a_Dt) override;
virtual void KilledBy(TakeDamageInfo & a_TDI) override;
int GetSize(void) const { return m_Size; }
diff --git a/src/Mobs/SnowGolem.cpp b/src/Mobs/SnowGolem.cpp
index 8c4178beb..e1510b203 100644
--- a/src/Mobs/SnowGolem.cpp
+++ b/src/Mobs/SnowGolem.cpp
@@ -27,7 +27,7 @@ void cSnowGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-void cSnowGolem::Tick(float a_Dt, cChunk & a_Chunk)
+void cSnowGolem::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
if (IsBiomeNoDownfall(m_World->GetBiomeAt((int) floor(GetPosX()), (int) floor(GetPosZ()))))
diff --git a/src/Mobs/SnowGolem.h b/src/Mobs/SnowGolem.h
index f036b1867..9c95e21c5 100644
--- a/src/Mobs/SnowGolem.h
+++ b/src/Mobs/SnowGolem.h
@@ -17,7 +17,7 @@ public:
CLASS_PROTODEF(cSnowGolem)
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
} ;
diff --git a/src/Mobs/Squid.cpp b/src/Mobs/Squid.cpp
index 59ee963ae..3c508b65f 100644
--- a/src/Mobs/Squid.cpp
+++ b/src/Mobs/Squid.cpp
@@ -33,7 +33,7 @@ void cSquid::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-void cSquid::Tick(float a_Dt, cChunk & a_Chunk)
+void cSquid::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
// We must first process current location, and only then tick, otherwise we risk processing a location in a chunk
// that is not where the entity currently resides (FS #411)
diff --git a/src/Mobs/Squid.h b/src/Mobs/Squid.h
index a46d738c6..7e944a17e 100644
--- a/src/Mobs/Squid.h
+++ b/src/Mobs/Squid.h
@@ -15,7 +15,7 @@ class cSquid :
public:
cSquid();
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
CLASS_PROTODEF(cSquid)
diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp
index 963595347..6f647ac18 100644
--- a/src/Mobs/Villager.cpp
+++ b/src/Mobs/Villager.cpp
@@ -51,7 +51,7 @@ bool cVillager::DoTakeDamage(TakeDamageInfo & a_TDI)
-void cVillager::Tick(float a_Dt, cChunk & a_Chunk)
+void cVillager::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
diff --git a/src/Mobs/Villager.h b/src/Mobs/Villager.h
index d3a38dbf0..2de79295c 100644
--- a/src/Mobs/Villager.h
+++ b/src/Mobs/Villager.h
@@ -31,7 +31,7 @@ public:
// cEntity overrides
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
- virtual void Tick (float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
// cVillager functions
/** return true if the given blocktype are: crops, potatoes or carrots.*/
diff --git a/src/Mobs/Wither.cpp b/src/Mobs/Wither.cpp
index 578b47995..6ef81ce1b 100644
--- a/src/Mobs/Wither.cpp
+++ b/src/Mobs/Wither.cpp
@@ -66,7 +66,7 @@ bool cWither::DoTakeDamage(TakeDamageInfo & a_TDI)
-void cWither::Tick(float a_Dt, cChunk & a_Chunk)
+void cWither::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
diff --git a/src/Mobs/Wither.h b/src/Mobs/Wither.h
index a20fed3d3..9e333c7fa 100644
--- a/src/Mobs/Wither.h
+++ b/src/Mobs/Wither.h
@@ -28,7 +28,7 @@ public:
virtual bool Initialize(cWorld & a_World) override;
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void KilledBy(TakeDamageInfo & a_TDI) override;
virtual bool IsUndead(void) override { return true; }
diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp
index 4711d5a7a..b3eefdf79 100644
--- a/src/Mobs/Wolf.cpp
+++ b/src/Mobs/Wolf.cpp
@@ -43,7 +43,7 @@ bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI)
-void cWolf::Attack(float a_Dt)
+void cWolf::Attack(std::chrono::milliseconds a_Dt)
{
UNUSED(a_Dt);
@@ -145,7 +145,7 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
-void cWolf::Tick(float a_Dt, cChunk & a_Chunk)
+void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
if (!IsAngry())
{
diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h
index 7500854f8..73ffb55c2 100644
--- a/src/Mobs/Wolf.h
+++ b/src/Mobs/Wolf.h
@@ -20,9 +20,9 @@ public:
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
virtual void OnRightClicked(cPlayer & a_Player) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void TickFollowPlayer();
- virtual void Attack(float a_Dt) override;
+ virtual void Attack(std::chrono::milliseconds a_Dt) override;
// Get functions
bool IsSitting (void) const { return m_IsSitting; }
diff --git a/src/OSSupport/CMakeLists.txt b/src/OSSupport/CMakeLists.txt
index e943ceb18..9424b63da 100644
--- a/src/OSSupport/CMakeLists.txt
+++ b/src/OSSupport/CMakeLists.txt
@@ -10,12 +10,17 @@ SET (SRCS
Event.cpp
File.cpp
GZipFile.cpp
+ HostnameLookup.cpp
+ IPLookup.cpp
IsThread.cpp
ListenThread.cpp
+ NetworkSingleton.cpp
Semaphore.cpp
+ ServerHandleImpl.cpp
Socket.cpp
SocketThreads.cpp
StackTrace.cpp
+ TCPLinkImpl.cpp
)
SET (HDRS
@@ -24,13 +29,19 @@ SET (HDRS
Event.h
File.h
GZipFile.h
+ HostnameLookup.h
+ IPLookup.h
IsThread.h
ListenThread.h
+ Network.h
+ NetworkSingleton.h
Queue.h
Semaphore.h
+ ServerHandleImpl.h
Socket.h
SocketThreads.h
StackTrace.h
+ TCPLinkImpl.h
)
if(NOT MSVC)
diff --git a/src/OSSupport/CriticalSection.cpp b/src/OSSupport/CriticalSection.cpp
index 13a3e4d9f..5248356c5 100644
--- a/src/OSSupport/CriticalSection.cpp
+++ b/src/OSSupport/CriticalSection.cpp
@@ -1,5 +1,6 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+#include "CriticalSection.h"
diff --git a/src/OSSupport/HostnameLookup.cpp b/src/OSSupport/HostnameLookup.cpp
new file mode 100644
index 000000000..3a2997ffd
--- /dev/null
+++ b/src/OSSupport/HostnameLookup.cpp
@@ -0,0 +1,124 @@
+
+// HostnameLookup.cpp
+
+// Implements the cHostnameLookup class representing an in-progress hostname-to-IP lookup
+
+#include "Globals.h"
+#include "HostnameLookup.h"
+#include <event2/dns.h>
+#include "NetworkSingleton.h"
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cHostnameLookup:
+
+cHostnameLookup::cHostnameLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks):
+ m_Callbacks(a_Callbacks)
+{
+}
+
+
+
+
+
+void cHostnameLookup::Lookup(const AString & a_Hostname)
+{
+ // Store the hostname for the callback:
+ m_Hostname = a_Hostname;
+
+ // Start the lookup:
+ // Note that we don't have to store the LibEvent lookup handle, LibEvent will free it on its own.
+ evutil_addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = EVUTIL_AI_CANONNAME;
+ evdns_getaddrinfo(cNetworkSingleton::Get().GetDNSBase(), a_Hostname.c_str(), nullptr, &hints, Callback, this);
+}
+
+
+
+
+
+void cHostnameLookup::Callback(int a_ErrCode, evutil_addrinfo * a_Addr, void * a_Self)
+{
+ // Get the Self class:
+ cHostnameLookup * Self = reinterpret_cast<cHostnameLookup *>(a_Self);
+ ASSERT(Self != nullptr);
+
+ // If an error has occurred, notify the error callback:
+ if (a_ErrCode != 0)
+ {
+ Self->m_Callbacks->OnError(a_ErrCode, evutil_socket_error_to_string(a_ErrCode));
+ cNetworkSingleton::Get().RemoveHostnameLookup(Self);
+ return;
+ }
+
+ // Call the success handler for each entry received:
+ bool HasResolved = false;
+ evutil_addrinfo * OrigAddr = a_Addr;
+ for (;a_Addr != nullptr; a_Addr = a_Addr->ai_next)
+ {
+ char IP[128];
+ switch (a_Addr->ai_family)
+ {
+ case AF_INET: // IPv4
+ {
+ sockaddr_in * sin = reinterpret_cast<sockaddr_in *>(a_Addr->ai_addr);
+ evutil_inet_ntop(AF_INET, &(sin->sin_addr), IP, sizeof(IP));
+ break;
+ }
+ case AF_INET6: // IPv6
+ {
+ sockaddr_in6 * sin = reinterpret_cast<sockaddr_in6 *>(a_Addr->ai_addr);
+ evutil_inet_ntop(AF_INET6, &(sin->sin6_addr), IP, sizeof(IP));
+ break;
+ }
+ default:
+ {
+ // Unknown address family, handle as if this entry wasn't received
+ continue; // for (a_Addr)
+ }
+ }
+ Self->m_Callbacks->OnNameResolved(Self->m_Hostname, IP);
+ HasResolved = true;
+ } // for (a_Addr)
+
+ // If only unsupported families were reported, call the Error handler:
+ if (!HasResolved)
+ {
+ Self->m_Callbacks->OnError(DNS_ERR_NODATA, "The name does not resolve to any known address.");
+ }
+ else
+ {
+ Self->m_Callbacks->OnFinished();
+ }
+ evutil_freeaddrinfo(OrigAddr);
+ cNetworkSingleton::Get().RemoveHostnameLookup(Self);
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cNetwork API:
+
+bool cNetwork::HostnameToIP(
+ const AString & a_Hostname,
+ cNetwork::cResolveNameCallbacksPtr a_Callbacks
+)
+{
+ auto Lookup = std::make_shared<cHostnameLookup>(a_Callbacks);
+ cNetworkSingleton::Get().AddHostnameLookup(Lookup);
+ Lookup->Lookup(a_Hostname);
+ return true;
+}
+
+
+
+
diff --git a/src/OSSupport/HostnameLookup.h b/src/OSSupport/HostnameLookup.h
new file mode 100644
index 000000000..d69f24707
--- /dev/null
+++ b/src/OSSupport/HostnameLookup.h
@@ -0,0 +1,47 @@
+
+// HostnameLookup.h
+
+// Declares the cHostnameLookup class representing an in-progress hostname-to-IP lookup
+
+// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead
+
+
+
+
+
+#pragma once
+
+#include "Network.h"
+#include <event2/util.h>
+
+
+
+
+
+/** Holds information about an in-progress Hostname-to-IP lookup. */
+class cHostnameLookup
+{
+public:
+ /** Creates the lookup object. Doesn't start the lookup yet. */
+ cHostnameLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks);
+
+ /** Starts the lookup. */
+ void Lookup(const AString & a_Hostname);
+
+protected:
+
+ /** The callbacks to call for resolved names / errors. */
+ cNetwork::cResolveNameCallbacksPtr m_Callbacks;
+
+ /** The hostname that was queried (needed for the callbacks). */
+ AString m_Hostname;
+
+ static void Callback(int a_ErrCode, struct evutil_addrinfo * a_Addr, void * a_Self);
+};
+typedef SharedPtr<cHostnameLookup> cHostnameLookupPtr;
+typedef std::vector<cHostnameLookupPtr> cHostnameLookupPtrs;
+
+
+
+
+
diff --git a/src/OSSupport/IPLookup.cpp b/src/OSSupport/IPLookup.cpp
new file mode 100644
index 000000000..8cdc5132d
--- /dev/null
+++ b/src/OSSupport/IPLookup.cpp
@@ -0,0 +1,111 @@
+
+// IPLookup.cpp
+
+// Implements the cIPLookup class representing an IP-to-hostname lookup in progress.
+
+#include "Globals.h"
+#include "IPLookup.h"
+#include <event2/dns.h>
+#include "NetworkSingleton.h"
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cIPLookup:
+
+cIPLookup::cIPLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks):
+ m_Callbacks(a_Callbacks)
+{
+ ASSERT(a_Callbacks != nullptr);
+}
+
+
+
+
+
+bool cIPLookup::Lookup(const AString & a_IP)
+{
+ // Parse the IP address string into a sockaddr structure:
+ m_IP = a_IP;
+ sockaddr_storage sa;
+ int salen = static_cast<int>(sizeof(sa));
+ memset(&sa, 0, sizeof(sa));
+ if (evutil_parse_sockaddr_port(a_IP.c_str(), reinterpret_cast<sockaddr *>(&sa), &salen) != 0)
+ {
+ LOGD("Failed to parse IP address \"%s\".", a_IP.c_str());
+ return false;
+ }
+
+ // Call the proper resolver based on the address family:
+ // Note that there's no need to store the evdns_request handle returned, LibEvent frees it on its own.
+ switch (sa.ss_family)
+ {
+ case AF_INET:
+ {
+ sockaddr_in * sa4 = reinterpret_cast<sockaddr_in *>(&sa);
+ evdns_base_resolve_reverse(cNetworkSingleton::Get().GetDNSBase(), &(sa4->sin_addr), 0, Callback, this);
+ break;
+ }
+ case AF_INET6:
+ {
+ sockaddr_in6 * sa6 = reinterpret_cast<sockaddr_in6 *>(&sa);
+ evdns_base_resolve_reverse_ipv6(cNetworkSingleton::Get().GetDNSBase(), &(sa6->sin6_addr), 0, Callback, this);
+ break;
+ }
+ default:
+ {
+ LOGWARNING("%s: Unknown address family: %d", __FUNCTION__, sa.ss_family);
+ ASSERT(!"Unknown address family");
+ return false;
+ }
+ } // switch (address family)
+ return true;
+}
+
+
+
+
+
+void cIPLookup::Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self)
+{
+ // Get the Self class:
+ cIPLookup * Self = reinterpret_cast<cIPLookup *>(a_Self);
+ ASSERT(Self != nullptr);
+
+ // Call the proper callback based on the event received:
+ if ((a_Result != 0) || (a_Addresses == nullptr))
+ {
+ // An error has occurred, notify the error callback:
+ Self->m_Callbacks->OnError(a_Result, evutil_socket_error_to_string(a_Result));
+ }
+ else
+ {
+ // Call the success handler:
+ Self->m_Callbacks->OnNameResolved(*(reinterpret_cast<char **>(a_Addresses)), Self->m_IP);
+ Self->m_Callbacks->OnFinished();
+ }
+ cNetworkSingleton::Get().RemoveIPLookup(Self);
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cNetwork API:
+
+bool cNetwork::IPToHostName(
+ const AString & a_IP,
+ cNetwork::cResolveNameCallbacksPtr a_Callbacks
+)
+{
+ auto res = std::make_shared<cIPLookup>(a_Callbacks);
+ cNetworkSingleton::Get().AddIPLookup(res);
+ return res->Lookup(a_IP);
+}
+
+
+
+
diff --git a/src/OSSupport/IPLookup.h b/src/OSSupport/IPLookup.h
new file mode 100644
index 000000000..af878cbf1
--- /dev/null
+++ b/src/OSSupport/IPLookup.h
@@ -0,0 +1,49 @@
+
+// IPLookup.h
+
+// Declares the cIPLookup class representing an IP-to-hostname lookup in progress.
+
+// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead
+
+
+
+
+
+#pragma once
+
+#include "Network.h"
+
+
+
+
+
+/** Holds information about an in-progress IP-to-Hostname lookup. */
+class cIPLookup
+{
+public:
+ /** Creates the lookup object. Doesn't start the lookup yet. */
+ cIPLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks);
+
+ /** Starts the lookup.
+ Returns true if lookup started successfully, false on failure (invalid IP format etc.) */
+ bool Lookup(const AString & a_IP);
+
+protected:
+
+ /** The callbacks to call for resolved names / errors. */
+ cNetwork::cResolveNameCallbacksPtr m_Callbacks;
+
+ /** The IP that was queried (needed for the callbacks). */
+ AString m_IP;
+
+
+ /** Callback that is called by LibEvent when there's an event for the request. */
+ static void Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self);
+};
+typedef SharedPtr<cIPLookup> cIPLookupPtr;
+typedef std::vector<cIPLookupPtr> cIPLookupPtrs;
+
+
+
+
+
diff --git a/src/OSSupport/IsThread.cpp b/src/OSSupport/IsThread.cpp
index 94bed1f56..55e96b622 100644
--- a/src/OSSupport/IsThread.cpp
+++ b/src/OSSupport/IsThread.cpp
@@ -68,11 +68,22 @@ cIsThread::~cIsThread()
+void cIsThread::DoExecute(void)
+{
+ m_evtStart.Wait();
+ Execute();
+}
+
+
+
+
+
bool cIsThread::Start(void)
{
try
{
- m_Thread = std::thread(&cIsThread::Execute, this);
+ // Initialize the thread:
+ m_Thread = std::thread(&cIsThread::DoExecute, this);
#if defined (_MSC_VER) && defined(_DEBUG)
if (!m_ThreadName.empty())
@@ -81,9 +92,12 @@ bool cIsThread::Start(void)
}
#endif
+ // Notify the thread that initialization is complete and it can run its code safely:
+ m_evtStart.Set();
+
return true;
}
- catch (std::system_error & a_Exception)
+ catch (const std::system_error & a_Exception)
{
LOGERROR("cIsThread::Start error %i: could not construct thread %s; %s", a_Exception.code().value(), m_ThreadName.c_str(), a_Exception.code().message().c_str());
return false;
diff --git a/src/OSSupport/IsThread.h b/src/OSSupport/IsThread.h
index 131c6950e..f642c8724 100644
--- a/src/OSSupport/IsThread.h
+++ b/src/OSSupport/IsThread.h
@@ -25,23 +25,28 @@ In the descending class' constructor call the Start() method to start the thread
class cIsThread
{
protected:
- /// This is the main thread entrypoint
+ /** This is the main thread entrypoint.
+ This function, overloaded by the descendants, is called in the new thread. */
virtual void Execute(void) = 0;
- /// The overriden Execute() method should check this value periodically and terminate if this is true
+ /** The overriden Execute() method should check this value periodically and terminate if this is true. */
volatile bool m_ShouldTerminate;
+private:
+ /** Wrapper for Execute() that waits for the initialization event, to prevent race conditions in thread initialization. */
+ void DoExecute(void);
+
public:
cIsThread(const AString & a_ThreadName);
virtual ~cIsThread();
- /// Starts the thread; returns without waiting for the actual start
+ /** Starts the thread; returns without waiting for the actual start. */
bool Start(void);
- /// Signals the thread to terminate and waits until it's finished
+ /** Signals the thread to terminate and waits until it's finished. */
void Stop(void);
- /// Waits for the thread to finish. Doesn't signalize the ShouldTerminate flag
+ /** Waits for the thread to finish. Doesn't signalize the ShouldTerminate flag. */
bool Wait(void);
/** Returns true if the thread calling this function is the thread contained within this object. */
@@ -50,6 +55,10 @@ public:
protected:
AString m_ThreadName;
std::thread m_Thread;
+
+ /** The event that is used to wait with the thread's execution until the thread object is fully initialized.
+ This prevents the IsCurrentThread() call to fail because of a race-condition. */
+ cEvent m_evtStart;
} ;
diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h
new file mode 100644
index 000000000..cdf6ba0e9
--- /dev/null
+++ b/src/OSSupport/Network.h
@@ -0,0 +1,246 @@
+
+// Network.h
+
+// Declares the classes used for the Network API
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// fwd:
+class cTCPLink;
+typedef SharedPtr<cTCPLink> cTCPLinkPtr;
+typedef std::vector<cTCPLinkPtr> cTCPLinkPtrs;
+class cServerHandle;
+typedef SharedPtr<cServerHandle> cServerHandlePtr;
+typedef std::vector<cServerHandlePtr> cServerHandlePtrs;
+
+
+
+
+
+/** Interface that provides the methods available on a single TCP connection. */
+class cTCPLink
+{
+ friend class cNetwork;
+
+public:
+ class cCallbacks
+ {
+ public:
+ // Force a virtual destructor for all descendants:
+ virtual ~cCallbacks() {}
+
+ /** Called when the cTCPLink for the connection is created.
+ The callback may store the cTCPLink instance for later use, but it should remove it in OnError(), OnRemoteClosed() or right after Close(). */
+ virtual void OnLinkCreated(cTCPLinkPtr a_Link) = 0;
+
+ /** Called when there's data incoming from the remote peer. */
+ virtual void OnReceivedData(const char * a_Data, size_t a_Length) = 0;
+
+ /** Called when the remote end closes the connection.
+ The link is still available for connection information query (IP / port).
+ Sending data on the link is not an error, but the data won't be delivered. */
+ virtual void OnRemoteClosed(void) = 0;
+
+ /** Called when an error is detected on the connection. */
+ virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0;
+ };
+ typedef SharedPtr<cCallbacks> cCallbacksPtr;
+
+
+ // Force a virtual destructor for all descendants:
+ virtual ~cTCPLink() {}
+
+ /** Queues the specified data for sending to the remote peer.
+ Returns true on success, false on failure. Note that this success or failure only reports the queue status, not the actual data delivery. */
+ virtual bool Send(const void * a_Data, size_t a_Length) = 0;
+
+ /** Queues the specified data for sending to the remote peer.
+ Returns true on success, false on failure. Note that this success or failure only reports the queue status, not the actual data delivery. */
+ bool Send(const AString & a_Data)
+ {
+ return Send(a_Data.data(), a_Data.size());
+ }
+
+ /** Returns the IP address of the local endpoint of the connection. */
+ virtual AString GetLocalIP(void) const = 0;
+
+ /** Returns the port used by the local endpoint of the connection. */
+ virtual UInt16 GetLocalPort(void) const = 0;
+
+ /** Returns the IP address of the remote endpoint of the connection. */
+ virtual AString GetRemoteIP(void) const = 0;
+
+ /** Returns the port used by the remote endpoint of the connection. */
+ virtual UInt16 GetRemotePort(void) const = 0;
+
+ /** Closes the link gracefully.
+ The link will send any queued outgoing data, then it will send the FIN packet.
+ The link will still receive incoming data from remote until the remote closes the connection. */
+ virtual void Shutdown(void) = 0;
+
+ /** Drops the connection without any more processing.
+ Sends the RST packet, queued outgoing and incoming data is lost. */
+ virtual void Close(void) = 0;
+
+protected:
+ /** Callbacks to be used for the various situations. */
+ cCallbacksPtr m_Callbacks;
+
+
+ /** Creates a new link, with the specified callbacks. */
+ cTCPLink(cCallbacksPtr a_Callbacks):
+ m_Callbacks(a_Callbacks)
+ {
+ }
+};
+
+
+
+
+
+/** Interface that provides the methods available on a listening server socket. */
+class cServerHandle
+{
+ friend class cNetwork;
+public:
+
+ // Force a virtual destructor for all descendants:
+ virtual ~cServerHandle() {}
+
+ /** Stops the server, no more incoming connections will be accepted.
+ All current connections will be shut down (cTCPLink::Shutdown()). */
+ virtual void Close(void) = 0;
+
+ /** Returns true if the server has been started correctly and is currently listening for incoming connections. */
+ virtual bool IsListening(void) const = 0;
+};
+
+
+
+
+
+class cNetwork
+{
+public:
+ /** Callbacks used for connecting to other servers as a client. */
+ class cConnectCallbacks
+ {
+ public:
+ // Force a virtual destructor for all descendants:
+ virtual ~cConnectCallbacks() {}
+
+ /** Called when the Connect call succeeds.
+ Provides the newly created link that can be used for communication. */
+ virtual void OnConnected(cTCPLink & a_Link) = 0;
+
+ /** Called when the Connect call fails. */
+ virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0;
+ };
+ typedef SharedPtr<cConnectCallbacks> cConnectCallbacksPtr;
+
+
+ /** Callbacks used when listening for incoming connections as a server. */
+ class cListenCallbacks
+ {
+ public:
+ // Force a virtual destructor for all descendants:
+ virtual ~cListenCallbacks() {}
+
+ /** Called when the TCP server created with Listen() receives a new incoming connection.
+ Returns the link callbacks that the server should use for the newly created link.
+ If a nullptr is returned, the connection is dropped immediately;
+ otherwise a new cTCPLink instance is created and OnAccepted() is called. */
+ virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) = 0;
+
+ /** Called when the TCP server created with Listen() creates a new link for an incoming connection.
+ Provides the newly created Link that can be used for communication.
+ Called right after a successful OnIncomingConnection(). */
+ virtual void OnAccepted(cTCPLink & a_Link) = 0;
+
+ /** Called when the socket fails to listen on the specified port. */
+ virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0;
+ };
+ typedef SharedPtr<cListenCallbacks> cListenCallbacksPtr;
+
+
+ /** Callbacks used when resolving names to IPs. */
+ class cResolveNameCallbacks
+ {
+ public:
+ // Force a virtual destructor for all descendants:
+ virtual ~cResolveNameCallbacks() {}
+
+ /** Called when the hostname is successfully resolved into an IP address.
+ May be called multiple times if a name resolves to multiple addresses.
+ a_IP may be either an IPv4 or an IPv6 address with their proper formatting. */
+ virtual void OnNameResolved(const AString & a_Name, const AString & a_IP) = 0;
+
+ /** Called when an error is encountered while resolving.
+ If an error is reported, the OnFinished() callback is not called. */
+ virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0;
+
+ /** Called when all the addresses resolved have been reported via the OnNameResolved() callback.
+ Only called if there was no error reported. */
+ virtual void OnFinished(void) = 0;
+ };
+ typedef SharedPtr<cResolveNameCallbacks> cResolveNameCallbacksPtr;
+
+
+ /** Queues a TCP connection to be made to the specified host.
+ Calls one the connection callbacks (success, error) when the connection is successfully established, or upon failure.
+ The a_LinkCallbacks is passed to the newly created cTCPLink.
+ Returns true if queueing was successful, false on failure to queue.
+ Note that the return value doesn't report the success of the actual connection; the connection is established asynchronously in the background.
+ Implemented in TCPLinkImpl.cpp. */
+ static bool Connect(
+ const AString & a_Host,
+ UInt16 a_Port,
+ cConnectCallbacksPtr a_ConnectCallbacks,
+ cTCPLink::cCallbacksPtr a_LinkCallbacks
+ );
+
+
+ /** Opens up the specified port for incoming connections.
+ Calls an OnAccepted callback for each incoming connection.
+ A cTCPLink with the specified link callbacks is created for each connection.
+ Returns a cServerHandle that can be used to query the operation status and close the server.
+ Implemented in ServerHandleImpl.cpp. */
+ static cServerHandlePtr Listen(
+ UInt16 a_Port,
+ cListenCallbacksPtr a_ListenCallbacks
+ );
+
+
+ /** Queues a DNS query to resolve the specified hostname to IP address.
+ Calls one of the callbacks when the resolving succeeds, or when it fails.
+ Returns true if queueing was successful, false if not.
+ Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background.
+ Implemented in HostnameLookup.cpp. */
+ static bool HostnameToIP(
+ const AString & a_Hostname,
+ cResolveNameCallbacksPtr a_Callbacks
+ );
+
+
+ /** Queues a DNS query to resolve the specified IP address to a hostname.
+ Calls one of the callbacks when the resolving succeeds, or when it fails.
+ Returns true if queueing was successful, false if not.
+ Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background.
+ Implemented in IPLookup.cpp. */
+ static bool IPToHostName(
+ const AString & a_IP,
+ cResolveNameCallbacksPtr a_Callbacks
+ );
+};
+
+
+
+
diff --git a/src/OSSupport/NetworkSingleton.cpp b/src/OSSupport/NetworkSingleton.cpp
new file mode 100644
index 000000000..92f0604cd
--- /dev/null
+++ b/src/OSSupport/NetworkSingleton.cpp
@@ -0,0 +1,245 @@
+
+// NetworkSingleton.cpp
+
+// Implements the cNetworkSingleton class representing the storage for global data pertaining to network API
+// such as a list of all connections, all listening sockets and the LibEvent dispatch thread.
+
+#include "Globals.h"
+#include "NetworkSingleton.h"
+#include <event2/event.h>
+#include <event2/thread.h>
+#include <event2/bufferevent.h>
+#include <event2/dns.h>
+#include <event2/listener.h>
+#include "IPLookup.h"
+#include "HostnameLookup.h"
+
+
+
+
+
+cNetworkSingleton::cNetworkSingleton(void)
+{
+ // Windows: initialize networking:
+ #ifdef _WIN32
+ WSADATA wsaData;
+ memset(&wsaData, 0, sizeof(wsaData));
+ int res = WSAStartup (MAKEWORD(2, 2), &wsaData);
+ if (res != 0)
+ {
+ int err = WSAGetLastError();
+ LOGWARNING("WSAStartup failed: %d, WSAGLE = %d (%s)", res, err, evutil_socket_error_to_string(err));
+ exit(1);
+ }
+ #endif // _WIN32
+
+ // Initialize LibEvent logging:
+ event_set_log_callback(LogCallback);
+
+ // Initialize threading:
+ #if defined(EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED)
+ evthread_use_windows_threads();
+ #elif defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED)
+ evthread_use_pthreads();
+ #else
+ #error No threading implemented for EVTHREAD
+ #endif
+
+ // Create the main event_base:
+ m_EventBase = event_base_new();
+ if (m_EventBase == nullptr)
+ {
+ LOGERROR("Failed to initialize LibEvent. The server will now terminate.");
+ abort();
+ }
+
+ // Create the DNS lookup helper:
+ m_DNSBase = evdns_base_new(m_EventBase, 1);
+ if (m_DNSBase == nullptr)
+ {
+ LOGERROR("Failed to initialize LibEvent's DNS subsystem. The server will now terminate.");
+ abort();
+ }
+
+ // Create the event loop thread:
+ std::thread EventLoopThread(RunEventLoop, this);
+ EventLoopThread.detach();
+}
+
+
+
+
+
+cNetworkSingleton::~cNetworkSingleton()
+{
+ // Wait for the LibEvent event loop to terminate:
+ event_base_loopbreak(m_EventBase);
+ m_EventLoopTerminated.Wait();
+
+ // Remove all objects:
+ {
+ cCSLock Lock(m_CS);
+ m_Connections.clear();
+ m_Servers.clear();
+ m_HostnameLookups.clear();
+ m_IPLookups.clear();
+ }
+
+ // Free the underlying LibEvent objects:
+ evdns_base_free(m_DNSBase, true);
+ event_base_free(m_EventBase);
+
+ libevent_global_shutdown();
+}
+
+
+
+
+
+cNetworkSingleton & cNetworkSingleton::Get(void)
+{
+ static cNetworkSingleton Instance;
+ return Instance;
+}
+
+
+
+
+
+void cNetworkSingleton::LogCallback(int a_Severity, const char * a_Msg)
+{
+ switch (a_Severity)
+ {
+ case _EVENT_LOG_DEBUG: LOGD ("LibEvent: %s", a_Msg); break;
+ case _EVENT_LOG_MSG: LOG ("LibEvent: %s", a_Msg); break;
+ case _EVENT_LOG_WARN: LOGWARNING("LibEvent: %s", a_Msg); break;
+ case _EVENT_LOG_ERR: LOGERROR ("LibEvent: %s", a_Msg); break;
+ default:
+ {
+ LOGWARNING("LibEvent: Unknown log severity (%d): %s", a_Severity, a_Msg);
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cNetworkSingleton::RunEventLoop(cNetworkSingleton * a_Self)
+{
+ event_base_loop(a_Self->m_EventBase, EVLOOP_NO_EXIT_ON_EMPTY);
+ a_Self->m_EventLoopTerminated.Set();
+}
+
+
+
+
+
+void cNetworkSingleton::AddHostnameLookup(cHostnameLookupPtr a_HostnameLookup)
+{
+ cCSLock Lock(m_CS);
+ m_HostnameLookups.push_back(a_HostnameLookup);
+}
+
+
+
+
+
+void cNetworkSingleton::RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup)
+{
+ cCSLock Lock(m_CS);
+ for (auto itr = m_HostnameLookups.begin(), end = m_HostnameLookups.end(); itr != end; ++itr)
+ {
+ if (itr->get() == a_HostnameLookup)
+ {
+ m_HostnameLookups.erase(itr);
+ return;
+ }
+ } // for itr - m_HostnameLookups[]
+}
+
+
+
+
+
+void cNetworkSingleton::AddIPLookup(cIPLookupPtr a_IPLookup)
+{
+ cCSLock Lock(m_CS);
+ m_IPLookups.push_back(a_IPLookup);
+}
+
+
+
+
+
+void cNetworkSingleton::RemoveIPLookup(const cIPLookup * a_IPLookup)
+{
+ cCSLock Lock(m_CS);
+ for (auto itr = m_IPLookups.begin(), end = m_IPLookups.end(); itr != end; ++itr)
+ {
+ if (itr->get() == a_IPLookup)
+ {
+ m_IPLookups.erase(itr);
+ return;
+ }
+ } // for itr - m_IPLookups[]
+}
+
+
+
+
+
+void cNetworkSingleton::AddLink(cTCPLinkImplPtr a_Link)
+{
+ cCSLock Lock(m_CS);
+ m_Connections.push_back(a_Link);
+}
+
+
+
+
+
+void cNetworkSingleton::RemoveLink(const cTCPLinkImpl * a_Link)
+{
+ cCSLock Lock(m_CS);
+ for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr)
+ {
+ if (itr->get() == a_Link)
+ {
+ m_Connections.erase(itr);
+ return;
+ }
+ } // for itr - m_Connections[]
+}
+
+
+
+
+
+void cNetworkSingleton::AddServer(cServerHandleImplPtr a_Server)
+{
+ cCSLock Lock(m_CS);
+ m_Servers.push_back(a_Server);
+}
+
+
+
+
+
+void cNetworkSingleton::RemoveServer(const cServerHandleImpl * a_Server)
+{
+ cCSLock Lock(m_CS);
+ for (auto itr = m_Servers.begin(), end = m_Servers.end(); itr != end; ++itr)
+ {
+ if (itr->get() == a_Server)
+ {
+ m_Servers.erase(itr);
+ return;
+ }
+ } // for itr - m_Servers[]
+}
+
+
+
+
diff --git a/src/OSSupport/NetworkSingleton.h b/src/OSSupport/NetworkSingleton.h
new file mode 100644
index 000000000..1d26fc8f4
--- /dev/null
+++ b/src/OSSupport/NetworkSingleton.h
@@ -0,0 +1,134 @@
+
+// NetworkSingleton.h
+
+// Declares the cNetworkSingleton class representing the storage for global data pertaining to network API
+// such as a list of all connections, all listening sockets and the LibEvent dispatch thread.
+
+// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead
+
+
+
+
+
+#pragma once
+
+#include "Network.h"
+#include "CriticalSection.h"
+#include "Event.h"
+
+
+
+
+
+// fwd:
+struct event_base;
+struct evdns_base;
+class cTCPLinkImpl;
+typedef SharedPtr<cTCPLinkImpl> cTCPLinkImplPtr;
+typedef std::vector<cTCPLinkImplPtr> cTCPLinkImplPtrs;
+class cServerHandleImpl;
+typedef SharedPtr<cServerHandleImpl> cServerHandleImplPtr;
+typedef std::vector<cServerHandleImplPtr> cServerHandleImplPtrs;
+class cHostnameLookup;
+typedef SharedPtr<cHostnameLookup> cHostnameLookupPtr;
+typedef std::vector<cHostnameLookupPtr> cHostnameLookupPtrs;
+class cIPLookup;
+typedef SharedPtr<cIPLookup> cIPLookupPtr;
+typedef std::vector<cIPLookupPtr> cIPLookupPtrs;
+
+
+
+
+
+class cNetworkSingleton
+{
+public:
+ ~cNetworkSingleton();
+
+ /** Returns the singleton instance of this class */
+ static cNetworkSingleton & Get(void);
+
+ /** Returns the main LibEvent handle for event registering. */
+ event_base * GetEventBase(void) { return m_EventBase; }
+
+ /** Returns the LibEvent handle for DNS lookups. */
+ evdns_base * GetDNSBase(void) { return m_DNSBase; }
+
+ /** Adds the specified hostname lookup to m_HostnameLookups.
+ Used by the underlying lookup implementation when a new lookup is initiated. */
+ void AddHostnameLookup(cHostnameLookupPtr a_HostnameLookup);
+
+ /** Removes the specified hostname lookup from m_HostnameLookups.
+ Used by the underlying lookup implementation when the lookup is finished. */
+ void RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup);
+
+ /** Adds the specified IP lookup to M_IPLookups.
+ Used by the underlying lookup implementation when a new lookup is initiated. */
+ void AddIPLookup(cIPLookupPtr a_IPLookup);
+
+ /** Removes the specified IP lookup from m_IPLookups.
+ Used by the underlying lookup implementation when the lookup is finished. */
+ void RemoveIPLookup(const cIPLookup * a_IPLookup);
+
+ /** Adds the specified link to m_Connections.
+ Used by the underlying link implementation when a new link is created. */
+ void AddLink(cTCPLinkImplPtr a_Link);
+
+ /** Removes the specified link from m_Connections.
+ Used by the underlying link implementation when the link is closed / errored. */
+ void RemoveLink(const cTCPLinkImpl * a_Link);
+
+ /** Adds the specified link to m_Servers.
+ Used by the underlying server handle implementation when a new listening server is created.
+ Only servers that succeed in listening are added. */
+ void AddServer(cServerHandleImplPtr a_Server);
+
+ /** Removes the specified server from m_Servers.
+ Used by the underlying server handle implementation when the server is closed. */
+ void RemoveServer(const cServerHandleImpl * a_Server);
+
+protected:
+
+ /** The main LibEvent container for driving the event loop. */
+ event_base * m_EventBase;
+
+ /** The LibEvent handle for doing DNS lookups. */
+ evdns_base * m_DNSBase;
+
+ /** Container for all client connections, including ones with pending-connect. */
+ cTCPLinkImplPtrs m_Connections;
+
+ /** Container for all servers that are currently active. */
+ cServerHandleImplPtrs m_Servers;
+
+ /** Container for all pending hostname lookups. */
+ cHostnameLookupPtrs m_HostnameLookups;
+
+ /** Container for all pending IP lookups. */
+ cIPLookupPtrs m_IPLookups;
+
+ /** Mutex protecting all containers against multithreaded access. */
+ cCriticalSection m_CS;
+
+ /** Event that gets signalled when the event loop terminates. */
+ cEvent m_EventLoopTerminated;
+
+
+ /** Initializes the LibEvent internals. */
+ cNetworkSingleton(void);
+
+ /** Converts LibEvent-generated log events into log messages in MCS log. */
+ static void LogCallback(int a_Severity, const char * a_Msg);
+
+ /** Implements the thread that runs LibEvent's event dispatcher loop. */
+ static void RunEventLoop(cNetworkSingleton * a_Self);
+};
+
+
+
+
+
+
+
+
+
diff --git a/src/OSSupport/Queue.h b/src/OSSupport/Queue.h
index 82f221453..afbde1b0d 100644
--- a/src/OSSupport/Queue.h
+++ b/src/OSSupport/Queue.h
@@ -86,7 +86,7 @@ public:
bool TryDequeueItem(ItemType & item)
{
cCSLock Lock(m_CS);
- if (m_Contents.size() == 0)
+ if (m_Contents.empty())
{
return false;
}
@@ -101,7 +101,7 @@ public:
ItemType DequeueItem(void)
{
cCSLock Lock(m_CS);
- while (m_Contents.size() == 0)
+ while (m_Contents.empty())
{
cCSUnlock Unlock(Lock);
m_evtAdded.Wait();
diff --git a/src/OSSupport/ServerHandleImpl.cpp b/src/OSSupport/ServerHandleImpl.cpp
new file mode 100644
index 000000000..ba38dbf2e
--- /dev/null
+++ b/src/OSSupport/ServerHandleImpl.cpp
@@ -0,0 +1,334 @@
+
+// ServerHandleImpl.cpp
+
+// Implements the cServerHandleImpl class implementing the TCP server functionality
+
+#include "Globals.h"
+#include "ServerHandleImpl.h"
+#include "TCPLinkImpl.h"
+#include "NetworkSingleton.h"
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Globals:
+
+static bool IsValidSocket(evutil_socket_t a_Socket)
+{
+ #ifdef _WIN32
+ return (a_Socket != INVALID_SOCKET);
+ #else // _WIN32
+ return (a_Socket >= 0);
+ #endif // else _WIN32
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cServerHandleImpl:
+
+cServerHandleImpl::cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks):
+ m_ListenCallbacks(a_ListenCallbacks),
+ m_ConnListener(nullptr),
+ m_SecondaryConnListener(nullptr),
+ m_IsListening(false),
+ m_ErrorCode(0)
+{
+}
+
+
+
+
+
+cServerHandleImpl::~cServerHandleImpl()
+{
+ if (m_ConnListener != nullptr)
+ {
+ evconnlistener_free(m_ConnListener);
+ }
+ if (m_SecondaryConnListener != nullptr)
+ {
+ evconnlistener_free(m_SecondaryConnListener);
+ }
+}
+
+
+
+
+
+void cServerHandleImpl::Close(void)
+{
+ // Stop the listener sockets:
+ evconnlistener_disable(m_ConnListener);
+ if (m_SecondaryConnListener != nullptr)
+ {
+ evconnlistener_disable(m_SecondaryConnListener);
+ }
+ m_IsListening = false;
+
+ // Shutdown all connections:
+ cTCPLinkImplPtrs Conns;
+ {
+ cCSLock Lock(m_CS);
+ std::swap(Conns, m_Connections);
+ }
+ for (auto conn: Conns)
+ {
+ conn->Shutdown();
+ }
+
+ // Remove the ptr to self, so that the object may be freed:
+ m_SelfPtr.reset();
+}
+
+
+
+
+
+cServerHandleImplPtr cServerHandleImpl::Listen(
+ UInt16 a_Port,
+ cNetwork::cListenCallbacksPtr a_ListenCallbacks
+)
+{
+ cServerHandleImplPtr res = cServerHandleImplPtr{new cServerHandleImpl(a_ListenCallbacks)};
+ res->m_SelfPtr = res;
+ if (res->Listen(a_Port))
+ {
+ cNetworkSingleton::Get().AddServer(res);
+ }
+ else
+ {
+ a_ListenCallbacks->OnError(res->m_ErrorCode, res->m_ErrorMsg);
+ res->m_SelfPtr.reset();
+ }
+ return res;
+}
+
+
+
+
+
+bool cServerHandleImpl::Listen(UInt16 a_Port)
+{
+ // Make sure the cNetwork internals are innitialized:
+ cNetworkSingleton::Get();
+
+ // Set up the main socket:
+ // It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available.
+ bool NeedsTwoSockets = false;
+ int err;
+ evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
+ if (!IsValidSocket(MainSock))
+ {
+ // Failed to create IPv6 socket, create an IPv4 one instead:
+ err = EVUTIL_SOCKET_ERROR();
+ LOGD("Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err));
+ MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (!IsValidSocket(MainSock))
+ {
+ m_ErrorCode = EVUTIL_SOCKET_ERROR();
+ Printf(m_ErrorMsg, "Cannot create socket for port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode));
+ return false;
+ }
+
+ // Bind to all interfaces:
+ sockaddr_in name;
+ memset(&name, 0, sizeof(name));
+ name.sin_family = AF_INET;
+ name.sin_port = ntohs(a_Port);
+ if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
+ {
+ m_ErrorCode = EVUTIL_SOCKET_ERROR();
+ Printf(m_ErrorMsg, "Cannot bind IPv4 socket to port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode));
+ evutil_closesocket(MainSock);
+ return false;
+ }
+ }
+ else
+ {
+ // IPv6 socket created, switch it into "dualstack" mode:
+ UInt32 Zero = 0;
+ #ifdef _WIN32
+ // WinXP doesn't support this feature, so if the setting fails, create another socket later on:
+ int res = setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero));
+ err = EVUTIL_SOCKET_ERROR();
+ NeedsTwoSockets = ((res == SOCKET_ERROR) && (err == WSAENOPROTOOPT));
+ LOGD("setsockopt(IPV6_V6ONLY) returned %d, err is %d (%s). %s",
+ res, err, evutil_socket_error_to_string(err),
+ NeedsTwoSockets ? "Second socket will be created" : "Second socket not needed"
+ );
+ #else
+ setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero));
+ #endif
+
+ // Bind to all interfaces:
+ sockaddr_in6 name;
+ memset(&name, 0, sizeof(name));
+ name.sin6_family = AF_INET6;
+ name.sin6_port = ntohs(a_Port);
+ if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
+ {
+ m_ErrorCode = EVUTIL_SOCKET_ERROR();
+ Printf(m_ErrorMsg, "Cannot bind IPv6 socket to port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
+ evutil_closesocket(MainSock);
+ return false;
+ }
+ }
+ if (evutil_make_socket_nonblocking(MainSock) != 0)
+ {
+ m_ErrorCode = EVUTIL_SOCKET_ERROR();
+ Printf(m_ErrorMsg, "Cannot make socket on port %d non-blocking: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
+ evutil_closesocket(MainSock);
+ return false;
+ }
+ if (listen(MainSock, 0) != 0)
+ {
+ m_ErrorCode = EVUTIL_SOCKET_ERROR();
+ Printf(m_ErrorMsg, "Cannot listen on port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
+ evutil_closesocket(MainSock);
+ return false;
+ }
+ m_ConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, MainSock);
+ m_IsListening = true;
+ if (!NeedsTwoSockets)
+ {
+ return true;
+ }
+
+ // If a secondary socket is required (WinXP dual-stack), create it here:
+ LOGD("Creating a second socket for IPv4");
+ evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (!IsValidSocket(SecondSock))
+ {
+ err = EVUTIL_SOCKET_ERROR();
+ LOGD("socket(AF_INET, ...) failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err));
+ return true; // Report as success, the primary socket is working
+ }
+
+ // Make the secondary socket nonblocking:
+ if (evutil_make_socket_nonblocking(SecondSock) != 0)
+ {
+ err = EVUTIL_SOCKET_ERROR();
+ LOGD("evutil_make_socket_nonblocking() failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err));
+ evutil_closesocket(SecondSock);
+ return true; // Report as success, the primary socket is working
+ }
+
+ // Bind to all IPv4 interfaces:
+ sockaddr_in name;
+ memset(&name, 0, sizeof(name));
+ name.sin_family = AF_INET;
+ name.sin_port = ntohs(a_Port);
+ if (bind(SecondSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
+ {
+ err = EVUTIL_SOCKET_ERROR();
+ LOGD("Cannot bind secondary socket to port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
+ evutil_closesocket(SecondSock);
+ return true; // Report as success, the primary socket is working
+ }
+
+ if (listen(SecondSock, 0) != 0)
+ {
+ err = EVUTIL_SOCKET_ERROR();
+ LOGD("Cannot listen on on secondary socket on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
+ evutil_closesocket(SecondSock);
+ return true; // Report as success, the primary socket is working
+ }
+
+ m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock);
+ return true;
+}
+
+
+
+
+
+void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self)
+{
+ // Cast to true self:
+ cServerHandleImpl * Self = reinterpret_cast<cServerHandleImpl *>(a_Self);
+ ASSERT(Self != nullptr);
+ ASSERT(Self->m_SelfPtr != nullptr);
+
+ // Get the textual IP address and port number out of a_Addr:
+ char IPAddress[128];
+ evutil_inet_ntop(a_Addr->sa_family, a_Addr->sa_data, IPAddress, ARRAYCOUNT(IPAddress));
+ UInt16 Port = 0;
+ switch (a_Addr->sa_family)
+ {
+ case AF_INET:
+ {
+ sockaddr_in * sin = reinterpret_cast<sockaddr_in *>(a_Addr);
+ Port = ntohs(sin->sin_port);
+ break;
+ }
+ case AF_INET6:
+ {
+ sockaddr_in6 * sin6 = reinterpret_cast<sockaddr_in6 *>(a_Addr);
+ Port = ntohs(sin6->sin6_port);
+ break;
+ }
+ }
+
+ // Call the OnIncomingConnection callback to get the link callbacks to use:
+ cTCPLink::cCallbacksPtr LinkCallbacks = Self->m_ListenCallbacks->OnIncomingConnection(IPAddress, Port);
+ if (LinkCallbacks == nullptr)
+ {
+ // Drop the connection:
+ evutil_closesocket(a_Socket);
+ return;
+ }
+
+ // Create a new cTCPLink for the incoming connection:
+ cTCPLinkImplPtr Link = std::make_shared<cTCPLinkImpl>(a_Socket, LinkCallbacks, Self->m_SelfPtr, a_Addr, static_cast<socklen_t>(a_Len));
+ {
+ cCSLock Lock(Self->m_CS);
+ Self->m_Connections.push_back(Link);
+ } // Lock(m_CS)
+ LinkCallbacks->OnLinkCreated(Link);
+ Link->Enable(Link);
+
+ // Call the OnAccepted callback:
+ Self->m_ListenCallbacks->OnAccepted(*Link);
+}
+
+
+
+
+
+void cServerHandleImpl::RemoveLink(const cTCPLinkImpl * a_Link)
+{
+ cCSLock Lock(m_CS);
+ for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr)
+ {
+ if (itr->get() == a_Link)
+ {
+ m_Connections.erase(itr);
+ return;
+ }
+ } // for itr - m_Connections[]
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cNetwork API:
+
+cServerHandlePtr cNetwork::Listen(
+ UInt16 a_Port,
+ cNetwork::cListenCallbacksPtr a_ListenCallbacks
+)
+{
+ return cServerHandleImpl::Listen(a_Port, a_ListenCallbacks);
+}
+
+
+
+
+
diff --git a/src/OSSupport/ServerHandleImpl.h b/src/OSSupport/ServerHandleImpl.h
new file mode 100644
index 000000000..dbb18fc6d
--- /dev/null
+++ b/src/OSSupport/ServerHandleImpl.h
@@ -0,0 +1,105 @@
+
+// ServerHandleImpl.h
+
+// Declares the cServerHandleImpl class implementing the TCP server functionality
+
+// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead
+
+
+
+
+
+#pragma once
+
+#include "Network.h"
+#include <event2/listener.h>
+#include "CriticalSection.h"
+
+
+
+
+
+// fwd:
+class cTCPLinkImpl;
+typedef SharedPtr<cTCPLinkImpl> cTCPLinkImplPtr;
+typedef std::vector<cTCPLinkImplPtr> cTCPLinkImplPtrs;
+class cServerHandleImpl;
+typedef SharedPtr<cServerHandleImpl> cServerHandleImplPtr;
+typedef std::vector<cServerHandleImplPtr> cServerHandleImplPtrs;
+
+
+
+
+
+class cServerHandleImpl:
+ public cServerHandle
+{
+ typedef cServerHandle super;
+ friend class cTCPLinkImpl;
+
+public:
+ /** Closes the server, dropping all the connections. */
+ ~cServerHandleImpl();
+
+ /** Creates a new server instance listening on the specified port.
+ Both IPv4 and IPv6 interfaces are used, if possible.
+ Always returns a server instance; in the event of a failure, the instance holds the error details. Use IsListening() to query success. */
+ static cServerHandleImplPtr Listen(
+ UInt16 a_Port,
+ cNetwork::cListenCallbacksPtr a_ListenCallbacks
+ );
+
+ // cServerHandle overrides:
+ virtual void Close(void) override;
+ virtual bool IsListening(void) const override { return m_IsListening; }
+
+protected:
+ /** The callbacks used to notify about incoming connections. */
+ cNetwork::cListenCallbacksPtr m_ListenCallbacks;
+
+ /** The LibEvent handle representing the main listening socket. */
+ evconnlistener * m_ConnListener;
+
+ /** The LibEvent handle representing the secondary listening socket (only when side-by-side listening is needed, such as WinXP). */
+ evconnlistener * m_SecondaryConnListener;
+
+ /** Set to true when the server is initialized successfully and is listening for incoming connections. */
+ bool m_IsListening;
+
+ /** Container for all currently active connections on this server. */
+ cTCPLinkImplPtrs m_Connections;
+
+ /** Mutex protecting m_Connections againt multithreaded access. */
+ cCriticalSection m_CS;
+
+ /** Contains the error code for the failure to listen. Only valid for non-listening instances. */
+ int m_ErrorCode;
+
+ /** Contains the error message for the failure to listen. Only valid for non-listening instances. */
+ AString m_ErrorMsg;
+
+ /** The SharedPtr to self, so that it can be passed to created links. */
+ cServerHandleImplPtr m_SelfPtr;
+
+
+
+ /** Creates a new instance with the specified callbacks.
+ Initializes the internals, but doesn't start listening yet. */
+ cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks);
+
+ /** Starts listening on the specified port.
+ Returns true if successful, false on failure. On failure, sets m_ErrorCode and m_ErrorMsg. */
+ bool Listen(UInt16 a_Port);
+
+ /** The callback called by LibEvent upon incoming connection. */
+ static void Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self);
+
+ /** Removes the specified link from m_Connections.
+ Called by cTCPLinkImpl when the link is terminated. */
+ void RemoveLink(const cTCPLinkImpl * a_Link);
+};
+
+
+
+
+
diff --git a/src/OSSupport/StackTrace.cpp b/src/OSSupport/StackTrace.cpp
index a56568457..015a53ba0 100644
--- a/src/OSSupport/StackTrace.cpp
+++ b/src/OSSupport/StackTrace.cpp
@@ -34,7 +34,7 @@ void PrintStackTrace(void)
// Use the backtrace() function to get and output the stackTrace:
// Code adapted from http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes
void * stackTrace[30];
- size_t numItems = backtrace(stackTrace, ARRAYCOUNT(stackTrace));
+ int numItems = backtrace(stackTrace, ARRAYCOUNT(stackTrace));
backtrace_symbols_fd(stackTrace, numItems, STDERR_FILENO);
#endif
}
diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp
new file mode 100644
index 000000000..b4cefa60c
--- /dev/null
+++ b/src/OSSupport/TCPLinkImpl.cpp
@@ -0,0 +1,331 @@
+
+// TCPLinkImpl.cpp
+
+// Implements the cTCPLinkImpl class implementing the TCP link functionality
+
+#include "Globals.h"
+#include "TCPLinkImpl.h"
+#include "NetworkSingleton.h"
+#include "ServerHandleImpl.h"
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cTCPLinkImpl:
+
+cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks):
+ super(a_LinkCallbacks),
+ m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), -1, BEV_OPT_CLOSE_ON_FREE))
+{
+}
+
+
+
+
+
+cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks, cServerHandleImplPtr a_Server, const sockaddr * a_Address, socklen_t a_AddrLen):
+ super(a_LinkCallbacks),
+ m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), a_Socket, BEV_OPT_CLOSE_ON_FREE)),
+ m_Server(a_Server)
+{
+ // Update the endpoint addresses:
+ UpdateLocalAddress();
+ UpdateAddress(a_Address, a_AddrLen, m_RemoteIP, m_RemotePort);
+}
+
+
+
+
+
+cTCPLinkImpl::~cTCPLinkImpl()
+{
+ bufferevent_free(m_BufferEvent);
+}
+
+
+
+
+
+cTCPLinkImplPtr cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cTCPLink::cCallbacksPtr a_LinkCallbacks, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks)
+{
+ ASSERT(a_LinkCallbacks != nullptr);
+ ASSERT(a_ConnectCallbacks != nullptr);
+
+ // Create a new link:
+ cTCPLinkImplPtr res{new cTCPLinkImpl(a_LinkCallbacks)}; // Cannot use std::make_shared here, constructor is not accessible
+ res->m_ConnectCallbacks = a_ConnectCallbacks;
+ cNetworkSingleton::Get().AddLink(res);
+ res->m_Callbacks->OnLinkCreated(res);
+ res->Enable(res);
+
+ // If a_Host is an IP address, schedule a connection immediately:
+ sockaddr_storage sa;
+ int salen = static_cast<int>(sizeof(sa));
+ if (evutil_parse_sockaddr_port(a_Host.c_str(), reinterpret_cast<sockaddr *>(&sa), &salen) == 0)
+ {
+ // Insert the correct port:
+ if (sa.ss_family == AF_INET6)
+ {
+ reinterpret_cast<sockaddr_in6 *>(&sa)->sin6_port = htons(a_Port);
+ }
+ else
+ {
+ reinterpret_cast<sockaddr_in *>(&sa)->sin_port = htons(a_Port);
+ }
+
+ // Queue the connect request:
+ if (bufferevent_socket_connect(res->m_BufferEvent, reinterpret_cast<sockaddr *>(&sa), salen) == 0)
+ {
+ // Success
+ return res;
+ }
+ // Failure
+ cNetworkSingleton::Get().RemoveLink(res.get());
+ return nullptr;
+ }
+
+ // a_Host is a hostname, connect after a lookup:
+ if (bufferevent_socket_connect_hostname(res->m_BufferEvent, cNetworkSingleton::Get().GetDNSBase(), AF_UNSPEC, a_Host.c_str(), a_Port) == 0)
+ {
+ // Success
+ return res;
+ }
+ // Failure
+ cNetworkSingleton::Get().RemoveLink(res.get());
+ return nullptr;
+}
+
+
+
+
+
+void cTCPLinkImpl::Enable(cTCPLinkImplPtr a_Self)
+{
+ // Take hold of a shared copy of self, to keep as long as the callbacks are coming:
+ m_Self = a_Self;
+
+ // Set the LibEvent callbacks and enable processing:
+ bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this);
+ bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE);
+}
+
+
+
+
+
+bool cTCPLinkImpl::Send(const void * a_Data, size_t a_Length)
+{
+ return (bufferevent_write(m_BufferEvent, a_Data, a_Length) == 0);
+}
+
+
+
+
+
+void cTCPLinkImpl::Shutdown(void)
+{
+ #ifdef _WIN32
+ shutdown(bufferevent_getfd(m_BufferEvent), SD_SEND);
+ #else
+ shutdown(bufferevent_getfd(m_BufferEvent), SHUT_WR);
+ #endif
+ bufferevent_disable(m_BufferEvent, EV_WRITE);
+}
+
+
+
+
+
+void cTCPLinkImpl::Close(void)
+{
+ // Disable all events on the socket, but keep it alive:
+ bufferevent_disable(m_BufferEvent, EV_READ | EV_WRITE);
+ if (m_Server == nullptr)
+ {
+ cNetworkSingleton::Get().RemoveLink(this);
+ }
+ else
+ {
+ m_Server->RemoveLink(this);
+ }
+ m_Self.reset();
+}
+
+
+
+
+
+
+void cTCPLinkImpl::ReadCallback(bufferevent * a_BufferEvent, void * a_Self)
+{
+ ASSERT(a_Self != nullptr);
+ cTCPLinkImpl * Self = static_cast<cTCPLinkImpl *>(a_Self);
+ ASSERT(Self->m_Callbacks != nullptr);
+
+ // Read all the incoming data, in 1024-byte chunks:
+ char data[1024];
+ size_t length;
+ while ((length = bufferevent_read(a_BufferEvent, data, sizeof(data))) > 0)
+ {
+ Self->m_Callbacks->OnReceivedData(data, length);
+ }
+}
+
+
+
+
+
+void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self)
+{
+ ASSERT(a_Self != nullptr);
+ cTCPLinkImplPtr Self = static_cast<cTCPLinkImpl *>(a_Self)->m_Self;
+
+ // If an error is reported, call the error callback:
+ if (a_What & BEV_EVENT_ERROR)
+ {
+ // Choose the proper callback to call based on whether we were waiting for connection or not:
+ int err = EVUTIL_SOCKET_ERROR();
+ if (Self->m_ConnectCallbacks != nullptr)
+ {
+ if (err == 0)
+ {
+ // This could be a DNS failure
+ err = bufferevent_socket_get_dns_error(a_BufferEvent);
+ }
+ Self->m_ConnectCallbacks->OnError(err, evutil_socket_error_to_string(err));
+ }
+ else
+ {
+ Self->m_Callbacks->OnError(err, evutil_socket_error_to_string(err));
+ if (Self->m_Server == nullptr)
+ {
+ cNetworkSingleton::Get().RemoveLink(Self.get());
+ }
+ else
+ {
+ Self->m_Server->RemoveLink(Self.get());
+ }
+ }
+ Self->m_Self.reset();
+ return;
+ }
+
+ // Pending connection succeeded, call the connection callback:
+ if (a_What & BEV_EVENT_CONNECTED)
+ {
+ if (Self->m_ConnectCallbacks != nullptr)
+ {
+ Self->m_ConnectCallbacks->OnConnected(*Self);
+ // Reset the connect callbacks so that later errors get reported through the link callbacks:
+ Self->m_ConnectCallbacks.reset();
+ return;
+ }
+ Self->UpdateLocalAddress();
+ Self->UpdateRemoteAddress();
+ }
+
+ // If the connection has been closed, call the link callback and remove the connection:
+ if (a_What & BEV_EVENT_EOF)
+ {
+ Self->m_Callbacks->OnRemoteClosed();
+ if (Self->m_Server != nullptr)
+ {
+ Self->m_Server->RemoveLink(Self.get());
+ }
+ else
+ {
+ cNetworkSingleton::Get().RemoveLink(Self.get());
+ }
+ Self->m_Self.reset();
+ return;
+ }
+
+ // Unknown event, report it:
+ LOGWARNING("cTCPLinkImpl: Unhandled LibEvent event %d (0x%x)", a_What, a_What);
+ ASSERT(!"cTCPLinkImpl: Unhandled LibEvent event");
+}
+
+
+
+
+
+void cTCPLinkImpl::UpdateAddress(const sockaddr * a_Address, socklen_t a_AddrLen, AString & a_IP, UInt16 & a_Port)
+{
+ // Based on the family specified in the address, use the correct datastructure to convert to IP string:
+ char IP[128];
+ switch (a_Address->sa_family)
+ {
+ case AF_INET: // IPv4:
+ {
+ const sockaddr_in * sin = reinterpret_cast<const sockaddr_in *>(a_Address);
+ evutil_inet_ntop(AF_INET, &(sin->sin_addr), IP, sizeof(IP));
+ a_Port = ntohs(sin->sin_port);
+ break;
+ }
+ case AF_INET6: // IPv6
+ {
+ const sockaddr_in6 * sin = reinterpret_cast<const sockaddr_in6 *>(a_Address);
+ evutil_inet_ntop(AF_INET6, &(sin->sin6_addr), IP, sizeof(IP));
+ a_Port = ntohs(sin->sin6_port);
+ break;
+ }
+
+ default:
+ {
+ LOGWARNING("%s: Unknown socket address family: %d", __FUNCTION__, a_Address->sa_family);
+ ASSERT(!"Unknown socket address family");
+ break;
+ }
+ }
+ a_IP.assign(IP);
+}
+
+
+
+
+
+void cTCPLinkImpl::UpdateLocalAddress(void)
+{
+ sockaddr_storage sa;
+ socklen_t salen = static_cast<socklen_t>(sizeof(sa));
+ getsockname(bufferevent_getfd(m_BufferEvent), reinterpret_cast<sockaddr *>(&sa), &salen);
+ UpdateAddress(reinterpret_cast<const sockaddr *>(&sa), salen, m_LocalIP, m_LocalPort);
+}
+
+
+
+
+
+void cTCPLinkImpl::UpdateRemoteAddress(void)
+{
+ sockaddr_storage sa;
+ socklen_t salen = static_cast<socklen_t>(sizeof(sa));
+ getpeername(bufferevent_getfd(m_BufferEvent), reinterpret_cast<sockaddr *>(&sa), &salen);
+ UpdateAddress(reinterpret_cast<const sockaddr *>(&sa), salen, m_RemoteIP, m_RemotePort);
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cNetwork API:
+
+bool cNetwork::Connect(
+ const AString & a_Host,
+ UInt16 a_Port,
+ cNetwork::cConnectCallbacksPtr a_ConnectCallbacks,
+ cTCPLink::cCallbacksPtr a_LinkCallbacks
+)
+{
+ // Add a connection request to the queue:
+ cTCPLinkImplPtr Conn = cTCPLinkImpl::Connect(a_Host, a_Port, a_LinkCallbacks, a_ConnectCallbacks);
+ return (Conn != nullptr);
+}
+
+
+
+
+
diff --git a/src/OSSupport/TCPLinkImpl.h b/src/OSSupport/TCPLinkImpl.h
new file mode 100644
index 000000000..735e8ed9d
--- /dev/null
+++ b/src/OSSupport/TCPLinkImpl.h
@@ -0,0 +1,122 @@
+
+// TCPLinkImpl.h
+
+// Declares the cTCPLinkImpl class implementing the TCP link functionality
+
+// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead
+
+
+
+
+
+#pragma once
+
+#include "Network.h"
+#include <event2/event.h>
+#include <event2/bufferevent.h>
+
+
+
+
+
+// fwd:
+class cServerHandleImpl;
+typedef SharedPtr<cServerHandleImpl> cServerHandleImplPtr;
+class cTCPLinkImpl;
+typedef SharedPtr<cTCPLinkImpl> cTCPLinkImplPtr;
+typedef std::vector<cTCPLinkImplPtr> cTCPLinkImplPtrs;
+
+
+
+
+
+class cTCPLinkImpl:
+ public cTCPLink
+{
+ typedef cTCPLink super;
+
+public:
+ /** Creates a new link based on the given socket.
+ Used for connections accepted in a server using cNetwork::Listen().
+ a_Address and a_AddrLen describe the remote peer that has connected.
+ The link is created disabled, you need to call Enable() to start the regular communication. */
+ cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks, cServerHandleImplPtr a_Server, const sockaddr * a_Address, socklen_t a_AddrLen);
+
+ /** Destroys the LibEvent handle representing the link. */
+ ~cTCPLinkImpl();
+
+ /** Queues a connection request to the specified host.
+ a_ConnectCallbacks must be valid.
+ Returns a link that has the connection request queued, or NULL for failure. */
+ static cTCPLinkImplPtr Connect(const AString & a_Host, UInt16 a_Port, cTCPLink::cCallbacksPtr a_LinkCallbacks, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks);
+
+ /** Enables communication over the link.
+ Links are created with communication disabled, so that creation callbacks can be called first.
+ This function then enables the regular communication to be reported.
+ The a_Self parameter is used so that the socket can keep itself alive as long as the callbacks are coming. */
+ void Enable(cTCPLinkImplPtr a_Self);
+
+ // cTCPLink overrides:
+ virtual bool Send(const void * a_Data, size_t a_Length) override;
+ virtual AString GetLocalIP(void) const override { return m_LocalIP; }
+ virtual UInt16 GetLocalPort(void) const override { return m_LocalPort; }
+ virtual AString GetRemoteIP(void) const override { return m_RemoteIP; }
+ virtual UInt16 GetRemotePort(void) const override { return m_RemotePort; }
+ virtual void Shutdown(void) override;
+ virtual void Close(void) override;
+
+protected:
+
+ /** Callbacks to call when the connection is established.
+ May be NULL if not used. Only used for outgoing connections (cNetwork::Connect()). */
+ cNetwork::cConnectCallbacksPtr m_ConnectCallbacks;
+
+ /** The LibEvent handle representing this connection. */
+ bufferevent * m_BufferEvent;
+
+ /** The server handle that has created this link.
+ Only valid for incoming connections, nullptr for outgoing connections. */
+ cServerHandleImplPtr m_Server;
+
+ /** The IP address of the local endpoint. Valid only after the socket has been connected. */
+ AString m_LocalIP;
+
+ /** The port of the local endpoint. Valid only after the socket has been connected. */
+ UInt16 m_LocalPort;
+
+ /** The IP address of the remote endpoint. Valid only after the socket has been connected. */
+ AString m_RemoteIP;
+
+ /** The port of the remote endpoint. Valid only after the socket has been connected. */
+ UInt16 m_RemotePort;
+
+ /** SharedPtr to self, used to keep this object alive as long as the callbacks are coming.
+ Initialized in Enable(), cleared in Close() and EventCallback(RemoteClosed). */
+ cTCPLinkImplPtr m_Self;
+
+
+ /** Creates a new link to be queued to connect to a specified host:port.
+ Used for outgoing connections created using cNetwork::Connect().
+ To be used only by the Connect() factory function.
+ The link is created disabled, you need to call Enable() to start the regular communication. */
+ cTCPLinkImpl(const cCallbacksPtr a_LinkCallbacks);
+
+ /** Callback that LibEvent calls when there's data available from the remote peer. */
+ static void ReadCallback(bufferevent * a_BufferEvent, void * a_Self);
+
+ /** Callback that LibEvent calls when there's a non-data-related event on the socket. */
+ static void EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self);
+
+ /** Sets a_IP and a_Port to values read from a_Address, based on the correct address family. */
+ static void UpdateAddress(const sockaddr * a_Address, socklen_t a_AddrLen, AString & a_IP, UInt16 & a_Port);
+
+ /** Updates m_LocalIP and m_LocalPort based on the metadata read from the socket. */
+ void UpdateLocalAddress(void);
+
+ /** Updates m_RemoteIP and m_RemotePort based on the metadata read from the socket. */
+ void UpdateRemoteAddress(void);
+};
+
+
+
+
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index 1e5fe5586..169367949 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -174,10 +174,10 @@ void cProtocol172::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, cha
cPacketizer Pkt(*this, 0x24); // Block Action packet
Pkt.WriteInt(a_BlockX);
- Pkt.WriteShort(a_BlockY);
+ Pkt.WriteShort(static_cast<short>(a_BlockY));
Pkt.WriteInt(a_BlockZ);
- Pkt.WriteByte(a_Byte1);
- Pkt.WriteByte(a_Byte2);
+ Pkt.WriteByte(static_cast<Byte>(a_Byte1));
+ Pkt.WriteByte(static_cast<Byte>(a_Byte2));
Pkt.WriteVarInt(a_BlockType);
}
@@ -190,7 +190,7 @@ void cProtocol172::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, 0x25); // Block Break Animation packet
- Pkt.WriteVarInt(a_EntityID);
+ Pkt.WriteVarInt(static_cast<UInt32>(a_EntityID));
Pkt.WriteInt(a_BlockX);
Pkt.WriteInt(a_BlockY);
Pkt.WriteInt(a_BlockZ);
@@ -204,10 +204,11 @@ void cProtocol172::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY
void cProtocol172::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
ASSERT(m_State == 3); // In game mode?
+ ASSERT((a_BlockY >= 0) && (a_BlockY < 256));
cPacketizer Pkt(*this, 0x23); // Block Change packet
Pkt.WriteInt(a_BlockX);
- Pkt.WriteByte(a_BlockY);
+ Pkt.WriteByte(static_cast<Byte>(a_BlockY));
Pkt.WriteInt(a_BlockZ);
Pkt.WriteVarInt(a_BlockType);
Pkt.WriteByte(a_BlockMeta);
@@ -228,8 +229,8 @@ void cProtocol172::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockV
Pkt.WriteInt((int)a_Changes.size() * 4);
for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr)
{
- unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12);
- unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4);
+ int Coords = itr->m_RelY | (itr->m_RelZ << 8) | (itr->m_RelX << 12);
+ int Blocks = static_cast<int>(itr->m_BlockMeta | (itr->m_BlockType << 4));
Pkt.WriteInt((Coords << 16) | Blocks);
} // for itr - a_Changes[]
}
@@ -352,11 +353,13 @@ void cProtocol172::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
void cProtocol172::SendEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration)
{
ASSERT(m_State == 3); // In game mode?
+ ASSERT((a_EffectID >= 0) && (a_EffectID < 256));
+ ASSERT((a_Amplifier >= 0) && (a_Amplifier < 256));
cPacketizer Pkt(*this, 0x1D); // Entity Effect packet
Pkt.WriteInt(a_Entity.GetUniqueID());
- Pkt.WriteByte(a_EffectID);
- Pkt.WriteByte(a_Amplifier);
+ Pkt.WriteByte(static_cast<Byte>(a_EffectID));
+ Pkt.WriteByte(static_cast<Byte>(a_Amplifier));
Pkt.WriteShort(a_Duration);
}
@@ -438,9 +441,9 @@ void cProtocol172::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char
cPacketizer Pkt(*this, 0x15); // Entity Relative Move packet
Pkt.WriteInt(a_Entity.GetUniqueID());
- Pkt.WriteByte(a_RelX);
- Pkt.WriteByte(a_RelY);
- Pkt.WriteByte(a_RelZ);
+ Pkt.WriteChar(a_RelX);
+ Pkt.WriteChar(a_RelY);
+ Pkt.WriteChar(a_RelZ);
}
@@ -453,9 +456,9 @@ void cProtocol172::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX,
cPacketizer Pkt(*this, 0x17); // Entity Look And Relative Move packet
Pkt.WriteInt(a_Entity.GetUniqueID());
- Pkt.WriteByte(a_RelX);
- Pkt.WriteByte(a_RelY);
- Pkt.WriteByte(a_RelZ);
+ Pkt.WriteChar(a_RelX);
+ Pkt.WriteChar(a_RelY);
+ Pkt.WriteChar(a_RelZ);
Pkt.WriteByteAngle(a_Entity.GetYaw());
Pkt.WriteByteAngle(a_Entity.GetPitch());
}
@@ -537,9 +540,9 @@ void cProtocol172::SendHealth(void)
cPacketizer Pkt(*this, 0x06); // Update Health packet
cPlayer * Player = m_Client->GetPlayer();
- Pkt.WriteFloat((float)Player->GetHealth());
- Pkt.WriteShort(Player->GetFoodLevel());
- Pkt.WriteFloat((float)Player->GetFoodSaturationLevel());
+ Pkt.WriteFloat(static_cast<float>(Player->GetHealth()));
+ Pkt.WriteShort(static_cast<short>(Player->GetFoodLevel()));
+ Pkt.WriteFloat(static_cast<float>(Player->GetFoodSaturationLevel()));
}
@@ -584,10 +587,10 @@ void cProtocol172::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
cServer * Server = cRoot::Get()->GetServer();
cPacketizer Pkt(*this, 0x01); // Join Game packet
Pkt.WriteInt(a_Player.GetUniqueID());
- Pkt.WriteByte((Byte)a_Player.GetEffectiveGameMode() | (Server->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4
- Pkt.WriteChar((char)a_World.GetDimension());
+ Pkt.WriteByte(static_cast<Byte>(a_Player.GetEffectiveGameMode()) | (Server->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4
+ Pkt.WriteChar(static_cast<char>(a_World.GetDimension()));
Pkt.WriteByte(2); // TODO: Difficulty (set to Normal)
- Pkt.WriteByte(std::min(Server->GetMaxPlayers(), 60));
+ Pkt.WriteByte(static_cast<Byte>(std::min(Server->GetMaxPlayers(), 60)));
Pkt.WriteString("default"); // Level type - wtf?
}
m_LastSentDimension = a_World.GetDimension();
@@ -595,9 +598,9 @@ void cProtocol172::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
// Send the spawn position:
{
cPacketizer Pkt(*this, 0x05); // Spawn Position packet
- Pkt.WriteInt((int)a_World.GetSpawnX());
- Pkt.WriteInt((int)a_World.GetSpawnY());
- Pkt.WriteInt((int)a_World.GetSpawnZ());
+ Pkt.WriteInt(static_cast<int>(a_World.GetSpawnX()));
+ Pkt.WriteInt(static_cast<int>(a_World.GetSpawnY()));
+ Pkt.WriteInt(static_cast<int>(a_World.GetSpawnZ()));
}
// Send player abilities:
@@ -629,11 +632,11 @@ void cProtocol172::SendPaintingSpawn(const cPainting & a_Painting)
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, 0x10); // Spawn Painting packet
- Pkt.WriteVarInt(a_Painting.GetUniqueID());
+ Pkt.WriteVarInt(static_cast<UInt32>(a_Painting.GetUniqueID()));
Pkt.WriteString(a_Painting.GetName().c_str());
- Pkt.WriteInt((int)a_Painting.GetPosX());
- Pkt.WriteInt((int)a_Painting.GetPosY());
- Pkt.WriteInt((int)a_Painting.GetPosZ());
+ Pkt.WriteInt(static_cast<int>(a_Painting.GetPosX()));
+ Pkt.WriteInt(static_cast<int>(a_Painting.GetPosY()));
+ Pkt.WriteInt(static_cast<int>(a_Painting.GetPosZ()));
Pkt.WriteInt(a_Painting.GetDirection());
}
@@ -644,19 +647,19 @@ void cProtocol172::SendPaintingSpawn(const cPainting & a_Painting)
void cProtocol172::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length, unsigned int m_Scale)
{
ASSERT(m_State == 3); // In game mode?
+ ASSERT(a_Length + 3 <= USHRT_MAX);
+ ASSERT((a_X >= 0) && (a_X < 256));
+ ASSERT((a_Y >= 0) && (a_Y < 256));
cPacketizer Pkt(*this, 0x34);
- Pkt.WriteVarInt(a_ID);
- Pkt.WriteShort (3 + a_Length);
+ Pkt.WriteVarInt(static_cast<UInt32>(a_ID));
+ Pkt.WriteShort (static_cast<short>(3 + a_Length));
Pkt.WriteByte(0);
- Pkt.WriteByte(a_X);
- Pkt.WriteByte(a_Y);
+ Pkt.WriteByte(static_cast<Byte>(a_X));
+ Pkt.WriteByte(static_cast<Byte>(a_Y));
- for (unsigned int i = 0; i < a_Length; ++i)
- {
- Pkt.WriteByte(a_Colors[i]);
- }
+ Pkt.WriteBuf(reinterpret_cast<const char *>(a_Colors), a_Length);
}
@@ -666,18 +669,21 @@ void cProtocol172::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colo
void cProtocol172::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators, unsigned int m_Scale)
{
ASSERT(m_State == 3); // In game mode?
+ ASSERT(1 + 3 * a_Decorators.size() < USHRT_MAX);
cPacketizer Pkt(*this, 0x34);
- Pkt.WriteVarInt(a_ID);
- Pkt.WriteShort ((short)(1 + (3 * a_Decorators.size())));
+ Pkt.WriteVarInt(static_cast<UInt32>(a_ID));
+ Pkt.WriteShort (static_cast<short>(1 + (3 * a_Decorators.size())));
Pkt.WriteByte(1);
for (cMapDecoratorList::const_iterator it = a_Decorators.begin(); it != a_Decorators.end(); ++it)
{
- Pkt.WriteByte((it->GetType() << 4) | (it->GetRot() & 0xf));
- Pkt.WriteByte(it->GetPixelX());
- Pkt.WriteByte(it->GetPixelZ());
+ ASSERT((it->GetPixelX() >= 0) && (it->GetPixelX() < 256));
+ ASSERT((it->GetPixelZ() >= 0) && (it->GetPixelZ() < 256));
+ Pkt.WriteByte(static_cast<Byte>((it->GetType() << 4) | static_cast<Byte>(it->GetRot() & 0xf)));
+ Pkt.WriteByte(static_cast<Byte>(it->GetPixelX()));
+ Pkt.WriteByte(static_cast<Byte>(it->GetPixelZ()));
}
}
@@ -688,13 +694,14 @@ void cProtocol172::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decor
void cProtocol172::SendMapInfo(int a_ID, unsigned int a_Scale)
{
ASSERT(m_State == 3); // In game mode?
+ ASSERT((a_Scale >= 0) && (a_Scale < 256));
cPacketizer Pkt(*this, 0x34);
- Pkt.WriteVarInt(a_ID);
+ Pkt.WriteVarInt(static_cast<UInt32>(a_ID));
Pkt.WriteShort (2);
Pkt.WriteByte(2);
- Pkt.WriteByte(a_Scale);
+ Pkt.WriteByte(static_cast<Byte>(a_Scale));
}
@@ -707,7 +714,7 @@ void cProtocol172::SendPickupSpawn(const cPickup & a_Pickup)
{
cPacketizer Pkt(*this, 0x0e); // Spawn Object packet
- Pkt.WriteVarInt(a_Pickup.GetUniqueID());
+ Pkt.WriteVarInt(static_cast<UInt32>(a_Pickup.GetUniqueID()));
Pkt.WriteByte(2); // Type = Pickup
Pkt.WriteFPInt(a_Pickup.GetPosX());
Pkt.WriteFPInt(a_Pickup.GetPosY());
@@ -763,7 +770,7 @@ void cProtocol172::SendEntityAnimation(const cEntity & a_Entity, char a_Animatio
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, 0x0b); // Animation packet
- Pkt.WriteVarInt(a_Entity.GetUniqueID());
+ Pkt.WriteVarInt(static_cast<UInt32>(a_Entity.GetUniqueID()));
Pkt.WriteChar(a_Animation);
}
@@ -865,7 +872,7 @@ void cProtocol172::SendPlayerMaxSpeed(void)
{
Pkt.WriteShort(1); // Modifier count
Pkt.WriteInt64(0x662a6b8dda3e4c1c);
- Pkt.WriteInt64(0x881396ea6097278d); // UUID of the modifier
+ Pkt.WriteInt64(static_cast<Int64>(0x881396ea6097278d)); // UUID of the modifier
Pkt.WriteDouble(Player->GetSprintingMaxSpeed() - Player->GetNormalMaxSpeed());
Pkt.WriteByte(2);
}
@@ -960,10 +967,11 @@ void cProtocol172::SendPluginMessage(const AString & a_Channel, const AString &
void cProtocol172::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID)
{
ASSERT(m_State == 3); // In game mode?
+ ASSERT((a_EffectID >= 0) && (a_EffectID < 256));
cPacketizer Pkt(*this, 0x1e);
Pkt.WriteInt(a_Entity.GetUniqueID());
- Pkt.WriteByte(a_EffectID);
+ Pkt.WriteByte(static_cast<Byte>(a_EffectID));
}
@@ -1009,13 +1017,14 @@ void cProtocol172::SendExperience (void)
void cProtocol172::SendExperienceOrb(const cExpOrb & a_ExpOrb)
{
ASSERT(m_State == 3); // In game mode?
+ ASSERT((a_ExpOrb.GetReward() >= 0) && (a_ExpOrb.GetReward() < SHRT_MAX));
cPacketizer Pkt(*this, 0x11);
- Pkt.WriteVarInt(a_ExpOrb.GetUniqueID());
+ Pkt.WriteVarInt(static_cast<UInt32>(a_ExpOrb.GetUniqueID()));
Pkt.WriteFPInt(a_ExpOrb.GetPosX());
Pkt.WriteFPInt(a_ExpOrb.GetPosY());
Pkt.WriteFPInt(a_ExpOrb.GetPosZ());
- Pkt.WriteShort(a_ExpOrb.GetReward());
+ Pkt.WriteShort(static_cast<short>(a_ExpOrb.GetReward()));
}
@@ -1060,7 +1069,7 @@ void cProtocol172::SendDisplayObjective(const AString & a_Objective, cScoreboard
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, 0x3d);
- Pkt.WriteByte((int) a_Display);
+ Pkt.WriteByte(static_cast<Byte>(a_Display));
Pkt.WriteString(a_Objective);
}
@@ -1074,11 +1083,11 @@ void cProtocol172::SendSoundEffect(const AString & a_SoundName, double a_X, doub
cPacketizer Pkt(*this, 0x29); // Sound Effect packet
Pkt.WriteString(a_SoundName);
- Pkt.WriteInt((int)(a_X * 8.0));
- Pkt.WriteInt((int)(a_Y * 8.0));
- Pkt.WriteInt((int)(a_Z * 8.0));
+ Pkt.WriteInt(static_cast<int>(a_X * 8.0));
+ Pkt.WriteInt(static_cast<int>(a_Y * 8.0));
+ Pkt.WriteInt(static_cast<int>(a_Z * 8.0));
Pkt.WriteFloat(a_Volume);
- Pkt.WriteByte((Byte)(a_Pitch * 63));
+ Pkt.WriteByte(static_cast<Byte>(a_Pitch * 63));
}
@@ -1088,11 +1097,12 @@ void cProtocol172::SendSoundEffect(const AString & a_SoundName, double a_X, doub
void cProtocol172::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
{
ASSERT(m_State == 3); // In game mode?
+ ASSERT((a_SrcY >= 0) && (a_SrcY < 256));
cPacketizer Pkt(*this, 0x28); // Effect packet
Pkt.WriteInt(a_EffectID);
Pkt.WriteInt(a_SrcX);
- Pkt.WriteByte(a_SrcY);
+ Pkt.WriteByte(static_cast<Byte>(a_SrcY));
Pkt.WriteInt(a_SrcZ);
Pkt.WriteInt(a_Data);
Pkt.WriteBool(false);
@@ -1107,17 +1117,17 @@ void cProtocol172::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, 0x0e); // Spawn Object packet
- Pkt.WriteVarInt(a_FallingBlock.GetUniqueID());
+ Pkt.WriteVarInt(static_cast<UInt32>(a_FallingBlock.GetUniqueID()));
Pkt.WriteByte(70); // Falling block
Pkt.WriteFPInt(a_FallingBlock.GetPosX());
Pkt.WriteFPInt(a_FallingBlock.GetPosY());
Pkt.WriteFPInt(a_FallingBlock.GetPosZ());
Pkt.WriteByteAngle(a_FallingBlock.GetYaw());
Pkt.WriteByteAngle(a_FallingBlock.GetPitch());
- Pkt.WriteInt(((int)a_FallingBlock.GetBlockType()) | (((int)a_FallingBlock.GetBlockMeta()) << 16)); // Or 0x10
- Pkt.WriteShort((short)(a_FallingBlock.GetSpeedX() * 400));
- Pkt.WriteShort((short)(a_FallingBlock.GetSpeedY() * 400));
- Pkt.WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400));
+ Pkt.WriteInt((static_cast<int>(a_FallingBlock.GetBlockType()) | ((static_cast<int>(a_FallingBlock.GetBlockMeta()) << 16))));
+ Pkt.WriteShort(static_cast<short>(a_FallingBlock.GetSpeedX() * 400));
+ Pkt.WriteShort(static_cast<short>(a_FallingBlock.GetSpeedY() * 400));
+ Pkt.WriteShort(static_cast<short>(a_FallingBlock.GetSpeedZ() * 400));
}
@@ -1129,7 +1139,7 @@ void cProtocol172::SendSpawnMob(const cMonster & a_Mob)
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, 0x0f); // Spawn Mob packet
- Pkt.WriteVarInt(a_Mob.GetUniqueID());
+ Pkt.WriteVarInt(static_cast<UInt32>(a_Mob.GetUniqueID()));
Pkt.WriteByte((Byte)a_Mob.GetMobType());
Pkt.WriteFPInt(a_Mob.GetPosX());
Pkt.WriteFPInt(a_Mob.GetPosY());
@@ -1137,9 +1147,9 @@ void cProtocol172::SendSpawnMob(const cMonster & a_Mob)
Pkt.WriteByteAngle(a_Mob.GetPitch());
Pkt.WriteByteAngle(a_Mob.GetHeadYaw());
Pkt.WriteByteAngle(a_Mob.GetYaw());
- Pkt.WriteShort((short)(a_Mob.GetSpeedX() * 400));
- Pkt.WriteShort((short)(a_Mob.GetSpeedY() * 400));
- Pkt.WriteShort((short)(a_Mob.GetSpeedZ() * 400));
+ Pkt.WriteShort(static_cast<short>(a_Mob.GetSpeedX() * 400));
+ Pkt.WriteShort(static_cast<short>(a_Mob.GetSpeedY() * 400));
+ Pkt.WriteShort(static_cast<short>(a_Mob.GetSpeedZ() * 400));
Pkt.WriteEntityMetadata(a_Mob);
Pkt.WriteByte(0x7f); // Metadata terminator
}
@@ -1153,8 +1163,8 @@ void cProtocol172::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType,
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, 0xe); // Spawn Object packet
- Pkt.WriteVarInt(a_Entity.GetUniqueID());
- Pkt.WriteByte(a_ObjectType);
+ Pkt.WriteVarInt(static_cast<UInt32>(a_Entity.GetUniqueID()));
+ Pkt.WriteChar(a_ObjectType);
Pkt.WriteFPInt(a_Entity.GetPosX());
Pkt.WriteFPInt(a_Entity.GetPosY());
Pkt.WriteFPInt(a_Entity.GetPosZ());
@@ -1163,9 +1173,9 @@ void cProtocol172::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType,
Pkt.WriteInt(a_ObjectData);
if (a_ObjectData != 0)
{
- Pkt.WriteShort((short)(a_Entity.GetSpeedX() * 400));
- Pkt.WriteShort((short)(a_Entity.GetSpeedY() * 400));
- Pkt.WriteShort((short)(a_Entity.GetSpeedZ() * 400));
+ Pkt.WriteShort(static_cast<short>(a_Entity.GetSpeedX() * 400));
+ Pkt.WriteShort(static_cast<short>(a_Entity.GetSpeedY() * 400));
+ Pkt.WriteShort(static_cast<short>(a_Entity.GetSpeedZ() * 400));
}
}
@@ -1178,8 +1188,8 @@ void cProtocol172::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTyp
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, 0xe); // Spawn Object packet
- Pkt.WriteVarInt(a_Vehicle.GetUniqueID());
- Pkt.WriteByte(a_VehicleType);
+ Pkt.WriteVarInt(static_cast<UInt32>(a_Vehicle.GetUniqueID()));
+ Pkt.WriteChar(a_VehicleType);
Pkt.WriteFPInt(a_Vehicle.GetPosX());
Pkt.WriteFPInt(a_Vehicle.GetPosY());
Pkt.WriteFPInt(a_Vehicle.GetPosZ());
@@ -1188,9 +1198,9 @@ void cProtocol172::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTyp
Pkt.WriteInt(a_VehicleSubType);
if (a_VehicleSubType != 0)
{
- Pkt.WriteShort((short)(a_Vehicle.GetSpeedX() * 400));
- Pkt.WriteShort((short)(a_Vehicle.GetSpeedY() * 400));
- Pkt.WriteShort((short)(a_Vehicle.GetSpeedZ() * 400));
+ Pkt.WriteShort(static_cast<short>(a_Vehicle.GetSpeedX() * 400));
+ Pkt.WriteShort(static_cast<short>(a_Vehicle.GetSpeedY() * 400));
+ Pkt.WriteShort(static_cast<short>(a_Vehicle.GetSpeedZ() * 400));
}
}
@@ -1211,7 +1221,7 @@ void cProtocol172::SendStatistics(const cStatManager & a_Manager)
const AString & StatName = cStatInfo::GetName((eStatistic) i);
Pkt.WriteString(StatName);
- Pkt.WriteVarInt(Value);
+ Pkt.WriteVarInt(static_cast<UInt32>(Value));
}
}
@@ -1224,7 +1234,7 @@ void cProtocol172::SendTabCompletionResults(const AStringVector & a_Results)
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, 0x3a); // Tab-Complete packet
- Pkt.WriteVarInt((int)a_Results.size());
+ Pkt.WriteVarInt(static_cast<UInt32>(a_Results.size()));
for (AStringVector::const_iterator itr = a_Results.begin(), end = a_Results.end(); itr != end; ++itr)
{
@@ -1309,7 +1319,7 @@ void cProtocol172::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity)
cPacketizer Pkt(*this, 0x35); // Update tile entity packet
Pkt.WriteInt(a_BlockEntity.GetPosX());
- Pkt.WriteShort(a_BlockEntity.GetPosY());
+ Pkt.WriteShort(static_cast<short>(a_BlockEntity.GetPosY()));
Pkt.WriteInt(a_BlockEntity.GetPosZ());
Byte Action = 0;
@@ -1389,7 +1399,7 @@ void cProtocol172::SendWholeInventory(const cWindow & a_Window)
cPacketizer Pkt(*this, 0x30); // Window Items packet
Pkt.WriteChar(a_Window.GetWindowID());
- Pkt.WriteShort(a_Window.GetNumSlots());
+ Pkt.WriteShort(static_cast<short>(a_Window.GetNumSlots()));
cItems Slots;
a_Window.GetSlots(*(m_Client->GetPlayer()), Slots);
for (cItems::const_iterator itr = Slots.begin(), end = Slots.end(); itr != end; ++itr)
@@ -1426,9 +1436,9 @@ void cProtocol172::SendWindowOpen(const cWindow & a_Window)
cPacketizer Pkt(*this, 0x2d);
Pkt.WriteChar(a_Window.GetWindowID());
- Pkt.WriteChar(a_Window.GetWindowType());
+ Pkt.WriteChar(static_cast<char>(a_Window.GetWindowType()));
Pkt.WriteString(a_Window.GetWindowTitle());
- Pkt.WriteChar(a_Window.GetNumNonInventorySlots());
+ Pkt.WriteChar(static_cast<char>(a_Window.GetNumNonInventorySlots()));
Pkt.WriteBool(true);
if (a_Window.GetWindowType() == cWindow::wtAnimalChest)
{
@@ -1505,7 +1515,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, size_t a_Size)
break;
}
cByteBuffer bb(PacketLen + 1);
- VERIFY(m_ReceivedData.ReadToByteBuffer(bb, (int)PacketLen));
+ VERIFY(m_ReceivedData.ReadToByteBuffer(bb, static_cast<size_t>(PacketLen)));
m_ReceivedData.CommitRead();
UInt32 PacketType;
@@ -1524,7 +1534,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, size_t a_Size)
AString PacketData;
bb.ReadAll(PacketData);
bb.ResetRead();
- bb.ReadVarInt(PacketType);
+ bb.ReadVarInt(PacketType); // We have already read the packet type once, it will be there again.
ASSERT(PacketData.size() > 0); // We have written an extra NUL, so there had to be at least one byte read
PacketData.resize(PacketData.size() - 1);
AString PacketDataHex;
@@ -1748,21 +1758,30 @@ void cProtocol172::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffe
{
short EncKeyLength, EncNonceLength;
a_ByteBuffer.ReadBEShort(EncKeyLength);
+ if ((EncKeyLength < 0) || (EncKeyLength > MAX_ENC_LEN))
+ {
+ LOGD("Invalid Encryption Key length: %d. Kicking client.", EncKeyLength);
+ m_Client->Kick("Invalid EncKeyLength");
+ return;
+ }
AString EncKey;
- if (!a_ByteBuffer.ReadString(EncKey, EncKeyLength))
+ if (!a_ByteBuffer.ReadString(EncKey, static_cast<size_t>(EncKeyLength)))
{
return;
}
- a_ByteBuffer.ReadBEShort(EncNonceLength);
- AString EncNonce;
- if (!a_ByteBuffer.ReadString(EncNonce, EncNonceLength))
+ if (!a_ByteBuffer.ReadBEShort(EncNonceLength))
{
return;
}
- if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN))
+ if ((EncNonceLength < 0) || (EncNonceLength > MAX_ENC_LEN))
+ {
+ LOGD("Invalid Encryption Nonce length: %d. Kicking client.", EncNonceLength);
+ m_Client->Kick("Invalid EncNonceLength");
+ return;
+ }
+ AString EncNonce;
+ if (!a_ByteBuffer.ReadString(EncNonce, static_cast<size_t>(EncNonceLength)))
{
- LOGD("Too long encryption");
- m_Client->Kick("Hacked client");
return;
}
@@ -1851,7 +1870,7 @@ void cProtocol172::HandlePacketAnimation(cByteBuffer & a_ByteBuffer)
void cProtocol172::HandlePacketBlockDig(cByteBuffer & a_ByteBuffer)
{
- HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Status);
+ HANDLE_READ(a_ByteBuffer, ReadChar, char, Status);
HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockX);
HANDLE_READ(a_ByteBuffer, ReadByte, Byte, BlockY);
HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockZ);
@@ -2069,7 +2088,7 @@ void cProtocol172::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer)
if (Length + 1 != (int)a_ByteBuffer.GetReadableSpace())
{
LOGD("Invalid plugin message packet, payload length doesn't match packet length (exp %d, got %d)",
- (int)a_ByteBuffer.GetReadableSpace() - 1, Length
+ static_cast<int>(a_ByteBuffer.GetReadableSpace()) - 1, Length
);
return;
}
@@ -2083,7 +2102,7 @@ void cProtocol172::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer)
// Read the plugin message and relay to clienthandle:
AString Data;
- if (!a_ByteBuffer.ReadString(Data, Length))
+ if (!a_ByteBuffer.ReadString(Data, static_cast<size_t>(Length)))
{
return;
}
@@ -2239,6 +2258,7 @@ void cProtocol172::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const
{
if (a_Channel == "MC|AdvCdm")
{
+ size_t BeginningSpace = a_ByteBuffer.GetReadableSpace();
HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Mode);
switch (Mode)
{
@@ -2262,13 +2282,23 @@ void cProtocol172::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const
return;
}
} // switch (Mode)
+
+ // Read the remainder of the packet (Vanilla sometimes sends bogus data at the end of the packet; #1692):
+ size_t BytesRead = BeginningSpace - a_ByteBuffer.GetReadableSpace();
+ if (BytesRead < static_cast<size_t>(a_PayloadLength))
+ {
+ LOGD("Protocol 1.7: Skipping garbage data at the end of a vanilla MC|AdvCdm packet, %u bytes",
+ static_cast<unsigned>(a_PayloadLength - BytesRead)
+ );
+ a_ByteBuffer.SkipRead(static_cast<size_t>(a_PayloadLength) - BytesRead);
+ }
return;
}
else if (a_Channel == "MC|Brand")
{
// Read the client's brand:
AString Brand;
- if (a_ByteBuffer.ReadString(Brand, a_PayloadLength))
+ if (a_ByteBuffer.ReadString(Brand, static_cast<size_t>(a_PayloadLength)))
{
m_Client->SetClientBrand(Brand);
}
@@ -2287,7 +2317,7 @@ void cProtocol172::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const
else if (a_Channel == "MC|ItemName")
{
AString ItemName;
- if (a_ByteBuffer.ReadString(ItemName, a_PayloadLength))
+ if (a_ByteBuffer.ReadString(ItemName, static_cast<size_t>(a_PayloadLength)))
{
m_Client->HandleAnvilItemName(ItemName);
}
@@ -2303,7 +2333,7 @@ void cProtocol172::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const
// Read the payload and send it through to the clienthandle:
AString Message;
- VERIFY(a_ByteBuffer.ReadString(Message, a_PayloadLength));
+ VERIFY(a_ByteBuffer.ReadString(Message, static_cast<size_t>(a_PayloadLength)));
m_Client->HandlePluginMessage(a_Channel, Message);
}
@@ -2363,7 +2393,7 @@ bool cProtocol172::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item)
// Read the metadata
AString Metadata;
- if (!a_ByteBuffer.ReadString(Metadata, MetadataLength))
+ if (!a_ByteBuffer.ReadString(Metadata, static_cast<size_t>(MetadataLength)))
{
return false;
}
@@ -2532,7 +2562,7 @@ void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item)
}
WriteShort(ItemType);
- WriteByte (a_Item.m_ItemCount);
+ WriteChar (a_Item.m_ItemCount);
WriteShort(a_Item.m_ItemDamage);
if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty() && (a_Item.m_ItemType != E_ITEM_FIREWORK_ROCKET) && (a_Item.m_ItemType != E_ITEM_FIREWORK_STAR))
@@ -2692,7 +2722,7 @@ void cProtocol172::cPacketizer::WriteBlockEntity(const cBlockEntity & a_BlockEnt
void cProtocol172::cPacketizer::WriteByteAngle(double a_Angle)
{
- WriteByte((char)(255 * a_Angle / 360));
+ WriteChar(static_cast<char>(255 * a_Angle / 360));
}
@@ -2835,7 +2865,7 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob)
case mtCreeper:
{
WriteByte(0x10);
- WriteByte(((const cCreeper &)a_Mob).IsBlowing() ? 1 : -1);
+ WriteByte(((const cCreeper &)a_Mob).IsBlowing() ? 1 : 0);
WriteByte(0x11);
WriteByte(((const cCreeper &)a_Mob).IsCharged() ? 1 : 0);
break;
@@ -2904,15 +2934,14 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob)
WriteByte(0x13);
WriteByte(Wolf.IsBegging() ? 1 : 0);
WriteByte(0x14);
- WriteByte(Wolf.GetCollarColor());
+ WriteByte(static_cast<Byte>(Wolf.GetCollarColor()));
break;
}
case mtSheep:
{
WriteByte(0x10);
- Byte SheepMetadata = 0;
- SheepMetadata = ((const cSheep &)a_Mob).GetFurColor();
+ Byte SheepMetadata = static_cast<Byte>(((const cSheep &)a_Mob).GetFurColor() & 0x0f);
if (((const cSheep &)a_Mob).IsSheared())
{
SheepMetadata |= 0x10;
@@ -2949,7 +2978,7 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob)
case mtWither:
{
WriteByte(0x54); // Int at index 20
- WriteInt(((const cWither &)a_Mob).GetWitherInvulnerableTicks());
+ WriteInt(static_cast<int>(((const cWither &)a_Mob).GetWitherInvulnerableTicks()));
WriteByte(0x66); // Float at index 6
WriteFloat((float)(a_Mob.GetHealth()));
break;
@@ -2958,14 +2987,14 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob)
case mtSlime:
{
WriteByte(0x10);
- WriteByte(((const cSlime &)a_Mob).GetSize());
+ WriteByte(static_cast<Byte>(((const cSlime &)a_Mob).GetSize()));
break;
}
case mtMagmaCube:
{
WriteByte(0x10);
- WriteByte(((const cMagmaCube &)a_Mob).GetSize());
+ WriteByte(static_cast<Byte>(((const cMagmaCube &)a_Mob).GetSize()));
break;
}
@@ -3004,7 +3033,7 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob)
WriteByte(0x50); // Int at index 16
WriteInt(Flags);
WriteByte(0x13); // Byte at index 19
- WriteByte(Horse.GetHorseType());
+ WriteByte(static_cast<Byte>(Horse.GetHorseType()));
WriteByte(0x54); // Int at index 20
int Appearance = 0;
Appearance = Horse.GetHorseColor();
@@ -3014,6 +3043,12 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob)
WriteInt(Horse.GetHorseArmour());
break;
}
+
+ default:
+ {
+ // No data to send for this mob
+ break;
+ }
} // switch (a_Mob.GetType())
// Custom name:
@@ -3066,7 +3101,7 @@ void cProtocol176::SendPlayerSpawn(const cPlayer & a_Player)
{
// Called to spawn another player for the client
cPacketizer Pkt(*this, 0x0c); // Spawn Player packet
- Pkt.WriteVarInt(a_Player.GetUniqueID());
+ Pkt.WriteVarInt(static_cast<UInt32>(a_Player.GetUniqueID()));
Pkt.WriteString(cMojangAPI::MakeUUIDDashed(a_Player.GetClientHandle()->GetUUID()));
if (a_Player.HasCustomName())
{
diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp
index ce580d73e..3c4e049bd 100644
--- a/src/Protocol/Protocol18x.cpp
+++ b/src/Protocol/Protocol18x.cpp
@@ -207,9 +207,9 @@ void cProtocol180::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockV
Pkt.WriteVarInt((UInt32)a_Changes.size());
for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr)
{
- short Coords = (short) (itr->y | (itr->z << 8) | (itr->x << 12));
+ short Coords = (short) (itr->m_RelY | (itr->m_RelZ << 8) | (itr->m_RelX << 12));
Pkt.WriteShort(Coords);
- Pkt.WriteVarInt((itr->BlockType & 0xFFF) << 4 | (itr->BlockMeta & 0xF));
+ Pkt.WriteVarInt((itr->m_BlockType & 0xFFF) << 4 | (itr->m_BlockMeta & 0xF));
} // for itr - a_Changes[]
}
@@ -1723,7 +1723,11 @@ void cProtocol180::AddReceivedData(const char * a_Data, size_t a_Size)
{
// Decompress the data:
AString CompressedData;
- m_ReceivedData.ReadString(CompressedData, CompressedSize);
+ if (!m_ReceivedData.ReadString(CompressedData, CompressedSize))
+ {
+ m_Client->Kick("Compression failure");
+ return;
+ }
InflateString(CompressedData.data(), CompressedSize, UncompressedData);
PacketLen = UncompressedData.size();
}
@@ -1765,7 +1769,7 @@ void cProtocol180::AddReceivedData(const char * a_Data, size_t a_Size)
AString PacketData;
bb.ReadAll(PacketData);
bb.ResetRead();
- bb.ReadVarInt(PacketType);
+ bb.ReadVarInt(PacketType); // We have already read the packet type once, it will be there again
ASSERT(PacketData.size() > 0); // We have written an extra NUL, so there had to be at least one byte read
PacketData.resize(PacketData.size() - 1);
AString PacketDataHex;
@@ -2327,6 +2331,16 @@ void cProtocol180::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer)
if (Channel.substr(0, 3) == "MC|")
{
HandleVanillaPluginMessage(a_ByteBuffer, Channel);
+
+ // Skip any unread data (vanilla sometimes sends garbage at the end of a packet; #1692):
+ if (a_ByteBuffer.GetReadableSpace() > 1)
+ {
+ LOGD("Protocol 1.8: Skipping garbage data at the end of a vanilla PluginMessage packet, %u bytes",
+ static_cast<unsigned>(a_ByteBuffer.GetReadableSpace() - 1)
+ );
+ a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace() - 1);
+ }
+
return;
}
diff --git a/src/Root.cpp b/src/Root.cpp
index 9f8ffeeff..eaacf3608 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -47,6 +47,7 @@ cRoot::cRoot(void) :
m_FurnaceRecipe(nullptr),
m_WebAdmin(nullptr),
m_PluginManager(nullptr),
+ m_MojangAPI(nullptr),
m_bStop(false),
m_bRestart(false)
{
@@ -135,8 +136,9 @@ void cRoot::Start(void)
}
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
+ 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))
{
IniFile.WriteFile("settings.ini");
@@ -149,7 +151,7 @@ void cRoot::Start(void)
LOGD("Loading settings...");
m_RankManager.reset(new cRankManager());
- m_RankManager->Initialize(m_MojangAPI);
+ m_RankManager->Initialize(*m_MojangAPI);
m_CraftingRecipes = new cCraftingRecipes;
m_FurnaceRecipe = new cFurnaceRecipe();
@@ -196,7 +198,7 @@ void cRoot::Start(void)
}
#endif
- LOG("Startup complete, took %ld ms!", static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count()));
+ LOG("Startup complete, took %ldms!", static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count()));
#ifdef _WIN32
EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button
#endif
@@ -213,21 +215,28 @@ void cRoot::Start(void)
// Stop the server:
m_WebAdmin->Stop();
+
LOG("Shutting down server...");
m_Server->Shutdown();
+ delete m_MojangAPI; m_MojangAPI = nullptr;
+
LOGD("Shutting down deadlock detector...");
dd.Stop();
+
LOGD("Stopping world threads...");
StopWorlds();
+
LOGD("Stopping authenticator...");
m_Authenticator.Stop();
LOGD("Freeing MonsterConfig...");
delete m_MonsterConfig; m_MonsterConfig = nullptr;
delete m_WebAdmin; m_WebAdmin = nullptr;
+
LOGD("Unloading recipes...");
delete m_FurnaceRecipe; m_FurnaceRecipe = nullptr;
delete m_CraftingRecipes; m_CraftingRecipes = nullptr;
+
LOGD("Unloading worlds...");
UnloadWorlds();
@@ -238,6 +247,7 @@ void cRoot::Start(void)
LOG("Cleaning up...");
delete m_Server; m_Server = nullptr;
+
LOG("Shutdown successful!");
}
@@ -629,7 +639,7 @@ bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallbac
bool cRoot::DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback)
{
- for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end();itr++)
+ for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr)
{
if (itr->second->DoWithPlayerByUUID(a_PlayerUUID, a_Callback))
{
diff --git a/src/Root.h b/src/Root.h
index 2c512a5df..fdaf444bd 100644
--- a/src/Root.h
+++ b/src/Root.h
@@ -86,7 +86,7 @@ public:
cWebAdmin * GetWebAdmin (void) { return m_WebAdmin; } // tolua_export
cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export
cAuthenticator & GetAuthenticator (void) { return m_Authenticator; }
- cMojangAPI & GetMojangAPI (void) { return m_MojangAPI; }
+ cMojangAPI & GetMojangAPI (void) { return *m_MojangAPI; }
cRankManager * GetRankManager (void) { return m_RankManager.get(); }
/** Queues a console command for execution through the cServer class.
@@ -191,7 +191,7 @@ private:
cWebAdmin * m_WebAdmin;
cPluginManager * m_PluginManager;
cAuthenticator m_Authenticator;
- cMojangAPI m_MojangAPI;
+ cMojangAPI * m_MojangAPI;
std::unique_ptr<cRankManager> m_RankManager;
diff --git a/src/Server.cpp b/src/Server.cpp
index 3eaf6e096..4dbe59ac6 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -764,7 +764,7 @@ void cServer::cNotifyWriteThread::Execute(void)
while (!m_ShouldTerminate)
{
cCSLock Lock(m_CS);
- while (m_Clients.size() == 0)
+ while (m_Clients.empty())
{
cCSUnlock Unlock(Lock);
m_Event.Wait();
diff --git a/src/SetChunkData.cpp b/src/SetChunkData.cpp
index e335176a8..5a0bea980 100644
--- a/src/SetChunkData.cpp
+++ b/src/SetChunkData.cpp
@@ -132,7 +132,7 @@ void cSetChunkData::RemoveInvalidBlockEntities(void)
ItemTypeToString(WorldBlockType).c_str(), WorldBlockType
);
cBlockEntityList::iterator itr2 = itr;
- itr2++;
+ ++itr2;
delete *itr;
m_BlockEntities.erase(itr);
itr = itr2;
diff --git a/src/Simulator/DelayedFluidSimulator.cpp b/src/Simulator/DelayedFluidSimulator.cpp
index dc8dffe2d..0973962b6 100644
--- a/src/Simulator/DelayedFluidSimulator.cpp
+++ b/src/Simulator/DelayedFluidSimulator.cpp
@@ -130,7 +130,7 @@ void cDelayedFluidSimulator::Simulate(float a_Dt)
-void cDelayedFluidSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
+void cDelayedFluidSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
{
void * ChunkDataRaw = (m_FluidBlock == E_BLOCK_WATER) ? a_Chunk->GetWaterSimulatorData() : a_Chunk->GetLavaSimulatorData();
cDelayedFluidSimulatorChunkData * ChunkData = (cDelayedFluidSimulatorChunkData *)ChunkDataRaw;
diff --git a/src/Simulator/DelayedFluidSimulator.h b/src/Simulator/DelayedFluidSimulator.h
index 8a6c26c7a..e3182812d 100644
--- a/src/Simulator/DelayedFluidSimulator.h
+++ b/src/Simulator/DelayedFluidSimulator.h
@@ -56,7 +56,7 @@ public:
// cSimulator overrides:
virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override;
virtual void Simulate(float a_Dt) override;
- virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
+ virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
virtual cFluidSimulatorData * CreateChunkData(void) override { return new cDelayedFluidSimulatorChunkData(m_TickDelay); }
protected:
diff --git a/src/Simulator/FireSimulator.cpp b/src/Simulator/FireSimulator.cpp
index 75ebefcf7..8456ed11d 100644
--- a/src/Simulator/FireSimulator.cpp
+++ b/src/Simulator/FireSimulator.cpp
@@ -88,11 +88,11 @@ cFireSimulator::~cFireSimulator()
-void cFireSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
+void cFireSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
{
cCoordWithIntList & Data = a_Chunk->GetFireSimulatorData();
- int NumMSecs = (int)a_Dt;
+ int NumMSecs = static_cast<int>(a_Dt.count());
for (cCoordWithIntList::iterator itr = Data.begin(); itr != Data.end();)
{
int x = itr->x;
diff --git a/src/Simulator/FireSimulator.h b/src/Simulator/FireSimulator.h
index 9ccc3ef4f..a40e29565 100644
--- a/src/Simulator/FireSimulator.h
+++ b/src/Simulator/FireSimulator.h
@@ -23,7 +23,7 @@ public:
~cFireSimulator();
virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used
- virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
+ virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override;
diff --git a/src/Simulator/FloodyFluidSimulator.cpp b/src/Simulator/FloodyFluidSimulator.cpp
index 37d58307b..bcd083294 100644
--- a/src/Simulator/FloodyFluidSimulator.cpp
+++ b/src/Simulator/FloodyFluidSimulator.cpp
@@ -105,7 +105,7 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re
}
// Spread to the neighbors:
if (SpreadFurther && (NewMeta < 8))
- {
+ {
SpreadXZ(a_Chunk, a_RelX, a_RelY, a_RelZ, NewMeta);
}
// If source creation is on, check for it here:
diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp
index 8a56287a8..1cc5340dd 100644
--- a/src/Simulator/IncrementalRedstoneSimulator.cpp
+++ b/src/Simulator/IncrementalRedstoneSimulator.cpp
@@ -93,7 +93,7 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY,
), SimulatedPlayerToggleableBlocks.end());
- auto & RepeatersDelayList = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_RepeatersDelayList;
+ auto & RepeatersDelayList = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_RepeatersDelayList;
RepeatersDelayList.erase(std::remove_if(RepeatersDelayList.begin(), RepeatersDelayList.end(), [RelX, a_BlockY, RelZ, Block](const sRepeatersDelayList & itr)
{
return itr.a_RelBlockPos.Equals(Vector3i(RelX, a_BlockY, RelZ)) && (Block != E_BLOCK_REDSTONE_REPEATER_ON) && (Block != E_BLOCK_REDSTONE_REPEATER_OFF);
@@ -145,7 +145,7 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY,
-void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
+void cIncrementalRedstoneSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
{
m_RedstoneSimulatorChunkData = (cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData();
if (m_RedstoneSimulatorChunkData == nullptr)
@@ -1488,62 +1488,92 @@ bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_RelBlockX, int a_Rel
for (const auto & itr : *m_PoweredBlocks)
{
- if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; }
+ if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ)))
+ {
+ continue;
+ }
switch (a_Meta & 0x3)
{
case 0x0:
{
// Flip the coords to check the back of the repeater
- if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1)))) { return true; }
+ if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1))))
+ {
+ return true;
+ }
break;
}
case 0x1:
{
- if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ)))) { return true; }
+ if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ))))
+ {
+ return true;
+ }
break;
}
case 0x2:
{
- if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1)))) { return true; }
+ if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1))))
+ {
+ return true;
+ }
break;
}
case 0x3:
{
- if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ)))) { return true; }
+ if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ))))
+ {
+ return true;
+ }
break;
}
}
- }
+ } // for itr - m_PoweredBlocks[]
for (const auto & itr : *m_LinkedPoweredBlocks)
{
- if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; }
+ if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ)))
+ {
+ continue;
+ }
switch (a_Meta & 0x3)
{
case 0x0:
{
- if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1)))) { return true; }
+ if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1))))
+ {
+ return true;
+ }
break;
}
case 0x1:
{
- if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ)))) { return true; }
+ if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ))))
+ {
+ return true;
+ }
break;
}
case 0x2:
{
- if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1)))) { return true; }
+ if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1))))
+ {
+ return true;
+ }
break;
}
case 0x3:
{
- if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ)))) { return true; }
+ if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ))))
+ {
+ return true;
+ }
break;
}
}
- }
+ } // for itr - m_LinkedPoweredBlocks[]
return false; // Couldn't find power source behind repeater
}
@@ -1559,10 +1589,13 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB
case 0x0:
case 0x2:
{
- // Check if eastern(right) neighbor is a powered on repeater who is facing us
+ // Check if eastern (right) neighbor is a powered on repeater who is facing us
BLOCKTYPE Block = 0;
NIBBLETYPE OtherRepeaterDir = 0;
- if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) // Is right neighbor a powered repeater?
+ if (
+ m_Chunk->UnboundedRelGetBlock(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) &&
+ (Block == E_BLOCK_REDSTONE_REPEATER_ON)
+ )
{
if ((OtherRepeaterDir & 0x03) == 0x3)
{
@@ -1571,7 +1604,10 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB
}
// Check if western(left) neighbor is a powered on repeater who is facing us
- if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON))
+ if (
+ m_Chunk->UnboundedRelGetBlock(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) &&
+ (Block == E_BLOCK_REDSTONE_REPEATER_ON)
+ )
{
if ((OtherRepeaterDir & 0x03) == 0x1)
{
@@ -1590,7 +1626,10 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB
BLOCKTYPE Block = 0;
NIBBLETYPE OtherRepeaterDir = 0;
- if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON))
+ if (
+ m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, Block, OtherRepeaterDir) &&
+ (Block == E_BLOCK_REDSTONE_REPEATER_ON)
+ )
{
if ((OtherRepeaterDir & 0x30) == 0x00)
{
@@ -1599,7 +1638,10 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB
}
// Check if northern(up) neighbor is a powered on repeater who is facing us
- if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON))
+ if (
+ m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, Block, OtherRepeaterDir) &&
+ (Block == E_BLOCK_REDSTONE_REPEATER_ON)
+ )
{
if ((OtherRepeaterDir & 0x03) == 0x02)
{
@@ -1625,7 +1667,10 @@ bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_RelBlockX, int a_RelBl
for (const auto & itr : *m_PoweredBlocks)
{
- if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; }
+ if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ)))
+ {
+ continue;
+ }
int X = a_RelBlockX, Z = a_RelBlockZ;
AddFaceDirection(X, a_RelBlockY, Z, Face);
@@ -1638,7 +1683,10 @@ bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_RelBlockX, int a_RelBl
for (const auto & itr : *m_LinkedPoweredBlocks)
{
- if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; }
+ if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ)))
+ {
+ continue;
+ }
int X = a_RelBlockX, Z = a_RelBlockZ;
AddFaceDirection(X, a_RelBlockY, Z, Face);
@@ -2074,9 +2122,11 @@ void cIncrementalRedstoneSimulator::SetSourceUnpowered(int a_RelSourceX, int a_R
for (const auto & itr : BlocksPotentiallyUnpowered)
{
- if (!AreCoordsPowered(itr.x, itr.y, itr.z))
+ auto Neighbour = a_Chunk->GetRelNeighborChunk(itr.x, itr.z);
+ if (!AreCoordsPowered(itr.x, itr.y, itr.z) && (Neighbour->GetBlock(itr) != E_BLOCK_REDSTONE_REPEATER_ON))
{
- SetSourceUnpowered(itr.x, itr.y, itr.z, a_Chunk->GetRelNeighborChunk(itr.x, itr.z));
+ // Repeaters time themselves with regards to unpowering; ensure we don't do it for them
+ SetSourceUnpowered(itr.x, itr.y, itr.z, Neighbour);
}
}
}
diff --git a/src/Simulator/IncrementalRedstoneSimulator.h b/src/Simulator/IncrementalRedstoneSimulator.h
index 3d2962c08..43f0e89d0 100644
--- a/src/Simulator/IncrementalRedstoneSimulator.h
+++ b/src/Simulator/IncrementalRedstoneSimulator.h
@@ -32,7 +32,7 @@ public:
}
virtual void Simulate(float a_Dt) override { UNUSED(a_Dt); } // not used
- virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
+ virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override { return IsRedstone(a_BlockType); }
virtual void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override;
diff --git a/src/Simulator/NoopRedstoneSimulator.h b/src/Simulator/NoopRedstoneSimulator.h
index 4e05529f5..b8c797472 100644
--- a/src/Simulator/NoopRedstoneSimulator.h
+++ b/src/Simulator/NoopRedstoneSimulator.h
@@ -21,7 +21,7 @@ public:
// ~cRedstoneNoopSimulator();
virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used
- virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override
+ virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override
{
UNUSED(a_Dt);
UNUSED(a_ChunkX);
diff --git a/src/Simulator/SandSimulator.cpp b/src/Simulator/SandSimulator.cpp
index dfbd3e458..497f81999 100644
--- a/src/Simulator/SandSimulator.cpp
+++ b/src/Simulator/SandSimulator.cpp
@@ -24,7 +24,7 @@ cSandSimulator::cSandSimulator(cWorld & a_World, cIniFile & a_IniFile) :
-void cSandSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
+void cSandSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
{
cSandSimulatorChunkData & ChunkData = a_Chunk->GetSandSimulatorData();
if (ChunkData.empty())
diff --git a/src/Simulator/SandSimulator.h b/src/Simulator/SandSimulator.h
index 93b1de8e2..8fff659ed 100644
--- a/src/Simulator/SandSimulator.h
+++ b/src/Simulator/SandSimulator.h
@@ -18,7 +18,7 @@ public:
// cSimulator overrides:
virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used
- virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
+ virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override;
/// Returns true if a falling-able block can start falling through the specified block type
diff --git a/src/Simulator/Simulator.h b/src/Simulator/Simulator.h
index f28a07e35..c8066edfd 100644
--- a/src/Simulator/Simulator.h
+++ b/src/Simulator/Simulator.h
@@ -24,7 +24,7 @@ public:
virtual void Simulate(float a_Dt) = 0;
/// Called in each tick for each chunk, a_Dt is the time passed since the last tick, in msec; direct access to chunk data available
- virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
+ virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
{
UNUSED(a_Dt);
UNUSED(a_ChunkX);
diff --git a/src/Simulator/SimulatorManager.cpp b/src/Simulator/SimulatorManager.cpp
index 918bac7a1..e74642fc0 100644
--- a/src/Simulator/SimulatorManager.cpp
+++ b/src/Simulator/SimulatorManager.cpp
@@ -42,7 +42,7 @@ void cSimulatorManager::Simulate(float a_Dt)
-void cSimulatorManager::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
+void cSimulatorManager::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
{
// m_Ticks has already been increased in Simulate()
for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr)
diff --git a/src/Simulator/SimulatorManager.h b/src/Simulator/SimulatorManager.h
index 31a709316..b96f6ca84 100644
--- a/src/Simulator/SimulatorManager.h
+++ b/src/Simulator/SimulatorManager.h
@@ -33,7 +33,7 @@ public:
void Simulate(float a_Dt);
- void SimulateChunk(float a_DT, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk);
+ void SimulateChunk(std::chrono::milliseconds a_DT, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk);
void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk);
diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp
index 5febf5d6c..a63525356 100644
--- a/src/StringUtils.cpp
+++ b/src/StringUtils.cpp
@@ -23,7 +23,7 @@ AString & AppendVPrintf(AString & str, const char * format, va_list args)
ASSERT(format != nullptr);
char buffer[2048];
- size_t len;
+ int len;
#ifdef va_copy
va_list argsCopy;
va_copy(argsCopy, args);
@@ -34,14 +34,14 @@ AString & AppendVPrintf(AString & str, const char * format, va_list args)
// MS CRT provides secure printf that doesn't behave like in the C99 standard
if ((len = _vsnprintf_s(buffer, ARRAYCOUNT(buffer), _TRUNCATE, format, argsCopy)) != -1)
#else // _MSC_VER
- if ((len = vsnprintf(buffer, ARRAYCOUNT(buffer), format, argsCopy)) < ARRAYCOUNT(buffer))
+ if ((len = vsnprintf(buffer, ARRAYCOUNT(buffer), format, argsCopy)) < static_cast<int>(ARRAYCOUNT(buffer)))
#endif // else _MSC_VER
{
// The result did fit into the static buffer
#ifdef va_copy
va_end(argsCopy);
#endif
- str.append(buffer, len);
+ str.append(buffer, static_cast<size_t>(len));
return str;
}
#ifdef va_copy
@@ -51,7 +51,6 @@ AString & AppendVPrintf(AString & str, const char * format, va_list args)
// The result did not fit into the static buffer, use a dynamic buffer:
#ifdef _MSC_VER
// for MS CRT, we need to calculate the result length
- // MS doesn't have va_copy() and does nod need it at all
len = _vscprintf(format, args);
if (len == -1)
{
@@ -63,11 +62,11 @@ AString & AppendVPrintf(AString & str, const char * format, va_list args)
#ifdef va_copy
va_copy(argsCopy, args);
#endif
- std::vector<char> Buffer(len + 1);
+ std::vector<char> Buffer(static_cast<size_t>(len) + 1);
#ifdef _MSC_VER
- vsprintf_s((char *)&(Buffer.front()), Buffer.size(), format, argsCopy);
+ vsprintf_s(&(Buffer.front()), Buffer.size(), format, argsCopy);
#else // _MSC_VER
- vsnprintf((char *)&(Buffer.front()), Buffer.size(), format, argsCopy);
+ vsnprintf(&(Buffer.front()), Buffer.size(), format, argsCopy);
#endif // else _MSC_VER
str.append(&(Buffer.front()), Buffer.size() - 1);
#ifdef va_copy
@@ -85,7 +84,7 @@ AString & Printf(AString & str, const char * format, ...)
str.clear();
va_list args;
va_start(args, format);
- std::string &retval = AppendVPrintf(str, format, args);
+ std::string & retval = AppendVPrintf(str, format, args);
va_end(args);
return retval;
}
@@ -108,11 +107,11 @@ AString Printf(const char * format, ...)
-AString & AppendPrintf(AString &str, const char * format, ...)
+AString & AppendPrintf(AString & dst, const char * format, ...)
{
va_list args;
va_start(args, format);
- std::string &retval = AppendVPrintf(str, format, args);
+ std::string & retval = AppendVPrintf(dst, format, args);
va_end(args);
return retval;
}
@@ -297,7 +296,6 @@ void ReplaceString(AString & iHayStack, const AString & iNeedle, const AString &
-// Converts a stream of BE shorts into UTF-8 string; returns a ref to a_UTF8
AString & RawBEToUTF8(const char * a_RawData, size_t a_NumShorts, AString & a_UTF8)
{
a_UTF8.clear();
@@ -314,22 +312,22 @@ AString & RawBEToUTF8(const char * a_RawData, size_t a_NumShorts, AString & a_UT
a_UTF8.push_back((char)(192 + c / 64));
a_UTF8.push_back((char)(128 + c % 64));
}
- else if (c - 0xd800u < 0x800)
+ else if (c - 0xd800 < 0x800)
{
// Error, silently drop
}
else if (c < 0x10000)
{
- a_UTF8.push_back((char)(224 + c / 4096));
- a_UTF8.push_back((char)(128 + c / 64 % 64));
- a_UTF8.push_back((char)(128 + c % 64));
+ a_UTF8.push_back(static_cast<char>(224 + c / 4096));
+ a_UTF8.push_back(static_cast<char>(128 + (c / 64) % 64));
+ a_UTF8.push_back(static_cast<char>(128 + c % 64));
}
else if (c < 0x110000)
{
- a_UTF8.push_back((char)(240 + c / 262144));
- a_UTF8.push_back((char)(128 + c / 4096 % 64));
- a_UTF8.push_back((char)(128 + c / 64 % 64));
- a_UTF8.push_back((char)(128 + c % 64));
+ a_UTF8.push_back(static_cast<char>(240 + c / 262144));
+ a_UTF8.push_back(static_cast<char>(128 + (c / 4096) % 64));
+ a_UTF8.push_back(static_cast<char>(128 + (c / 64) % 64));
+ a_UTF8.push_back(static_cast<char>(128 + c % 64));
}
else
{
@@ -382,7 +380,7 @@ Notice from the original file:
-static const char trailingBytesForUTF8[256] =
+static const Byte trailingBytesForUTF8[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -572,18 +570,18 @@ AString & CreateHexDump(AString & a_Out, const void * a_Data, size_t a_Size, siz
int Count = sprintf(line, "%08x:", (unsigned)i);
#endif
// Remove the terminating nullptr / leftover garbage in line, after the sprintf-ed value
- memset(line + Count, 32, sizeof(line) - Count);
+ memset(line + Count, 32, sizeof(line) - static_cast<size_t>(Count));
p = line + 10;
q = p + 2 + a_BytesPerLine * 3 + 1;
for (size_t j = 0; j < k; j++)
{
- unsigned char c = ((unsigned char *)a_Data)[i + j];
+ Byte c = (reinterpret_cast<const Byte *>(a_Data))[i + j];
p[0] = HEX(c >> 4);
p[1] = HEX(c & 0xf);
p[2] = ' ';
if (c >= ' ')
{
- q[0] = (char)c;
+ q[0] = static_cast<char>(c);
}
else
{
@@ -708,7 +706,7 @@ AString URLDecode(const AString & a_String)
res.push_back(ch);
continue;
}
- res.push_back((hi << 4) | lo);
+ res.push_back(static_cast<char>((hi << 4) | lo));
i += 2;
} // for i - a_String[]
return res;
@@ -767,7 +765,8 @@ AString Base64Decode(const AString & a_Base64String)
{
AString res;
size_t i, len = a_Base64String.size();
- int o, c;
+ size_t o;
+ int c;
res.resize((len * 4) / 3 + 5, 0); // Approximate the upper bound on the result length
for (o = 0, i = 0; i < len; i++)
{
@@ -850,7 +849,7 @@ AString Base64Encode(const AString & a_Input)
short GetBEShort(const char * a_Mem)
{
const Byte * Bytes = (const Byte *)a_Mem;
- return (Bytes[0] << 8) | Bytes[1];
+ return static_cast<short>((Bytes[0] << 8) | Bytes[1]);
}
@@ -870,9 +869,9 @@ int GetBEInt(const char * a_Mem)
void SetBEInt(char * a_Mem, Int32 a_Value)
{
a_Mem[0] = a_Value >> 24;
- a_Mem[1] = (a_Value >> 16) & 0xff;
- a_Mem[2] = (a_Value >> 8) & 0xff;
- a_Mem[3] = a_Value & 0xff;
+ a_Mem[1] = static_cast<char>((a_Value >> 16) & 0xff);
+ a_Mem[2] = static_cast<char>((a_Value >> 8) & 0xff);
+ a_Mem[3] = static_cast<char>(a_Value & 0xff);
}
diff --git a/src/StringUtils.h b/src/StringUtils.h
index 159e8ecac..bfe2a41fa 100644
--- a/src/StringUtils.h
+++ b/src/StringUtils.h
@@ -21,31 +21,40 @@ typedef std::list<AString> AStringList;
-/** Add the formated string to the existing data in the string */
-extern AString & AppendVPrintf(AString & str, const char * format, va_list args) FORMATSTRING(2, 0);
+/** Add the formated string to the existing data in the string.
+Returns a_Dst. */
+extern AString & AppendVPrintf(AString & a_Dst, const char * format, va_list args) FORMATSTRING(2, 0);
-/// Output the formatted text into the string
-extern AString & Printf (AString & str, const char * format, ...) FORMATSTRING(2, 3);
+/** Output the formatted text into the string.
+Returns a_Dst. */
+extern AString & Printf (AString & a_Dst, const char * format, ...) FORMATSTRING(2, 3);
-/// Output the formatted text into string, return string by value
+/** Output the formatted text into string
+Returns the formatted string by value. */
extern AString Printf(const char * format, ...) FORMATSTRING(1, 2);
-/// Add the formatted string to the existing data in the string
-extern AString & AppendPrintf (AString & str, const char * format, ...) FORMATSTRING(2, 3);
+/** Add the formatted string to the existing data in the string.
+Returns a_Dst */
+extern AString & AppendPrintf (AString & a_Dst, const char * format, ...) FORMATSTRING(2, 3);
-/// Split the string at any of the listed delimiters, return as a stringvector
+/** Split the string at any of the listed delimiters.
+Return the splitted strings as a stringvector. */
extern AStringVector StringSplit(const AString & str, const AString & delim);
-/// Split the string at any of the listed delimiters and trim each value, return as a stringvector
+/** Split the string at any of the listed delimiters and trim each value.
+Returns the splitted strings as a stringvector. */
extern AStringVector StringSplitAndTrim(const AString & str, const AString & delim);
-/// Trime whitespace at both ends of the string
+/** Trims whitespace at both ends of the string.
+Returns a trimmed copy of the original string. */
extern AString TrimString(const AString & str); // tolua_export
-/// In-place string conversion to uppercase; returns the same string
+/** In-place string conversion to uppercase.
+Returns the same string object. */
extern AString & InPlaceUppercase(AString & s);
-/// In-place string conversion to lowercase; returns the same string
+/** In-place string conversion to lowercase.
+Returns the same string object. */
extern AString & InPlaceLowercase(AString & s);
/** Returns an upper-cased copy of the string */
@@ -54,28 +63,30 @@ extern AString StrToUpper(const AString & s);
/** Returns a lower-cased copy of the string */
extern AString StrToLower(const AString & s);
-/// Case-insensitive string comparison; returns 0 if the strings are the same
+/** Case-insensitive string comparison.
+Returns 0 if the strings are the same, <0 if s1 < s2 and >0 if s1 > s2. */
extern int NoCaseCompare(const AString & s1, const AString & s2); // tolua_export
-/// Case-insensitive string comparison that returns a rating of equal-ness between [0 - s1.length()]
+/** 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 ref to a_UTF8
+/** Converts a stream of BE shorts into UTF-8 string; returns a_UTF8. */
extern AString & RawBEToUTF8(const char * a_RawData, size_t a_NumShorts, AString & a_UTF8);
-/// Converts a UTF-8 string into a UTF-16 BE string; returns a ref to a_UTF16
+/** Converts a UTF-8 string into a UTF-16 BE string. */
extern AString UTF8ToRawBEUTF16(const char * a_UTF8, size_t a_UTF8Length);
-/// Creates a nicely formatted HEX dump of the given memory block. Max a_BytesPerLine is 120
+/** Creates a nicely formatted HEX dump of the given memory block.
+Max a_BytesPerLine is 120. */
extern AString & CreateHexDump(AString & a_Out, const void * a_Data, size_t a_Size, size_t a_BytesPerLine);
-/// Returns a copy of a_Message with all quotes and backslashes escaped by a backslash
+/** Returns a copy of a_Message with all quotes and backslashes escaped by a backslash. */
extern AString EscapeString(const AString & a_Message); // tolua_export
-/// Removes all control codes used by MC for colors and styles
+/** Removes all control codes used by MC for colors and styles. */
extern AString StripColorCodes(const AString & a_Message); // tolua_export
/// URL-Decodes the given string, replacing all "%HH" into the correct characters. Invalid % sequences are left intact
diff --git a/src/Vector3.h b/src/Vector3.h
index 1e4a1f5d9..1f3f6b955 100644
--- a/src/Vector3.h
+++ b/src/Vector3.h
@@ -307,6 +307,22 @@ public:
return (a_X - x) / (a_OtherEnd.x - x);
}
+ /** Rotates the vector 90 degrees clockwise around the vertical axis.
+ Note that this is specific to minecraft's axis ordering, which is X+ left, Z+ down. */
+ inline void TurnCW(void)
+ {
+ std::swap(x, z);
+ x = -x;
+ }
+
+ /** Rotates the vector 90 degrees counterclockwise around the vertical axis.
+ Note that this is specific to minecraft's axis ordering, which is X+ left, Z+ down. */
+ inline void TurnCCW(void)
+ {
+ std::swap(x, z);
+ z = -z;
+ }
+
/** The max difference between two coords for which the coords are assumed equal. */
static const double EPS;
diff --git a/src/World.cpp b/src/World.cpp
index 69b39f831..24b1a9b40 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -233,21 +233,19 @@ cWorld::cTickThread::cTickThread(cWorld & a_World) :
void cWorld::cTickThread::Execute(void)
{
auto LastTime = std::chrono::steady_clock::now();
- static const auto msPerTick = std::chrono::milliseconds(50);
- auto TickTime = std::chrono::steady_clock::duration(50);
+ auto TickTime = std::chrono::duration_cast<std::chrono::milliseconds>(cTickTime(1));
while (!m_ShouldTerminate)
{
auto NowTime = std::chrono::steady_clock::now();
- auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(NowTime - LastTime).count();
- auto LastTickMsec = std::chrono::duration_cast<std::chrono::duration<int>>(TickTime).count();
- m_World.Tick(static_cast<float>(msec), LastTickMsec);
- TickTime = std::chrono::steady_clock::now() - NowTime;
+ auto WaitTime = std::chrono::duration_cast<std::chrono::milliseconds>(NowTime - LastTime);
+ m_World.Tick(WaitTime, TickTime);
+ TickTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - NowTime);
- if (TickTime < msPerTick)
+ if (TickTime < cTickTime(1))
{
- // Stretch tick time until it's at least msPerTick
- std::this_thread::sleep_for(msPerTick - TickTime);
+ // Stretch tick time until it's at least 1 tick
+ std::this_thread::sleep_for(cTickTime(1) - TickTime);
}
LastTime = NowTime;
@@ -273,12 +271,17 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin
#endif
m_Dimension(a_Dimension),
m_IsSpawnExplicitlySet(false),
+ m_SpawnX(0),
+ m_SpawnY(0),
+ m_SpawnZ(0),
+ m_BroadcastDeathMessages(true),
+ m_BroadcastAchievementMessages(true),
m_IsDaylightCycleEnabled(true),
- m_WorldAgeSecs(0),
- m_TimeOfDaySecs(0),
m_WorldAge(0),
m_TimeOfDay(0),
m_LastTimeUpdate(0),
+ m_LastUnload(0),
+ m_LastSave(0),
m_SkyDarkness(0),
m_GameMode(gmNotSet),
m_bEnabledPVP(false),
@@ -616,13 +619,10 @@ void cWorld::Start(void)
InitialiseGeneratorDefaults(IniFile);
InitialiseAndLoadMobSpawningValues(IniFile);
- SetTimeOfDay(IniFile.GetValueSetI("General", "TimeInTicks", m_TimeOfDay));
+ SetTimeOfDay(IniFile.GetValueSetI("General", "TimeInTicks", GetTimeOfDay()));
m_ChunkMap = make_unique<cChunkMap>(this);
- m_LastSave = 0;
- m_LastUnload = 0;
-
// preallocate some memory for ticking blocks so we don't need to allocate that often
m_BlockTickQueue.reserve(1000);
m_BlockTickQueueCopy.reserve(1000);
@@ -646,10 +646,10 @@ void cWorld::Start(void)
m_TickThread.Start();
// Init of the spawn monster time (as they are supposed to have different spawn rate)
- m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfHostile, 0));
- m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfPassive, 0));
- m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfAmbient, 0));
- m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfWater, 0));
+ m_LastSpawnMonster.insert(std::map<cMonster::eFamily, cTickTimeLong>::value_type(cMonster::mfHostile, cTickTimeLong(0)));
+ m_LastSpawnMonster.insert(std::map<cMonster::eFamily, cTickTimeLong>::value_type(cMonster::mfPassive, cTickTimeLong(0)));
+ m_LastSpawnMonster.insert(std::map<cMonster::eFamily, cTickTimeLong>::value_type(cMonster::mfAmbient, cTickTimeLong(0)));
+ m_LastSpawnMonster.insert(std::map<cMonster::eFamily, cTickTimeLong>::value_type(cMonster::mfWater, cTickTimeLong(0)));
m_MapManager.LoadMapData();
@@ -840,7 +840,7 @@ void cWorld::Stop(void)
IniFile.SetValueB("Mechanics", "UseChatPrefixes", m_bUseChatPrefixes);
IniFile.SetValueB("General", "IsDaylightCycleEnabled", m_IsDaylightCycleEnabled);
IniFile.SetValueI("General", "Weather", (int)m_Weather);
- IniFile.SetValueI("General", "TimeInTicks", m_TimeOfDay);
+ IniFile.SetValueI("General", "TimeInTicks", GetTimeOfDay());
IniFile.WriteFile(m_IniFileName);
m_TickThread.Stop();
@@ -854,7 +854,7 @@ void cWorld::Stop(void)
-void cWorld::Tick(float a_Dt, int a_LastTickDurationMSec)
+void cWorld::Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec)
{
// Call the plugins
cPluginManager::Get()->CallHookWorldTick(*this, a_Dt, a_LastTickDurationMSec);
@@ -870,30 +870,27 @@ void cWorld::Tick(float a_Dt, int a_LastTickDurationMSec)
SetChunkData(**itr);
} // for itr - SetChunkDataQueue[]
- m_WorldAgeSecs += (double)a_Dt / 1000.0;
- m_WorldAge = (Int64)(m_WorldAgeSecs * 20.0);
+ m_WorldAge += a_Dt;
if (m_IsDaylightCycleEnabled)
{
- // We need sub-tick precision here, that's why we store the time in seconds and calculate ticks off of it
- m_TimeOfDaySecs += (double)a_Dt / 1000.0;
+ // We need sub-tick precision here, that's why we store the time in milliseconds and calculate ticks off of it
+ m_TimeOfDay += a_Dt;
// Wrap time of day each 20 minutes (1200 seconds)
- if (m_TimeOfDaySecs > 1200.0)
+ if (m_TimeOfDay > std::chrono::minutes(20))
{
- m_TimeOfDaySecs -= 1200.0;
+ m_TimeOfDay -= std::chrono::minutes(20);
}
- m_TimeOfDay = static_cast<int>(m_TimeOfDaySecs * 20.0);
-
// Updates the sky darkness based on current time of day
UpdateSkyDarkness();
// Broadcast time update every 40 ticks (2 seconds)
- if (m_LastTimeUpdate < m_WorldAge - 40)
+ if (m_LastTimeUpdate < m_WorldAge - cTickTime(40))
{
BroadcastTimeUpdate();
- m_LastTimeUpdate = m_WorldAge;
+ m_LastTimeUpdate = std::chrono::duration_cast<cTickTimeLong>(m_WorldAge);
}
}
@@ -913,23 +910,23 @@ void cWorld::Tick(float a_Dt, int a_LastTickDurationMSec)
m_ChunkMap->Tick(a_Dt);
- TickClients(a_Dt);
+ TickClients(static_cast<float>(a_Dt.count()));
TickQueuedBlocks();
TickQueuedTasks();
TickScheduledTasks();
- GetSimulatorManager()->Simulate(a_Dt);
+ GetSimulatorManager()->Simulate(static_cast<float>(a_Dt.count()));
- TickWeather(a_Dt);
+ TickWeather(static_cast<float>(a_Dt.count()));
m_ChunkMap->FastSetQueuedBlocks();
- if (m_WorldAge - m_LastSave > 60 * 5 * 20) // Save each 5 minutes
+ if (m_WorldAge - m_LastSave > std::chrono::minutes(5)) // Save each 5 minutes
{
SaveAllChunks();
}
- if (m_WorldAge - m_LastUnload > 10 * 20) // Unload every 10 seconds
+ if (m_WorldAge - m_LastUnload > std::chrono::minutes(5)) // Unload every 10 seconds
{
UnloadUnusedChunks();
}
@@ -975,7 +972,7 @@ void cWorld::TickWeather(float a_Dt)
-void cWorld::TickMobs(float a_Dt)
+void cWorld::TickMobs(std::chrono::milliseconds a_Dt)
{
// _X 2013_10_22: This is a quick fix for #283 - the world needs to be locked while ticking mobs
cWorld::cLock Lock(*this);
@@ -996,7 +993,7 @@ void cWorld::TickMobs(float a_Dt)
for (size_t i = 0; i < ARRAYCOUNT(AllFamilies); i++)
{
cMonster::eFamily Family = AllFamilies[i];
- int SpawnDelay = cMonster::GetSpawnDelay(Family);
+ cTickTime SpawnDelay = cTickTime(cMonster::GetSpawnDelay(Family));
if (
(m_LastSpawnMonster[Family] > m_WorldAge - SpawnDelay) || // Not reached the needed ticks before the next round
MobCensus.IsCapped(Family)
@@ -1004,7 +1001,7 @@ void cWorld::TickMobs(float a_Dt)
{
continue;
}
- m_LastSpawnMonster[Family] = m_WorldAge;
+ m_LastSpawnMonster[Family] = std::chrono::duration_cast<cTickTimeLong>(m_WorldAge);
cMobSpawner Spawner(Family, m_AllowedMobs);
if (Spawner.CanSpawnAnything())
{
@@ -1068,7 +1065,7 @@ void cWorld::TickScheduledTasks(void)
// 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
{
- if ((*itr)->m_TargetTick < WorldAge)
+ if ((*itr)->m_TargetTick < std::chrono::duration_cast<cTickTimeLong>(WorldAge).count())
{
auto next = itr;
++next;
@@ -1143,7 +1140,7 @@ void cWorld::TickClients(float a_Dt)
void cWorld::UpdateSkyDarkness(void)
{
- int TempTime = (int)m_TimeOfDay;
+ int TempTime = std::chrono::duration_cast<cTickTime>(m_TimeOfDay).count();
if (TempTime <= TIME_SUNSET)
{
m_SkyDarkness = 0;
@@ -1424,14 +1421,15 @@ void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, NIBBLETYPE a_Sapling
{
cNoise Noise(m_Generator.GetSeed());
sSetBlockVector Logs, Other;
+ auto WorldAge = (int)(std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count() & 0xffffffff);
switch (a_SaplingMeta & 0x07)
{
- case E_META_SAPLING_APPLE: GetAppleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break;
- case E_META_SAPLING_BIRCH: GetBirchTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break;
- case E_META_SAPLING_CONIFER: GetConiferTreeImage(a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break;
- case E_META_SAPLING_JUNGLE: GetJungleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break;
- case E_META_SAPLING_ACACIA: GetAcaciaTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break;
- case E_META_SAPLING_DARK_OAK: GetDarkoakTreeImage(a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break;
+ case E_META_SAPLING_APPLE: GetAppleTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break;
+ case E_META_SAPLING_BIRCH: GetBirchTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break;
+ case E_META_SAPLING_CONIFER: GetConiferTreeImage(a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break;
+ case E_META_SAPLING_JUNGLE: GetJungleTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break;
+ case E_META_SAPLING_ACACIA: GetAcaciaTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break;
+ case E_META_SAPLING_DARK_OAK: GetDarkoakTreeImage(a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break;
}
Other.insert(Other.begin(), Logs.begin(), Logs.end());
Logs.clear();
@@ -1446,7 +1444,7 @@ void cWorld::GrowTreeByBiome(int a_X, int a_Y, int a_Z)
{
cNoise Noise(m_Generator.GetSeed());
sSetBlockVector Logs, Other;
- GetTreeImageByBiome(a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), GetBiomeAt(a_X, a_Z), Logs, Other);
+ GetTreeImageByBiome(a_X, a_Y, a_Z, Noise, (int)(std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count() & 0xffffffff), GetBiomeAt(a_X, a_Z), Logs, Other);
Other.insert(Other.begin(), Logs.begin(), Logs.end());
Logs.clear();
GrowTreeImage(Other);
@@ -1464,7 +1462,7 @@ void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks)
sSetBlockVector b2;
for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
{
- if (itr->BlockType == E_BLOCK_LOG)
+ if (itr->m_BlockType == E_BLOCK_LOG)
{
b2.push_back(*itr);
}
@@ -1479,7 +1477,7 @@ void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks)
// Check that at each log's coord there's an block allowed to be overwritten:
for (sSetBlockVector::const_iterator itr = b2.begin(); itr != b2.end(); ++itr)
{
- switch (itr->BlockType)
+ switch (itr->m_BlockType)
{
CASE_TREE_ALLOWED_BLOCKS:
{
@@ -1927,6 +1925,15 @@ void cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, int a_FuseTicks,
+void cWorld::SetBlocks(const sSetBlockVector & a_Blocks)
+{
+ m_ChunkMap->SetBlocks(a_Blocks);
+}
+
+
+
+
+
void cWorld::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType)
{
m_ChunkMap->ReplaceBlocks(a_Blocks, a_FilterBlockType);
@@ -2398,7 +2405,7 @@ void cWorld::BroadcastTimeUpdate(const cClientHandle * a_Exclude)
{
continue;
}
- ch->SendTimeUpdate(m_WorldAge, m_TimeOfDay, m_IsDaylightCycleEnabled);
+ ch->SendTimeUpdate(std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count(), std::chrono::duration_cast<cTickTimeLong>(m_TimeOfDay).count(), m_IsDaylightCycleEnabled);
}
}
@@ -2600,7 +2607,7 @@ bool cWorld::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const
void cWorld::UnloadUnusedChunks(void)
{
- m_LastUnload = m_WorldAge;
+ m_LastUnload = std::chrono::duration_cast<cTickTimeLong>(m_WorldAge);
m_ChunkMap->UnloadUnusedChunks();
}
@@ -3076,7 +3083,7 @@ bool cWorld::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunk
void cWorld::SaveAllChunks(void)
{
- m_LastSave = m_WorldAge;
+ m_LastSave = std::chrono::duration_cast<cTickTimeLong>(m_WorldAge);
m_ChunkMap->SaveAllChunks();
}
@@ -3105,7 +3112,7 @@ void cWorld::QueueTask(std::unique_ptr<cTask> a_Task)
void cWorld::ScheduleTask(int a_DelayTicks, cTask * a_Task)
{
- Int64 TargetTick = a_DelayTicks + m_WorldAge;
+ Int64 TargetTick = a_DelayTicks + std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count();
// Insert the task into the list of scheduled tasks, ordered by its target tick
cCSLock Lock(m_CSScheduledTasks);
diff --git a/src/World.h b/src/World.h
index 4f24280a4..e7519dab8 100644
--- a/src/World.h
+++ b/src/World.h
@@ -157,8 +157,8 @@ public:
BroadcastTimeUpdate();
}
- virtual Int64 GetWorldAge (void) const override { return m_WorldAge; }
- virtual int GetTimeOfDay(void) const override { return m_TimeOfDay; }
+ virtual Int64 GetWorldAge (void) const override { return std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count(); }
+ virtual int GetTimeOfDay(void) const override { return std::chrono::duration_cast<cTickTime>(m_TimeOfDay).count(); }
void SetTicksUntilWeatherChange(int a_WeatherInterval)
{
@@ -167,8 +167,7 @@ public:
virtual void SetTimeOfDay(int a_TimeOfDay) override
{
- m_TimeOfDay = a_TimeOfDay;
- m_TimeOfDaySecs = (double)a_TimeOfDay / 20.0;
+ m_TimeOfDay = cTickTime(a_TimeOfDay);
UpdateSkyDarkness();
BroadcastTimeUpdate();
}
@@ -491,6 +490,11 @@ public:
// tolua_end
+ /** Performs the specified single-block set operations simultaneously, as if SetBlock() was called for each item.
+ Is more efficient than calling SetBlock() multiple times.
+ If the chunk for any of the blocks is not loaded, the set operation is ignored silently. */
+ void SetBlocks(const sSetBlockVector & a_Blocks);
+
/** Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType */
void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType);
@@ -799,7 +803,7 @@ public:
int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const cItem * a_Item, const Vector3d * a_Speed = nullptr); // tolua_export
/** Returns a random number from the m_TickRand in range [0 .. a_Range]. To be used only in the tick thread! */
- int GetTickRandomNumber(unsigned a_Range) { return (int)(m_TickRand.randInt(a_Range)); }
+ int GetTickRandomNumber(int a_Range) { return (int)(m_TickRand.randInt(a_Range)); }
/** Appends all usernames starting with a_Text (case-insensitive) into Results */
void TabCompleteUserName(const AString & a_Text, AStringVector & a_Results);
@@ -909,14 +913,13 @@ private:
bool m_BroadcastAchievementMessages;
bool m_IsDaylightCycleEnabled;
- double m_WorldAgeSecs; // World age, in seconds. Is only incremented, cannot be set by plugins.
- double m_TimeOfDaySecs; // Time of day in seconds. Can be adjusted. Is wrapped to zero each day.
- Int64 m_WorldAge; // World age in ticks, calculated off of m_WorldAgeSecs
- int m_TimeOfDay; // Time in ticks, calculated off of m_TimeOfDaySecs
- Int64 m_LastTimeUpdate; // The tick in which the last time update has been sent.
- Int64 m_LastUnload; // The last WorldAge (in ticks) in which unloading was triggerred
- Int64 m_LastSave; // The last WorldAge (in ticks) in which save-all was triggerred
- std::map<cMonster::eFamily, Int64> m_LastSpawnMonster; // The last WorldAge (in ticks) in which a monster was spawned (for each megatype of monster) // MG TODO : find a way to optimize without creating unmaintenability (if mob IDs are becoming unrowed)
+ // std::chrono::milliseconds is guaranteed to be good for 292 years by the standard.
+ std::chrono::milliseconds m_WorldAge;
+ std::chrono::milliseconds m_TimeOfDay;
+ cTickTimeLong m_LastTimeUpdate; // The tick in which the last time update has been sent.
+ cTickTimeLong m_LastUnload; // The last WorldAge (in ticks) in which unloading was triggerred
+ cTickTimeLong m_LastSave; // The last WorldAge (in ticks) in which save-all was triggerred
+ std::map<cMonster::eFamily, cTickTimeLong> m_LastSpawnMonster; // The last WorldAge (in ticks) in which a monster was spawned (for each megatype of monster) // MG TODO : find a way to optimize without creating unmaintenability (if mob IDs are becoming unrowed)
NIBBLETYPE m_SkyDarkness;
@@ -1046,13 +1049,13 @@ private:
cWorld(const AString & a_WorldName, eDimension a_Dimension = dimOverworld, const AString & a_OverworldName = "");
virtual ~cWorld();
- void Tick(float a_Dt, int a_LastTickDurationMSec);
+ void Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec);
/** Handles the weather in each tick */
void TickWeather(float a_Dt);
/** Handles the mob spawning/moving/destroying each tick */
- void TickMobs(float a_Dt);
+ void TickMobs(std::chrono::milliseconds a_Dt);
/** Executes all tasks queued onto the tick thread */
void TickQueuedTasks(void);
diff --git a/src/WorldStorage/FastNBT.cpp b/src/WorldStorage/FastNBT.cpp
index ed8e8bb14..033a07601 100644
--- a/src/WorldStorage/FastNBT.cpp
+++ b/src/WorldStorage/FastNBT.cpp
@@ -10,6 +10,13 @@
+/** If a list being loaded has more than this number of items, it's considered corrupted. */
+static const int MAX_LIST_ITEMS = 10000;
+
+
+
+
+
// The number of NBT tags that are reserved when an NBT parsing is started.
// You can override this by using a cmdline define
#ifndef NBT_RESERVE_SIZE
@@ -102,7 +109,7 @@ bool cParsedNBT::ReadCompound(void)
ASSERT(m_Tags.size() > 0);
// Reads the latest tag as a compound
- int ParentIdx = (int)m_Tags.size() - 1;
+ size_t ParentIdx = m_Tags.size() - 1;
int PrevSibling = -1;
for (;;)
{
@@ -113,10 +120,10 @@ bool cParsedNBT::ReadCompound(void)
{
break;
}
- m_Tags.push_back(cFastNBTTag(TagType, ParentIdx, PrevSibling));
+ m_Tags.push_back(cFastNBTTag(TagType, static_cast<int>(ParentIdx), PrevSibling));
if (PrevSibling >= 0)
{
- m_Tags[PrevSibling].m_NextSibling = (int)m_Tags.size() - 1;
+ m_Tags[static_cast<size_t>(PrevSibling)].m_NextSibling = (int)m_Tags.size() - 1;
}
else
{
@@ -142,26 +149,27 @@ bool cParsedNBT::ReadList(eTagType a_ChildrenType)
NEEDBYTES(4);
int Count = GetBEInt(m_Data + m_Pos);
m_Pos += 4;
- if (Count < 0)
+ if ((Count < 0) || (Count > MAX_LIST_ITEMS))
{
return false;
}
// Read items:
- int ParentIdx = (int)m_Tags.size() - 1;
+ ASSERT(m_Tags.size() > 0);
+ size_t ParentIdx = m_Tags.size() - 1;
int PrevSibling = -1;
for (int i = 0; i < Count; i++)
{
- m_Tags.push_back(cFastNBTTag(a_ChildrenType, ParentIdx, PrevSibling));
+ m_Tags.push_back(cFastNBTTag(a_ChildrenType, static_cast<int>(ParentIdx), PrevSibling));
if (PrevSibling >= 0)
{
- m_Tags[PrevSibling].m_NextSibling = (int)m_Tags.size() - 1;
+ m_Tags[static_cast<size_t>(PrevSibling)].m_NextSibling = static_cast<int>(m_Tags.size()) - 1;
}
else
{
- m_Tags[ParentIdx].m_FirstChild = (int)m_Tags.size() - 1;
+ m_Tags[ParentIdx].m_FirstChild = static_cast<int>(m_Tags.size()) - 1;
}
- PrevSibling = (int)m_Tags.size() - 1;
+ PrevSibling = static_cast<int>(m_Tags.size()) - 1;
RETURN_FALSE_IF_FALSE(ReadTag());
} // for (i)
m_Tags[ParentIdx].m_LastChild = PrevSibling;
@@ -210,16 +218,16 @@ bool cParsedNBT::ReadTag(void)
return false;
}
NEEDBYTES(len);
- Tag.m_DataLength = len;
+ Tag.m_DataLength = static_cast<size_t>(len);
Tag.m_DataStart = m_Pos;
- m_Pos += len;
+ m_Pos += static_cast<size_t>(len);
return true;
}
case TAG_List:
{
NEEDBYTES(1);
- eTagType ItemType = (eTagType)m_Data[m_Pos];
+ eTagType ItemType = static_cast<eTagType>(m_Data[m_Pos]);
m_Pos++;
RETURN_FALSE_IF_FALSE(ReadList(ItemType));
return true;
@@ -243,9 +251,9 @@ bool cParsedNBT::ReadTag(void)
}
len *= 4;
NEEDBYTES(len);
- Tag.m_DataLength = len;
+ Tag.m_DataLength = static_cast<size_t>(len);
Tag.m_DataStart = m_Pos;
- m_Pos += len;
+ m_Pos += static_cast<size_t>(len);
return true;
}
@@ -269,7 +277,7 @@ int cParsedNBT::FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLen
{
return -1;
}
- if (m_Tags[a_Tag].m_Type != TAG_Compound)
+ if (m_Tags[static_cast<size_t>(a_Tag)].m_Type != TAG_Compound)
{
return -1;
}
@@ -278,11 +286,11 @@ int cParsedNBT::FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLen
{
a_NameLength = strlen(a_Name);
}
- for (int Child = m_Tags[a_Tag].m_FirstChild; Child != -1; Child = m_Tags[Child].m_NextSibling)
+ for (int Child = m_Tags[static_cast<size_t>(a_Tag)].m_FirstChild; Child != -1; Child = m_Tags[static_cast<size_t>(Child)].m_NextSibling)
{
if (
- (m_Tags[Child].m_NameLength == a_NameLength) &&
- (memcmp(m_Data + m_Tags[Child].m_NameStart, a_Name, a_NameLength) == 0)
+ (m_Tags[static_cast<size_t>(Child)].m_NameLength == a_NameLength) &&
+ (memcmp(m_Data + m_Tags[static_cast<size_t>(Child)].m_NameStart, a_Name, a_NameLength) == 0)
)
{
return Child;
@@ -406,7 +414,7 @@ void cFastNBTWriter::EndList(void)
ASSERT(m_Stack[m_CurrentStack].m_Type == TAG_List);
// Update the list count:
- SetBEInt((char *)(m_Result.c_str() + m_Stack[m_CurrentStack].m_Pos), m_Stack[m_CurrentStack].m_Count);
+ SetBEInt(const_cast<char *>(m_Result.c_str() + m_Stack[m_CurrentStack].m_Pos), m_Stack[m_CurrentStack].m_Count);
--m_CurrentStack;
}
@@ -418,7 +426,7 @@ void cFastNBTWriter::EndList(void)
void cFastNBTWriter::AddByte(const AString & a_Name, unsigned char a_Value)
{
TagCommon(a_Name, TAG_Byte);
- m_Result.push_back(a_Value);
+ m_Result.push_back(static_cast<char>(a_Value));
}
@@ -428,8 +436,8 @@ void cFastNBTWriter::AddByte(const AString & a_Name, unsigned char a_Value)
void cFastNBTWriter::AddShort(const AString & a_Name, Int16 a_Value)
{
TagCommon(a_Name, TAG_Short);
- Int16 Value = htons(a_Value);
- m_Result.append((const char *)&Value, 2);
+ UInt16 Value = htons(a_Value);
+ m_Result.append(reinterpret_cast<const char *>(&Value), 2);
}
@@ -439,8 +447,8 @@ void cFastNBTWriter::AddShort(const AString & a_Name, Int16 a_Value)
void cFastNBTWriter::AddInt(const AString & a_Name, Int32 a_Value)
{
TagCommon(a_Name, TAG_Int);
- Int32 Value = htonl(a_Value);
- m_Result.append((const char *)&Value, 4);
+ UInt32 Value = htonl(a_Value);
+ m_Result.append(reinterpret_cast<const char *>(&Value), 4);
}
@@ -450,8 +458,8 @@ void cFastNBTWriter::AddInt(const AString & a_Name, Int32 a_Value)
void cFastNBTWriter::AddLong(const AString & a_Name, Int64 a_Value)
{
TagCommon(a_Name, TAG_Long);
- Int64 Value = HostToNetwork8(&a_Value);
- m_Result.append((const char *)&Value, 8);
+ UInt64 Value = HostToNetwork8(&a_Value);
+ m_Result.append(reinterpret_cast<const char *>(&Value), 8);
}
@@ -461,8 +469,8 @@ void cFastNBTWriter::AddLong(const AString & a_Name, Int64 a_Value)
void cFastNBTWriter::AddFloat(const AString & a_Name, float a_Value)
{
TagCommon(a_Name, TAG_Float);
- Int32 Value = HostToNetwork4(&a_Value);
- m_Result.append((const char *)&Value, 4);
+ UInt32 Value = HostToNetwork4(&a_Value);
+ m_Result.append(reinterpret_cast<const char *>(&Value), 4);
}
@@ -472,8 +480,8 @@ void cFastNBTWriter::AddFloat(const AString & a_Name, float a_Value)
void cFastNBTWriter::AddDouble(const AString & a_Name, double a_Value)
{
TagCommon(a_Name, TAG_Double);
- Int64 Value = HostToNetwork8(&a_Value);
- m_Result.append((const char *)&Value, 8);
+ UInt64 Value = HostToNetwork8(&a_Value);
+ m_Result.append(reinterpret_cast<const char *>(&Value), 8);
}
@@ -483,8 +491,8 @@ void cFastNBTWriter::AddDouble(const AString & a_Name, double a_Value)
void cFastNBTWriter::AddString(const AString & a_Name, const AString & a_Value)
{
TagCommon(a_Name, TAG_String);
- Int16 len = htons((short)(a_Value.size()));
- m_Result.append((const char *)&len, 2);
+ UInt16 len = htons(static_cast<short>(a_Value.size()));
+ m_Result.append(reinterpret_cast<const char *>(&len), 2);
m_Result.append(a_Value.c_str(), a_Value.size());
}
@@ -495,8 +503,8 @@ void cFastNBTWriter::AddString(const AString & a_Name, const AString & a_Value)
void cFastNBTWriter::AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements)
{
TagCommon(a_Name, TAG_ByteArray);
- u_long len = htonl((u_long)a_NumElements);
- m_Result.append((const char *)&len, 4);
+ u_long len = htonl(static_cast<u_long>(a_NumElements));
+ m_Result.append(reinterpret_cast<const char *>(&len), 4);
m_Result.append(a_Value, a_NumElements);
}
@@ -507,18 +515,18 @@ void cFastNBTWriter::AddByteArray(const AString & a_Name, const char * a_Value,
void cFastNBTWriter::AddIntArray(const AString & a_Name, const int * a_Value, size_t a_NumElements)
{
TagCommon(a_Name, TAG_IntArray);
- u_long len = htonl((u_long)a_NumElements);
+ u_long len = htonl(static_cast<u_long>(a_NumElements));
size_t cap = m_Result.capacity();
size_t size = m_Result.length();
if ((cap - size) < (4 + a_NumElements * 4))
{
m_Result.reserve(size + 4 + (a_NumElements * 4));
}
- m_Result.append((const char *)&len, 4);
+ m_Result.append(reinterpret_cast<const char *>(&len), 4);
for (size_t i = 0; i < a_NumElements; i++)
{
- int Element = htonl(a_Value[i]);
- m_Result.append((const char *)&Element, 4);
+ UInt32 Element = htonl(a_Value[i]);
+ m_Result.append(reinterpret_cast<const char *>(&Element), 4);
}
}
@@ -538,8 +546,8 @@ void cFastNBTWriter::Finish(void)
void cFastNBTWriter::WriteString(const char * a_Data, UInt16 a_Length)
{
- Int16 Len = htons(a_Length);
- m_Result.append((const char *)&Len, 2);
+ UInt16 Len = htons(a_Length);
+ m_Result.append(reinterpret_cast<const char *>(&Len), 2);
m_Result.append(a_Data, a_Length);
}
diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp
index 432e122b5..c87397542 100644
--- a/src/WorldStorage/NBTChunkSerializer.cpp
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -495,12 +495,14 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster)
case mtEnderman: EntityClass = "Enderman"; break;
case mtGhast: EntityClass = "Ghast"; break;
case mtGiant: EntityClass = "Giant"; break;
+ case mtGuardian: EntityClass = "Guardian"; break;
case mtHorse: EntityClass = "Horse"; break;
case mtIronGolem: EntityClass = "VillagerGolem"; break;
case mtMagmaCube: EntityClass = "LavaSlime"; break;
case mtMooshroom: EntityClass = "MushroomCow"; break;
case mtOcelot: EntityClass = "Ozelot"; break;
case mtPig: EntityClass = "Pig"; break;
+ case mtRabbit: EntityClass = "Rabbit"; break;
case mtSheep: EntityClass = "Sheep"; break;
case mtSilverfish: EntityClass = "Silverfish"; break;
case mtSkeleton: EntityClass = "Skeleton"; break;
@@ -627,10 +629,12 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster)
case mtEnderDragon:
case mtGhast:
case mtGiant:
+ case mtGuardian:
case mtIronGolem:
case mtMooshroom:
case mtOcelot:
case mtPig:
+ case mtRabbit:
case mtSilverfish:
case mtSnowGolem:
case mtSpider:
diff --git a/src/WorldStorage/SchematicFileSerializer.cpp b/src/WorldStorage/SchematicFileSerializer.cpp
index 64f4cb00d..fb881e290 100644
--- a/src/WorldStorage/SchematicFileSerializer.cpp
+++ b/src/WorldStorage/SchematicFileSerializer.cpp
@@ -184,7 +184,9 @@ bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cP
{
LOG("Dimensions are missing from the schematic file (%d, %d, %d), (%d, %d, %d)",
TSizeX, TSizeY, TSizeZ,
- a_NBT.GetType(TSizeX), a_NBT.GetType(TSizeY), a_NBT.GetType(TSizeZ)
+ (TSizeX >= 0) ? a_NBT.GetType(TSizeX) : -1,
+ (TSizeY >= 0) ? a_NBT.GetType(TSizeY) : -1,
+ (TSizeZ >= 0) ? a_NBT.GetType(TSizeZ) : -1
);
return false;
}
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index af65db700..a76e9461a 100755
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -1410,6 +1410,10 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
LoadGiantFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
+ else if (strncmp(a_IDTag, "Guardian", a_IDTagLength) == 0)
+ {
+ LoadGuardianFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
else if (strncmp(a_IDTag, "Horse", a_IDTagLength) == 0)
{
LoadHorseFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
@@ -1438,6 +1442,10 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
LoadPigFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
+ else if (strncmp(a_IDTag, "Rabbit", a_IDTagLength) == 0)
+ {
+ LoadRabbitFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
else if (strncmp(a_IDTag, "Sheep", a_IDTagLength) == 0)
{
LoadSheepFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
@@ -2197,6 +2205,26 @@ void cWSSAnvil::LoadGiantFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
+void cWSSAnvil::LoadGuardianFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ std::unique_ptr<cGuardian> Monster(new cGuardian());
+ if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
+ a_Entities.push_back(Monster.release());
+}
+
+
+
+
+
void cWSSAnvil::LoadHorseFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "Type");
@@ -2339,6 +2367,26 @@ void cWSSAnvil::LoadPigFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB
+void cWSSAnvil::LoadRabbitFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ std::unique_ptr<cRabbit> Monster(new cRabbit());
+ if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
+ a_Entities.push_back(Monster.release());
+}
+
+
+
+
+
void cWSSAnvil::LoadSheepFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
int ColorIdx = a_NBT.FindChildByName(a_TagIdx, "Color");
@@ -2897,9 +2945,9 @@ bool cWSSAnvil::GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int
{
return false;
}
- a_X = a_NBT.GetInt(x);
- a_Y = a_NBT.GetInt(y);
- a_Z = a_NBT.GetInt(z);
+ a_X = Clamp(a_NBT.GetInt(x), -40000000, 40000000); // World is limited to 30M blocks in XZ, we clamp to 40M
+ a_Y = Clamp(a_NBT.GetInt(y), -10000, 10000); // Y is limited to 0 .. 255, we clamp to 10K
+ a_Z = Clamp(a_NBT.GetInt(z), -40000000, 40000000);
return true;
}
diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h
index 974ba932e..362796614 100755
--- a/src/WorldStorage/WSSAnvil.h
+++ b/src/WorldStorage/WSSAnvil.h
@@ -191,12 +191,14 @@ protected:
void LoadEndermanFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadGhastFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadGiantFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadGuardianFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadHorseFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadIronGolemFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMagmaCubeFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMooshroomFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadOcelotFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadPigFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadRabbitFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadSheepFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadSilverfishFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadSkeletonFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 1fbd88f04..265640cc8 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -5,3 +5,4 @@ enable_testing()
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_subdirectory(ChunkData)
+add_subdirectory(Network)
diff --git a/tests/Network/CMakeLists.txt b/tests/Network/CMakeLists.txt
new file mode 100644
index 000000000..c0af50e2c
--- /dev/null
+++ b/tests/Network/CMakeLists.txt
@@ -0,0 +1,60 @@
+cmake_minimum_required (VERSION 2.6)
+
+enable_testing()
+
+include_directories(${CMAKE_SOURCE_DIR}/src/)
+include_directories(${CMAKE_SOURCE_DIR}/lib/libevent/include)
+
+add_definitions(-DTEST_GLOBALS=1)
+
+# Create a single Network library that contains all the networking code:
+set (Network_SRCS
+ ${CMAKE_SOURCE_DIR}/src/OSSupport/CriticalSection.cpp
+ ${CMAKE_SOURCE_DIR}/src/OSSupport/Event.cpp
+ ${CMAKE_SOURCE_DIR}/src/OSSupport/HostnameLookup.cpp
+ ${CMAKE_SOURCE_DIR}/src/OSSupport/IPLookup.cpp
+ ${CMAKE_SOURCE_DIR}/src/OSSupport/NetworkSingleton.cpp
+ ${CMAKE_SOURCE_DIR}/src/OSSupport/ServerHandleImpl.cpp
+ ${CMAKE_SOURCE_DIR}/src/OSSupport/TCPLinkImpl.cpp
+ ${CMAKE_SOURCE_DIR}/src/StringUtils.cpp
+)
+
+set (Network_HDRS
+ ${CMAKE_SOURCE_DIR}/src/OSSupport/CriticalSection.h
+ ${CMAKE_SOURCE_DIR}/src/OSSupport/Event.h
+ ${CMAKE_SOURCE_DIR}/src/OSSupport/HostnameLookup.h
+ ${CMAKE_SOURCE_DIR}/src/OSSupport/IPLookup.h
+ ${CMAKE_SOURCE_DIR}/src/OSSupport/Network.h
+ ${CMAKE_SOURCE_DIR}/src/OSSupport/NetworkSingleton.h
+ ${CMAKE_SOURCE_DIR}/src/OSSupport/ServerHandleImpl.h
+ ${CMAKE_SOURCE_DIR}/src/OSSupport/TCPLinkImpl.h
+ ${CMAKE_SOURCE_DIR}/src/StringUtils.h
+)
+
+add_library(Network
+ ${Network_SRCS}
+ ${Network_HDRS}
+)
+
+target_link_libraries(Network event_core event_extra)
+if (MSVC)
+ target_link_libraries(Network ws2_32.lib)
+endif()
+
+
+
+
+# Define individual tests:
+
+# Google: download the google.com frontpage using http client socket:
+add_executable(Google-exe Google.cpp)
+target_link_libraries(Google-exe Network)
+add_test(NAME Google-test COMMAND Google-exe)
+
+# EchoServer: Listen on port 9876, echo everything back:
+add_executable(EchoServer EchoServer.cpp)
+target_link_libraries(EchoServer Network)
+
+# NameLookup: Lookup hostname-to-IP and IP-to-hostname:
+add_executable(NameLookup NameLookup.cpp)
+target_link_libraries(NameLookup Network)
diff --git a/tests/Network/EchoServer.cpp b/tests/Network/EchoServer.cpp
new file mode 100644
index 000000000..728db0b7c
--- /dev/null
+++ b/tests/Network/EchoServer.cpp
@@ -0,0 +1,132 @@
+
+// EchoServer.cpp
+
+// Implements an Echo server using the LibEvent-based cNetwork API, as a test of that API
+
+#include "Globals.h"
+#include <iostream>
+#include <string>
+#include "OSSupport/Network.h"
+
+
+
+
+
+/** cTCPLink callbacks that echo everything they receive back to the remote peer. */
+class cEchoLinkCallbacks:
+ public cTCPLink::cCallbacks
+{
+ // cTCPLink::cCallbacks overrides:
+ virtual void OnLinkCreated(cTCPLinkPtr a_Link) override
+ {
+ ASSERT(m_Link == nullptr);
+ m_Link = a_Link;
+ }
+
+
+ virtual void OnReceivedData(const char * a_Data, size_t a_Size) override
+ {
+ ASSERT(m_Link != nullptr);
+
+ // Echo the incoming data back to outgoing data:
+ LOGD("%p (%s:%d): Data received (%u bytes), echoing back.", m_Link.get(), m_Link->GetRemoteIP().c_str(), m_Link->GetRemotePort(), static_cast<unsigned>(a_Size));
+ m_Link->Send(a_Data, a_Size);
+ LOGD("Echo queued");
+
+ // Search for a Ctrl+Z, if found, drop the connection:
+ for (size_t i = 0; i < a_Size; i++)
+ {
+ if (a_Data[i] == '\x1a')
+ {
+ m_Link->Close();
+ m_Link.reset();
+ return;
+ }
+ }
+ }
+
+
+ virtual void OnRemoteClosed(void) override
+ {
+ ASSERT(m_Link != nullptr);
+
+ LOGD("%p (%s:%d): Remote has closed the connection.", m_Link.get(), m_Link->GetRemoteIP().c_str(), m_Link->GetRemotePort());
+ m_Link.reset();
+ }
+
+
+ virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
+ {
+ ASSERT(m_Link != nullptr);
+
+ LOGD("%p (%s:%d): Error %d in the cEchoLinkCallbacks: %s", m_Link.get(), m_Link->GetRemoteIP().c_str(), m_Link->GetRemotePort(), a_ErrorCode, a_ErrorMsg.c_str());
+ m_Link.reset();
+ }
+
+ /** The link attached to this callbacks instance. */
+ cTCPLinkPtr m_Link;
+};
+
+
+
+
+
+class cEchoServerCallbacks:
+ public cNetwork::cListenCallbacks
+{
+ virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort)
+ {
+ LOGD("New incoming connection(%s:%d).", a_RemoteIPAddress.c_str(), a_RemotePort);
+ return std::make_shared<cEchoLinkCallbacks>();
+ }
+
+ virtual void OnAccepted(cTCPLink & a_Link) override
+ {
+ LOGD("New client accepted (%s:%d), sending welcome message.", a_Link.GetRemoteIP().c_str(), a_Link.GetRemotePort());
+ // Send a welcome message to each connecting client:
+ a_Link.Send("Welcome to the simple echo server.\r\n");
+ LOGD("Welcome message queued.");
+ }
+
+ virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
+ {
+ LOGWARNING("An error occured while listening for connections: %d (%s).", a_ErrorCode, a_ErrorMsg.c_str());
+ }
+};
+
+
+
+
+
+int main()
+{
+ LOGD("EchoServer: starting up");
+ cServerHandlePtr Server = cNetwork::Listen(9876, std::make_shared<cEchoServerCallbacks>());
+ if (!Server->IsListening())
+ {
+ LOGWARNING("Cannot listen on port 9876");
+ abort();
+ }
+ ASSERT(Server->IsListening());
+
+ // Wait for the user to terminate the server:
+ printf("Press enter to close the server.\n");
+ AString line;
+ std::getline(std::cin, line);
+
+ // Close the server and all its active connections:
+ LOG("Server terminating.");
+ Server->Close();
+ ASSERT(!Server->IsListening());
+ LOGD("Server has been closed.");
+
+ printf("Press enter to exit test.\n");
+ std::getline(std::cin, line);
+
+ LOG("Network test finished.");
+ return 0;
+}
+
+
+
+
diff --git a/tests/Network/Google.cpp b/tests/Network/Google.cpp
new file mode 100644
index 000000000..2b8830c24
--- /dev/null
+++ b/tests/Network/Google.cpp
@@ -0,0 +1,118 @@
+
+// Google.cpp
+
+// Implements a HTTP download of the google's front page using the LibEvent-based cNetwork API
+
+#include "Globals.h"
+#include <thread>
+#include "OSSupport/Event.h"
+#include "OSSupport/Network.h"
+
+
+
+
+
+/** Connect callbacks that send a HTTP GET request for google.com when connected. */
+class cHTTPConnectCallbacks:
+ public cNetwork::cConnectCallbacks
+{
+ cEvent & m_Event;
+ virtual void OnConnected(cTCPLink & a_Link) override
+ {
+ LOGD("Connected, sending HTTP GET");
+ if (!a_Link.Send("GET / HTTP/1.0\r\nHost:google.com\r\n\r\n"))
+ {
+ LOGWARNING("Sending HTTP GET failed");
+ }
+ LOGD("HTTP GET queued.");
+ }
+
+ virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
+ {
+ LOGD("Error while connecting HTTP: %d (%s)", a_ErrorCode, a_ErrorMsg.c_str());
+ m_Event.Set();
+ }
+
+public:
+ cHTTPConnectCallbacks(cEvent & a_Event):
+ m_Event(a_Event)
+ {
+ }
+};
+
+
+
+
+
+/** cTCPLink callbacks that dump everything it received to the log. */
+class cDumpCallbacks:
+ public cTCPLink::cCallbacks
+{
+ cEvent & m_Event;
+ cTCPLinkPtr m_Link;
+
+ virtual void OnLinkCreated(cTCPLinkPtr a_Link) override
+ {
+ ASSERT(m_Link == nullptr);
+ m_Link = a_Link;
+ }
+
+ virtual void OnReceivedData(const char * a_Data, size_t a_Size) override
+ {
+ ASSERT(m_Link != nullptr);
+
+ // Log the incoming data size:
+ AString Hex;
+ CreateHexDump(Hex, a_Data, a_Size, 16);
+ LOGD("Incoming data: %u bytes:\n%s", static_cast<unsigned>(a_Size), Hex.c_str());
+ }
+
+ virtual void OnRemoteClosed(void) override
+ {
+ ASSERT(m_Link != nullptr);
+
+ LOGD("Remote has closed the connection.");
+ m_Link.reset();
+ m_Event.Set();
+ }
+
+ virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
+ {
+ ASSERT(m_Link != nullptr);
+
+ LOGD("Error %d (%s) in the cDumpCallbacks.", a_ErrorCode, a_ErrorMsg.c_str());
+ m_Link.reset();
+ m_Event.Set();
+ }
+
+public:
+ cDumpCallbacks(cEvent & a_Event):
+ m_Event(a_Event)
+ {
+ }
+};
+
+
+
+
+
+int main()
+{
+ cEvent evtFinish;
+
+ LOGD("Network test: Connecting to google.com:80, reading front page via HTTP.");
+ if (!cNetwork::Connect("google.com", 80, std::make_shared<cHTTPConnectCallbacks>(evtFinish), std::make_shared<cDumpCallbacks>(evtFinish)))
+ {
+ LOGWARNING("Cannot queue connection to google.com");
+ abort();
+ }
+ LOGD("Connect request has been queued.");
+
+ evtFinish.Wait();
+ LOGD("Network test finished");
+ return 0;
+}
+
+
+
+
diff --git a/tests/Network/NameLookup.cpp b/tests/Network/NameLookup.cpp
new file mode 100644
index 000000000..822a96baf
--- /dev/null
+++ b/tests/Network/NameLookup.cpp
@@ -0,0 +1,80 @@
+
+// NameLookup.cpp
+
+// Implements a DNS name lookup using the LibEvent-based cNetwork API
+
+#include "Globals.h"
+#include <thread>
+#include "OSSupport/Event.h"
+#include "OSSupport/Network.h"
+
+
+
+
+
+class cFinishLookupCallbacks:
+ public cNetwork::cResolveNameCallbacks
+{
+ cEvent & m_Event;
+
+ virtual void OnNameResolved(const AString & a_Name, const AString & a_IP) override
+ {
+ LOGD("%s resolves to IP %s", a_Name.c_str(), a_IP.c_str());
+ }
+
+ virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
+ {
+ LOGD("Error %d (%s) while performing lookup!", a_ErrorCode, a_ErrorMsg.c_str());
+ exit(a_ErrorCode);
+ }
+
+ virtual void OnFinished(void) override
+ {
+ LOGD("Resolving finished.");
+ m_Event.Set();
+ }
+
+public:
+ cFinishLookupCallbacks(cEvent & a_Event):
+ m_Event(a_Event)
+ {
+ }
+};
+
+
+
+
+
+int main()
+{
+ cEvent evtFinish;
+
+ // Look up google.com (has multiple IP addresses):
+ LOGD("Network test: Looking up google.com");
+ if (!cNetwork::HostnameToIP("google.com", std::make_shared<cFinishLookupCallbacks>(evtFinish)))
+ {
+ LOGWARNING("Cannot resolve google.com to IP");
+ abort();
+ }
+ LOGD("Name lookup has been successfully queued");
+ evtFinish.Wait();
+ LOGD("Lookup finished.");
+
+ // Look up 8.8.8.8 (Google free DNS):
+ LOGD("Network test: Looking up IP 8.8.8.8");
+ if (!cNetwork::IPToHostName("8.8.8.8", std::make_shared<cFinishLookupCallbacks>(evtFinish)))
+ {
+ LOGWARNING("Cannot resolve 8.8.8.8 to name");
+ abort();
+ }
+ LOGD("IP lookup has been successfully queued");
+ evtFinish.Wait();
+ LOGD("IP lookup finished.");
+
+ LOGD("Network test finished");
+ return 0;
+}
+
+
+
+