summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMattes D <github@xoft.cz>2014-12-07 18:15:23 +0100
committerMattes D <github@xoft.cz>2014-12-07 18:15:23 +0100
commit8ad1afcc1b98c03bd77b0d85236643ba04795d38 (patch)
treee340889a2d505f10fd45a80f7b951f3165d6294a /src
parentcEvent: Changed chrono duration resolution. (diff)
parentFixed format warning. (diff)
downloadcuberite-8ad1afcc1b98c03bd77b0d85236643ba04795d38.tar
cuberite-8ad1afcc1b98c03bd77b0d85236643ba04795d38.tar.gz
cuberite-8ad1afcc1b98c03bd77b0d85236643ba04795d38.tar.bz2
cuberite-8ad1afcc1b98c03bd77b0d85236643ba04795d38.tar.lz
cuberite-8ad1afcc1b98c03bd77b0d85236643ba04795d38.tar.xz
cuberite-8ad1afcc1b98c03bd77b0d85236643ba04795d38.tar.zst
cuberite-8ad1afcc1b98c03bd77b0d85236643ba04795d38.zip
Diffstat (limited to 'src')
-rw-r--r--src/Bindings/AllToLua.pkg1
-rw-r--r--src/Bindings/DeprecatedBindings.cpp117
-rw-r--r--src/Bindings/ManualBindings.cpp9
-rw-r--r--src/Bindings/Plugin.h2
-rw-r--r--src/Bindings/PluginLua.cpp2
-rw-r--r--src/Bindings/PluginLua.h2
-rw-r--r--src/Bindings/PluginManager.cpp11
-rw-r--r--src/Bindings/PluginManager.h2
-rw-r--r--src/BlockEntities/BeaconEntity.cpp19
-rw-r--r--src/BlockEntities/BeaconEntity.h12
-rw-r--r--src/BlockEntities/BlockEntity.cpp4
-rw-r--r--src/BlockEntities/BlockEntity.h7
-rw-r--r--src/BlockEntities/BlockEntityWithItems.h2
-rw-r--r--src/BlockEntities/CMakeLists.txt2
-rw-r--r--src/BlockEntities/ChestEntity.h7
-rw-r--r--src/BlockEntities/CommandBlockEntity.h10
-rw-r--r--src/BlockEntities/DispenserEntity.h2
-rw-r--r--src/BlockEntities/DropSpenserEntity.h6
-rw-r--r--src/BlockEntities/DropperEntity.h2
-rw-r--r--src/BlockEntities/EnderChestEntity.h2
-rw-r--r--src/BlockEntities/FlowerPotEntity.h12
-rw-r--r--src/BlockEntities/FurnaceEntity.h7
-rw-r--r--src/BlockEntities/HopperEntity.h2
-rw-r--r--src/BlockEntities/JukeboxEntity.h11
-rw-r--r--src/BlockEntities/MobHeadEntity.h22
-rw-r--r--src/BlockEntities/MobSpawnerEntity.cpp290
-rw-r--r--src/BlockEntities/MobSpawnerEntity.h78
-rw-r--r--src/BlockEntities/NoteEntity.h8
-rw-r--r--src/BlockEntities/SignEntity.h11
-rw-r--r--src/BlockID.cpp2
-rw-r--r--src/BlockID.h2
-rw-r--r--src/Blocks/BlockBed.h5
-rw-r--r--src/Blocks/BlockDoor.cpp10
-rw-r--r--src/Blocks/BlockMobSpawner.h12
-rw-r--r--src/Blocks/BlockRail.h88
-rw-r--r--src/ByteBuffer.cpp132
-rw-r--r--src/ByteBuffer.h10
-rw-r--r--src/CMakeLists.txt21
-rw-r--r--src/CheckBasicStyle.lua51
-rw-r--r--src/Chunk.cpp20
-rw-r--r--src/ClientHandle.cpp84
-rw-r--r--src/ClientHandle.h42
-rw-r--r--src/CraftingRecipes.cpp8
-rw-r--r--src/DeadlockDetect.cpp6
-rw-r--r--src/DeadlockDetect.h2
-rw-r--r--src/Enchantments.cpp2
-rw-r--r--src/Entities/Entity.cpp22
-rw-r--r--src/Entities/Minecart.cpp29
-rw-r--r--src/Entities/Minecart.h2
-rw-r--r--src/Entities/Player.cpp44
-rw-r--r--src/Entities/Player.h2
-rw-r--r--src/FastRandom.cpp116
-rw-r--r--src/FastRandom.h47
-rw-r--r--src/FurnaceRecipe.cpp6
-rw-r--r--src/Generating/BioGen.h2
-rw-r--r--src/Generating/CMakeLists.txt12
-rw-r--r--src/Generating/Caves.cpp10
-rw-r--r--src/Generating/Caves.h1
-rw-r--r--src/Generating/ChunkDesc.cpp65
-rw-r--r--src/Generating/ChunkDesc.h25
-rw-r--r--src/Generating/ChunkGenerator.cpp8
-rw-r--r--src/Generating/CompoGen.cpp350
-rw-r--r--src/Generating/CompoGen.h47
-rw-r--r--src/Generating/CompoGenBiomal.cpp586
-rw-r--r--src/Generating/CompoGenBiomal.h21
-rw-r--r--src/Generating/ComposableGenerator.cpp120
-rw-r--r--src/Generating/ComposableGenerator.h95
-rw-r--r--src/Generating/CompositedHeiGen.h49
-rw-r--r--src/Generating/DistortedHeightmap.cpp567
-rw-r--r--src/Generating/DistortedHeightmap.h61
-rw-r--r--src/Generating/DungeonRoomsFinisher.cpp48
-rw-r--r--src/Generating/DungeonRoomsFinisher.h8
-rw-r--r--src/Generating/EndGen.cpp44
-rw-r--r--src/Generating/EndGen.h10
-rw-r--r--src/Generating/FinishGen.cpp336
-rw-r--r--src/Generating/FinishGen.h88
-rw-r--r--src/Generating/GridStructGen.h2
-rw-r--r--src/Generating/HeiGen.cpp94
-rw-r--r--src/Generating/HeiGen.h100
-rw-r--r--src/Generating/MineShafts.cpp5
-rw-r--r--src/Generating/MineShafts.h1
-rw-r--r--src/Generating/Noise3DGenerator.cpp678
-rw-r--r--src/Generating/Noise3DGenerator.h147
-rw-r--r--src/Generating/PieceGenerator.h2
-rw-r--r--src/Generating/Ravines.h1
-rw-r--r--src/Generating/ShapeGen.cpp145
-rw-r--r--src/Generating/StructGen.cpp36
-rw-r--r--src/Generating/StructGen.h35
-rw-r--r--src/Generating/Trees.cpp106
-rw-r--r--src/Generating/Trees.h8
-rw-r--r--src/Generating/TwoHeights.cpp121
-rw-r--r--src/Generating/TwoHeights.h23
-rw-r--r--src/Generating/VillageGen.cpp6
-rw-r--r--src/Globals.h12
-rw-r--r--src/HTTPServer/HTTPFormParser.h2
-rw-r--r--src/Inventory.cpp8
-rw-r--r--src/ItemGrid.cpp2
-rw-r--r--src/Logger.cpp4
-rw-r--r--src/MersenneTwister.h456
-rw-r--r--src/MobSpawner.cpp34
-rw-r--r--src/MobSpawner.h7
-rw-r--r--src/Mobs/AggressiveMonster.cpp2
-rw-r--r--src/Mobs/Blaze.cpp2
-rw-r--r--src/Mobs/Ghast.cpp2
-rw-r--r--src/Mobs/Monster.cpp156
-rw-r--r--src/Mobs/Monster.h21
-rw-r--r--src/Mobs/MonsterTypes.h10
-rw-r--r--src/Mobs/Pig.cpp26
-rw-r--r--src/Mobs/Pig.h3
-rw-r--r--src/Mobs/Skeleton.cpp2
-rw-r--r--src/Mobs/SnowGolem.cpp2
-rw-r--r--src/Mobs/Villager.cpp7
-rw-r--r--src/Mobs/Witch.cpp1
-rw-r--r--src/Mobs/Witch.h1
-rw-r--r--src/Noise/CMakeLists.txt21
-rw-r--r--src/Noise/InterpolNoise.h524
-rw-r--r--src/Noise/Noise.cpp (renamed from src/Noise.cpp)550
-rw-r--r--src/Noise/Noise.h (renamed from src/Noise.h)262
-rw-r--r--src/Noise/OctavedNoise.h196
-rw-r--r--src/Noise/RidgedNoise.h91
-rw-r--r--src/OSSupport/CMakeLists.txt10
-rw-r--r--src/OSSupport/CriticalSection.cpp52
-rw-r--r--src/OSSupport/CriticalSection.h14
-rw-r--r--src/OSSupport/IsThread.cpp174
-rw-r--r--src/OSSupport/IsThread.h51
-rw-r--r--src/OSSupport/Sleep.cpp19
-rw-r--r--src/OSSupport/Sleep.h7
-rw-r--r--src/OSSupport/Socket.h2
-rw-r--r--src/OSSupport/SocketThreads.cpp14
-rw-r--r--src/OSSupport/StackTrace.cpp44
-rw-r--r--src/OSSupport/StackTrace.h15
-rw-r--r--src/OSSupport/Thread.cpp137
-rw-r--r--src/OSSupport/Thread.h26
-rw-r--r--src/OSSupport/Timer.cpp37
-rw-r--r--src/OSSupport/Timer.h32
-rw-r--r--src/ProbabDistrib.cpp2
-rw-r--r--src/Protocol/Protocol17x.cpp14
-rw-r--r--src/Protocol/Protocol18x.cpp13
-rw-r--r--src/Root.cpp56
-rw-r--r--src/Root.h16
-rw-r--r--src/Server.cpp19
-rw-r--r--src/Simulator/FluidSimulator.cpp6
-rw-r--r--src/StringUtils.cpp41
-rw-r--r--src/Tracer.cpp120
-rw-r--r--src/UI/SlotArea.cpp4
-rw-r--r--src/Vector3.h15
-rw-r--r--src/VoronoiMap.h2
-rw-r--r--src/World.cpp330
-rw-r--r--src/World.h25
-rw-r--r--src/WorldStorage/FireworksSerializer.cpp8
-rw-r--r--src/WorldStorage/MapSerializer.cpp4
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp16
-rw-r--r--src/WorldStorage/NBTChunkSerializer.h26
-rw-r--r--src/WorldStorage/WSSAnvil.cpp213
-rw-r--r--src/WorldStorage/WSSAnvil.h1
-rw-r--r--src/XMLParser.h14
-rw-r--r--src/main.cpp11
157 files changed, 5672 insertions, 3812 deletions
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg
index 7b78578ee..7e174e770 100644
--- a/src/Bindings/AllToLua.pkg
+++ b/src/Bindings/AllToLua.pkg
@@ -74,6 +74,7 @@ $cfile "../BlockEntities/JukeboxEntity.h"
$cfile "../BlockEntities/NoteEntity.h"
$cfile "../BlockEntities/SignEntity.h"
$cfile "../BlockEntities/MobHeadEntity.h"
+$cfile "../BlockEntities/MobSpawnerEntity.h"
$cfile "../BlockEntities/FlowerPotEntity.h"
$cfile "../WebAdmin.h"
$cfile "../Root.h"
diff --git a/src/Bindings/DeprecatedBindings.cpp b/src/Bindings/DeprecatedBindings.cpp
index ded7a0142..6dd6a4e59 100644
--- a/src/Bindings/DeprecatedBindings.cpp
+++ b/src/Bindings/DeprecatedBindings.cpp
@@ -7,6 +7,9 @@
#include "../BlockInfo.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+#include "LuaState.h"
@@ -49,7 +52,9 @@ static int tolua_get_AllToLua_g_BlockSpreadLightFalloff(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
+ {
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
+ }
}
#endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0);
@@ -75,7 +80,9 @@ static int tolua_get_AllToLua_g_BlockTransparent(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
+ {
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
+ }
}
#endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0);
@@ -101,7 +108,9 @@ static int tolua_get_AllToLua_g_BlockOneHitDig(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
+ {
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
+ }
}
#endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0);
@@ -127,7 +136,9 @@ static int tolua_get_AllToLua_g_BlockPistonBreakable(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
+ {
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
+ }
}
#endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0);
@@ -153,7 +164,9 @@ static int tolua_get_AllToLua_g_BlockIsSnowable(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
+ {
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
+ }
}
#endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0);
@@ -179,7 +192,9 @@ static int tolua_get_AllToLua_g_BlockIsSolid(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
+ {
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
+ }
}
#endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0);
@@ -205,7 +220,9 @@ static int tolua_get_AllToLua_g_BlockFullyOccupiesVoxel(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
+ {
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
+ }
}
#endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0);
@@ -222,6 +239,100 @@ static int tolua_get_AllToLua_g_BlockFullyOccupiesVoxel(lua_State* tolua_S)
+/* function: StringToMobType */
+static int tolua_AllToLua_StringToMobType00(lua_State* tolua_S)
+{
+ cLuaState LuaState(tolua_S);
+
+ #ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_iscppstring(tolua_S, 1, 0, &tolua_err) ||
+ !tolua_isnoobj(tolua_S, 2, &tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ #endif
+ {
+ const AString a_MobString = tolua_tocppstring(LuaState, 1, 0);
+ eMonsterType MobType = cMonster::StringToMobType(a_MobString);
+ tolua_pushnumber(LuaState, (lua_Number) MobType);
+ tolua_pushcppstring(LuaState, (const char *) a_MobString);
+ }
+
+ LOGWARNING("Warning in function call 'StringToMobType': StringToMobType() is deprecated. Please use cMonster:StringToMobType()");
+ LuaState.LogStackTrace(0);
+ return 2;
+
+ #ifndef TOLUA_RELEASE
+tolua_lerror:
+ tolua_error(LuaState, "#ferror in function 'StringToMobType'.", &tolua_err);
+ return 0;
+ #endif
+}
+
+
+
+
+
+/** function: cWorld:SetSignLines */
+static int tolua_cWorld_SetSignLines(lua_State * tolua_S)
+{
+ cLuaState LuaState(tolua_S);
+
+ #ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype (LuaState, 1, "cWorld", 0, &tolua_err) ||
+ !tolua_isnumber (LuaState, 2, 0, &tolua_err) ||
+ !tolua_isnumber (LuaState, 3, 0, &tolua_err) ||
+ !tolua_isnumber (LuaState, 4, 0, &tolua_err) ||
+ !tolua_iscppstring(LuaState, 5, 0, &tolua_err) ||
+ !tolua_iscppstring(LuaState, 6, 0, &tolua_err) ||
+ !tolua_iscppstring(LuaState, 7, 0, &tolua_err) ||
+ !tolua_iscppstring(LuaState, 8, 0, &tolua_err) ||
+ !tolua_isusertype (LuaState, 9, "cPlayer", 1, &tolua_err) ||
+ !tolua_isnoobj (LuaState, 10, &tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ #endif
+ {
+ cWorld * self = (cWorld *) tolua_tousertype (LuaState, 1, nullptr);
+ int BlockX = (int) tolua_tonumber (LuaState, 2, 0);
+ int BlockY = (int) tolua_tonumber (LuaState, 3, 0);
+ int BlockZ = (int) tolua_tonumber (LuaState, 4, 0);
+ const AString Line1 = tolua_tocppstring(LuaState, 5, 0);
+ const AString Line2 = tolua_tocppstring(LuaState, 6, 0);
+ const AString Line3 = tolua_tocppstring(LuaState, 7, 0);
+ const AString Line4 = tolua_tocppstring(LuaState, 8, 0);
+ cPlayer * Player = (cPlayer *)tolua_tousertype (LuaState, 9, nullptr);
+ #ifndef TOLUA_RELEASE
+ if (self == nullptr)
+ {
+ tolua_error(LuaState, "invalid 'self' in function 'UpdateSign'", nullptr);
+ }
+ #endif
+ {
+ bool res = self->SetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4, Player);
+ tolua_pushboolean(LuaState, res ? 1 : 0);
+ }
+ }
+ LOGWARNING("Warning in function call 'UpdateSign': UpdateSign() is deprecated. Please use SetSignLines()");
+ LuaState.LogStackTrace(0);
+ return 1;
+
+ #ifndef TOLUA_RELEASE
+tolua_lerror:
+ tolua_error(LuaState, "#ferror in function 'UpdateSign'.", &tolua_err);
+ return 0;
+ #endif
+}
+
+
+
+
+
void DeprecatedBindings::Bind(lua_State * tolua_S)
{
tolua_beginmodule(tolua_S, nullptr);
@@ -235,6 +346,12 @@ void DeprecatedBindings::Bind(lua_State * tolua_S)
tolua_array(tolua_S, "g_BlockIsSolid", tolua_get_AllToLua_g_BlockIsSolid, nullptr);
tolua_array(tolua_S, "g_BlockFullyOccupiesVoxel", tolua_get_AllToLua_g_BlockFullyOccupiesVoxel, nullptr);
+ tolua_function(tolua_S, "StringToMobType", tolua_AllToLua_StringToMobType00);
+
+ tolua_beginmodule(tolua_S, "cWorld");
+ tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines);
+ tolua_endmodule(tolua_S);
+
tolua_endmodule(tolua_S);
}
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index a4a5d79b4..750f7c65a 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -1034,11 +1034,11 @@ static int tolua_cWorld_SetSignLines(lua_State * tolua_S)
#ifndef TOLUA_RELEASE
if (self == nullptr)
{
- tolua_error(tolua_S, "invalid 'self' in function 'SetSignLines' / 'UpdateSign'", nullptr);
+ tolua_error(tolua_S, "invalid 'self' in function 'SetSignLines'", nullptr);
}
#endif
{
- bool res = self->UpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4, Player);
+ bool res = self->SetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4, Player);
tolua_pushboolean(tolua_S, res ? 1 : 0);
}
}
@@ -1046,7 +1046,7 @@ static int tolua_cWorld_SetSignLines(lua_State * tolua_S)
#ifndef TOLUA_RELEASE
tolua_lerror:
- tolua_error(tolua_S, "#ferror in function 'SetSignLines' / 'UpdateSign'.", &tolua_err);
+ tolua_error(tolua_S, "#ferror in function 'SetSignLines'.", &tolua_err);
return 0;
#endif
}
@@ -3368,6 +3368,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_beginmodule(tolua_S, "cRoot");
tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith <cRoot, cPlayer, &cRoot::FindAndDoWithPlayer>);
+ tolua_function(tolua_S, "DoWithPlayerByUUID", tolua_DoWith <cRoot, cPlayer, &cRoot::DoWithPlayerByUUID>);
tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach<cRoot, cPlayer, &cRoot::ForEachPlayer>);
tolua_function(tolua_S, "ForEachWorld", tolua_ForEach<cRoot, cWorld, &cRoot::ForEachWorld>);
tolua_function(tolua_S, "GetFurnaceRecipe", tolua_cRoot_GetFurnaceRecipe);
@@ -3389,6 +3390,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "DoWithFlowerPotAt", tolua_DoWithXYZ<cWorld, cFlowerPotEntity, &cWorld::DoWithFlowerPotAt>);
tolua_function(tolua_S, "DoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>);
tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>);
+ tolua_function(tolua_S, "DoWithPlayerByUUID", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayerByUUID>);
tolua_function(tolua_S, "ForEachBlockEntityInChunk", tolua_ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>);
tolua_function(tolua_S, "ForEachChestInChunk", tolua_ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>);
tolua_function(tolua_S, "ForEachEntity", tolua_ForEach< cWorld, cEntity, &cWorld::ForEachEntity>);
@@ -3403,7 +3405,6 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "ScheduleTask", tolua_cWorld_ScheduleTask);
tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines);
tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight);
- tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cMapManager");
diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h
index 8cc9ff0cd..08677553c 100644
--- a/src/Bindings/Plugin.h
+++ b/src/Bindings/Plugin.h
@@ -54,7 +54,7 @@ public:
virtual bool OnChunkUnloaded (cWorld & a_World, int a_ChunkX, int a_ChunkZ) = 0;
virtual bool OnChunkUnloading (cWorld & a_World, int a_ChunkX, int a_ChunkZ) = 0;
virtual bool OnCollectingPickup (cPlayer & a_Player, cPickup & a_Pickup) = 0;
- virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe * a_Recipe) = 0;
+ virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) = 0;
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0;
virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) = 0;
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0;
diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp
index 391d8bcbe..ea782ea3f 100644
--- a/src/Bindings/PluginLua.cpp
+++ b/src/Bindings/PluginLua.cpp
@@ -382,7 +382,7 @@ bool cPluginLua::OnCollectingPickup(cPlayer & a_Player, cPickup & a_Pickup)
-bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe * a_Recipe)
+bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe)
{
cCSLock Lock(m_CriticalSection);
bool res = false;
diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h
index 6bb134efc..7de5ffec4 100644
--- a/src/Bindings/PluginLua.h
+++ b/src/Bindings/PluginLua.h
@@ -78,7 +78,7 @@ public:
virtual bool OnChunkUnloaded (cWorld & a_World, int a_ChunkX, int a_ChunkZ) override;
virtual bool OnChunkUnloading (cWorld & a_World, int a_ChunkX, int a_ChunkZ) override;
virtual bool OnCollectingPickup (cPlayer & a_Player, cPickup & a_Pickup) override;
- virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe * a_Recipe) override;
+ virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) override;
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) override;
virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) override;
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override;
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index f63578885..406a540f4 100644
--- a/src/Bindings/PluginManager.cpp
+++ b/src/Bindings/PluginManager.cpp
@@ -448,7 +448,7 @@ bool cPluginManager::CallHookCollectingPickup(cPlayer & a_Player, cPickup & a_Pi
-bool cPluginManager::CallHookCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe * a_Recipe)
+bool cPluginManager::CallHookCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe)
{
FIND_HOOK(HOOK_CRAFTING_NO_RECIPE);
VERIFY_HOOK;
@@ -1459,11 +1459,16 @@ cPluginManager::CommandResult cPluginManager::HandleCommand(cPlayer & a_Player,
-cPlugin * cPluginManager::GetPlugin( const AString & a_Plugin) const
+cPlugin * cPluginManager::GetPlugin(const AString & a_Plugin) const
{
for (PluginMap::const_iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr)
{
- if (itr->second == nullptr) continue;
+ if (itr->second == nullptr)
+ {
+ // The plugin is currently unloaded
+ continue;
+ }
+
if (itr->second->GetName().compare(a_Plugin) == 0)
{
return itr->second;
diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h
index bc8c1f5e6..3a2aecc92 100644
--- a/src/Bindings/PluginManager.h
+++ b/src/Bindings/PluginManager.h
@@ -187,7 +187,7 @@ public:
bool CallHookChunkUnloaded (cWorld & a_World, int a_ChunkX, int a_ChunkZ);
bool CallHookChunkUnloading (cWorld & a_World, int a_ChunkX, int a_ChunkZ);
bool CallHookCollectingPickup (cPlayer & a_Player, cPickup & a_Pickup);
- bool CallHookCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe * a_Recipe);
+ bool CallHookCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe);
bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason);
bool CallHookEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier);
bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == nullptr, it is a console cmd
diff --git a/src/BlockEntities/BeaconEntity.cpp b/src/BlockEntities/BeaconEntity.cpp
index 85819446c..409f2937c 100644
--- a/src/BlockEntities/BeaconEntity.cpp
+++ b/src/BlockEntities/BeaconEntity.cpp
@@ -247,15 +247,16 @@ void cBeaconEntity::GiveEffects(void)
}
public:
- cPlayerCallback(int a_Radius, int a_PosX, int a_PosY, int a_PosZ, cEntityEffect::eType a_PrimaryEffect, cEntityEffect::eType a_SecondaryEffect, short a_EffectLevel)
- : m_Radius(a_Radius)
- , m_PosX(a_PosX)
- , m_PosY(a_PosY)
- , m_PosZ(a_PosZ)
- , m_PrimaryEffect(a_PrimaryEffect)
- , m_SecondaryEffect(a_SecondaryEffect)
- , m_EffectLevel(a_EffectLevel)
- {};
+ cPlayerCallback(int a_Radius, int a_PosX, int a_PosY, int a_PosZ, cEntityEffect::eType a_PrimaryEffect, cEntityEffect::eType a_SecondaryEffect, short a_EffectLevel):
+ m_Radius(a_Radius),
+ m_PosX(a_PosX),
+ m_PosY(a_PosY),
+ m_PosZ(a_PosZ),
+ m_PrimaryEffect(a_PrimaryEffect),
+ m_SecondaryEffect(a_SecondaryEffect),
+ m_EffectLevel(a_EffectLevel)
+ {
+ }
} PlayerCallback(Radius, m_PosX, m_PosY, m_PosZ, m_PrimaryEffect, SecondaryEffect, EffectLevel);
GetWorld()->ForEachPlayer(PlayerCallback);
diff --git a/src/BlockEntities/BeaconEntity.h b/src/BlockEntities/BeaconEntity.h
index d1db3a68f..bc27e92b0 100644
--- a/src/BlockEntities/BeaconEntity.h
+++ b/src/BlockEntities/BeaconEntity.h
@@ -1,3 +1,4 @@
+
// BeaconEntity.h
// Declares the cBeaconEntity class representing a single beacon in the world
@@ -14,15 +15,6 @@
-namespace Json
-{
- class Value;
-}
-
-
-
-
-
// tolua_begin
class cBeaconEntity :
public cBlockEntityWithItems
@@ -32,7 +24,7 @@ class cBeaconEntity :
public:
// tolua_end
- BLOCKENTITY_PROTODEF(cBeaconEntity);
+ BLOCKENTITY_PROTODEF(cBeaconEntity)
cBeaconEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
diff --git a/src/BlockEntities/BlockEntity.cpp b/src/BlockEntities/BlockEntity.cpp
index 3d96e891e..c59198e79 100644
--- a/src/BlockEntities/BlockEntity.cpp
+++ b/src/BlockEntities/BlockEntity.cpp
@@ -14,10 +14,11 @@
#include "FlowerPotEntity.h"
#include "FurnaceEntity.h"
#include "HopperEntity.h"
+#include "MobHeadEntity.h"
+#include "MobSpawnerEntity.h"
#include "JukeboxEntity.h"
#include "NoteEntity.h"
#include "SignEntity.h"
-#include "MobHeadEntity.h"
@@ -37,6 +38,7 @@ cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE
case E_BLOCK_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World);
case E_BLOCK_HEAD: return new cMobHeadEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_HOPPER: return new cHopperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_MOB_SPAWNER: return new cMobSpawnerEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_JUKEBOX: return new cJukeboxEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_LIT_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World);
case E_BLOCK_SIGN_POST: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World);
diff --git a/src/BlockEntities/BlockEntity.h b/src/BlockEntities/BlockEntity.h
index ffd6ee856..056a88721 100644
--- a/src/BlockEntities/BlockEntity.h
+++ b/src/BlockEntities/BlockEntity.h
@@ -28,11 +28,6 @@
-namespace Json
-{
- class Value;
-};
-
class cChunk;
class cPlayer;
class cWorld;
@@ -115,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(float a_Dt, cChunk & a_Chunk)
{
UNUSED(a_Dt);
return false;
diff --git a/src/BlockEntities/BlockEntityWithItems.h b/src/BlockEntities/BlockEntityWithItems.h
index 8c7d4749b..2c2ced1cb 100644
--- a/src/BlockEntities/BlockEntityWithItems.h
+++ b/src/BlockEntities/BlockEntityWithItems.h
@@ -29,7 +29,7 @@ class cBlockEntityWithItems :
public:
// tolua_end
- BLOCKENTITY_PROTODEF(cBlockEntityWithItems);
+ BLOCKENTITY_PROTODEF(cBlockEntityWithItems)
cBlockEntityWithItems(
BLOCKTYPE a_BlockType, // Type of the block that the entity represents
diff --git a/src/BlockEntities/CMakeLists.txt b/src/BlockEntities/CMakeLists.txt
index d87594b0d..5f4af288d 100644
--- a/src/BlockEntities/CMakeLists.txt
+++ b/src/BlockEntities/CMakeLists.txt
@@ -18,6 +18,7 @@ SET (SRCS
HopperEntity.cpp
JukeboxEntity.cpp
MobHeadEntity.cpp
+ MobSpawnerEntity.cpp
NoteEntity.cpp
SignEntity.cpp)
@@ -36,6 +37,7 @@ SET (HDRS
HopperEntity.h
JukeboxEntity.h
MobHeadEntity.h
+ MobSpawnerEntity.h
NoteEntity.h
SignEntity.h)
diff --git a/src/BlockEntities/ChestEntity.h b/src/BlockEntities/ChestEntity.h
index 09fffb923..645dbf4bc 100644
--- a/src/BlockEntities/ChestEntity.h
+++ b/src/BlockEntities/ChestEntity.h
@@ -7,11 +7,6 @@
-namespace Json
-{
- class Value;
-};
-
class cClientHandle;
@@ -33,7 +28,7 @@ public:
// tolua_end
- BLOCKENTITY_PROTODEF(cChestEntity);
+ BLOCKENTITY_PROTODEF(cChestEntity)
/** Constructor used for normal operation */
cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World, BLOCKTYPE a_Type);
diff --git a/src/BlockEntities/CommandBlockEntity.h b/src/BlockEntities/CommandBlockEntity.h
index 217390293..d8ac054f0 100644
--- a/src/BlockEntities/CommandBlockEntity.h
+++ b/src/BlockEntities/CommandBlockEntity.h
@@ -15,14 +15,6 @@
-namespace Json
-{
- class Value;
-}
-
-
-
-
// tolua_begin
@@ -36,7 +28,7 @@ public:
// tolua_end
- BLOCKENTITY_PROTODEF(cCommandBlockEntity);
+ BLOCKENTITY_PROTODEF(cCommandBlockEntity)
/// Creates a new empty command block entity
cCommandBlockEntity(int a_X, int a_Y, int a_Z, cWorld * a_World);
diff --git a/src/BlockEntities/DispenserEntity.h b/src/BlockEntities/DispenserEntity.h
index 5ba87b716..12e12942a 100644
--- a/src/BlockEntities/DispenserEntity.h
+++ b/src/BlockEntities/DispenserEntity.h
@@ -17,7 +17,7 @@ public:
// tolua_end
- BLOCKENTITY_PROTODEF(cDispenserEntity);
+ BLOCKENTITY_PROTODEF(cDispenserEntity)
/** Constructor used for normal operation */
cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
diff --git a/src/BlockEntities/DropSpenserEntity.h b/src/BlockEntities/DropSpenserEntity.h
index f77a28c28..6c23a402f 100644
--- a/src/BlockEntities/DropSpenserEntity.h
+++ b/src/BlockEntities/DropSpenserEntity.h
@@ -16,10 +16,6 @@
-namespace Json
-{
- class Value;
-}
class cClientHandle;
@@ -45,7 +41,7 @@ public:
// tolua_end
- BLOCKENTITY_PROTODEF(cDropSpenserEntity);
+ BLOCKENTITY_PROTODEF(cDropSpenserEntity)
cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
virtual ~cDropSpenserEntity();
diff --git a/src/BlockEntities/DropperEntity.h b/src/BlockEntities/DropperEntity.h
index 91adf660f..c638dafa8 100644
--- a/src/BlockEntities/DropperEntity.h
+++ b/src/BlockEntities/DropperEntity.h
@@ -25,7 +25,7 @@ public:
// tolua_end
- BLOCKENTITY_PROTODEF(cDropperEntity);
+ BLOCKENTITY_PROTODEF(cDropperEntity)
/// Constructor used for normal operation
cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
diff --git a/src/BlockEntities/EnderChestEntity.h b/src/BlockEntities/EnderChestEntity.h
index 17abd880a..af59cf170 100644
--- a/src/BlockEntities/EnderChestEntity.h
+++ b/src/BlockEntities/EnderChestEntity.h
@@ -18,7 +18,7 @@ class cEnderChestEntity :
public:
// tolua_end
- BLOCKENTITY_PROTODEF(cEnderChestEntity);
+ BLOCKENTITY_PROTODEF(cEnderChestEntity)
cEnderChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
virtual ~cEnderChestEntity();
diff --git a/src/BlockEntities/FlowerPotEntity.h b/src/BlockEntities/FlowerPotEntity.h
index fc886c51f..a4246bb7d 100644
--- a/src/BlockEntities/FlowerPotEntity.h
+++ b/src/BlockEntities/FlowerPotEntity.h
@@ -15,16 +15,6 @@
-
-namespace Json
-{
- class Value;
-}
-
-
-
-
-
// tolua_begin
class cFlowerPotEntity :
@@ -36,7 +26,7 @@ public:
// tolua_end
- BLOCKENTITY_PROTODEF(cFlowerPotEntity);
+ BLOCKENTITY_PROTODEF(cFlowerPotEntity)
/** Creates a new flowerpot entity at the specified block coords. a_World may be nullptr */
cFlowerPotEntity(int a_BlocX, int a_BlockY, int a_BlockZ, cWorld * a_World);
diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h
index 71c2fe127..fbe9d6c75 100644
--- a/src/BlockEntities/FurnaceEntity.h
+++ b/src/BlockEntities/FurnaceEntity.h
@@ -8,11 +8,6 @@
-namespace Json
-{
- class Value;
-}
-
class cClientHandle;
@@ -38,7 +33,7 @@ public:
// tolua_end
- BLOCKENTITY_PROTODEF(cFurnaceEntity);
+ BLOCKENTITY_PROTODEF(cFurnaceEntity)
/** Constructor used for normal operation */
cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cWorld * a_World);
diff --git a/src/BlockEntities/HopperEntity.h b/src/BlockEntities/HopperEntity.h
index 7070bbad3..da65aa671 100644
--- a/src/BlockEntities/HopperEntity.h
+++ b/src/BlockEntities/HopperEntity.h
@@ -31,7 +31,7 @@ public:
// tolua_end
- BLOCKENTITY_PROTODEF(cHopperEntity);
+ BLOCKENTITY_PROTODEF(cHopperEntity)
/// Constructor used for normal operation
cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
diff --git a/src/BlockEntities/JukeboxEntity.h b/src/BlockEntities/JukeboxEntity.h
index 7a69d6499..000f7d87e 100644
--- a/src/BlockEntities/JukeboxEntity.h
+++ b/src/BlockEntities/JukeboxEntity.h
@@ -7,15 +7,6 @@
-namespace Json
-{
- class Value;
-}
-
-
-
-
-
// tolua_begin
class cJukeboxEntity :
@@ -26,7 +17,7 @@ public:
// tolua_end
- BLOCKENTITY_PROTODEF(cJukeboxEntity);
+ BLOCKENTITY_PROTODEF(cJukeboxEntity)
cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
virtual ~cJukeboxEntity();
diff --git a/src/BlockEntities/MobHeadEntity.h b/src/BlockEntities/MobHeadEntity.h
index 7132ef558..7f08c5ab2 100644
--- a/src/BlockEntities/MobHeadEntity.h
+++ b/src/BlockEntities/MobHeadEntity.h
@@ -14,14 +14,6 @@
-namespace Json
-{
- class Value;
-}
-
-
-
-
// tolua_begin
@@ -34,29 +26,29 @@ public:
// tolua_end
- BLOCKENTITY_PROTODEF(cMobHeadEntity);
+ BLOCKENTITY_PROTODEF(cMobHeadEntity)
/** Creates a new mob head entity at the specified block coords. a_World may be nullptr */
cMobHeadEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
// tolua_begin
- /** Set the Type */
+ /** Set the type of the mob head */
void SetType(const eMobHeadType & a_SkullType);
- /** Set the Rotation */
+ /** Set the rotation of the mob head */
void SetRotation(eMobHeadRotation a_Rotation);
- /** Set the Player Name for Mobheads with Player type */
+ /** Set the player name for mob heads with player type */
void SetOwner(const AString & a_Owner);
- /** Get the Type */
+ /** Returns the type of the mob head */
eMobHeadType GetType(void) const { return m_Type; }
- /** Get the Rotation */
+ /** Returns the rotation of the mob head */
eMobHeadRotation GetRotation(void) const { return m_Rotation; }
- /** Get the setted Player Name */
+ /** Returns the player name of the mob head */
AString GetOwner(void) const { return m_Owner; }
// tolua_end
diff --git a/src/BlockEntities/MobSpawnerEntity.cpp b/src/BlockEntities/MobSpawnerEntity.cpp
new file mode 100644
index 000000000..5edee888a
--- /dev/null
+++ b/src/BlockEntities/MobSpawnerEntity.cpp
@@ -0,0 +1,290 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "MobSpawnerEntity.h"
+
+#include "../World.h"
+#include "../FastRandom.h"
+#include "../MobSpawner.h"
+#include "../Items/ItemSpawnEgg.h"
+
+
+
+
+
+cMobSpawnerEntity::cMobSpawnerEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World)
+ : super(E_BLOCK_MOB_SPAWNER, a_BlockX, a_BlockY, a_BlockZ, a_World)
+ , m_Entity(mtPig)
+ , m_SpawnDelay(100)
+ , m_IsActive(false)
+{
+}
+
+
+
+
+
+void cMobSpawnerEntity::SendTo(cClientHandle & a_Client)
+{
+ a_Client.SendUpdateBlockEntity(*this);
+}
+
+
+
+
+
+void cMobSpawnerEntity::UsedBy(cPlayer * a_Player)
+{
+ if (a_Player->GetEquippedItem().m_ItemType == E_ITEM_SPAWN_EGG)
+ {
+ eMonsterType MonsterType = cItemSpawnEggHandler::ItemDamageToMonsterType(a_Player->GetEquippedItem().m_ItemDamage);
+ if (MonsterType == eMonsterType::mtInvalidType)
+ {
+ return;
+ }
+
+ m_Entity = MonsterType;
+ ResetTimer();
+ if (!a_Player->IsGameModeCreative())
+ {
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ }
+ LOGD("Changed monster spawner at {%d, %d, %d} to type %s.", GetPosX(), GetPosY(), GetPosZ(), cMonster::MobTypeToString(MonsterType).c_str());
+ }
+}
+
+
+
+
+
+void cMobSpawnerEntity::UpdateActiveState(void)
+{
+ if (GetNearbyPlayersNum() > 0)
+ {
+ m_IsActive = true;
+ }
+ else
+ {
+ m_IsActive = false;
+ }
+}
+
+
+
+
+
+bool cMobSpawnerEntity::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ // Update the active flag every 5 seconds
+ if ((m_World->GetWorldAge() % 100) == 0)
+ {
+ UpdateActiveState();
+ }
+
+ if (!m_IsActive)
+ {
+ return false;
+ }
+
+ if (m_SpawnDelay <= 0)
+ {
+ SpawnEntity();
+ return true;
+ }
+ else
+ {
+ m_SpawnDelay--;
+ }
+ return false;
+}
+
+
+
+
+
+void cMobSpawnerEntity::ResetTimer(void)
+{
+ m_SpawnDelay = static_cast<short>(200 + m_World->GetTickRandomNumber(600));
+ m_World->BroadcastBlockEntity(m_PosX, m_PosY, m_PosZ);
+}
+
+
+
+
+
+void cMobSpawnerEntity::SpawnEntity(void)
+{
+ int NearbyEntities = GetNearbyMonsterNum(m_Entity);
+ if (NearbyEntities >= 6)
+ {
+ ResetTimer();
+ return;
+ }
+
+ class cCallback : public cChunkCallback
+ {
+ public:
+ cCallback(int a_RelX, int a_RelY, int a_RelZ, eMonsterType a_MobType, int a_NearbyEntitiesNum) :
+ m_RelX(a_RelX),
+ m_RelY(a_RelY),
+ m_RelZ(a_RelZ),
+ m_MobType(a_MobType),
+ m_NearbyEntitiesNum(a_NearbyEntitiesNum)
+ {
+ }
+
+ virtual bool Item(cChunk * a_Chunk)
+ {
+ cFastRandom Random;
+
+ bool EntitiesSpawned = false;
+ for (size_t i = 0; i < 4; i++)
+ {
+ if (m_NearbyEntitiesNum >= 6)
+ {
+ break;
+ }
+
+ int RelX = (int) (m_RelX + (double)(Random.NextFloat() - Random.NextFloat()) * 4.0);
+ int RelY = m_RelY + Random.NextInt(3) - 1;
+ int RelZ = (int) (m_RelZ + (double)(Random.NextFloat() - Random.NextFloat()) * 4.0);
+
+ cChunk * Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(RelX, RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ continue;
+ }
+ EMCSBiome Biome = Chunk->GetBiomeAt(RelX, RelZ);
+
+ if (cMobSpawner::CanSpawnHere(Chunk, RelX, RelY, RelZ, m_MobType, Biome))
+ {
+ double PosX = Chunk->GetPosX() * cChunkDef::Width + RelX;
+ double PosZ = Chunk->GetPosZ() * cChunkDef::Width + RelZ;
+
+ cMonster * Monster = cMonster::NewMonsterFromType(m_MobType);
+ if (Monster == NULL)
+ {
+ continue;
+ }
+
+ Monster->SetPosition(PosX, RelY, PosZ);
+ Monster->SetYaw(Random.NextFloat() * 360.0f);
+ if (Chunk->GetWorld()->SpawnMobFinalize(Monster) != mtInvalidType)
+ {
+ EntitiesSpawned = true;
+ Chunk->BroadcastSoundParticleEffect(2004, (int)(PosX * 8.0), (int)(RelY * 8.0), (int)(PosZ * 8.0), 0);
+ m_NearbyEntitiesNum++;
+ }
+ }
+ }
+ return EntitiesSpawned;
+ }
+ protected:
+ int m_RelX, m_RelY, m_RelZ;
+ eMonsterType m_MobType;
+ int m_NearbyEntitiesNum;
+ } Callback(m_RelX, m_PosY, m_RelZ, m_Entity, NearbyEntities);
+
+ if (m_World->DoWithChunk(GetChunkX(), GetChunkZ(), Callback))
+ {
+ ResetTimer();
+ }
+}
+
+
+
+
+
+int cMobSpawnerEntity::GetNearbyPlayersNum(void)
+{
+ Vector3d SpawnerPos(m_PosX + 0.5, m_PosY + 0.5, m_PosZ + 0.5);
+ int NumPlayers = 0;
+
+ class cCallback : public cChunkDataCallback
+ {
+ public:
+ cCallback(Vector3d a_SpawnerPos, int & a_NumPlayers) :
+ m_SpawnerPos(a_SpawnerPos),
+ m_NumPlayers(a_NumPlayers)
+ {
+ }
+
+ virtual void Entity(cEntity * a_Entity) override
+ {
+ if (!a_Entity->IsPlayer())
+ {
+ return;
+ }
+
+ if ((m_SpawnerPos - a_Entity->GetPosition()).Length() <= 16)
+ {
+ m_NumPlayers++;
+ }
+ }
+
+ protected:
+ Vector3d m_SpawnerPos;
+ int & m_NumPlayers;
+ } Callback(SpawnerPos, NumPlayers);
+
+ int ChunkX = GetChunkX();
+ int ChunkZ = GetChunkZ();
+ m_World->ForEachChunkInRect(ChunkX - 1, ChunkX + 1, ChunkZ - 1, ChunkZ + 1, Callback);
+
+ return NumPlayers;
+}
+
+
+
+
+
+int cMobSpawnerEntity::GetNearbyMonsterNum(eMonsterType a_EntityType)
+{
+ Vector3d SpawnerPos(m_PosX + 0.5, m_PosY + 0.5, m_PosZ + 0.5);
+ int NumEntities = 0;
+
+ class cCallback : public cChunkDataCallback
+ {
+ public:
+ cCallback(Vector3d a_SpawnerPos, eMonsterType a_EntityType, int & a_NumEntities) :
+ m_SpawnerPos(a_SpawnerPos),
+ m_EntityType(a_EntityType),
+ m_NumEntities(a_NumEntities)
+ {
+ }
+
+ virtual void Entity(cEntity * a_Entity) override
+ {
+ if (!a_Entity->IsMob())
+ {
+ return;
+ }
+
+ cMonster * Mob = (cMonster *)a_Entity;
+ if (Mob->GetMobType() != m_EntityType)
+ {
+ return;
+ }
+
+ if ((Diff(m_SpawnerPos.x, a_Entity->GetPosX()) <= 8.0) && (Diff(m_SpawnerPos.y, a_Entity->GetPosY()) <= 4.0) && (Diff(m_SpawnerPos.z, a_Entity->GetPosZ()) <= 8.0))
+ {
+ m_NumEntities++;
+ }
+ }
+
+ protected:
+ Vector3d m_SpawnerPos;
+ eMonsterType m_EntityType;
+ int & m_NumEntities;
+ } Callback(SpawnerPos, a_EntityType, NumEntities);
+
+ int ChunkX = GetChunkX();
+ int ChunkZ = GetChunkZ();
+ m_World->ForEachChunkInRect(ChunkX - 1, ChunkX + 1, ChunkZ - 1, ChunkZ + 1, Callback);
+
+ return NumEntities;
+}
+
+
+
+
diff --git a/src/BlockEntities/MobSpawnerEntity.h b/src/BlockEntities/MobSpawnerEntity.h
new file mode 100644
index 000000000..594b5301e
--- /dev/null
+++ b/src/BlockEntities/MobSpawnerEntity.h
@@ -0,0 +1,78 @@
+// MobSpawnerEntity.h
+
+// Declares the cMobSpawnerEntity class representing a single mob spawner in the world
+
+
+
+
+
+#pragma once
+
+#include "BlockEntity.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+// tolua_begin
+
+class cMobSpawnerEntity :
+ public cBlockEntity
+{
+ typedef cBlockEntity super;
+public:
+
+ // tolua_end
+
+ cMobSpawnerEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
+
+ virtual void SendTo(cClientHandle & a_Client) override;
+ virtual void UsedBy(cPlayer * a_Player) override;
+ virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
+
+ // tolua_begin
+
+ /** Upate the active flag from the mob spawner. This function will called every 5 seconds from the Tick() function. */
+ void UpdateActiveState(void);
+
+ /** Sets the spawn delay to a new random value. */
+ void ResetTimer(void);
+
+ /** Spawns the entity. This function automaticly change the spawn delay! */
+ void SpawnEntity(void);
+
+ /** Returns the entity type that will be spawn by this mob spawner. */
+ eMonsterType GetEntity(void) const { return m_Entity; }
+
+ /** Sets the entity type who will be spawn by this mob spawner. */
+ void SetEntity(eMonsterType a_EntityType) { m_Entity = a_EntityType; }
+
+ /** Returns the spawn delay. This is the tick delay that is needed to spawn new monsters. */
+ short GetSpawnDelay(void) const { return m_SpawnDelay; }
+
+ /** Sets the spawn delay. */
+ void SetSpawnDelay(short a_Delay) { m_SpawnDelay = a_Delay; }
+
+ /** Returns the amount of the nearby players in a 16-block radius. */
+ int GetNearbyPlayersNum(void);
+
+ /** Returns the amount of this monster type in a 8-block radius (Y: 4-block radius). */
+ int GetNearbyMonsterNum(eMonsterType a_EntityType);
+
+ // tolua_end
+
+ static const char * GetClassStatic(void) { return "cMobSpawnerEntity"; }
+
+private:
+ /** The entity to spawn. */
+ eMonsterType m_Entity;
+
+ short m_SpawnDelay;
+
+ bool m_IsActive;
+} ; // tolua_end
+
+
+
+
diff --git a/src/BlockEntities/NoteEntity.h b/src/BlockEntities/NoteEntity.h
index fc5f27d07..d3f85e9d2 100644
--- a/src/BlockEntities/NoteEntity.h
+++ b/src/BlockEntities/NoteEntity.h
@@ -5,12 +5,6 @@
#include "RedstonePoweredEntity.h"
-namespace Json
-{
- class Value;
-}
-
-
@@ -40,7 +34,7 @@ public:
// tolua_end
- BLOCKENTITY_PROTODEF(cNoteEntity);
+ BLOCKENTITY_PROTODEF(cNoteEntity)
/// Creates a new note entity. a_World may be nullptr
cNoteEntity(int a_X, int a_Y, int a_Z, cWorld * a_World);
diff --git a/src/BlockEntities/SignEntity.h b/src/BlockEntities/SignEntity.h
index 52baa486d..9480537ed 100644
--- a/src/BlockEntities/SignEntity.h
+++ b/src/BlockEntities/SignEntity.h
@@ -14,15 +14,6 @@
-namespace Json
-{
- class Value;
-}
-
-
-
-
-
// tolua_begin
class cSignEntity :
@@ -34,7 +25,7 @@ public:
// tolua_end
- BLOCKENTITY_PROTODEF(cSignEntity);
+ BLOCKENTITY_PROTODEF(cSignEntity)
/// Creates a new empty sign entity at the specified block coords and block type (wall or standing). a_World may be nullptr
cSignEntity(BLOCKTYPE a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World);
diff --git a/src/BlockID.cpp b/src/BlockID.cpp
index c98e0cad1..06f4232d3 100644
--- a/src/BlockID.cpp
+++ b/src/BlockID.cpp
@@ -200,7 +200,7 @@ public:
-BLOCKTYPE BlockStringToType(const AString & a_BlockTypeString)
+int BlockStringToType(const AString & a_BlockTypeString)
{
int res = atoi(a_BlockTypeString.c_str());
if ((res != 0) || (a_BlockTypeString.compare("0") == 0))
diff --git a/src/BlockID.h b/src/BlockID.h
index 24de2dc8a..8f2cee02e 100644
--- a/src/BlockID.h
+++ b/src/BlockID.h
@@ -1096,7 +1096,7 @@ class cIniFile;
// tolua_begin
/// Translates a blocktype string into blocktype. Takes either a number or an items.ini alias as input. Returns -1 on failure.
-extern BLOCKTYPE BlockStringToType(const AString & a_BlockTypeString);
+extern int BlockStringToType(const AString & a_BlockTypeString);
/// Translates an itemtype string into an item. Takes either a number, number^number, number:number or an items.ini alias as input. Returns true if successful.
extern bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item);
diff --git a/src/Blocks/BlockBed.h b/src/Blocks/BlockBed.h
index 9fd45644b..a8b5be899 100644
--- a/src/Blocks/BlockBed.h
+++ b/src/Blocks/BlockBed.h
@@ -52,7 +52,10 @@ public:
static NIBBLETYPE RotationToMetaData(double a_Rotation)
{
a_Rotation += 180 + (180 / 4); // So its not aligned with axis
- if (a_Rotation > 360) a_Rotation -= 360;
+ if (a_Rotation > 360)
+ {
+ a_Rotation -= 360;
+ }
a_Rotation = (a_Rotation / 360) * 4;
diff --git a/src/Blocks/BlockDoor.cpp b/src/Blocks/BlockDoor.cpp
index 96345a2df..90b7b15c2 100644
--- a/src/Blocks/BlockDoor.cpp
+++ b/src/Blocks/BlockDoor.cpp
@@ -145,7 +145,10 @@ NIBBLETYPE cBlockDoorHandler::MetaMirrorXY(NIBBLETYPE a_Meta)
// in only the bottom tile while the hinge position is in the top tile. This function only operates on one tile at a time,
// so the function can only see either the hinge position or orientation, but not both, at any given time. The class itself
// needs extra datamembers.
- if (a_Meta & 0x08) return a_Meta;
+ if (a_Meta & 0x08)
+ {
+ return a_Meta;
+ }
// Holds open/closed meta data. 0x0C == 1100.
NIBBLETYPE OtherMeta = a_Meta & 0x0C;
@@ -173,7 +176,10 @@ NIBBLETYPE cBlockDoorHandler::MetaMirrorYZ(NIBBLETYPE a_Meta)
// so the function can only see either the hinge position or orientation, but not both, at any given time.The class itself
// needs extra datamembers.
- if (a_Meta & 0x08) return a_Meta;
+ if (a_Meta & 0x08)
+ {
+ return a_Meta;
+ }
// Holds open/closed meta data. 0x0C == 1100.
NIBBLETYPE OtherMeta = a_Meta & 0x0C;
diff --git a/src/Blocks/BlockMobSpawner.h b/src/Blocks/BlockMobSpawner.h
index a51fbaafc..d5e7c273f 100644
--- a/src/Blocks/BlockMobSpawner.h
+++ b/src/Blocks/BlockMobSpawner.h
@@ -19,6 +19,18 @@ public:
}
+ virtual void OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override
+ {
+ a_ChunkInterface.UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ);
+ }
+
+
+ virtual bool IsUseable() override
+ {
+ return true;
+ }
+
+
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
// No pickups
diff --git a/src/Blocks/BlockRail.h b/src/Blocks/BlockRail.h
index 87ce069ab..21a34d8ce 100644
--- a/src/Blocks/BlockRail.h
+++ b/src/Blocks/BlockRail.h
@@ -151,13 +151,21 @@ public:
Neighbors[6] = (IsUnstable(a_ChunkInterface, a_BlockX, a_BlockY + 1, a_BlockZ - 1) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_NONE));
Neighbors[7] = (IsUnstable(a_ChunkInterface, a_BlockX, a_BlockY + 1, a_BlockZ + 1) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_NONE));
if (IsUnstable(a_ChunkInterface, a_BlockX + 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_EAST))
+ {
Neighbors[0] = true;
+ }
if (IsUnstable(a_ChunkInterface, a_BlockX - 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_WEST))
+ {
Neighbors[1] = true;
+ }
if (IsUnstable(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ - 1) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_NORTH))
+ {
Neighbors[2] = true;
+ }
if (IsUnstable(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ + 1) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_SOUTH))
+ {
Neighbors[3] = true;
+ }
for (int i = 0; i < 8; i++)
{
if (Neighbors[i])
@@ -167,12 +175,30 @@ public:
}
if (RailsCnt == 1)
{
- if (Neighbors[7]) return E_META_RAIL_ASCEND_ZP;
- else if (Neighbors[6]) return E_META_RAIL_ASCEND_ZM;
- else if (Neighbors[5]) return E_META_RAIL_ASCEND_XM;
- else if (Neighbors[4]) return E_META_RAIL_ASCEND_XP;
- else if (Neighbors[0] || Neighbors[1]) return E_META_RAIL_XM_XP;
- else if (Neighbors[2] || Neighbors[3]) return E_META_RAIL_ZM_ZP;
+ if (Neighbors[7])
+ {
+ return E_META_RAIL_ASCEND_ZP;
+ }
+ else if (Neighbors[6])
+ {
+ return E_META_RAIL_ASCEND_ZM;
+ }
+ else if (Neighbors[5])
+ {
+ return E_META_RAIL_ASCEND_XM;
+ }
+ else if (Neighbors[4])
+ {
+ return E_META_RAIL_ASCEND_XP;
+ }
+ else if (Neighbors[0] || Neighbors[1])
+ {
+ return E_META_RAIL_XM_XP;
+ }
+ else if (Neighbors[2] || Neighbors[3])
+ {
+ return E_META_RAIL_ZM_ZP;
+ }
ASSERT(!"Weird neighbor count");
return Meta;
}
@@ -185,16 +211,46 @@ public:
}
if (RailsCnt > 1)
{
- if (Neighbors[3] && Neighbors[0] && CanThisRailCurve()) return E_META_RAIL_CURVED_ZP_XP;
- else if (Neighbors[3] && Neighbors[1] && CanThisRailCurve()) return E_META_RAIL_CURVED_ZP_XM;
- else if (Neighbors[2] && Neighbors[0] && CanThisRailCurve()) return E_META_RAIL_CURVED_ZM_XP;
- else if (Neighbors[2] && Neighbors[1] && CanThisRailCurve()) return E_META_RAIL_CURVED_ZM_XM;
- else if (Neighbors[7] && Neighbors[2]) return E_META_RAIL_ASCEND_ZP;
- else if (Neighbors[3] && Neighbors[6]) return E_META_RAIL_ASCEND_ZM;
- else if (Neighbors[5] && Neighbors[0]) return E_META_RAIL_ASCEND_XM;
- else if (Neighbors[4] && Neighbors[1]) return E_META_RAIL_ASCEND_XP;
- else if (Neighbors[0] && Neighbors[1]) return E_META_RAIL_XM_XP;
- else if (Neighbors[2] && Neighbors[3]) return E_META_RAIL_ZM_ZP;
+ if (Neighbors[3] && Neighbors[0] && CanThisRailCurve())
+ {
+ return E_META_RAIL_CURVED_ZP_XP;
+ }
+ else if (Neighbors[3] && Neighbors[1] && CanThisRailCurve())
+ {
+ return E_META_RAIL_CURVED_ZP_XM;
+ }
+ else if (Neighbors[2] && Neighbors[0] && CanThisRailCurve())
+ {
+ return E_META_RAIL_CURVED_ZM_XP;
+ }
+ else if (Neighbors[2] && Neighbors[1] && CanThisRailCurve())
+ {
+ return E_META_RAIL_CURVED_ZM_XM;
+ }
+ else if (Neighbors[7] && Neighbors[2])
+ {
+ return E_META_RAIL_ASCEND_ZP;
+ }
+ else if (Neighbors[3] && Neighbors[6])
+ {
+ return E_META_RAIL_ASCEND_ZM;
+ }
+ else if (Neighbors[5] && Neighbors[0])
+ {
+ return E_META_RAIL_ASCEND_XM;
+ }
+ else if (Neighbors[4] && Neighbors[1])
+ {
+ return E_META_RAIL_ASCEND_XP;
+ }
+ else if (Neighbors[0] && Neighbors[1])
+ {
+ return E_META_RAIL_XM_XP;
+ }
+ else if (Neighbors[2] && Neighbors[3])
+ {
+ return E_META_RAIL_ZM_ZP;
+ }
if (CanThisRailCurve())
{
diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp
index 012105ca1..080176dcd 100644
--- a/src/ByteBuffer.cpp
+++ b/src/ByteBuffer.cpp
@@ -111,26 +111,36 @@ public:
#ifdef _DEBUG
-/// Simple RAII class that uses one internal unsigned long for checking if two threads are using an object simultanously
-class cSingleThreadAccessChecker
-{
-public:
- cSingleThreadAccessChecker(unsigned long * a_ThreadID) :
- m_ThreadID(a_ThreadID)
+ /** 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.
+ It uses that storage to check if the thread ID of consecutive calls is the same all the time. */
+ class cSingleThreadAccessChecker
{
- ASSERT((*a_ThreadID == 0) || (*a_ThreadID == cIsThread::GetCurrentID()));
- }
-
- ~cSingleThreadAccessChecker()
- {
- *m_ThreadID = 0;
- }
-
-protected:
- unsigned long * m_ThreadID;
-} ;
+ public:
+ cSingleThreadAccessChecker(std::thread::id * a_ThreadID) :
+ m_ThreadID(a_ThreadID)
+ {
+ 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
+ );
+
+ // Mark as being used by this thread:
+ *m_ThreadID = std::this_thread::get_id();
+ }
+
+ ~cSingleThreadAccessChecker()
+ {
+ // Mark as not being used by any thread:
+ *m_ThreadID = std::thread::id();
+ }
+
+ protected:
+ /** Points to the storage used for ID of the thread using the object. */
+ std::thread::id * m_ThreadID;
+ };
-#define CHECK_THREAD cSingleThreadAccessChecker Checker(const_cast<unsigned long *>(&m_ThreadID))
+ #define CHECK_THREAD cSingleThreadAccessChecker Checker(&m_ThreadID);
#else
#define CHECK_THREAD
@@ -146,9 +156,6 @@ protected:
cByteBuffer::cByteBuffer(size_t a_BufferSize) :
m_Buffer(new char[a_BufferSize + 1]),
m_BufferSize(a_BufferSize + 1),
- #ifdef _DEBUG
- m_ThreadID(0),
- #endif // _DEBUG
m_DataStart(0),
m_WritePos(0),
m_ReadPos(0)
@@ -174,7 +181,7 @@ cByteBuffer::~cByteBuffer()
bool cByteBuffer::Write(const void * a_Bytes, size_t a_Count)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
// Store the current free space for a check after writing:
@@ -221,7 +228,7 @@ bool cByteBuffer::Write(const void * a_Bytes, size_t a_Count)
size_t cByteBuffer::GetFreeSpace(void) const
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
if (m_WritePos >= m_DataStart)
{
@@ -243,7 +250,7 @@ size_t cByteBuffer::GetFreeSpace(void) const
/// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes()
size_t cByteBuffer::GetUsedSpace(void) const
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
ASSERT(m_BufferSize >= GetFreeSpace());
ASSERT((m_BufferSize - GetFreeSpace()) >= 1);
@@ -257,7 +264,7 @@ size_t cByteBuffer::GetUsedSpace(void) const
/// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already)
size_t cByteBuffer::GetReadableSpace(void) const
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
if (m_ReadPos > m_WritePos)
{
@@ -276,7 +283,7 @@ size_t cByteBuffer::GetReadableSpace(void) const
bool cByteBuffer::CanReadBytes(size_t a_Count) const
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
return (a_Count <= GetReadableSpace());
}
@@ -287,7 +294,7 @@ bool cByteBuffer::CanReadBytes(size_t a_Count) const
bool cByteBuffer::CanWriteBytes(size_t a_Count) const
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
return (a_Count <= GetFreeSpace());
}
@@ -298,7 +305,7 @@ bool cByteBuffer::CanWriteBytes(size_t a_Count) const
bool cByteBuffer::ReadChar(char & a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
NEEDBYTES(1);
ReadBuf(&a_Value, 1);
@@ -311,7 +318,7 @@ bool cByteBuffer::ReadChar(char & a_Value)
bool cByteBuffer::ReadByte(unsigned char & a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
NEEDBYTES(1);
ReadBuf(&a_Value, 1);
@@ -324,7 +331,7 @@ bool cByteBuffer::ReadByte(unsigned char & a_Value)
bool cByteBuffer::ReadBEShort(short & a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
NEEDBYTES(2);
ReadBuf(&a_Value, 2);
@@ -338,7 +345,7 @@ bool cByteBuffer::ReadBEShort(short & a_Value)
bool cByteBuffer::ReadBEInt(int & a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
NEEDBYTES(4);
ReadBuf(&a_Value, 4);
@@ -352,7 +359,7 @@ bool cByteBuffer::ReadBEInt(int & a_Value)
bool cByteBuffer::ReadBEInt64(Int64 & a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
NEEDBYTES(8);
ReadBuf(&a_Value, 8);
@@ -366,7 +373,7 @@ bool cByteBuffer::ReadBEInt64(Int64 & a_Value)
bool cByteBuffer::ReadBEFloat(float & a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
NEEDBYTES(4);
ReadBuf(&a_Value, 4);
@@ -380,7 +387,7 @@ bool cByteBuffer::ReadBEFloat(float & a_Value)
bool cByteBuffer::ReadBEDouble(double & a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
NEEDBYTES(8);
ReadBuf(&a_Value, 8);
@@ -394,7 +401,7 @@ bool cByteBuffer::ReadBEDouble(double & a_Value)
bool cByteBuffer::ReadBool(bool & a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
NEEDBYTES(1);
char Value = 0;
@@ -409,7 +416,7 @@ bool cByteBuffer::ReadBool(bool & a_Value)
bool cByteBuffer::ReadBEUTF16String16(AString & a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
short Length;
if (!ReadBEShort(Length))
@@ -430,7 +437,7 @@ bool cByteBuffer::ReadBEUTF16String16(AString & a_Value)
bool cByteBuffer::ReadVarInt(UInt32 & a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
UInt32 Value = 0;
int Shift = 0;
@@ -452,7 +459,7 @@ bool cByteBuffer::ReadVarInt(UInt32 & a_Value)
bool cByteBuffer::ReadVarUTF8String(AString & a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
UInt32 Size = 0;
if (!ReadVarInt(Size))
@@ -472,7 +479,7 @@ bool cByteBuffer::ReadVarUTF8String(AString & a_Value)
bool cByteBuffer::ReadLEInt(int & a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
NEEDBYTES(4);
ReadBuf(&a_Value, 4);
@@ -491,6 +498,7 @@ bool cByteBuffer::ReadLEInt(int & a_Value)
bool cByteBuffer::ReadPosition(int & a_BlockX, int & a_BlockY, int & a_BlockZ)
{
+ CHECK_THREAD
Int64 Value;
if (!ReadBEInt64(Value))
{
@@ -515,7 +523,7 @@ bool cByteBuffer::ReadPosition(int & a_BlockX, int & a_BlockY, int & a_BlockZ)
bool cByteBuffer::WriteChar(char a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
PUTBYTES(1);
return WriteBuf(&a_Value, 1);
@@ -527,7 +535,7 @@ bool cByteBuffer::WriteChar(char a_Value)
bool cByteBuffer::WriteByte(unsigned char a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
PUTBYTES(1);
return WriteBuf(&a_Value, 1);
@@ -539,7 +547,7 @@ bool cByteBuffer::WriteByte(unsigned char a_Value)
bool cByteBuffer::WriteBEShort(short a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
PUTBYTES(2);
u_short Converted = htons((u_short)a_Value);
@@ -552,7 +560,7 @@ bool cByteBuffer::WriteBEShort(short a_Value)
bool cByteBuffer::WriteBEUShort(unsigned short a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
PUTBYTES(2);
u_short Converted = htons((u_short)a_Value);
@@ -565,7 +573,7 @@ bool cByteBuffer::WriteBEUShort(unsigned short a_Value)
bool cByteBuffer::WriteBEInt(int a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
PUTBYTES(4);
UInt32 Converted = HostToNetwork4(&a_Value);
@@ -578,7 +586,7 @@ bool cByteBuffer::WriteBEInt(int a_Value)
bool cByteBuffer::WriteBEInt64(Int64 a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
PUTBYTES(8);
UInt64 Converted = HostToNetwork8(&a_Value);
@@ -591,7 +599,7 @@ bool cByteBuffer::WriteBEInt64(Int64 a_Value)
bool cByteBuffer::WriteBEFloat(float a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
PUTBYTES(4);
UInt32 Converted = HostToNetwork4(&a_Value);
@@ -604,7 +612,7 @@ bool cByteBuffer::WriteBEFloat(float a_Value)
bool cByteBuffer::WriteBEDouble(double a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
PUTBYTES(8);
UInt64 Converted = HostToNetwork8(&a_Value);
@@ -618,7 +626,7 @@ bool cByteBuffer::WriteBEDouble(double a_Value)
bool cByteBuffer::WriteBool(bool a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
return WriteChar(a_Value ? 1 : 0);
}
@@ -629,7 +637,7 @@ bool cByteBuffer::WriteBool(bool a_Value)
bool cByteBuffer::WriteVarInt(UInt32 a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
// A 32-bit integer can be encoded by at most 5 bytes:
@@ -650,7 +658,7 @@ bool cByteBuffer::WriteVarInt(UInt32 a_Value)
bool cByteBuffer::WriteVarUTF8String(const AString & a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
PUTBYTES(a_Value.size() + 1); // This is a lower-bound on the bytes that will be actually written. Fail early.
bool res = WriteVarInt((UInt32)(a_Value.size()));
@@ -667,7 +675,7 @@ bool cByteBuffer::WriteVarUTF8String(const AString & a_Value)
bool cByteBuffer::WriteLEInt(int a_Value)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
#ifdef IS_LITTLE_ENDIAN
return WriteBuf((const char *)&a_Value, 4);
@@ -683,6 +691,7 @@ bool cByteBuffer::WriteLEInt(int a_Value)
bool cByteBuffer::WritePosition(int a_BlockX, int a_BlockY, int a_BlockZ)
{
+ CHECK_THREAD
return WriteBEInt64(((Int64)a_BlockX & 0x3FFFFFF) << 38 | ((Int64)a_BlockY & 0xFFF) << 26 | ((Int64)a_BlockZ & 0x3FFFFFF));
}
@@ -692,7 +701,7 @@ bool cByteBuffer::WritePosition(int a_BlockX, int a_BlockY, int a_BlockZ)
bool cByteBuffer::ReadBuf(void * a_Buffer, size_t a_Count)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
NEEDBYTES(a_Count);
char * Dst = (char *)a_Buffer; // So that we can do byte math
@@ -725,7 +734,7 @@ bool cByteBuffer::ReadBuf(void * a_Buffer, size_t a_Count)
bool cByteBuffer::WriteBuf(const void * a_Buffer, size_t a_Count)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
PUTBYTES(a_Count);
char * Src = (char *)a_Buffer; // So that we can do byte math
@@ -755,7 +764,7 @@ bool cByteBuffer::WriteBuf(const void * a_Buffer, size_t a_Count)
bool cByteBuffer::ReadString(AString & a_String, size_t a_Count)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
NEEDBYTES(a_Count);
a_String.clear();
@@ -790,7 +799,7 @@ bool cByteBuffer::ReadString(AString & a_String, size_t a_Count)
bool cByteBuffer::ReadUTF16String(AString & a_String, size_t a_NumChars)
{
// Reads 2 * a_NumChars bytes and interprets it as a UTF16 string, converting it into UTF8 string a_String
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
AString RawData;
if (!ReadString(RawData, a_NumChars * 2))
@@ -807,7 +816,7 @@ bool cByteBuffer::ReadUTF16String(AString & a_String, size_t a_NumChars)
bool cByteBuffer::SkipRead(size_t a_Count)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
if (!CanReadBytes(a_Count))
{
@@ -823,7 +832,7 @@ bool cByteBuffer::SkipRead(size_t a_Count)
void cByteBuffer::ReadAll(AString & a_Data)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
ReadString(a_Data, GetReadableSpace());
}
@@ -834,6 +843,7 @@ void cByteBuffer::ReadAll(AString & a_Data)
bool cByteBuffer::ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes)
{
+ CHECK_THREAD
if (!a_Dst.CanWriteBytes(a_NumBytes) || !CanReadBytes(a_NumBytes))
{
// There's not enough source bytes or space in the dest BB
@@ -858,7 +868,7 @@ bool cByteBuffer::ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes)
void cByteBuffer::CommitRead(void)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
m_DataStart = m_ReadPos;
}
@@ -869,7 +879,7 @@ void cByteBuffer::CommitRead(void)
void cByteBuffer::ResetRead(void)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
m_ReadPos = m_DataStart;
}
@@ -882,7 +892,7 @@ void cByteBuffer::ReadAgain(AString & a_Out)
{
// Return the data between m_DataStart and m_ReadPos (the data that has been read but not committed)
// Used by ProtoProxy to repeat communication twice, once for parsing and the other time for the remote party
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
size_t DataStart = m_DataStart;
if (m_ReadPos < m_DataStart)
@@ -902,7 +912,7 @@ void cByteBuffer::ReadAgain(AString & a_Out)
void cByteBuffer::AdvanceReadPos(size_t a_Count)
{
- CHECK_THREAD;
+ CHECK_THREAD
CheckValid();
m_ReadPos += a_Count;
if (m_ReadPos >= m_BufferSize)
diff --git a/src/ByteBuffer.h b/src/ByteBuffer.h
index 70de419f0..2a316fa32 100644
--- a/src/ByteBuffer.h
+++ b/src/ByteBuffer.h
@@ -130,13 +130,15 @@ protected:
char * m_Buffer;
size_t m_BufferSize; // Total size of the ringbuffer
- #ifdef _DEBUG
- volatile unsigned long m_ThreadID; // Thread that is currently accessing the object, checked via cSingleThreadAccessChecker
- #endif // _DEBUG
-
size_t m_DataStart; // Where the data starts in the ringbuffer
size_t m_WritePos; // Where the data ends in the ringbuffer
size_t m_ReadPos; // Where the next read will start in the ringbuffer
+
+ #ifdef _DEBUG
+ /** The ID of the thread currently accessing the object.
+ Used for checking that only one thread accesses the object at a time, via cSingleThreadAccessChecker. */
+ mutable std::thread::id m_ThreadID;
+ #endif
/** Advances the m_ReadPos by a_Count bytes */
void AdvanceReadPos(size_t a_Count);
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 096fa824d..997326cc7 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -9,6 +9,7 @@ include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/polarssl/include
set(FOLDERS
OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++ Bindings
WorldStorage Mobs Entities Simulator UI BlockEntities Generating/Prefabs
+ Noise
)
SET (SRCS
@@ -50,7 +51,6 @@ SET (SRCS
MobProximityCounter.cpp
MobSpawner.cpp
MonsterConfig.cpp
- Noise.cpp
ProbabDistrib.cpp
RankManager.cpp
RCONServer.cpp
@@ -65,7 +65,8 @@ SET (SRCS
VoronoiMap.cpp
WebAdmin.cpp
World.cpp
- main.cpp)
+ main.cpp
+)
SET (HDRS
AllocationPool.h
@@ -103,7 +104,6 @@ SET (HDRS
Inventory.h
Item.h
ItemGrid.h
- LeakFinder.h
LightingThread.h
LineBlockTracer.h
LinearInterpolation.h
@@ -113,14 +113,11 @@ SET (HDRS
Map.h
MapManager.h
Matrix4.h
- MemoryLeak.h
- MersenneTwister.h
MobCensus.h
MobFamilyCollecter.h
MobProximityCounter.h
MobSpawner.h
MonsterConfig.h
- Noise.h
ProbabDistrib.h
RankManager.h
RCONServer.h
@@ -128,7 +125,6 @@ SET (HDRS
Scoreboard.h
Server.h
SetChunkData.h
- StackWalker.h
Statistics.h
StringCompression.h
StringUtils.h
@@ -137,7 +133,8 @@ SET (HDRS
VoronoiMap.h
WebAdmin.h
World.h
- XMLParser.h)
+ XMLParser.h
+)
include_directories(".")
include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/sqlite")
@@ -175,6 +172,10 @@ if (NOT MSVC)
else ()
# MSVC-specific handling: Put all files into one project, separate by the folders:
+ # Add the MSVC-specific LeakFinder sources:
+ list (APPEND SRCS LeakFinder.cpp StackWalker.cpp)
+ list (APPEND HDRS LeakFinder.h StackWalker.h MemoryLeak.h)
+
source_group(Bindings FILES "Bindings/Bindings.cpp" "Bindings/Bindings.h")
# Add all subfolders as solution-folders:
@@ -224,7 +225,7 @@ else ()
Bindings/Bindings.cpp PROPERTIES COMPILE_FLAGS "/Yc\"string.h\" /Fp\"$(IntDir)/Bindings.pch\""
)
SET_SOURCE_FILES_PROPERTIES(
- "StackWalker.cpp LeakFinder.h" PROPERTIES COMPILE_FLAGS "/Yc\"Globals.h\""
+ "StackWalker.cpp LeakFinder.cpp" PROPERTIES COMPILE_FLAGS "/Yc\"Globals.h\""
)
list(APPEND SOURCE "Resources/MCServer.rc")
@@ -314,7 +315,7 @@ endif ()
if (NOT MSVC)
target_link_libraries(${EXECUTABLE}
- OSSupport HTTPServer Bindings Items Blocks
+ OSSupport HTTPServer Bindings Items Blocks Noise
Protocol Generating Generating_Prefabs WorldStorage
Mobs Entities Simulator UI BlockEntities PolarSSL++
)
diff --git a/src/CheckBasicStyle.lua b/src/CheckBasicStyle.lua
index 76ae8c325..0c7b05d6d 100644
--- a/src/CheckBasicStyle.lua
+++ b/src/CheckBasicStyle.lua
@@ -92,6 +92,25 @@ end
local g_ViolationPatterns =
{
+ -- Parenthesis around comparisons:
+ {"==[^)]+&&", "Add parenthesis around comparison"},
+ {"&&[^(]+==", "Add parenthesis around comparison"},
+ {"==[^)]+||", "Add parenthesis around comparison"},
+ {"||[^(]+==", "Add parenthesis around comparison"},
+ {"!=[^)]+&&", "Add parenthesis around comparison"},
+ {"&&[^(]+!=", "Add parenthesis around comparison"},
+ {"!=[^)]+||", "Add parenthesis around comparison"},
+ {"||[^(]+!=", "Add parenthesis around comparison"},
+ {"<[^)T][^)]*&&", "Add parenthesis around comparison"}, -- Must take special care of templates: "template <T> fn(Args && ...)"
+ {"&&[^(]+<", "Add parenthesis around comparison"},
+ {"<[^)T][^)]*||", "Add parenthesis around comparison"}, -- Must take special care of templates: "template <T> fn(Args && ...)"
+ {"||[^(]+<", "Add parenthesis around comparison"},
+ -- Cannot check ">" because of "obj->m_Flag &&". Check at least ">=":
+ {">=[^)]+&&", "Add parenthesis around comparison"},
+ {"&&[^(]+>=", "Add parenthesis around comparison"},
+ {">=[^)]+||", "Add parenthesis around comparison"},
+ {"||[^(]+>=", "Add parenthesis around comparison"},
+
-- Check against indenting using spaces:
{"^\t* +", "Indenting with a space"},
@@ -116,11 +135,11 @@ local g_ViolationPatterns =
-- Space after keywords:
{"[^_]if%(", "Needs a space after \"if\""},
- {"for%(", "Needs a space after \"for\""},
- {"while%(", "Needs a space after \"while\""},
- {"switch%(", "Needs a space after \"switch\""},
- {"catch%(", "Needs a space after \"catch\""},
- {"template<", "Needs a space after \"template\""},
+ {"%sfor%(", "Needs a space after \"for\""},
+ {"%swhile%(", "Needs a space after \"while\""},
+ {"%sswitch%(", "Needs a space after \"switch\""},
+ {"%scatch%(", "Needs a space after \"catch\""},
+ {"%stemplate<", "Needs a space after \"template\""},
-- No space after keyword's parenthesis:
{"[^%a#]if %( ", "Remove the space after \"(\""},
@@ -162,6 +181,7 @@ local function ProcessFile(a_FileName)
-- Ref.: http://stackoverflow.com/questions/10416869/iterate-over-possibly-empty-lines-in-a-way-that-matches-the-expectations-of-exis
local lineCounter = 1
local lastIndentLevel = 0
+ local isLastLineControl = false
all:gsub("\r\n", "\n") -- normalize CRLF into LF-only
string.gsub(all .. "\n", "[^\n]*\n", -- Iterate over each line, while preserving empty lines
function(a_Line)
@@ -198,6 +218,24 @@ local function ProcessFile(a_FileName)
end
lastIndentLevel = indentLevel
end
+
+ -- Check that control statements have braces on separate lines after them:
+ -- Note that if statements can be broken into multiple lines, in which case this test is not taken
+ if (isLastLineControl) then
+ if not(a_Line:find("^%s*{") or a_Line:find("^%s*#")) then
+ -- Not followed by a brace, not followed by a preprocessor
+ ReportViolation(a_FileName, lineCounter - 1, 1, 1, "Control statement needs a brace on separate line")
+ end
+ end
+ local lineWithSpace = " " .. a_Line
+ isLastLineControl =
+ lineWithSpace:find("^%s+if %b()") or
+ lineWithSpace:find("^%s+else if %b()") or
+ lineWithSpace:find("^%s+for %b()") or
+ lineWithSpace:find("^%s+switch %b()") or
+ lineWithSpace:find("^%s+else\n") or
+ lineWithSpace:find("^%s+else //") or
+ lineWithSpace:find("^%s+do %b()")
lineCounter = lineCounter + 1
end
@@ -227,6 +265,9 @@ end
+-- Remove buffering from stdout, so that the output appears immediately in IDEs:
+io.stdout:setvbuf("no")
+
-- Process all files in the AllFiles.lst file (generated by cmake):
for fnam in io.lines("AllFiles.lst") do
ProcessItem(fnam)
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index a0224322a..017ceda26 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -16,18 +16,18 @@
#include "BlockEntities/ChestEntity.h"
#include "BlockEntities/DispenserEntity.h"
#include "BlockEntities/DropperEntity.h"
+#include "BlockEntities/FlowerPotEntity.h"
#include "BlockEntities/FurnaceEntity.h"
#include "BlockEntities/HopperEntity.h"
#include "BlockEntities/JukeboxEntity.h"
+#include "BlockEntities/MobHeadEntity.h"
+#include "BlockEntities/MobSpawnerEntity.h"
#include "BlockEntities/NoteEntity.h"
#include "BlockEntities/SignEntity.h"
-#include "BlockEntities/MobHeadEntity.h"
-#include "BlockEntities/FlowerPotEntity.h"
#include "Entities/Pickup.h"
#include "Item.h"
-#include "Noise.h"
+#include "Noise/Noise.h"
#include "Root.h"
-#include "MersenneTwister.h"
#include "Entities/Player.h"
#include "BlockArea.h"
#include "Bindings/PluginManager.h"
@@ -1348,6 +1348,7 @@ void cChunk::CreateBlockEntities(void)
case E_BLOCK_NOTE_BLOCK:
case E_BLOCK_JUKEBOX:
case E_BLOCK_FLOWER_POT:
+ case E_BLOCK_MOB_SPAWNER:
{
if (!HasBlockEntityAt(x + m_PosX * Width, y, z + m_PosZ * Width))
{
@@ -1479,6 +1480,7 @@ void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType,
case E_BLOCK_NOTE_BLOCK:
case E_BLOCK_JUKEBOX:
case E_BLOCK_FLOWER_POT:
+ case E_BLOCK_MOB_SPAWNER:
{
AddBlockEntity(cBlockEntity::CreateByBlockType(a_BlockType, a_BlockMeta, WorldPos.x, WorldPos.y, WorldPos.z, m_World));
break;
@@ -1869,18 +1871,18 @@ bool cChunk::AddClient(cClientHandle * a_Client)
void cChunk::RemoveClient(cClientHandle * a_Client)
{
- for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)
+ for (cClientHandleList::iterator itrC = m_LoadedByClient.begin(); itrC != m_LoadedByClient.end(); ++itrC)
{
- if (*itr != a_Client)
+ if (*itrC != a_Client)
{
continue;
}
- m_LoadedByClient.erase(itr);
+ m_LoadedByClient.erase(itrC);
if (!a_Client->IsDestroyed())
{
- for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
+ for (cEntityList::iterator itrE = m_Entities.begin(); itrE != m_Entities.end(); ++itrE)
{
/*
// DEBUG:
@@ -1889,7 +1891,7 @@ void cChunk::RemoveClient(cClientHandle * a_Client)
(*itr)->GetUniqueID(), a_Client->GetUsername().c_str()
);
*/
- a_Client->SendDestroyEntity(*(*itr));
+ a_Client->SendDestroyEntity(*(*itrE));
}
}
return;
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 94bace43a..c4a620565 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -16,7 +16,6 @@
#include "Mobs/Monster.h"
#include "ChatColor.h"
#include "OSSupport/Socket.h"
-#include "OSSupport/Timer.h"
#include "Items/ItemHandler.h"
#include "Blocks/BlockHandler.h"
#include "Blocks/BlockSlab.h"
@@ -25,8 +24,6 @@
#include "Root.h"
#include "Protocol/Authenticator.h"
-#include "MersenneTwister.h"
-
#include "Protocol/ProtocolRecognizer.h"
#include "CompositeChat.h"
#include "Items/ItemSword.h"
@@ -41,18 +38,13 @@
/** Maximum number of block change interactions a player can perform per tick - exceeding this causes a kick */
#define MAX_BLOCK_CHANGE_INTERACTIONS 20
+/** The interval for sending pings to clients.
+Vanilla sends one ping every 1 second. */
+static const std::chrono::milliseconds PING_TIME_MS = std::chrono::milliseconds(1000);
-#define RECI_RAND_MAX (1.f/RAND_MAX)
-inline int fRadRand(MTRand & r1, int a_BlockCoord)
-{
- return a_BlockCoord * 32 + (int)(16 * ((float)r1.rand() * RECI_RAND_MAX) * 16 - 8);
-}
-
-
-
int cClientHandle::s_ClientCount = 0;
@@ -65,7 +57,8 @@ int cClientHandle::s_ClientCount = 0;
// cClientHandle:
cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) :
- m_ViewDistance(a_ViewDistance),
+ m_CurrentViewDistance(a_ViewDistance),
+ m_RequestedViewDistance(a_ViewDistance),
m_IPString(a_Socket->GetIPString()),
m_OutgoingData(64 KiB),
m_Player(nullptr),
@@ -75,8 +68,6 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) :
m_TimeSinceLastPacket(0),
m_Ping(1000),
m_PingID(1),
- m_PingStartTime(0),
- m_LastPingTime(1000),
m_BlockDigAnimStage(-1),
m_BlockDigAnimSpeed(0),
m_BlockDigAnimX(0),
@@ -93,15 +84,14 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) :
m_UniqueID(0),
m_HasSentPlayerChunk(false),
m_Locale("en_GB"),
+ m_LastPlacedSign(0, -1, 0),
m_ProtocolVersion(0)
{
m_Protocol = new cProtocolRecognizer(this);
s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread
m_UniqueID = s_ClientCount;
-
- cTimer t1;
- m_LastPingTime = t1.GetNowTime();
+ m_PingStartTime = std::chrono::steady_clock::now();
LOGD("New ClientHandle created at %p", this);
}
@@ -197,9 +187,13 @@ void cClientHandle::GenerateOfflineUUID(void)
AString cClientHandle::FormatChatPrefix(bool ShouldAppendChatPrefixes, AString a_ChatPrefixS, AString m_Color1, AString m_Color2)
{
if (ShouldAppendChatPrefixes)
+ {
return Printf("%s[%s] %s", m_Color1.c_str(), a_ChatPrefixS.c_str(), m_Color2.c_str());
+ }
else
+ {
return Printf("%s", m_Color1.c_str());
+ }
}
@@ -395,8 +389,7 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID,
// Delay the first ping until the client "settles down"
// This should fix #889, "BadCast exception, cannot convert bit to fm" error in client
- cTimer t1;
- m_LastPingTime = t1.GetNowTime() + 3000; // Send the first KeepAlive packet in 3 seconds
+ m_PingStartTime = std::chrono::steady_clock::now() + std::chrono::seconds(3); // Send the first KeepAlive packet in 3 seconds
cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player);
}
@@ -430,7 +423,7 @@ bool cClientHandle::StreamNextChunk(void)
cCSLock Lock(m_CSChunkLists);
// High priority: Load the chunks that are in the view-direction of the player (with a radius of 3)
- for (int Range = 0; Range < m_ViewDistance; Range++)
+ for (int Range = 0; Range < m_CurrentViewDistance; Range++)
{
Vector3d Vector = Position + LookVector * cChunkDef::Width * Range;
@@ -447,7 +440,7 @@ bool cClientHandle::StreamNextChunk(void)
cChunkCoords Coords(ChunkX, ChunkZ);
// Checks if the chunk is in distance
- if ((Diff(ChunkX, ChunkPosX) > m_ViewDistance) || (Diff(ChunkZ, ChunkPosZ) > m_ViewDistance))
+ if ((Diff(ChunkX, ChunkPosX) > m_CurrentViewDistance) || (Diff(ChunkZ, ChunkPosZ) > m_CurrentViewDistance))
{
continue;
}
@@ -470,7 +463,7 @@ bool cClientHandle::StreamNextChunk(void)
}
// Low priority: Add all chunks that are in range. (From the center out to the edge)
- for (int d = 0; d <= m_ViewDistance; ++d) // cycle through (square) distance, from nearest to furthest
+ for (int d = 0; d <= m_CurrentViewDistance; ++d) // cycle through (square) distance, from nearest to furthest
{
// For each distance add chunks in a hollow square centered around current position:
cChunkCoordsList CurcleChunks;
@@ -528,7 +521,7 @@ void cClientHandle::UnloadOutOfRangeChunks(void)
{
int DiffX = Diff((*itr).m_ChunkX, ChunkPosX);
int DiffZ = Diff((*itr).m_ChunkZ, ChunkPosZ);
- if ((DiffX > m_ViewDistance) || (DiffZ > m_ViewDistance))
+ if ((DiffX > m_CurrentViewDistance) || (DiffZ > m_CurrentViewDistance))
{
ChunksToRemove.push_back(*itr);
itr = m_LoadedChunks.erase(itr);
@@ -543,7 +536,7 @@ void cClientHandle::UnloadOutOfRangeChunks(void)
{
int DiffX = Diff((*itr).m_ChunkX, ChunkPosX);
int DiffZ = Diff((*itr).m_ChunkZ, ChunkPosZ);
- if ((DiffX > m_ViewDistance) || (DiffZ > m_ViewDistance))
+ if ((DiffX > m_CurrentViewDistance) || (DiffZ > m_CurrentViewDistance))
{
itr = m_ChunksToSend.erase(itr);
}
@@ -1236,12 +1229,18 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
{
// TODO: Rewrite this function
- LOGD("HandleRightClick: {%d, %d, %d}, face %d, HeldItem: %s",
- a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, ItemToFullString(a_HeldItem).c_str()
+ // Distance from the block's center to the player's eye height
+ double dist = (Vector3d(a_BlockX, a_BlockY, a_BlockZ) + Vector3d(0.5, 0.5, 0.5) - m_Player->GetEyePosition()).Length();
+ LOGD("HandleRightClick: {%d, %d, %d}, face %d, HeldItem: %s; dist: %.02f",
+ a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, ItemToFullString(a_HeldItem).c_str(), dist
);
-
+
+ // Check the reach distance:
+ // _X 2014-11-25: I've maxed at 5.26 with a Survival client and 5.78 with a Creative client in my tests
+ double maxDist = m_Player->IsGameModeCreative() ? 5.78 : 5.26;
+ bool AreRealCoords = (dist <= maxDist);
+
cWorld * World = m_Player->GetWorld();
- bool AreRealCoords = (Vector3d(a_BlockX, a_BlockY, a_BlockZ) - m_Player->GetPosition()).Length() <= 5;
if (
(a_BlockFace != BLOCK_FACE_NONE) && // The client is interacting with a specific block
@@ -1500,6 +1499,7 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, e
{
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);
@@ -1677,8 +1677,11 @@ void cClientHandle::HandleUpdateSign(
const AString & a_Line3, const AString & a_Line4
)
{
- cWorld * World = m_Player->GetWorld();
- World->UpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, m_Player);
+ if (m_LastPlacedSign.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
+ {
+ m_LastPlacedSign.Set(0, -1, 0);
+ m_Player->GetWorld()->SetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, m_Player);
+ }
}
@@ -1767,8 +1770,7 @@ void cClientHandle::HandleKeepAlive(int a_KeepAliveID)
{
if (a_KeepAliveID == m_PingID)
{
- cTimer t1;
- m_Ping = (short)((t1.GetNowTime() - m_PingStartTime) / 2);
+ m_Ping = std::chrono::steady_clock::now() - m_PingStartTime;
}
}
@@ -1993,13 +1995,11 @@ void cClientHandle::Tick(float a_Dt)
// Send a ping packet:
if (m_State == csPlaying)
{
- cTimer t1;
- if ((m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime()))
+ if ((m_PingStartTime + PING_TIME_MS <= std::chrono::steady_clock::now()))
{
m_PingID++;
- m_PingStartTime = t1.GetNowTime();
+ m_PingStartTime = std::chrono::steady_clock::now();
m_Protocol->SendKeepAlive(m_PingID);
- m_LastPingTime = m_PingStartTime;
}
}
@@ -2257,6 +2257,7 @@ void cClientHandle::SendDisconnect(const AString & a_Reason)
void cClientHandle::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
{
+ m_LastPlacedSign.Set(a_BlockX, a_BlockY, a_BlockZ);
m_Protocol->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
}
@@ -2847,8 +2848,15 @@ void cClientHandle::SetUsername( const AString & a_Username)
void cClientHandle::SetViewDistance(int a_ViewDistance)
{
- m_ViewDistance = Clamp(a_ViewDistance, MIN_VIEW_DISTANCE, MAX_VIEW_DISTANCE);
- LOGD("Setted %s's view distance to %i", GetUsername().c_str(), m_ViewDistance);
+ m_RequestedViewDistance = a_ViewDistance;
+ LOGD("%s is requesting ViewDistance of %d!", GetUsername().c_str(), m_RequestedViewDistance);
+
+ // Set the current view distance based on the requested VD and world max VD:
+ cWorld * world = m_Player->GetWorld();
+ if (world != nullptr)
+ {
+ m_CurrentViewDistance = Clamp(a_ViewDistance, cClientHandle::MIN_VIEW_DISTANCE, world->GetMaxViewDistance());
+ }
}
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index 082ed2fcc..495348ac3 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -215,11 +215,17 @@ public:
const AString & GetUsername(void) const;
void SetUsername( const AString & a_Username);
- inline short GetPing(void) const { return m_Ping; }
+ inline short GetPing(void) const { return static_cast<short>(std::chrono::duration_cast<std::chrono::milliseconds>(m_Ping).count()); }
+ /** Sets the maximal view distance. */
void SetViewDistance(int a_ViewDistance);
- int GetViewDistance(void) const { return m_ViewDistance; }
-
+
+ /** Returns the view distance that the player currently have. */
+ int GetViewDistance(void) const { return m_CurrentViewDistance; }
+
+ /** Returns the view distance that the player request, not the used view distance. */
+ int GetRequestedViewDistance(void) const { return m_RequestedViewDistance; }
+
void SetLocale(AString & a_Locale) { m_Locale = a_Locale; }
AString GetLocale(void) const { return m_Locale; }
@@ -333,12 +339,12 @@ private:
/** The type used for storing the names of registered plugin channels. */
typedef std::set<AString> cChannels;
- /** Number of chunks the player can see in each direction */
- int m_ViewDistance;
-
- /** Server generates this many chunks AHEAD of player sight. */
- static const int GENERATEDISTANCE = 2;
-
+ /** The actual view distance used, the minimum of client's requested view distance and world's max view distance. */
+ int m_CurrentViewDistance;
+
+ /** The requested view distance from the player. It isn't clamped with 1 and the max view distance of the world. */
+ int m_RequestedViewDistance;
+
AString m_IPString;
AString m_Username;
@@ -372,12 +378,15 @@ private:
/** Seconds since the last packet data was received (updated in Tick(), reset in DataReceived()) */
float m_TimeSinceLastPacket;
- short m_Ping;
- int m_PingID;
- long long m_PingStartTime;
- long long m_LastPingTime;
- static const unsigned short PING_TIME_MS = 1000; // Vanilla sends 1 per 20 ticks (1 second or every 1000 ms)
-
+ /** Duration of the last completed client ping. */
+ std::chrono::steady_clock::duration m_Ping;
+
+ /** ID of the last ping request sent to the client. */
+ int m_PingID;
+
+ /** Time of the last ping request sent to the client. */
+ std::chrono::steady_clock::time_point m_PingStartTime;
+
// Values required for block dig animation
int m_BlockDigAnimStage; // Current stage of the animation; -1 if not digging
int m_BlockDigAnimSpeed; // Current speed of the animation (units ???)
@@ -432,6 +441,9 @@ private:
/** Client Settings */
AString m_Locale;
+
+ /** The positions from the last sign that the player placed. It's needed to verify the sign text change. */
+ Vector3i m_LastPlacedSign;
/** The plugin channels that the client has registered. */
cChannels m_PluginChannels;
diff --git a/src/CraftingRecipes.cpp b/src/CraftingRecipes.cpp
index 64fb21181..202fb900e 100644
--- a/src/CraftingRecipes.cpp
+++ b/src/CraftingRecipes.cpp
@@ -289,12 +289,12 @@ void cCraftingRecipes::GetRecipe(cPlayer & a_Player, cCraftingGrid & a_CraftingG
}
// Built-in recipes:
- std::auto_ptr<cRecipe> Recipe(FindRecipe(a_CraftingGrid.GetItems(), a_CraftingGrid.GetWidth(), a_CraftingGrid.GetHeight()));
+ std::unique_ptr<cRecipe> Recipe(FindRecipe(a_CraftingGrid.GetItems(), a_CraftingGrid.GetWidth(), a_CraftingGrid.GetHeight()));
a_Recipe.Clear();
if (Recipe.get() == nullptr)
{
// Allow plugins to intercept a no-recipe-found situation:
- cRoot::Get()->GetPluginManager()->CallHookCraftingNoRecipe(a_Player, a_CraftingGrid, &a_Recipe);
+ cRoot::Get()->GetPluginManager()->CallHookCraftingNoRecipe(a_Player, a_CraftingGrid, a_Recipe);
return;
}
for (cRecipeSlots::const_iterator itr = Recipe->m_Ingredients.begin(); itr != Recipe->m_Ingredients.end(); ++itr)
@@ -377,7 +377,7 @@ void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine
return;
}
- std::auto_ptr<cCraftingRecipes::cRecipe> Recipe(new cCraftingRecipes::cRecipe);
+ std::unique_ptr<cCraftingRecipes::cRecipe> Recipe(new cCraftingRecipes::cRecipe);
// Parse the result:
AStringVector ResultSplit = StringSplit(Sides[0], ",");
@@ -758,7 +758,7 @@ cCraftingRecipes::cRecipe * cCraftingRecipes::MatchRecipe(const cItem * a_Crafti
} // for y, for x
// The recipe has matched. Create a copy of the recipe and set its coords to match the crafting grid:
- std::auto_ptr<cRecipe> Recipe(new cRecipe);
+ std::unique_ptr<cRecipe> Recipe(new cRecipe);
Recipe->m_Result = a_Recipe->m_Result;
Recipe->m_Width = a_Recipe->m_Width;
Recipe->m_Height = a_Recipe->m_Height;
diff --git a/src/DeadlockDetect.cpp b/src/DeadlockDetect.cpp
index 7f703416c..3bb897221 100644
--- a/src/DeadlockDetect.cpp
+++ b/src/DeadlockDetect.cpp
@@ -13,7 +13,7 @@
-/// Number of milliseconds per cycle
+/** Number of milliseconds per cycle */
const int CYCLE_MILLISECONDS = 100;
@@ -87,7 +87,7 @@ void cDeadlockDetect::Execute(void)
} Checker(this);
cRoot::Get()->ForEachWorld(Checker);
- cSleep::MilliSleep(CYCLE_MILLISECONDS);
+ std::this_thread::sleep_for(std::chrono::milliseconds(CYCLE_MILLISECONDS));
} // while (should run)
}
@@ -119,7 +119,7 @@ void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age)
if (WorldAge.m_Age == a_Age)
{
WorldAge.m_NumCyclesSame += 1;
- if (WorldAge.m_NumCyclesSame > (1000 * m_IntervalSec) / CYCLE_MILLISECONDS)
+ if (WorldAge.m_NumCyclesSame > (m_IntervalSec * 1000) / CYCLE_MILLISECONDS)
{
DeadlockDetected();
}
diff --git a/src/DeadlockDetect.h b/src/DeadlockDetect.h
index 6aa98acbb..57027e923 100644
--- a/src/DeadlockDetect.h
+++ b/src/DeadlockDetect.h
@@ -28,7 +28,7 @@ class cDeadlockDetect :
public:
cDeadlockDetect(void);
- /// Starts the detection. Hides cIsThread's Start, because we need some initialization
+ /** Starts the detection. Hides cIsThread's Start, because we need some initialization */
bool Start(int a_IntervalSec);
protected:
diff --git a/src/Enchantments.cpp b/src/Enchantments.cpp
index 80a9810b6..36c451b81 100644
--- a/src/Enchantments.cpp
+++ b/src/Enchantments.cpp
@@ -6,7 +6,7 @@
#include "Enchantments.h"
#include "WorldStorage/FastNBT.h"
#include "FastRandom.h"
-#include "Noise.h"
+#include "Noise/Noise.h"
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index afc61c73f..54b9f2a20 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -665,7 +665,10 @@ int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_Dama
// Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover
// Filter out damage types that are not protected by armor:
- if (!ArmorCoversAgainst(a_DamageType)) return 0;
+ if (!ArmorCoversAgainst(a_DamageType))
+ {
+ return 0;
+ }
// Add up all armor points:
// Ref.: http://www.minecraftwiki.net/wiki/Armor#Defense_points as of 2012_12_20
@@ -1011,9 +1014,18 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
// Block hit was within our projected path
// Begin by stopping movement in the direction that we hit something. The Normal is the line perpendicular to a 2D face and in this case, stores what block face was hit through either -1 or 1.
// For example: HitNormal.y = -1 : BLOCK_FACE_YM; HitNormal.y = 1 : BLOCK_FACE_YP
- if (Tracer.HitNormal.x != 0.f) NextSpeed.x = 0.f;
- if (Tracer.HitNormal.y != 0.f) NextSpeed.y = 0.f;
- if (Tracer.HitNormal.z != 0.f) NextSpeed.z = 0.f;
+ if (Tracer.HitNormal.x != 0.f)
+ {
+ NextSpeed.x = 0.f;
+ }
+ if (Tracer.HitNormal.y != 0.f)
+ {
+ NextSpeed.y = 0.f;
+ }
+ if (Tracer.HitNormal.z != 0.f)
+ {
+ NextSpeed.z = 0.f;
+ }
if (Tracer.HitNormal.y == 1.f) // Hit BLOCK_FACE_YP, we are on the ground
{
@@ -1276,7 +1288,7 @@ bool cEntity::DetectPortal()
return false;
}
- if (IsPlayer() && !((cPlayer *)this)->IsGameModeCreative() && m_PortalCooldownData.m_TicksDelayed != 80)
+ if (IsPlayer() && !((cPlayer *)this)->IsGameModeCreative() && (m_PortalCooldownData.m_TicksDelayed != 80))
{
// Delay teleportation for four seconds if the entity is a non-creative player
m_PortalCooldownData.m_TicksDelayed++;
diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp
index 22e046800..fac4f0714 100644
--- a/src/Entities/Minecart.cpp
+++ b/src/Entities/Minecart.cpp
@@ -142,8 +142,13 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
if (!IsBlockRail(InsideType))
{
- Chunk->GetBlockTypeMeta(RelPosX, PosY + 1, RelPosZ, InsideType, InsideMeta); // When an descending minecart hits a flat rail, it goes through the ground; check for this
- if (IsBlockRail(InsideType)) AddPosY(1); // Push cart upwards
+ // When a descending minecart hits a flat rail, it goes through the ground; check for this
+ Chunk->GetBlockTypeMeta(RelPosX, PosY + 1, RelPosZ, InsideType, InsideMeta);
+ if (IsBlockRail(InsideType))
+ {
+ // Push cart upwards
+ AddPosY(1);
+ }
}
bool WasDetectorRail = false;
@@ -218,7 +223,10 @@ void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
// Execute both the entity and block collision checks
bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
- if (EntCol || BlckCol) return;
+ if (EntCol || BlckCol)
+ {
+ return;
+ }
if (GetSpeedZ() != NO_SPEED) // Don't do anything if cart is stationary
{
@@ -243,7 +251,10 @@ void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
SetSpeedZ(NO_SPEED);
bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
- if (EntCol || BlckCol) return;
+ if (EntCol || BlckCol)
+ {
+ return;
+ }
if (GetSpeedX() != NO_SPEED)
{
@@ -422,7 +433,10 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
SetSpeedX(0);
bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
- if (EntCol || BlckCol) return;
+ if (EntCol || BlckCol)
+ {
+ return;
+ }
if (GetSpeedZ() != NO_SPEED)
{
@@ -445,7 +459,10 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
SetSpeedZ(NO_SPEED);
bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
- if (EntCol || BlckCol) return;
+ if (EntCol || BlckCol)
+ {
+ return;
+ }
if (GetSpeedX() != NO_SPEED)
{
diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h
index 40e22c641..f7d0d5dda 100644
--- a/src/Entities/Minecart.h
+++ b/src/Entities/Minecart.h
@@ -128,7 +128,7 @@ public:
};
const cItem & GetSlot(int a_Idx) const { return m_Contents.GetSlot(a_Idx); }
- void SetSlot(size_t a_Idx, const cItem & a_Item) { m_Contents.SetSlot(a_Idx, a_Item); }
+ void SetSlot(size_t a_Idx, const cItem & a_Item) { m_Contents.SetSlot(static_cast<int>(a_Idx), a_Item); }
protected:
cItemGrid m_Contents;
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 5c18d8f96..8d2eb1e5f 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -11,7 +11,6 @@
#include "../BlockEntities/BlockEntity.h"
#include "../BlockEntities/EnderChestEntity.h"
#include "../Root.h"
-#include "../OSSupport/Timer.h"
#include "../Chunk.h"
#include "../Items/ItemHandler.h"
#include "../Vector3.h"
@@ -27,7 +26,7 @@
#define PLAYER_INVENTORY_SAVE_INTERVAL 6000
// 1000 = once per second
-#define PLAYER_LIST_TIME_MS 1000
+#define PLAYER_LIST_TIME_MS std::chrono::milliseconds(1000)
@@ -91,9 +90,7 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) :
SetMaxHealth(MAX_HEALTH);
m_Health = MAX_HEALTH;
- cTimer t1;
- m_LastPlayerListTime = t1.GetNowTime();
-
+ m_LastPlayerListTime = std::chrono::steady_clock::now();
m_PlayerName = a_PlayerName;
cWorld * World = nullptr;
@@ -120,6 +117,11 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) :
{
m_CanFly = true;
}
+ if (World->IsGameModeSpectator()) // Otherwise Player will fall out of the world on join
+ {
+ m_CanFly = true;
+ m_IsFlying = true;
+ }
}
cRoot::Get()->GetServer()->PlayerCreated(this);
@@ -263,11 +265,10 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
m_Inventory.UpdateItems();
// Send Player List (Once per m_LastPlayerListTime/1000 ms)
- cTimer t1;
- if (m_LastPlayerListTime + PLAYER_LIST_TIME_MS <= t1.GetNowTime())
+ if (m_LastPlayerListTime + PLAYER_LIST_TIME_MS <= std::chrono::steady_clock::now())
{
m_World->BroadcastPlayerListUpdatePing(*this);
- m_LastPlayerListTime = t1.GetNowTime();
+ m_LastPlayerListTime = std::chrono::steady_clock::now();
}
if (IsFlying())
@@ -358,7 +359,7 @@ float cPlayer::GetXpPercentage()
bool cPlayer::SetCurrentExperience(short int a_CurrentXp)
{
- if (!(a_CurrentXp >= 0) || (a_CurrentXp > (SHRT_MAX - m_LifetimeTotalXp)))
+ if (!(a_CurrentXp >= 0) || (a_CurrentXp > (std::numeric_limits<short>().max() - m_LifetimeTotalXp)))
{
LOGWARNING("Tried to update experiece with an invalid Xp value: %d", a_CurrentXp);
return false; // oops, they gave us a dodgey number
@@ -378,18 +379,17 @@ bool cPlayer::SetCurrentExperience(short int a_CurrentXp)
short cPlayer::DeltaExperience(short a_Xp_delta)
{
- if (a_Xp_delta > (SHRT_MAX - m_CurrentXp))
+ if (a_Xp_delta > (std::numeric_limits<short>().max() - m_CurrentXp))
{
// Value was bad, abort and report
- LOGWARNING("Attempt was made to increment Xp by %d, which overflowed the short datatype. Ignoring.",
- a_Xp_delta);
+ LOGWARNING("Attempt was made to increment Xp by %d, which overflowed the short datatype. Ignoring.", a_Xp_delta);
return -1; // Should we instead just return the current Xp?
}
m_CurrentXp += a_Xp_delta;
// Make sure they didn't subtract too much
- m_CurrentXp = std::max<short int>(m_CurrentXp, 0);
+ m_CurrentXp = std::max<short>(m_CurrentXp, 0);
// Update total for score calculation
if (a_Xp_delta > 0)
@@ -397,8 +397,7 @@ short cPlayer::DeltaExperience(short a_Xp_delta)
m_LifetimeTotalXp += a_Xp_delta;
}
- LOGD("Player \"%s\" gained/lost %d experience, total is now: %d",
- GetName().c_str(), a_Xp_delta, m_CurrentXp);
+ LOGD("Player \"%s\" gained/lost %d experience, total is now: %d", GetName().c_str(), a_Xp_delta, m_CurrentXp);
// Set experience to be updated
m_bDirtyExperience = true;
@@ -1074,7 +1073,7 @@ bool cPlayer::IsGameModeAdventure(void) const
bool cPlayer::IsGameModeSpectator(void) const
{
return (m_GameMode == gmSpectator) || // Either the player is explicitly in Spectator
- ((m_GameMode == gmNotSet) && m_World->IsGameModeSpectator()); // or they inherit from the world and the world is Adventure
+ ((m_GameMode == gmNotSet) && m_World->IsGameModeSpectator()); // or they inherit from the world and the world is Spectator
}
@@ -1602,6 +1601,9 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
a_World->AddPlayer(this);
SetWorld(a_World); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value
+ // Update the view distance.
+ m_ClientHandle->SetViewDistance(m_ClientHandle->GetRequestedViewDistance());
+
return true;
}
@@ -1890,8 +1892,8 @@ void cPlayer::UseEquippedItem(int a_Amount)
void cPlayer::TickBurning(cChunk & a_Chunk)
{
- // Don't burn in creative and stop burning in creative if necessary
- if (!IsGameModeCreative())
+ // Don't burn in creative or spectator and stop burning in creative if necessary
+ if (!IsGameModeCreative() && !IsGameModeSpectator())
{
super::TickBurning(a_Chunk);
}
@@ -1910,9 +1912,9 @@ void cPlayer::HandleFood(void)
{
// Ref.: http://www.minecraftwiki.net/wiki/Hunger
- if (IsGameModeCreative())
+ if (IsGameModeCreative() || IsGameModeSpectator())
{
- // Hunger is disabled for Creative
+ // Hunger is disabled for Creative and Spectator
return;
}
@@ -2077,7 +2079,7 @@ void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos)
void cPlayer::ApplyFoodExhaustionFromMovement()
{
- if (IsGameModeCreative())
+ if (IsGameModeCreative() || IsGameModeSpectator())
{
return;
}
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index 4bb51b556..c643aaa8e 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -516,7 +516,7 @@ protected:
/** The item being dragged by the cursor while in a UI window */
cItem m_DraggingItem;
- long long m_LastPlayerListTime;
+ std::chrono::steady_clock::time_point m_LastPlayerListTime;
cClientHandle * m_ClientHandle;
diff --git a/src/FastRandom.cpp b/src/FastRandom.cpp
index 42bf5f3f9..515dc25ea 100644
--- a/src/FastRandom.cpp
+++ b/src/FastRandom.cpp
@@ -4,13 +4,15 @@
// Implements the cFastRandom class representing a fast random number generator
#include "Globals.h"
-#include <time.h>
#include "FastRandom.h"
+////////////////////////////////////////////////////////////////////////////////
+// cFastRandom:
+
#if 0 && defined(_DEBUG)
// Self-test
// Both ints and floats are quick-tested to see if the random is calculated correctly, checking the range in ASSERTs,
@@ -83,16 +85,8 @@ public:
-
-int cFastRandom::m_SeedCounter = 0;
-
-
-
-
-
cFastRandom::cFastRandom(void) :
- m_Seed(m_SeedCounter++),
- m_Counter(0)
+ m_LinearRand(static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count()))
{
}
@@ -102,82 +96,96 @@ cFastRandom::cFastRandom(void) :
int cFastRandom::NextInt(int a_Range)
{
- ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges
- ASSERT(a_Range > 0);
-
- // Make the m_Counter operations as minimal as possible, to emulate atomicity
- int Counter = m_Counter++;
-
- // Use a_Range, m_Counter and m_Seed as inputs to the pseudorandom function:
- int n = a_Range + Counter * 57 + m_Seed * 57 * 57;
- n = (n << 13) ^ n;
- n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
- return ((n / 11) % a_Range);
+ std::uniform_int_distribution<> distribution(0, a_Range - 1);
+ return distribution(m_LinearRand);
}
+
int cFastRandom::NextInt(int a_Range, int a_Salt)
{
- ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges
- ASSERT(a_Range > 0);
-
- // Make the m_Counter operations as minimal as possible, to emulate atomicity
- int Counter = m_Counter++;
-
- // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function:
- int n = a_Range + Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57;
- n = (n << 13) ^ n;
- n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
- return ((n / 11) % a_Range);
+ m_LinearRand.seed(a_Salt);
+ std::uniform_int_distribution<> distribution(0, a_Range - 1);
+ return distribution(m_LinearRand);
}
+
float cFastRandom::NextFloat(float a_Range)
{
- // Make the m_Counter operations as minimal as possible, to emulate atomicity
- int Counter = m_Counter++;
-
- // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function:
- int n = (int)a_Range + Counter * 57 + m_Seed * 57 * 57;
- n = (n << 13) ^ n;
- n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
-
- // Convert the integer into float with the specified range:
- return (((float)n / (float)0x7fffffff) * a_Range);
+ std::uniform_real_distribution<float> distribution(0, a_Range);
+ return distribution(m_LinearRand);
}
+
float cFastRandom::NextFloat(float a_Range, int a_Salt)
{
- // Make the m_Counter operations as minimal as possible, to emulate atomicity
- int Counter = m_Counter++;
-
- // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function:
- int n = (int)a_Range + Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57;
- n = (n << 13) ^ n;
- n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
-
- // Convert the integer into float with the specified range:
- return (((float)n / (float)0x7fffffff) * a_Range);
+ m_LinearRand.seed(a_Salt);
+ std::uniform_real_distribution<float> distribution(0, a_Range);
+ return distribution(m_LinearRand);
}
+
int cFastRandom::GenerateRandomInteger(int a_Begin, int a_End)
{
- cFastRandom Random;
- return Random.NextInt(a_End - a_Begin + 1) + a_Begin;
+ std::uniform_int_distribution<> distribution(a_Begin, a_End);
+ return distribution(m_LinearRand);
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// MTRand:
+
+MTRand::MTRand() :
+ m_MersenneRand(static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count()))
+{
+}
+
+
+
+
+
+int MTRand::randInt(int a_Range)
+{
+ std::uniform_int_distribution<> distribution(0, a_Range);
+ return distribution(m_MersenneRand);
+}
+
+
+
+
+
+int MTRand::randInt()
+{
+ std::uniform_int_distribution<> distribution(0, std::numeric_limits<int>::max());
+ return distribution(m_MersenneRand);
+}
+
+
+
+
+
+double MTRand::rand(double a_Range)
+{
+ std::uniform_real_distribution<> distribution(0, a_Range);
+ return distribution(m_MersenneRand);
}
diff --git a/src/FastRandom.h b/src/FastRandom.h
index cebebad96..64a087c97 100644
--- a/src/FastRandom.h
+++ b/src/FastRandom.h
@@ -22,6 +22,7 @@ salts, the values they get will be different.
#pragma once
+#include <random>
@@ -30,18 +31,19 @@ salts, the values they get will be different.
class cFastRandom
{
public:
+
cFastRandom(void);
- /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M
+ /** Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M */
int NextInt(int a_Range);
- /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M; a_Salt is additional source of randomness
+ /** Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M; a_Salt is additional source of randomness */
int NextInt(int a_Range, int a_Salt);
- /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M
+ /** Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M */
float NextFloat(float a_Range);
- /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness
+ /** Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness */
float NextFloat(float a_Range, int a_Salt);
/** Returns a random float between 0 and 1. */
@@ -49,14 +51,35 @@ public:
/** Returns a random int in the range [a_Begin .. a_End] */
int GenerateRandomInteger(int a_Begin, int a_End);
-
-protected:
- int m_Seed;
- int m_Counter;
-
- /// Counter that is used to initialize the seed, incremented for each object created
- static int m_SeedCounter;
-} ;
+
+private:
+
+ std::minstd_rand m_LinearRand;
+};
+
+
+
+
+
+class MTRand
+{
+public:
+
+ MTRand(void);
+
+ /** Returns a random integer in the range [0 .. a_Range]. */
+ int randInt(int a_Range);
+
+ /** Returns a random integer in the range [0 .. MAX_INT]. */
+ int randInt(void);
+
+ /** Returns a random floating point number in the range [0 .. a_Range]. */
+ double rand(double a_Range);
+
+private:
+
+ std::mt19937 m_MersenneRand;
+};
diff --git a/src/FurnaceRecipe.cpp b/src/FurnaceRecipe.cpp
index 9b3b2ecbe..112aa8146 100644
--- a/src/FurnaceRecipe.cpp
+++ b/src/FurnaceRecipe.cpp
@@ -115,7 +115,7 @@ void cFurnaceRecipe::AddFuelFromLine(const AString & a_Line, unsigned int a_Line
Line.erase(Line.begin()); // Remove the beginning "!"
Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
- std::auto_ptr<cItem> Item(new cItem);
+ std::unique_ptr<cItem> Item(new cItem);
int BurnTime;
const AStringVector & Sides = StringSplit(Line, "=");
@@ -157,8 +157,8 @@ void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, unsigned int a_Li
Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
int CookTime = 200;
- std::auto_ptr<cItem> InputItem(new cItem());
- std::auto_ptr<cItem> OutputItem(new cItem());
+ std::unique_ptr<cItem> InputItem(new cItem());
+ std::unique_ptr<cItem> OutputItem(new cItem());
const AStringVector & Sides = StringSplit(Line, "=");
if (Sides.size() != 2)
diff --git a/src/Generating/BioGen.h b/src/Generating/BioGen.h
index 5fd0844d9..13fb40c5f 100644
--- a/src/Generating/BioGen.h
+++ b/src/Generating/BioGen.h
@@ -15,7 +15,7 @@ Interfaces to the various biome generators:
#pragma once
#include "ComposableGenerator.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
#include "../VoronoiMap.h"
diff --git a/src/Generating/CMakeLists.txt b/src/Generating/CMakeLists.txt
index 1a26bd0d5..a28510d40 100644
--- a/src/Generating/CMakeLists.txt
+++ b/src/Generating/CMakeLists.txt
@@ -10,6 +10,7 @@ SET (SRCS
ChunkDesc.cpp
ChunkGenerator.cpp
CompoGen.cpp
+ CompoGenBiomal.cpp
ComposableGenerator.cpp
DistortedHeightmap.cpp
DungeonRoomsFinisher.cpp
@@ -30,8 +31,10 @@ SET (SRCS
StructGen.cpp
TestRailsGen.cpp
Trees.cpp
+ TwoHeights.cpp
UnderwaterBaseGen.cpp
- VillageGen.cpp)
+ VillageGen.cpp
+)
SET (HDRS
BioGen.h
@@ -39,7 +42,9 @@ SET (HDRS
ChunkDesc.h
ChunkGenerator.h
CompoGen.h
+ CompoGenBiomal.h
ComposableGenerator.h
+ CompositedHeiGen.h
DistortedHeightmap.h
DungeonRoomsFinisher.h
EndGen.h
@@ -58,11 +63,14 @@ SET (HDRS
RainbowRoadsGen.h
Ravines.h
RoughRavines.h
+ ShapeGen.cpp
StructGen.h
TestRailsGen.h
Trees.h
+ TwoHeights.h
UnderwaterBaseGen.h
- VillageGen.h)
+ VillageGen.h
+)
if(NOT MSVC)
add_library(Generating ${SRCS} ${HDRS})
diff --git a/src/Generating/Caves.cpp b/src/Generating/Caves.cpp
index fc925a150..e4735cb83 100644
--- a/src/Generating/Caves.cpp
+++ b/src/Generating/Caves.cpp
@@ -692,8 +692,14 @@ static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise)
float oct1 = (a_Noise.CubicNoise3D(x * 0.1f, y * 0.1f, z * 0.1f)) * 4;
oct1 = oct1 * oct1 * oct1;
- if (oct1 < 0.f) oct1 = PI_2;
- if (oct1 > PI_2) oct1 = PI_2;
+ if (oct1 < 0.f)
+ {
+ oct1 = PI_2;
+ }
+ if (oct1 > PI_2)
+ {
+ oct1 = PI_2;
+ }
return oct1;
}
diff --git a/src/Generating/Caves.h b/src/Generating/Caves.h
index 0e17acf9e..691ef3e62 100644
--- a/src/Generating/Caves.h
+++ b/src/Generating/Caves.h
@@ -13,7 +13,6 @@
#pragma once
#include "GridStructGen.h"
-#include "../Noise.h"
diff --git a/src/Generating/ChunkDesc.cpp b/src/Generating/ChunkDesc.cpp
index 020d3bd0f..4a5ac5a18 100644
--- a/src/Generating/ChunkDesc.cpp
+++ b/src/Generating/ChunkDesc.cpp
@@ -7,7 +7,7 @@
#include "ChunkDesc.h"
#include "../BlockArea.h"
#include "../Cuboid.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
#include "../BlockEntities/BlockEntity.h"
@@ -152,6 +152,52 @@ int cChunkDesc::GetHeight(int a_RelX, int a_RelZ)
+void cChunkDesc::SetHeightFromShape(const Shape & a_Shape)
+{
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ for (int y = cChunkDef::Height - 1; y > 0; y--)
+ {
+ if (a_Shape[y + x * 256 + z * 16 * 256] != 0)
+ {
+ cChunkDef::SetHeight(m_HeightMap, x, z, y);
+ break;
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cChunkDesc::GetShapeFromHeight(Shape & a_Shape) const
+{
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int height = cChunkDef::GetHeight(m_HeightMap, x, z);
+ for (int y = 0; y <= height; y++)
+ {
+ a_Shape[y + x * 256 + z * 16 * 256] = 1;
+ }
+
+ for (int y = height + 1; y < cChunkDef::Height; y++)
+ {
+ a_Shape[y + x * 256 + z * 16 * 256] = 0;
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
+
void cChunkDesc::SetUseDefaultBiomes(bool a_bUseDefaultBiomes)
{
m_bUseDefaultBiomes = a_bUseDefaultBiomes;
@@ -366,6 +412,23 @@ HEIGHTTYPE cChunkDesc::GetMaxHeight(void) const
+HEIGHTTYPE cChunkDesc::GetMinHeight(void) const
+{
+ HEIGHTTYPE MinHeight = m_HeightMap[0];
+ for (size_t i = 1; i < ARRAYCOUNT(m_HeightMap); i++)
+ {
+ if (m_HeightMap[i] < MinHeight)
+ {
+ MinHeight = m_HeightMap[i];
+ }
+ }
+ return MinHeight;
+}
+
+
+
+
+
void cChunkDesc::FillRelCuboid(
int a_MinX, int a_MaxX,
int a_MinY, int a_MaxY,
diff --git a/src/Generating/ChunkDesc.h b/src/Generating/ChunkDesc.h
index 570132790..480106fb5 100644
--- a/src/Generating/ChunkDesc.h
+++ b/src/Generating/ChunkDesc.h
@@ -29,10 +29,17 @@ class cChunkDesc
{
public:
// tolua_end
+
+ /** The datatype used to represent the entire chunk worth of shape.
+ 0 = air
+ 1 = solid
+ Indexed as [y + 256 * x + 256 * 16 * z]. */
+ typedef Byte Shape[256 * 16 * 16];
/** Uncompressed block metas, 1 meta per byte */
typedef NIBBLETYPE BlockNibbleBytes[cChunkDef::NumBlocks];
+
cChunkDesc(int a_ChunkX, int a_ChunkZ);
~cChunkDesc();
@@ -57,10 +64,21 @@ public:
EMCSBiome GetBiome(int a_RelX, int a_RelZ);
// These operate on the heightmap, so they could get out of sync with the data
- // Use UpdateHeightmap() to re-sync
+ // Use UpdateHeightmap() to re-calculate heightmap from the block data
void SetHeight(int a_RelX, int a_RelZ, int a_Height);
int GetHeight(int a_RelX, int a_RelZ);
+ // tolua_end
+
+ /** Sets the heightmap to match the given shape data.
+ Note that this ignores overhangs; the method is mostly used by old composition generators. */
+ void SetHeightFromShape(const Shape & a_Shape);
+
+ /** Sets the shape in a_Shape to match the heightmap stored currently in m_HeightMap. */
+ void GetShapeFromHeight(Shape & a_Shape) const;
+
+ // tolua_begin
+
// Default generation:
void SetUseDefaultBiomes(bool a_bUseDefaultBiomes);
bool IsUsingDefaultBiomes(void) const;
@@ -77,8 +95,11 @@ public:
/** Reads an area from the chunk into a cBlockArea, blocktypes and blockmetas */
void ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ);
- /** Returns the maximum height value in the heightmap */
+ /** Returns the maximum height value in the heightmap. */
HEIGHTTYPE GetMaxHeight(void) const;
+
+ /** Returns the minimum height value in the heightmap. */
+ HEIGHTTYPE GetMinHeight(void) const;
/** Fills the relative cuboid with specified block; allows cuboid out of range of this chunk */
void FillRelCuboid(
diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp
index 92e1bb31d..d2e7b47b4 100644
--- a/src/Generating/ChunkGenerator.cpp
+++ b/src/Generating/ChunkGenerator.cpp
@@ -6,7 +6,7 @@
#include "ChunkDesc.h"
#include "ComposableGenerator.h"
#include "Noise3DGenerator.h"
-#include "../MersenneTwister.h"
+#include "FastRandom.h"
@@ -191,13 +191,13 @@ EMCSBiome cChunkGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ)
BLOCKTYPE cChunkGenerator::GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default)
{
AString BlockType = a_IniFile.GetValueSet(a_SectionName, a_ValueName, a_Default);
- BLOCKTYPE Block = BlockStringToType(BlockType);
+ int Block = BlockStringToType(BlockType);
if (Block < 0)
{
LOGWARN("[%s].%s Could not parse block value \"%s\". Using default: \"%s\".", a_SectionName.c_str(), a_ValueName.c_str(), BlockType.c_str(), a_Default.c_str());
- return BlockStringToType(a_Default);
+ return static_cast<BLOCKTYPE>(BlockStringToType(a_Default));
}
- return Block;
+ return static_cast<BLOCKTYPE>(Block);
}
diff --git a/src/Generating/CompoGen.cpp b/src/Generating/CompoGen.cpp
index 29b831dfd..cb9c04fd7 100644
--- a/src/Generating/CompoGen.cpp
+++ b/src/Generating/CompoGen.cpp
@@ -21,8 +21,9 @@
////////////////////////////////////////////////////////////////////////////////
// cCompoGenSameBlock:
-void cCompoGenSameBlock::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+void cCompoGenSameBlock::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
+ a_ChunkDesc.SetHeightFromShape(a_Shape);
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
for (int z = 0; z < cChunkDef::Width; z++)
{
@@ -63,7 +64,7 @@ void cCompoGenSameBlock::InitializeCompoGen(cIniFile & a_IniFile)
////////////////////////////////////////////////////////////////////////////////
// cCompoGenDebugBiomes:
-void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
static BLOCKTYPE Blocks[] =
{
@@ -92,6 +93,7 @@ void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc)
E_BLOCK_BEDROCK,
} ;
+ a_ChunkDesc.SetHeightFromShape(a_Shape);
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
for (int z = 0; z < cChunkDef::Width; z++)
@@ -131,7 +133,7 @@ cCompoGenClassic::cCompoGenClassic(void) :
-void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
/* The classic composition means:
- 1 layer of grass, 3 of dirt and the rest stone, if the height > sealevel + beachheight
@@ -142,6 +144,7 @@ void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc)
*/
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
+ a_ChunkDesc.SetHeightFromShape(a_Shape);
// The patterns to use for different situations, must be same length!
const BLOCKTYPE PatternGround[] = {m_BlockTop, m_BlockMiddle, m_BlockMiddle, m_BlockMiddle} ;
@@ -194,7 +197,7 @@ void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc)
void cCompoGenClassic::InitializeCompoGen(cIniFile & a_IniFile)
{
- m_SeaLevel = a_IniFile.GetValueSetI("Generator", "ClassicSeaLevel", m_SeaLevel);
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "SeaLevel", m_SeaLevel);
m_BeachHeight = a_IniFile.GetValueSetI("Generator", "ClassicBeachHeight", m_BeachHeight);
m_BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", m_BeachDepth);
m_BlockTop = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockTop", "grass").m_ItemType);
@@ -210,323 +213,6 @@ void cCompoGenClassic::InitializeCompoGen(cIniFile & a_IniFile)
////////////////////////////////////////////////////////////////////////////////
-// cCompoGenBiomal:
-
-void cCompoGenBiomal::ComposeTerrain(cChunkDesc & a_ChunkDesc)
-{
- a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
-
- int ChunkX = a_ChunkDesc.GetChunkX();
- int ChunkZ = a_ChunkDesc.GetChunkZ();
-
- /*
- _X 2013_04_22:
- There's no point in generating the whole cubic noise at once, because the noise values are used in
- only about 20 % of the cases, so the speed gained by precalculating is lost by precalculating too much data
- */
-
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- int Height = a_ChunkDesc.GetHeight(x, z);
- if (Height > m_SeaLevel)
- {
- switch (a_ChunkDesc.GetBiome(x, z))
- {
- case biOcean:
- case biPlains:
- case biExtremeHills:
- case biForest:
- case biTaiga:
- case biSwampland:
- case biRiver:
- case biFrozenOcean:
- case biFrozenRiver:
- case biIcePlains:
- case biIceMountains:
- case biForestHills:
- case biTaigaHills:
- case biExtremeHillsEdge:
- case biJungle:
- case biJungleHills:
- case biJungleEdge:
- case biDeepOcean:
- case biStoneBeach:
- case biColdBeach:
- case biBirchForest:
- case biBirchForestHills:
- case biRoofedForest:
- case biColdTaiga:
- case biColdTaigaHills:
- case biExtremeHillsPlus:
- case biSavanna:
- case biSavannaPlateau:
- case biSunflowerPlains:
- case biExtremeHillsM:
- case biFlowerForest:
- case biTaigaM:
- case biSwamplandM:
- case biIcePlainsSpikes:
- case biJungleM:
- case biJungleEdgeM:
- case biBirchForestM:
- case biBirchForestHillsM:
- case biRoofedForestM:
- case biColdTaigaM:
- case biExtremeHillsPlusM:
- case biSavannaM:
- case biSavannaPlateauM:
- {
- FillColumnGrass(x, z, Height, a_ChunkDesc.GetBlockTypes());
- break;
- }
-
- case biMesa:
- case biMesaPlateauF:
- case biMesaPlateau:
- case biMesaBryce:
- case biMesaPlateauFM:
- case biMesaPlateauM:
- {
- FillColumnClay(x, z, Height, a_ChunkDesc.GetBlockTypes());
- break;
- }
-
- case biMegaTaiga:
- case biMegaTaigaHills:
- case biMegaSpruceTaiga:
- case biMegaSpruceTaigaHills:
- {
- FillColumnDirt(x, z, Height, a_ChunkDesc.GetBlockTypes());
- break;
- }
-
- case biDesertHills:
- case biDesert:
- case biDesertM:
- case biBeach:
- {
- FillColumnSand(x, z, Height, a_ChunkDesc.GetBlockTypes());
- break;
- }
- case biMushroomIsland:
- case biMushroomShore:
- {
- FillColumnMycelium(x, z, Height, a_ChunkDesc.GetBlockTypes());
- break;
- }
- default:
- {
- // TODO
- ASSERT(!"CompoGenBiomal: Biome not implemented yet!");
- break;
- }
- }
- }
- else
- {
- switch (a_ChunkDesc.GetBiome(x, z))
- {
- case biDesert:
- case biBeach:
- {
- // Fill with water, sand, sandstone and stone
- FillColumnWaterSand(x, z, Height, a_ChunkDesc.GetBlockTypes());
- break;
- }
- default:
- {
- // Fill with water, sand/dirt/clay mix and stone
- if (m_Noise.CubicNoise2D(0.3f * (cChunkDef::Width * ChunkX + x), 0.3f * (cChunkDef::Width * ChunkZ + z)) < 0)
- {
- FillColumnWaterSand(x, z, Height, a_ChunkDesc.GetBlockTypes());
- }
- else
- {
- FillColumnWaterDirt(x, z, Height, a_ChunkDesc.GetBlockTypes());
- }
- break;
- }
- } // switch (biome)
- a_ChunkDesc.SetHeight(x, z, m_SeaLevel + 1);
- } // else (under water)
- a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
- } // for x
- } // for z
-}
-
-
-
-
-
-void cCompoGenBiomal::InitializeCompoGen(cIniFile & a_IniFile)
-{
- m_SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalSeaLevel", m_SeaLevel) - 1;
-}
-
-
-
-
-
-void cCompoGenBiomal::FillColumnGrass(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
-{
- BLOCKTYPE Pattern[] =
- {
- E_BLOCK_GRASS,
- E_BLOCK_DIRT,
- E_BLOCK_DIRT,
- E_BLOCK_DIRT,
- } ;
- FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
-
- for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
- }
-}
-
-
-
-
-
-void cCompoGenBiomal::FillColumnClay(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
-{
- BLOCKTYPE Pattern[] =
- {
- E_BLOCK_HARDENED_CLAY,
- E_BLOCK_HARDENED_CLAY,
- E_BLOCK_HARDENED_CLAY,
- E_BLOCK_HARDENED_CLAY,
- } ;
- FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
-
- for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
- }
-}
-
-
-
-
-
-void cCompoGenBiomal::FillColumnDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
-{
- for (int y = 0; y < 4; y++)
- {
- if (a_Height - y < 0)
- {
- return;
- }
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, a_Height - y, a_RelZ, E_BLOCK_DIRT);
- }
- for (int y = a_Height - 4; y > 0; y--)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
- }
-}
-
-
-
-
-
-void cCompoGenBiomal::FillColumnSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
-{
- BLOCKTYPE Pattern[] =
- {
- E_BLOCK_SAND,
- E_BLOCK_SAND,
- E_BLOCK_SAND,
- E_BLOCK_SANDSTONE,
- } ;
- FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
-
- for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
- }
-}
-
-
-
-
-
-
-void cCompoGenBiomal::FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
-{
- BLOCKTYPE Pattern[] =
- {
- E_BLOCK_MYCELIUM,
- E_BLOCK_DIRT,
- E_BLOCK_DIRT,
- E_BLOCK_DIRT,
- } ;
- FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
-
- for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
- }
-}
-
-
-
-
-
-void cCompoGenBiomal::FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
-{
- FillColumnSand(a_RelX, a_RelZ, a_Height, a_BlockTypes);
- for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
- }
-}
-
-
-
-
-
-void cCompoGenBiomal::FillColumnWaterDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
-{
- // Dirt
- BLOCKTYPE Pattern[] =
- {
- E_BLOCK_DIRT,
- E_BLOCK_DIRT,
- E_BLOCK_DIRT,
- E_BLOCK_DIRT,
- } ;
- FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
-
- for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
- }
- for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
- }
-}
-
-
-
-
-
-
-void cCompoGenBiomal::FillColumnPattern(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize)
-{
- for (int y = a_Height, idx = 0; (y >= 0) && (idx < a_PatternSize); y--, idx++)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, a_Pattern[idx]);
- }
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
// cCompoGenNether:
cCompoGenNether::cCompoGenNether(int a_Seed) :
@@ -540,7 +226,7 @@ cCompoGenNether::cCompoGenNether(int a_Seed) :
-void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight();
@@ -604,17 +290,7 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc)
BLOCKTYPE Block = E_BLOCK_AIR;
if (Val < m_Threshold) // Don't calculate if the block should be Netherrack or Soulsand when it's already decided that it's air.
{
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(BaseX + x)) / 8;
- NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(BaseZ + z)) / 8;
- NOISE_DATATYPE CompBlock = m_Noise1.CubicNoise3D(NoiseX, (float) (y + Segment) / 2, NoiseY);
- if (CompBlock < -0.5)
- {
- Block = E_BLOCK_SOULSAND;
- }
- else
- {
- Block = E_BLOCK_NETHERRACK;
- }
+ Block = E_BLOCK_NETHERRACK;
}
a_ChunkDesc.SetBlockType(x, y + Segment, z, Block);
}
@@ -638,7 +314,7 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc)
CeilingDisguise = -CeilingDisguise;
}
- int CeilingDisguiseHeight = Height - 2 - (int)CeilingDisguise * 3;
+ int CeilingDisguiseHeight = Height - 2 - FloorC(CeilingDisguise * 3);
for (int y = Height - 1; y > CeilingDisguiseHeight; y--)
{
@@ -696,7 +372,7 @@ cCompoGenCache::~cCompoGenCache()
-void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
#ifdef _DEBUG
if (((m_NumHits + m_NumMisses) % 1024) == 10)
@@ -731,6 +407,7 @@ void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc)
// Use the cached data:
memcpy(a_ChunkDesc.GetBlockTypes(), m_CacheData[Idx].m_BlockTypes, sizeof(a_ChunkDesc.GetBlockTypes()));
memcpy(a_ChunkDesc.GetBlockMetasUncompressed(), m_CacheData[Idx].m_BlockMetas, sizeof(a_ChunkDesc.GetBlockMetasUncompressed()));
+ memcpy(a_ChunkDesc.GetHeightMap(), m_CacheData[Idx].m_HeightMap, sizeof(a_ChunkDesc.GetHeightMap()));
m_NumHits++;
m_TotalChain += i;
@@ -739,7 +416,7 @@ void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc)
// Not in the cache:
m_NumMisses++;
- m_Underlying->ComposeTerrain(a_ChunkDesc);
+ m_Underlying->ComposeTerrain(a_ChunkDesc, a_Shape);
// Insert it as the first item in the MRU order:
int Idx = m_CacheOrder[m_CacheSize - 1];
@@ -750,6 +427,7 @@ void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc)
m_CacheOrder[0] = Idx;
memcpy(m_CacheData[Idx].m_BlockTypes, a_ChunkDesc.GetBlockTypes(), sizeof(a_ChunkDesc.GetBlockTypes()));
memcpy(m_CacheData[Idx].m_BlockMetas, a_ChunkDesc.GetBlockMetasUncompressed(), sizeof(a_ChunkDesc.GetBlockMetasUncompressed()));
+ memcpy(m_CacheData[Idx].m_HeightMap, a_ChunkDesc.GetHeightMap(), sizeof(a_ChunkDesc.GetHeightMap()));
m_CacheData[Idx].m_ChunkX = ChunkX;
m_CacheData[Idx].m_ChunkZ = ChunkZ;
}
diff --git a/src/Generating/CompoGen.h b/src/Generating/CompoGen.h
index b145b6ba3..3847688cd 100644
--- a/src/Generating/CompoGen.h
+++ b/src/Generating/CompoGen.h
@@ -17,7 +17,7 @@
#pragma once
#include "ComposableGenerator.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
@@ -38,7 +38,7 @@ protected:
bool m_IsBedrocked;
// cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ;
@@ -55,7 +55,7 @@ public:
protected:
// cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
} ;
@@ -81,7 +81,7 @@ protected:
BLOCKTYPE m_BlockSea;
// cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ;
@@ -89,40 +89,6 @@ protected:
-class cCompoGenBiomal :
- public cTerrainCompositionGen
-{
-public:
- cCompoGenBiomal(int a_Seed) :
- m_Noise(a_Seed + 1000),
- m_SeaLevel(62)
- {
- }
-
-protected:
-
- cNoise m_Noise;
- int m_SeaLevel;
-
- // cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
- virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
-
- void FillColumnGrass (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
- void FillColumnClay (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
- void FillColumnDirt (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
- void FillColumnSand (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
- void FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
- void FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
- void FillColumnWaterDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
-
- void FillColumnPattern (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize);
-} ;
-
-
-
-
-
class cCompoGenNether :
public cTerrainCompositionGen
{
@@ -136,7 +102,7 @@ protected:
int m_Threshold;
// cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ;
@@ -153,7 +119,7 @@ public:
~cCompoGenCache();
// cTerrainCompositionGen override:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
protected:
@@ -166,6 +132,7 @@ protected:
int m_ChunkZ;
cChunkDef::BlockTypes m_BlockTypes;
cChunkDesc::BlockNibbleBytes m_BlockMetas; // The metas are uncompressed, 1 meta per byte
+ cChunkDef::HeightMap m_HeightMap;
} ;
// To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data
diff --git a/src/Generating/CompoGenBiomal.cpp b/src/Generating/CompoGenBiomal.cpp
new file mode 100644
index 000000000..030c2baa5
--- /dev/null
+++ b/src/Generating/CompoGenBiomal.cpp
@@ -0,0 +1,586 @@
+
+// CompoGenBiomal.cpp
+
+// Implements the cCompoGenBiomal class representing the biome-aware composition generator
+
+#include "Globals.h"
+#include "ComposableGenerator.h"
+#include "../IniFile.h"
+#include "../Noise/Noise.h"
+#include "../LinearUpscale.h"
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cPattern:
+
+/** This class is used to store a column pattern initialized at runtime,
+so that the program doesn't need to explicitly set 256 values for each pattern
+Each pattern has 256 blocks so that there's no need to check pattern bounds when assigning the
+pattern - there will always be enough pattern left, even for the whole-chunk-height columns. */
+class cPattern
+{
+public:
+ struct BlockInfo
+ {
+ BLOCKTYPE m_BlockType;
+ NIBBLETYPE m_BlockMeta;
+ };
+
+ cPattern(BlockInfo * a_TopBlocks, size_t a_Count)
+ {
+ // Copy the pattern into the top:
+ for (size_t i = 0; i < a_Count; i++)
+ {
+ m_Pattern[i] = a_TopBlocks[i];
+ }
+
+ // Fill the rest with stone:
+ static BlockInfo Stone = {E_BLOCK_STONE, 0};
+ for (int i = static_cast<int>(a_Count); i < cChunkDef::Height; i++)
+ {
+ m_Pattern[i] = Stone;
+ }
+ }
+
+ const BlockInfo * Get(void) const { return m_Pattern; }
+
+protected:
+ BlockInfo m_Pattern[cChunkDef::Height];
+} ;
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// The arrays to use for the top block pattern definitions:
+
+static cPattern::BlockInfo tbGrass[] =
+{
+ {E_BLOCK_GRASS, 0},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+} ;
+
+static cPattern::BlockInfo tbSand[] =
+{
+ { E_BLOCK_SAND, 0},
+ { E_BLOCK_SAND, 0},
+ { E_BLOCK_SAND, 0},
+ { E_BLOCK_SANDSTONE, 0},
+} ;
+
+static cPattern::BlockInfo tbDirt[] =
+{
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+} ;
+
+static cPattern::BlockInfo tbPodzol[] =
+{
+ {E_BLOCK_DIRT, E_META_DIRT_PODZOL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+} ;
+
+static cPattern::BlockInfo tbGrassLess[] =
+{
+ {E_BLOCK_DIRT, E_META_DIRT_GRASSLESS},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+} ;
+
+static cPattern::BlockInfo tbMycelium[] =
+{
+ {E_BLOCK_MYCELIUM, 0},
+ {E_BLOCK_DIRT, 0},
+ {E_BLOCK_DIRT, 0},
+ {E_BLOCK_DIRT, 0},
+} ;
+
+static cPattern::BlockInfo tbGravel[] =
+{
+ {E_BLOCK_GRAVEL, 0},
+ {E_BLOCK_GRAVEL, 0},
+ {E_BLOCK_GRAVEL, 0},
+ {E_BLOCK_STONE, 0},
+} ;
+
+static cPattern::BlockInfo tbStone[] =
+{
+ {E_BLOCK_STONE, 0},
+ {E_BLOCK_STONE, 0},
+ {E_BLOCK_STONE, 0},
+ {E_BLOCK_STONE, 0},
+} ;
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Ocean floor pattern top-block definitions:
+
+static cPattern::BlockInfo tbOFSand[] =
+{
+ {E_BLOCK_SAND, 0},
+ {E_BLOCK_SAND, 0},
+ {E_BLOCK_SAND, 0},
+ {E_BLOCK_SANDSTONE, 0}
+} ;
+
+static cPattern::BlockInfo tbOFClay[] =
+{
+ { E_BLOCK_CLAY, 0},
+ { E_BLOCK_CLAY, 0},
+ { E_BLOCK_SAND, 0},
+ { E_BLOCK_SAND, 0},
+} ;
+
+static cPattern::BlockInfo tbOFOrangeClay[] =
+{
+ { E_BLOCK_STAINED_CLAY, E_META_STAINED_GLASS_ORANGE},
+ { E_BLOCK_STAINED_CLAY, E_META_STAINED_GLASS_ORANGE},
+ { E_BLOCK_STAINED_CLAY, E_META_STAINED_GLASS_ORANGE},
+} ;
+
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Individual patterns to use:
+
+static cPattern patGrass (tbGrass, ARRAYCOUNT(tbGrass));
+static cPattern patSand (tbSand, ARRAYCOUNT(tbSand));
+static cPattern patDirt (tbDirt, ARRAYCOUNT(tbDirt));
+static cPattern patPodzol (tbPodzol, ARRAYCOUNT(tbPodzol));
+static cPattern patGrassLess(tbGrassLess, ARRAYCOUNT(tbGrassLess));
+static cPattern patMycelium (tbMycelium, ARRAYCOUNT(tbMycelium));
+static cPattern patGravel (tbGravel, ARRAYCOUNT(tbGravel));
+static cPattern patStone (tbStone, ARRAYCOUNT(tbStone));
+
+static cPattern patOFSand (tbOFSand, ARRAYCOUNT(tbOFSand));
+static cPattern patOFClay (tbOFClay, ARRAYCOUNT(tbOFClay));
+static cPattern patOFOrangeClay(tbOFOrangeClay, ARRAYCOUNT(tbOFOrangeClay));
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cCompoGenBiomal:
+
+class cCompoGenBiomal :
+ public cTerrainCompositionGen
+{
+public:
+ cCompoGenBiomal(int a_Seed) :
+ m_SeaLevel(62),
+ m_OceanFloorSelect(a_Seed + 1),
+ m_MesaFloor(a_Seed + 2)
+ {
+ initMesaPattern(a_Seed);
+ }
+
+protected:
+ /** The block height at which water is generated instead of air. */
+ int m_SeaLevel;
+
+ /** The pattern used for mesa biomes. Initialized by seed on generator creation. */
+ cPattern::BlockInfo m_MesaPattern[2 * cChunkDef::Height];
+
+ /** Noise used for selecting between dirt and sand on the ocean floor. */
+ cNoise m_OceanFloorSelect;
+
+ /** Noise used for the floor of the clay blocks in mesa biomes. */
+ cNoise m_MesaFloor;
+
+
+ // cTerrainCompositionGen overrides:
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override
+ {
+ a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ ComposeColumn(a_ChunkDesc, x, z, &(a_Shape[x * 256 + z * 16 * 256]));
+ } // for x
+ } // for z
+ }
+
+
+
+ virtual void InitializeCompoGen(cIniFile & a_IniFile) override
+ {
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "SeaLevel", m_SeaLevel);
+ }
+
+
+
+ /** Initializes the m_MesaPattern with a pattern based on the generator's seed. */
+ void initMesaPattern(int a_Seed)
+ {
+ // In a loop, choose whether to use one, two or three layers of stained clay, then choose a color and width for each layer
+ // Separate each group with another layer of hardened clay
+ cNoise patternNoise((unsigned)a_Seed);
+ static NIBBLETYPE allowedColors[] =
+ {
+ E_META_STAINED_CLAY_YELLOW,
+ E_META_STAINED_CLAY_YELLOW,
+ E_META_STAINED_CLAY_RED,
+ E_META_STAINED_CLAY_RED,
+ E_META_STAINED_CLAY_WHITE,
+ E_META_STAINED_CLAY_BROWN,
+ E_META_STAINED_CLAY_BROWN,
+ E_META_STAINED_CLAY_BROWN,
+ E_META_STAINED_CLAY_ORANGE,
+ E_META_STAINED_CLAY_ORANGE,
+ E_META_STAINED_CLAY_ORANGE,
+ E_META_STAINED_CLAY_ORANGE,
+ E_META_STAINED_CLAY_ORANGE,
+ E_META_STAINED_CLAY_ORANGE,
+ E_META_STAINED_CLAY_LIGHTGRAY,
+ } ;
+ static int layerSizes[] = // Adjust the chance so that thinner layers occur more commonly
+ {
+ 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2,
+ 3, 3,
+ } ;
+ int idx = ARRAYCOUNT(m_MesaPattern) - 1;
+ while (idx >= 0)
+ {
+ // A layer group of 1 - 2 color stained clay:
+ int rnd = patternNoise.IntNoise1DInt(idx) / 7;
+ int numLayers = (rnd % 2) + 1;
+ rnd /= 2;
+ for (int lay = 0; lay < numLayers; lay++)
+ {
+ int numBlocks = layerSizes[(rnd % ARRAYCOUNT(layerSizes))];
+ NIBBLETYPE Color = allowedColors[(rnd / 4) % ARRAYCOUNT(allowedColors)];
+ if (
+ ((numBlocks == 3) && (numLayers == 2)) || // In two-layer mode disallow the 3-high layers:
+ (Color == E_META_STAINED_CLAY_WHITE)) // White stained clay can ever be only 1 block high
+ {
+ numBlocks = 1;
+ }
+ numBlocks = std::min(idx + 1, numBlocks); // Limit by idx so that we don't have to check inside the loop
+ rnd /= 32;
+ for (int block = 0; block < numBlocks; block++, idx--)
+ {
+ m_MesaPattern[idx].m_BlockMeta = Color;
+ m_MesaPattern[idx].m_BlockType = E_BLOCK_STAINED_CLAY;
+ } // for block
+ } // for lay
+
+ // A layer of hardened clay in between the layer group:
+ int numBlocks = (rnd % 4) + 1; // All heights the same probability
+ if ((numLayers == 2) && (numBlocks < 4))
+ {
+ // For two layers of stained clay, add an extra block of hardened clay:
+ numBlocks++;
+ }
+ numBlocks = std::min(idx + 1, numBlocks); // Limit by idx so that we don't have to check inside the loop
+ for (int block = 0; block < numBlocks; block++, idx--)
+ {
+ m_MesaPattern[idx].m_BlockMeta = 0;
+ m_MesaPattern[idx].m_BlockType = E_BLOCK_HARDENED_CLAY;
+ } // for block
+ } // while (idx >= 0)
+ }
+
+
+
+ /** Composes a single column in a_ChunkDesc. Chooses what to do based on the biome in that column. */
+ void ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ, const Byte * a_ShapeColumn)
+ {
+ // Frequencies for the podzol floor selecting noise:
+ const NOISE_DATATYPE FrequencyX = 8;
+ const NOISE_DATATYPE FrequencyZ = 8;
+
+ EMCSBiome Biome = a_ChunkDesc.GetBiome(a_RelX, a_RelZ);
+ switch (Biome)
+ {
+ case biOcean:
+ case biPlains:
+ case biForest:
+ case biTaiga:
+ case biSwampland:
+ case biRiver:
+ case biFrozenOcean:
+ case biFrozenRiver:
+ case biIcePlains:
+ case biIceMountains:
+ case biForestHills:
+ case biTaigaHills:
+ case biExtremeHillsEdge:
+ case biExtremeHillsPlus:
+ case biExtremeHills:
+ case biJungle:
+ case biJungleHills:
+ case biJungleEdge:
+ case biDeepOcean:
+ case biStoneBeach:
+ case biColdBeach:
+ case biBirchForest:
+ case biBirchForestHills:
+ case biRoofedForest:
+ case biColdTaiga:
+ case biColdTaigaHills:
+ case biSavanna:
+ case biSavannaPlateau:
+ case biSunflowerPlains:
+ case biFlowerForest:
+ case biTaigaM:
+ case biSwamplandM:
+ case biIcePlainsSpikes:
+ case biJungleM:
+ case biJungleEdgeM:
+ case biBirchForestM:
+ case biBirchForestHillsM:
+ case biRoofedForestM:
+ case biColdTaigaM:
+ case biSavannaM:
+ case biSavannaPlateauM:
+ {
+ FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patGrass.Get(), a_ShapeColumn);
+ return;
+ }
+
+ case biMegaTaiga:
+ case biMegaTaigaHills:
+ case biMegaSpruceTaiga:
+ case biMegaSpruceTaigaHills:
+ {
+ // Select the pattern to use - podzol, grass or grassless dirt:
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + a_RelX)) / FrequencyX;
+ NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ)) / FrequencyZ;
+ NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
+ const cPattern::BlockInfo * Pattern = (Val < -0.9) ? patGrassLess.Get() : ((Val > 0) ? patPodzol.Get() : patGrass.Get());
+ FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, Pattern, a_ShapeColumn);
+ return;
+ }
+
+ case biDesertHills:
+ case biDesert:
+ case biDesertM:
+ case biBeach:
+ {
+ FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patSand.Get(), a_ShapeColumn);
+ return;
+ }
+
+ case biMushroomIsland:
+ case biMushroomShore:
+ {
+ FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patMycelium.Get(), a_ShapeColumn);
+ return;
+ }
+
+ case biMesa:
+ case biMesaPlateauF:
+ case biMesaPlateau:
+ case biMesaBryce:
+ case biMesaPlateauFM:
+ case biMesaPlateauM:
+ {
+ // Mesa biomes need special handling, because they don't follow the usual "4 blocks from top pattern",
+ // instead, they provide a "from bottom" pattern with varying base height,
+ // usually 4 blocks below the ocean level
+ FillColumnMesa(a_ChunkDesc, a_RelX, a_RelZ, a_ShapeColumn);
+ return;
+ }
+
+ case biExtremeHillsPlusM:
+ case biExtremeHillsM:
+ {
+ // Select the pattern to use - gravel, stone or grass:
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + a_RelX)) / FrequencyX;
+ NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ)) / FrequencyZ;
+ NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
+ const cPattern::BlockInfo * Pattern = (Val < 0.0) ? patStone.Get() : patGrass.Get();
+ FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, Pattern, a_ShapeColumn);
+ return;
+ }
+ default:
+ {
+ ASSERT(!"Unhandled biome");
+ return;
+ }
+ } // switch (Biome)
+ }
+
+
+
+ /** Fills the specified column with the specified pattern; restarts the pattern when air is reached,
+ switches to ocean floor pattern if ocean is reached. Always adds bedrock at the very bottom. */
+ void FillColumnPattern(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ, const cPattern::BlockInfo * a_Pattern, const Byte * a_ShapeColumn)
+ {
+ bool HasHadWater = false;
+ int PatternIdx = 0;
+ int top = std::max(m_SeaLevel, a_ChunkDesc.GetHeight(a_RelX, a_RelZ));
+ for (int y = top; y > 0; y--)
+ {
+ if (a_ShapeColumn[y] > 0)
+ {
+ // "ground" part, use the pattern:
+ a_ChunkDesc.SetBlockTypeMeta(a_RelX, y, a_RelZ, a_Pattern[PatternIdx].m_BlockType, a_Pattern[PatternIdx].m_BlockMeta);
+ PatternIdx++;
+ continue;
+ }
+
+ // "air" or "water" part:
+ // Reset the pattern index to zero, so that the pattern is repeated from the top again:
+ PatternIdx = 0;
+
+ if (y >= m_SeaLevel)
+ {
+ // "air" part, do nothing
+ continue;
+ }
+
+ a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
+ if (HasHadWater)
+ {
+ continue;
+ }
+
+ // Select the ocean-floor pattern to use:
+ if (a_ChunkDesc.GetBiome(a_RelX, a_RelZ) == biDeepOcean)
+ {
+ a_Pattern = patGravel.Get();
+ }
+ else
+ {
+ a_Pattern = ChooseOceanFloorPattern(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(), a_RelX, a_RelZ);
+ }
+ HasHadWater = true;
+ } // for y
+ a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
+ }
+
+
+
+ /** Fills the specified column with mesa pattern, based on the column height */
+ void FillColumnMesa(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ, const Byte * a_ShapeColumn)
+ {
+ // Frequencies for the clay floor noise:
+ const NOISE_DATATYPE FrequencyX = 50;
+ const NOISE_DATATYPE FrequencyZ = 50;
+
+ int Top = a_ChunkDesc.GetHeight(a_RelX, a_RelZ);
+ if (Top < m_SeaLevel)
+ {
+ // The terrain is below sealevel, handle as regular ocean with red sand floor:
+ FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patOFOrangeClay.Get(), a_ShapeColumn);
+ return;
+ }
+
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + a_RelX)) / FrequencyX;
+ NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ)) / FrequencyZ;
+ int ClayFloor = m_SeaLevel - 6 + (int)(4.f * m_MesaFloor.CubicNoise2D(NoiseX, NoiseY));
+ if (ClayFloor >= Top)
+ {
+ ClayFloor = Top - 1;
+ }
+
+ if (Top - m_SeaLevel < 5)
+ {
+ // Simple case: top is red sand, then hardened clay down to ClayFloor, then stone:
+ a_ChunkDesc.SetBlockTypeMeta(a_RelX, Top, a_RelZ, E_BLOCK_SAND, E_META_SAND_RED);
+ for (int y = Top - 1; y >= ClayFloor; y--)
+ {
+ a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_HARDENED_CLAY);
+ }
+ for (int y = ClayFloor - 1; y > 0; y--)
+ {
+ a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STONE);
+ }
+ a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
+ return;
+ }
+
+ // Difficult case: use the mesa pattern and watch for overhangs:
+ int PatternIdx = cChunkDef::Height - (Top - ClayFloor); // We want the block at index ClayFloor to be pattern's 256th block (first stone)
+ const cPattern::BlockInfo * Pattern = m_MesaPattern;
+ bool HasHadWater = false;
+ for (int y = Top; y > 0; y--)
+ {
+ if (a_ShapeColumn[y] > 0)
+ {
+ // "ground" part, use the pattern:
+ a_ChunkDesc.SetBlockTypeMeta(a_RelX, y, a_RelZ, Pattern[PatternIdx].m_BlockType, Pattern[PatternIdx].m_BlockMeta);
+ PatternIdx++;
+ continue;
+ }
+
+ if (y >= m_SeaLevel)
+ {
+ // "air" part, do nothing
+ continue;
+ }
+
+ // "water" part, fill with water and choose new pattern for ocean floor, if not chosen already:
+ PatternIdx = 0;
+ a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
+ if (HasHadWater)
+ {
+ continue;
+ }
+
+ // Select the ocean-floor pattern to use:
+ Pattern = ChooseOceanFloorPattern(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(), a_RelX, a_RelZ);
+ HasHadWater = true;
+ } // for y
+ a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
+ }
+
+
+
+ /** Returns the pattern to use for an ocean floor in the specified column.
+ The returned pattern is guaranteed to be 256 blocks long. */
+ const cPattern::BlockInfo * ChooseOceanFloorPattern(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ)
+ {
+ // Frequencies for the ocean floor selecting noise:
+ const NOISE_DATATYPE FrequencyX = 3;
+ const NOISE_DATATYPE FrequencyZ = 3;
+
+ // Select the ocean-floor pattern to use:
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + a_RelX)) / FrequencyX;
+ NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ;
+ NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
+ if (Val < -0.95)
+ {
+ return patOFClay.Get();
+ }
+ else if (Val < 0)
+ {
+ return patOFSand.Get();
+ }
+ else
+ {
+ return patDirt.Get();
+ }
+ }
+} ;
+
+
+
+
+
+cTerrainCompositionGenPtr CreateCompoGenBiomal(int a_Seed)
+{
+ return std::make_shared<cCompoGenBiomal>(a_Seed);
+}
+
+
+
diff --git a/src/Generating/CompoGenBiomal.h b/src/Generating/CompoGenBiomal.h
new file mode 100644
index 000000000..a3a65d3dc
--- /dev/null
+++ b/src/Generating/CompoGenBiomal.h
@@ -0,0 +1,21 @@
+
+// CompoGenBiomal.h
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+
+
+
+
+
+/** Returns a new instance of the Biomal composition generator. */
+cTerrainCompositionGenPtr CreateCompoGenBiomal(int a_Seed);
+
+
+
+
diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp
index 169821050..bda45ad92 100644
--- a/src/Generating/ComposableGenerator.cpp
+++ b/src/Generating/ComposableGenerator.cpp
@@ -17,6 +17,10 @@
#include "StructGen.h"
#include "FinishGen.h"
+#include "CompoGenBiomal.h"
+
+#include "CompositedHeiGen.h"
+
#include "Caves.h"
#include "DistortedHeightmap.h"
#include "DungeonRoomsFinisher.h"
@@ -39,7 +43,7 @@
////////////////////////////////////////////////////////////////////////////////
// cTerrainCompositionGen:
-cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, cTerrainHeightGen & a_HeightGen, int a_Seed)
+cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, cTerrainShapeGenPtr a_ShapeGen, int a_Seed)
{
AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", "");
if (CompoGenName.empty())
@@ -48,59 +52,52 @@ cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile
CompoGenName = "Biomal";
}
- cTerrainCompositionGen * res = nullptr;
- if (NoCaseCompare(CompoGenName, "sameblock") == 0)
+ // Compositor list is alpha-sorted
+ cTerrainCompositionGenPtr res;
+ if (NoCaseCompare(CompoGenName, "Biomal") == 0)
+ {
+ res = CreateCompoGenBiomal(a_Seed);
+ }
+ else if (NoCaseCompare(CompoGenName, "BiomalNoise3D") == 0)
{
- res = new cCompoGenSameBlock;
+ // The composition that used to be provided with BiomalNoise3D is now provided by the Biomal compositor:
+ res = CreateCompoGenBiomal(a_Seed);
}
- else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0)
+ else if (NoCaseCompare(CompoGenName, "Classic") == 0)
{
- res = new cCompoGenDebugBiomes;
+ res = std::make_shared<cCompoGenClassic>();
}
- else if (NoCaseCompare(CompoGenName, "classic") == 0)
+ else if (NoCaseCompare(CompoGenName, "DebugBiomes") == 0)
{
- res = new cCompoGenClassic;
+ res = std::make_shared<cCompoGenDebugBiomes>();
}
else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0)
{
- res = new cDistortedHeightmap(a_Seed, a_BiomeGen);
+ // The composition that used to be provided with DistortedHeightmap is now provided by the Biomal compositor:
+ res = CreateCompoGenBiomal(a_Seed);
}
- else if (NoCaseCompare(CompoGenName, "end") == 0)
+ else if (NoCaseCompare(CompoGenName, "End") == 0)
{
- res = new cEndGen(a_Seed);
+ res = std::make_shared<cEndGen>(a_Seed);
}
- else if (NoCaseCompare(CompoGenName, "nether") == 0)
+ else if (NoCaseCompare(CompoGenName, "Nether") == 0)
{
- res = new cCompoGenNether(a_Seed);
+ res = std::make_shared<cCompoGenNether>(a_Seed);
}
else if (NoCaseCompare(CompoGenName, "Noise3D") == 0)
{
- res = new cNoise3DComposable(a_Seed);
+ // The composition that used to be provided with Noise3D is now provided by the Biomal compositor:
+ res = CreateCompoGenBiomal(a_Seed);
}
- else if (NoCaseCompare(CompoGenName, "biomal") == 0)
+ else if (NoCaseCompare(CompoGenName, "SameBlock") == 0)
{
- res = new cCompoGenBiomal(a_Seed);
-
- /*
- // Performance-testing:
- LOGINFO("Measuring performance of cCompoGenBiomal...");
- clock_t BeginTick = clock();
- for (int x = 0; x < 500; x++)
- {
- cChunkDesc Desc(200 + x * 8, 200 + x * 8);
- a_BiomeGen->GenBiomes(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetBiomeMap());
- a_HeightGen->GenHeightMap(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetHeightMap());
- res->ComposeTerrain(Desc);
- }
- clock_t Duration = clock() - BeginTick;
- LOGINFO("CompositionGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
- //*/
+ res = std::make_shared<cCompoGenSameBlock>();
}
else
{
LOGWARN("Unknown CompositionGen \"%s\", using \"Biomal\" instead.", CompoGenName.c_str());
a_IniFile.SetValue("Generator", "CompositionGen", "Biomal");
- return CreateCompositionGen(a_IniFile, a_BiomeGen, a_HeightGen, a_Seed);
+ return CreateCompositionGen(a_IniFile, a_BiomeGen, a_ShapeGen, a_Seed);
}
ASSERT(res != nullptr);
@@ -120,7 +117,7 @@ cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile
cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) :
super(a_ChunkGenerator),
m_BiomeGen(),
- m_HeightGen(),
+ m_ShapeGen(),
m_CompositionGen()
{
}
@@ -134,7 +131,7 @@ void cComposableGenerator::Initialize(cIniFile & a_IniFile)
super::Initialize(a_IniFile);
InitBiomeGen(a_IniFile);
- InitHeightGen(a_IniFile);
+ InitShapeGen(a_IniFile);
InitCompositionGen(a_IniFile);
InitFinishGens(a_IniFile);
}
@@ -162,16 +159,22 @@ void cComposableGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a
m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetBiomeMap());
}
+ cChunkDesc::Shape shape;
if (a_ChunkDesc.IsUsingDefaultHeight())
{
- m_HeightGen->GenHeightMap(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetHeightMap());
+ m_ShapeGen->GenShape(a_ChunkX, a_ChunkZ, shape);
+ a_ChunkDesc.SetHeightFromShape(shape);
+ }
+ else
+ {
+ // Convert the heightmap in a_ChunkDesc into shape:
+ a_ChunkDesc.GetShapeFromHeight(shape);
}
bool ShouldUpdateHeightmap = false;
if (a_ChunkDesc.IsUsingDefaultComposition())
{
- m_CompositionGen->ComposeTerrain(a_ChunkDesc);
- ShouldUpdateHeightmap = true;
+ m_CompositionGen->ComposeTerrain(a_ChunkDesc, shape);
}
if (a_ChunkDesc.IsUsingDefaultFinish())
@@ -230,13 +233,15 @@ void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile)
-void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile)
+void cComposableGenerator::InitShapeGen(cIniFile & a_IniFile)
{
bool CacheOffByDefault = false;
- m_HeightGen = cTerrainHeightGen::CreateHeightGen(a_IniFile, m_BiomeGen, m_ChunkGenerator.GetSeed(), CacheOffByDefault);
+ m_ShapeGen = cTerrainShapeGen::CreateShapeGen(a_IniFile, m_BiomeGen, m_ChunkGenerator.GetSeed(), CacheOffByDefault);
+ /*
+ // TODO
// Add a cache, if requested:
- int CacheSize = a_IniFile.GetValueSetI("Generator", "HeightGenCacheSize", CacheOffByDefault ? 0 : 64);
+ int CacheSize = a_IniFile.GetValueSetI("Generator", "ShapeGenCacheSize", CacheOffByDefault ? 0 : 64);
if (CacheSize > 0)
{
if (CacheSize < 4)
@@ -249,6 +254,7 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile)
LOGD("Using a cache for Heightgen of size %d.", CacheSize);
m_HeightGen = cTerrainHeightGenPtr(new cHeiGenCache(m_HeightGen, CacheSize));
}
+ */
}
@@ -257,13 +263,19 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile)
void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile)
{
- m_CompositionGen = cTerrainCompositionGen::CreateCompositionGen(a_IniFile, m_BiomeGen, *m_HeightGen, m_ChunkGenerator.GetSeed());
+ m_CompositionGen = cTerrainCompositionGen::CreateCompositionGen(a_IniFile, m_BiomeGen, m_ShapeGen, m_ChunkGenerator.GetSeed());
+ // Add a cache over the composition generator:
+ // Even a cache of size 1 is useful due to the CompositedHeiGen cache after us doing re-composition on its misses
int CompoGenCacheSize = a_IniFile.GetValueSetI("Generator", "CompositionGenCacheSize", 64);
- if (CompoGenCacheSize > 1)
+ if (CompoGenCacheSize > 0)
{
- m_CompositionGen = cTerrainCompositionGenPtr(new cCompoGenCache(m_CompositionGen, 32));
+ m_CompositionGen = std::make_shared<cCompoGenCache>(m_CompositionGen, CompoGenCacheSize);
}
+
+ // Create a cache of the composited heightmaps, so that finishers may use it:
+ m_CompositedHeightCache = std::make_shared<cHeiGenMultiCache>(std::make_shared<cCompositedHeiGen>(m_ShapeGen, m_CompositionGen), 16, 24);
+ // 24 subcaches of depth 16 each = 96 KiB of RAM. Acceptable, for the amount of work this saves.
}
@@ -282,7 +294,11 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
{
// Finishers, alpha-sorted:
- if (NoCaseCompare(*itr, "BottomLava") == 0)
+ if (NoCaseCompare(*itr, "Animals") == 0)
+ {
+ m_FinishGens.push_back(cFinishGenPtr(new cFinishGenPassiveMobs(Seed, a_IniFile, Dimension)));
+ }
+ else if (NoCaseCompare(*itr, "BottomLava") == 0)
{
int DefaultBottomLavaLevel = (Dimension == dimNether) ? 30 : 10;
int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel);
@@ -329,7 +345,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
int MaxSize = a_IniFile.GetValueSetI("Generator", "DungeonRoomsMaxSize", 7);
int MinSize = a_IniFile.GetValueSetI("Generator", "DungeonRoomsMinSize", 5);
AString HeightDistrib = a_IniFile.GetValueSet ("Generator", "DungeonRoomsHeightDistrib", "0, 0; 10, 10; 11, 500; 40, 500; 60, 40; 90, 1");
- m_FinishGens.push_back(cFinishGenPtr(new cDungeonRoomsFinisher(m_HeightGen, Seed, GridSize, MaxSize, MinSize, HeightDistrib)));
+ m_FinishGens.push_back(cFinishGenPtr(new cDungeonRoomsFinisher(m_ShapeGen, Seed, GridSize, MaxSize, MinSize, HeightDistrib)));
}
else if (NoCaseCompare(*itr, "Ice") == 0)
{
@@ -338,7 +354,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
else if (NoCaseCompare(*itr, "LavaLakes") == 0)
{
int Probability = a_IniFile.GetValueSetI("Generator", "LavaLakesProbability", 10);
- m_FinishGens.push_back(cFinishGenPtr(new cStructGenLakes(Seed * 5 + 16873, E_BLOCK_STATIONARY_LAVA, m_HeightGen, Probability)));
+ m_FinishGens.push_back(cFinishGenPtr(new cStructGenLakes(Seed * 5 + 16873, E_BLOCK_STATIONARY_LAVA, m_ShapeGen, Probability)));
}
else if (NoCaseCompare(*itr, "LavaSprings") == 0)
{
@@ -558,6 +574,10 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
GridSize, MaxOffset
)));
}
+ else if (NoCaseCompare(*itr, "SoulsandRims") == 0)
+ {
+ m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSoulsandRims(Seed)));
+ }
else if (NoCaseCompare(*itr, "Snow") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSnow));
@@ -576,7 +596,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
}
else if (NoCaseCompare(*itr, "Trees") == 0)
{
- m_FinishGens.push_back(cFinishGenPtr(new cStructGenTrees(Seed, m_BiomeGen, m_HeightGen, m_CompositionGen)));
+ m_FinishGens.push_back(cFinishGenPtr(new cStructGenTrees(Seed, m_BiomeGen, m_ShapeGen, m_CompositionGen)));
}
else if (NoCaseCompare(*itr, "UnderwaterBases") == 0)
{
@@ -584,7 +604,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
int MaxOffset = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxOffset", 128);
int MaxDepth = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxDepth", 7);
int MaxSize = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxSize", 128);
- m_FinishGens.push_back(cFinishGenPtr(new cUnderwaterBaseGen(Seed, GridSize, MaxOffset, MaxDepth, MaxSize, m_BiomeGen)));
+ m_FinishGens.push_back(std::make_shared<cUnderwaterBaseGen>(Seed, GridSize, MaxOffset, MaxDepth, MaxSize, m_BiomeGen));
}
else if (NoCaseCompare(*itr, "Villages") == 0)
{
@@ -594,12 +614,12 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
int MaxSize = a_IniFile.GetValueSetI("Generator", "VillageMaxSize", 128);
int MinDensity = a_IniFile.GetValueSetI("Generator", "VillageMinDensity", 50);
int MaxDensity = a_IniFile.GetValueSetI("Generator", "VillageMaxDensity", 80);
- m_FinishGens.push_back(cFinishGenPtr(new cVillageGen(Seed, GridSize, MaxOffset, MaxDepth, MaxSize, MinDensity, MaxDensity, m_BiomeGen, m_HeightGen)));
+ m_FinishGens.push_back(std::make_shared<cVillageGen>(Seed, GridSize, MaxOffset, MaxDepth, MaxSize, MinDensity, MaxDensity, m_BiomeGen, m_CompositedHeightCache));
}
else if (NoCaseCompare(*itr, "WaterLakes") == 0)
{
int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25);
- m_FinishGens.push_back(cFinishGenPtr(new cStructGenLakes(Seed * 3 + 652, E_BLOCK_STATIONARY_WATER, m_HeightGen, Probability)));
+ m_FinishGens.push_back(cFinishGenPtr(new cStructGenLakes(Seed * 3 + 652, E_BLOCK_STATIONARY_WATER, m_ShapeGen, Probability)));
}
else if (NoCaseCompare(*itr, "WaterSprings") == 0)
{
diff --git a/src/Generating/ComposableGenerator.h b/src/Generating/ComposableGenerator.h
index a091f8d53..86c30e090 100644
--- a/src/Generating/ComposableGenerator.h
+++ b/src/Generating/ComposableGenerator.h
@@ -26,20 +26,16 @@ See http://forum.mc-server.org/showthread.php?tid=409 for details.
// Forward-declare the shared pointers to subgenerator classes:
class cBiomeGen;
+class cTerrainShapeGen;
class cTerrainHeightGen;
class cTerrainCompositionGen;
class cFinishGen;
typedef SharedPtr<cBiomeGen> cBiomeGenPtr;
+typedef SharedPtr<cTerrainShapeGen> cTerrainShapeGenPtr;
typedef SharedPtr<cTerrainHeightGen> cTerrainHeightGenPtr;
typedef SharedPtr<cTerrainCompositionGen> cTerrainCompositionGenPtr;
typedef SharedPtr<cFinishGen> cFinishGenPtr;
-// fwd: Noise3DGenerator.h
-class cNoise3DComposable;
-
-// fwd: DistortedHeightmap.h
-class cDistortedHeightmap;
-
@@ -70,28 +66,54 @@ public:
-/** The interface that a terrain height generator must implement
-A terrain height generator takes chunk coords on input and outputs an array of terrain heights for that chunk.
-The output array is sequenced in the same way as the BiomeGen's biome data.
+/** The interface that a terrain shape generator must implement
+A terrain shape generator takes chunk coords on input and outputs a 3D array of "shape" for that chunk. The shape here
+represents the distinction between air and solid; there's no representation of Water since that is added by the
+composition geenrator.
+The output array is indexed [y + 256 * z + 16 * 256 * x], so that it's fast to later compose a single column of the terrain,
+which is the dominant operation following the shape generation.
The generator may request biome information from the underlying BiomeGen, it may even request information for
-other chunks than the one it's currently generating (possibly neighbors - for averaging)
+other chunks than the one it's currently generating (neighbors - for averaging)
*/
-class cTerrainHeightGen
+class cTerrainShapeGen
{
public:
- virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants
+ virtual ~cTerrainShapeGen() {} // Force a virtual destructor in descendants
- /** Generates heightmap for the given chunk */
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0;
+ /** Generates the shape for the given chunk */
+ virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) = 0;
/** Reads parameters from the ini file, prepares generator for use. */
- virtual void InitializeHeightGen(cIniFile & a_IniFile) {}
+ virtual void InitializeShapeGen(cIniFile & a_IniFile) {}
- /** Creates the correct TerrainHeightGen descendant based on the ini file settings and the seed provided.
- a_BiomeGen is the underlying biome generator, some height generators may depend on it to generate more biomes
+ /** Creates the correct TerrainShapeGen descendant based on the ini file settings and the seed provided.
+ a_BiomeGen is the underlying biome generator, some shape generators may depend on it providing additional biomes data around the chunk
a_CacheOffByDefault gets set to whether the cache should be disabled by default
- Implemented in HeiGen.cpp!
+ Implemented in ShapeGen.cpp!
*/
+ static cTerrainShapeGenPtr CreateShapeGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault);
+} ;
+
+
+
+
+
+/** The interface that is used to query terrain height from the shape generator.
+Usually the structure generators require only the final heightmap, and generating the whole shape only to
+consume the heightmap is wasteful, so this interface is used instead; it has a cache implemented over it so
+that data is retained. */
+class cTerrainHeightGen
+{
+public:
+ virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants
+
+ /** Retrieves the heightmap for the specified chunk. */
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0;
+
+ /** Initializes the generator, reading its parameters from the INI file. */
+ virtual void InitializeHeightGen(cIniFile & a_IniFile) {}
+
+ /** Creates a cTerrainHeightGen descendant based on the INI file settings. */
static cTerrainHeightGenPtr CreateHeightGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault);
} ;
@@ -109,16 +131,18 @@ class cTerrainCompositionGen
public:
virtual ~cTerrainCompositionGen() {} // Force a virtual destructor in descendants
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) = 0;
+ /** Generates the chunk's composition into a_ChunkDesc, using the terrain shape provided in a_Shape.
+ Is expected to fill a_ChunkDesc's heightmap with the data from a_Shape. */
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) = 0;
/** Reads parameters from the ini file, prepares generator for use. */
virtual void InitializeCompoGen(cIniFile & a_IniFile) {}
/** Creates the correct TerrainCompositionGen descendant based on the ini file settings and the seed provided.
- a_BiomeGen is the underlying biome generator, some composition generators may depend on it to generate more biomes
- a_HeightGen is the underlying height generator, some composition generators may depend on it providing additional values
+ a_BiomeGen is the underlying biome generator, some composition generators may depend on it providing additional biomes around the chunk
+ a_ShapeGen is the underlying shape generator, some composition generators may depend on it providing additional shape around the chunk
*/
- static cTerrainCompositionGenPtr CreateCompositionGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, cTerrainHeightGen & a_HeightGen, int a_Seed);
+ static cTerrainCompositionGenPtr CreateCompositionGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, cTerrainShapeGenPtr a_ShapeGen, int a_Seed);
} ;
@@ -128,7 +152,7 @@ public:
/** The interface that a finisher must implement
Finisher implements changes to the chunk after the rough terrain has been generated.
Examples of finishers are trees, snow, ore, lilypads and others.
-Note that a worldgenerator may contain multiple finishers.
+Note that a worldgenerator may contain multiple finishers, chained one after another.
Also note that previously we used to distinguish between a structuregen and a finisher; this distinction is
no longer relevant, all structure generators are considered finishers now (#398)
*/
@@ -154,23 +178,34 @@ class cComposableGenerator :
public:
cComposableGenerator(cChunkGenerator & a_ChunkGenerator);
+ // cChunkGenerator::cGenerator overrides:
virtual void Initialize(cIniFile & a_IniFile) override;
virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
protected:
- // The generation composition:
- cBiomeGenPtr m_BiomeGen;
- cTerrainHeightGenPtr m_HeightGen;
+ // The generator's composition:
+ /** The biome generator. */
+ cBiomeGenPtr m_BiomeGen;
+
+ /** The terrain shape generator. */
+ cTerrainShapeGenPtr m_ShapeGen;
+
+ /** The terrain composition generator. */
cTerrainCompositionGenPtr m_CompositionGen;
- cFinishGenList m_FinishGens;
+
+ /** The cache for the heights of the composited terrain. */
+ cTerrainHeightGenPtr m_CompositedHeightCache;
+
+ /** The finisher generators, in the order in which they are applied. */
+ cFinishGenList m_FinishGens;
- /** Reads the biome gen settings from the ini and initializes m_BiomeGen accordingly */
+ /** Reads the BiomeGen settings from the ini and initializes m_BiomeGen accordingly */
void InitBiomeGen(cIniFile & a_IniFile);
- /** Reads the HeightGen settings from the ini and initializes m_HeightGen accordingly */
- void InitHeightGen(cIniFile & a_IniFile);
+ /** Reads the ShapeGen settings from the ini and initializes m_ShapeGen accordingly */
+ void InitShapeGen(cIniFile & a_IniFile);
/** Reads the CompositionGen settings from the ini and initializes m_CompositionGen accordingly */
void InitCompositionGen(cIniFile & a_IniFile);
diff --git a/src/Generating/CompositedHeiGen.h b/src/Generating/CompositedHeiGen.h
new file mode 100644
index 000000000..fa33a7861
--- /dev/null
+++ b/src/Generating/CompositedHeiGen.h
@@ -0,0 +1,49 @@
+
+// CompositedHeiGen.h
+
+// Declares the cCompositedHeiGen class representing a cTerrainHeightGen descendant that calculates heightmap of the composited terrain
+// This is used to further cache heightmaps for chunks already generated for finishers that require only heightmap information
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+
+
+
+
+
+class cCompositedHeiGen:
+ public cTerrainHeightGen
+{
+public:
+ cCompositedHeiGen(cTerrainShapeGenPtr a_ShapeGen, cTerrainCompositionGenPtr a_CompositionGen):
+ m_ShapeGen(a_ShapeGen),
+ m_CompositionGen(a_CompositionGen)
+ {
+ }
+
+
+
+ // cTerrainheightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override
+ {
+ cChunkDesc::Shape shape;
+ m_ShapeGen->GenShape(a_ChunkX, a_ChunkZ, shape);
+ cChunkDesc desc(a_ChunkX, a_ChunkZ);
+ desc.SetHeightFromShape(shape);
+ m_CompositionGen->ComposeTerrain(desc, shape);
+ memcpy(a_HeightMap, desc.GetHeightMap(), sizeof(a_HeightMap));
+ }
+
+protected:
+ cTerrainShapeGenPtr m_ShapeGen;
+ cTerrainCompositionGenPtr m_CompositionGen;
+};
+
+
+
+
diff --git a/src/Generating/DistortedHeightmap.cpp b/src/Generating/DistortedHeightmap.cpp
index d5bc6ab55..37a51c18e 100644
--- a/src/Generating/DistortedHeightmap.cpp
+++ b/src/Generating/DistortedHeightmap.cpp
@@ -15,163 +15,6 @@
////////////////////////////////////////////////////////////////////////////////
-// cPattern:
-
-/// This class is used to store a column pattern initialized at runtime,
-/// so that the program doesn't need to explicitly set 256 values for each pattern
-/// Each pattern has 256 blocks so that there's no need to check pattern bounds when assigning the
-/// pattern - there will always be enough pattern left, even for the whole chunk height
-class cPattern
-{
-public:
- cPattern(cDistortedHeightmap::sBlockInfo * a_TopBlocks, size_t a_Count)
- {
- // Copy the pattern into the top:
- for (size_t i = 0; i < a_Count; i++)
- {
- m_Pattern[i] = a_TopBlocks[i];
- }
-
- // Fill the rest with stone:
- static cDistortedHeightmap::sBlockInfo Stone = {E_BLOCK_STONE, 0};
- for (size_t i = a_Count; i < cChunkDef::Height; i++)
- {
- m_Pattern[i] = Stone;
- }
- }
-
- const cDistortedHeightmap::sBlockInfo * Get(void) const { return m_Pattern; }
-
-protected:
- cDistortedHeightmap::sBlockInfo m_Pattern[cChunkDef::Height];
-} ;
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// The arrays to use for the top block pattern definitions:
-
-static cDistortedHeightmap::sBlockInfo tbGrass[] =
-{
- {E_BLOCK_GRASS, 0},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbSand[] =
-{
- { E_BLOCK_SAND, 0},
- { E_BLOCK_SAND, 0},
- { E_BLOCK_SAND, 0},
- { E_BLOCK_SANDSTONE, 0},
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbDirt[] =
-{
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbPodzol[] =
-{
- {E_BLOCK_DIRT, E_META_DIRT_PODZOL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbGrassLess[] =
-{
- {E_BLOCK_DIRT, E_META_DIRT_GRASSLESS},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbMycelium[] =
-{
- {E_BLOCK_MYCELIUM, 0},
- {E_BLOCK_DIRT, 0},
- {E_BLOCK_DIRT, 0},
- {E_BLOCK_DIRT, 0},
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbGravel[] =
-{
- {E_BLOCK_GRAVEL, 0},
- {E_BLOCK_GRAVEL, 0},
- {E_BLOCK_GRAVEL, 0},
- {E_BLOCK_STONE, 0},
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbStone[] =
-{
- {E_BLOCK_STONE, 0},
- {E_BLOCK_STONE, 0},
- {E_BLOCK_STONE, 0},
- {E_BLOCK_STONE, 0},
-} ;
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// Ocean floor pattern top-block definitions:
-
-static cDistortedHeightmap::sBlockInfo tbOFSand[] =
-{
- {E_BLOCK_SAND, 0},
- {E_BLOCK_SAND, 0},
- {E_BLOCK_SAND, 0},
- {E_BLOCK_SANDSTONE, 0}
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbOFClay[] =
-{
- { E_BLOCK_CLAY, 0},
- { E_BLOCK_CLAY, 0},
- { E_BLOCK_SAND, 0},
- { E_BLOCK_SAND, 0},
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbOFRedSand[] =
-{
- { E_BLOCK_SAND, E_META_SAND_RED},
- { E_BLOCK_SAND, E_META_SAND_RED},
- { E_BLOCK_SAND, E_META_SAND_RED},
- { E_BLOCK_SANDSTONE, 0},
-} ;
-
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// Individual patterns to use:
-
-static cPattern patGrass (tbGrass, ARRAYCOUNT(tbGrass));
-static cPattern patSand (tbSand, ARRAYCOUNT(tbSand));
-static cPattern patDirt (tbDirt, ARRAYCOUNT(tbDirt));
-static cPattern patPodzol (tbPodzol, ARRAYCOUNT(tbPodzol));
-static cPattern patGrassLess(tbGrassLess, ARRAYCOUNT(tbGrassLess));
-static cPattern patMycelium (tbMycelium, ARRAYCOUNT(tbMycelium));
-static cPattern patGravel (tbGravel, ARRAYCOUNT(tbGravel));
-static cPattern patStone (tbStone, ARRAYCOUNT(tbStone));
-
-static cPattern patOFSand (tbOFSand, ARRAYCOUNT(tbOFSand));
-static cPattern patOFClay (tbOFClay, ARRAYCOUNT(tbOFClay));
-static cPattern patOFRedSand(tbOFRedSand, ARRAYCOUNT(tbOFRedSand));
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
// cDistortedHeightmap:
/** This table assigns a relative maximum overhang size in each direction to biomes.
@@ -237,7 +80,7 @@ const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[256] =
{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 110 .. 119
{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 120 .. 128
- // Release 1.7 /* biome variants:
+ // Release 1.7 biome variants:
/* biSunflowerPlains */ { 1.0f, 1.0f}, // 129
/* biDesertM */ { 1.0f, 1.0f}, // 130
/* biExtremeHillsM */ {16.0f, 16.0f}, // 131
@@ -279,8 +122,8 @@ const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[256] =
cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGenPtr a_BiomeGen) :
m_NoiseDistortX(a_Seed + 1000),
m_NoiseDistortZ(a_Seed + 2000),
- m_OceanFloorSelect(a_Seed + 3000),
- m_MesaFloor(a_Seed + 4000),
+ m_CurChunkX(0x7fffffff), // Set impossible coords for the chunk so that it's always considered stale
+ m_CurChunkZ(0x7fffffff),
m_BiomeGen(a_BiomeGen),
m_UnderlyingHeiGen(new cHeiGenBiomal(a_Seed, a_BiomeGen)),
m_HeightGen(m_UnderlyingHeiGen, 64),
@@ -293,8 +136,6 @@ cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGenPtr a_BiomeGen) :
m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5);
m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1);
m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2);
-
- InitMesaPattern(a_Seed);
}
@@ -309,7 +150,7 @@ void cDistortedHeightmap::Initialize(cIniFile & a_IniFile)
}
// Read the params from the INI file:
- m_SeaLevel = a_IniFile.GetValueSetI("Generator", "DistortedHeightmapSeaLevel", 62);
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "SeaLevel", 62);
m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyX", 10);
m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyY", 10);
m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyZ", 10);
@@ -321,89 +162,6 @@ void cDistortedHeightmap::Initialize(cIniFile & a_IniFile)
-void cDistortedHeightmap::InitMesaPattern(int a_Seed)
-{
- // Stone in the bottom half of the pattern:
- for (int i = cChunkDef::Height; i < 2 * cChunkDef::Height; i++)
- {
- m_MesaPattern[i].BlockMeta = 0;
- m_MesaPattern[i].BlockType = E_BLOCK_STONE;
- }
-
- // Stained and hardened clay in the top half of the pattern
- // In a loop, choose whether to use one or two layers of stained clay, then choose a color and width for each layer
- // Separate each group with another layer of hardened clay
- cNoise PatternNoise((unsigned)a_Seed);
- static NIBBLETYPE AllowedColors[] =
- {
- E_META_STAINED_CLAY_YELLOW,
- E_META_STAINED_CLAY_YELLOW,
- E_META_STAINED_CLAY_RED,
- E_META_STAINED_CLAY_RED,
- E_META_STAINED_CLAY_WHITE,
- E_META_STAINED_CLAY_BROWN,
- E_META_STAINED_CLAY_BROWN,
- E_META_STAINED_CLAY_BROWN,
- E_META_STAINED_CLAY_ORANGE,
- E_META_STAINED_CLAY_ORANGE,
- E_META_STAINED_CLAY_ORANGE,
- E_META_STAINED_CLAY_ORANGE,
- E_META_STAINED_CLAY_ORANGE,
- E_META_STAINED_CLAY_ORANGE,
- E_META_STAINED_CLAY_LIGHTGRAY,
- } ;
- static int LayerSizes[] = // Adjust the chance so that thinner layers occur more commonly
- {
- 1, 1, 1, 1, 1, 1,
- 2, 2, 2, 2,
- 3, 3,
- } ;
- int Idx = cChunkDef::Height - 1;
- while (Idx >= 0)
- {
- // A layer group of 1 - 2 color stained clay:
- int Random = PatternNoise.IntNoise1DInt(Idx) / 7;
- int NumLayers = (Random % 2) + 1;
- Random /= 2;
- for (int Lay = 0; Lay < NumLayers; Lay++)
- {
- int NumBlocks = LayerSizes[(Random % ARRAYCOUNT(LayerSizes))];
- NIBBLETYPE Color = AllowedColors[(Random / 4) % ARRAYCOUNT(AllowedColors)];
- if (
- ((NumBlocks == 3) && (NumLayers == 2)) || // In two-layer mode disallow the 3-high layers:
- (Color == E_META_STAINED_CLAY_WHITE)) // White stained clay can ever be only 1 block high
- {
- NumBlocks = 1;
- }
- NumBlocks = std::min(Idx + 1, NumBlocks); // Limit by Idx so that we don't have to check inside the loop
- Random /= 32;
- for (int Block = 0; Block < NumBlocks; Block++, Idx--)
- {
- m_MesaPattern[Idx].BlockMeta = Color;
- m_MesaPattern[Idx].BlockType = E_BLOCK_STAINED_CLAY;
- } // for Block
- } // for Lay
-
- // A layer of hardened clay in between the layer group:
- int NumBlocks = (Random % 4) + 1; // All heights the same probability
- if ((NumLayers == 2) && (NumBlocks < 4))
- {
- // For two layers of stained clay, add an extra block of hardened clay:
- NumBlocks++;
- }
- NumBlocks = std::min(Idx + 1, NumBlocks); // Limit by Idx so that we don't have to check inside the loop
- for (int Block = 0; Block < NumBlocks; Block++, Idx--)
- {
- m_MesaPattern[Idx].BlockMeta = 0;
- m_MesaPattern[Idx].BlockType = E_BLOCK_HARDENED_CLAY;
- } // for Block
- } // while (Idx >= 0)
-}
-
-
-
-
-
void cDistortedHeightmap::PrepareState(int a_ChunkX, int a_ChunkZ)
{
if ((m_CurChunkX == a_ChunkX) && (m_CurChunkZ == a_ChunkZ))
@@ -474,23 +232,17 @@ void cDistortedHeightmap::GenerateHeightArray(void)
-void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+void cDistortedHeightmap::GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape)
{
PrepareState(a_ChunkX, a_ChunkZ);
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
- int NoiseArrayIdx = x + 17 * 257 * z;
- cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel - 1);
- for (int y = cChunkDef::Height - 1; y > m_SeaLevel - 1; y--)
+ int idx = x + 17 * 257 * z;
+ for (int y = 0; y < cChunkDef::Height; y++)
{
- int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y];
- if (y < HeightMapHeight)
- {
- cChunkDef::SetHeight(a_HeightMap, x, z, y);
- break;
- }
+ a_Shape[y + x * 256 + z * 16 * 256] = (y < m_DistortedHeightmap[idx + y * 17]) ? 1 : 0;
} // for y
} // for x
} // for z
@@ -500,36 +252,7 @@ void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::He
-void cDistortedHeightmap::InitializeHeightGen(cIniFile & a_IniFile)
-{
- Initialize(a_IniFile);
-}
-
-
-
-
-
-void cDistortedHeightmap::ComposeTerrain(cChunkDesc & a_ChunkDesc)
-{
- // Prepare the internal state for generating this chunk:
- PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
-
- // Compose:
- a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- ComposeColumn(a_ChunkDesc, x, z);
- } // for x
- } // for z
-}
-
-
-
-
-
-void cDistortedHeightmap::InitializeCompoGen(cIniFile & a_IniFile)
+void cDistortedHeightmap::InitializeShapeGen(cIniFile & a_IniFile)
{
Initialize(a_IniFile);
}
@@ -654,275 +377,3 @@ void cDistortedHeightmap::GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_R
-void cDistortedHeightmap::ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ)
-{
- // Frequencies for the podzol floor selecting noise:
- const NOISE_DATATYPE FrequencyX = 8;
- const NOISE_DATATYPE FrequencyZ = 8;
-
- EMCSBiome Biome = a_ChunkDesc.GetBiome(a_RelX, a_RelZ);
- switch (Biome)
- {
- case biOcean:
- case biPlains:
- case biForest:
- case biTaiga:
- case biSwampland:
- case biRiver:
- case biFrozenOcean:
- case biFrozenRiver:
- case biIcePlains:
- case biIceMountains:
- case biForestHills:
- case biTaigaHills:
- case biExtremeHillsEdge:
- case biExtremeHillsPlus:
- case biExtremeHills:
- case biJungle:
- case biJungleHills:
- case biJungleEdge:
- case biDeepOcean:
- case biStoneBeach:
- case biColdBeach:
- case biBirchForest:
- case biBirchForestHills:
- case biRoofedForest:
- case biColdTaiga:
- case biColdTaigaHills:
- case biSavanna:
- case biSavannaPlateau:
- case biSunflowerPlains:
- case biFlowerForest:
- case biTaigaM:
- case biSwamplandM:
- case biIcePlainsSpikes:
- case biJungleM:
- case biJungleEdgeM:
- case biBirchForestM:
- case biBirchForestHillsM:
- case biRoofedForestM:
- case biColdTaigaM:
- case biSavannaM:
- case biSavannaPlateauM:
- {
- FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patGrass.Get());
- return;
- }
-
- case biMegaTaiga:
- case biMegaTaigaHills:
- case biMegaSpruceTaiga:
- case biMegaSpruceTaigaHills:
- {
- // Select the pattern to use - podzol, grass or grassless dirt:
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX)) / FrequencyX;
- NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ;
- NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
- const sBlockInfo * Pattern = (Val < -0.9) ? patGrassLess.Get() : ((Val > 0) ? patPodzol.Get() : patGrass.Get());
- FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, Pattern);
- return;
- }
-
- case biDesertHills:
- case biDesert:
- case biDesertM:
- case biBeach:
- {
- FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patSand.Get());
- return;
- }
-
- case biMushroomIsland:
- case biMushroomShore:
- {
- FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patMycelium.Get());
- return;
- }
-
- case biMesa:
- case biMesaPlateauF:
- case biMesaPlateau:
- case biMesaBryce:
- case biMesaPlateauFM:
- case biMesaPlateauM:
- {
- // Mesa biomes need special handling, because they don't follow the usual "4 blocks from top pattern",
- // instead, they provide a "from bottom" pattern with varying base height,
- // usually 4 blocks below the ocean level
- FillColumnMesa(a_ChunkDesc, a_RelX, a_RelZ);
- return;
- }
-
- case biExtremeHillsPlusM:
- case biExtremeHillsM:
- {
- // Select the pattern to use - gravel, stone or grass:
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX)) / FrequencyX;
- NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ;
- NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
- const sBlockInfo * Pattern = (Val < 0.0) ? patStone.Get() : patGrass.Get();
- FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, Pattern);
- return;
- }
- default:
- {
- ASSERT(!"Unhandled biome");
- return;
- }
- } // switch (Biome)
-}
-
-
-
-
-
-void cDistortedHeightmap::FillColumnPattern(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ, const sBlockInfo * a_Pattern)
-{
- int NoiseArrayIdx = a_RelX + 17 * 257 * a_RelZ;
- bool HasHadWater = false;
- int PatternIdx = 0;
- for (int y = a_ChunkDesc.GetHeight(a_RelX, a_RelZ); y > 0; y--)
- {
- int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y];
-
- if (y < HeightMapHeight)
- {
- // "ground" part, use the pattern:
- a_ChunkDesc.SetBlockTypeMeta(a_RelX, y, a_RelZ, a_Pattern[PatternIdx].BlockType, a_Pattern[PatternIdx].BlockMeta);
- PatternIdx++;
- continue;
- }
-
- // "air" or "water" part:
- // Reset the pattern index to zero, so that the pattern is repeated from the top again:
- PatternIdx = 0;
-
- if (y >= m_SeaLevel)
- {
- // "air" part, do nothing
- continue;
- }
-
- a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
- if (HasHadWater)
- {
- continue;
- }
-
- // Select the ocean-floor pattern to use:
- a_Pattern = a_ChunkDesc.GetBiome(a_RelX, a_RelZ) == biDeepOcean ? patGravel.Get() : ChooseOceanFloorPattern(a_RelX, a_RelZ);
- HasHadWater = true;
- } // for y
- a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
-}
-
-
-
-
-
-void cDistortedHeightmap::FillColumnMesa(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ)
-{
- // Frequencies for the clay floor noise:
- const NOISE_DATATYPE FrequencyX = 50;
- const NOISE_DATATYPE FrequencyZ = 50;
-
- int Top = a_ChunkDesc.GetHeight(a_RelX, a_RelZ);
- if (Top < m_SeaLevel)
- {
- // The terrain is below sealevel, handle as regular ocean:
- FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patOFRedSand.Get());
- return;
- }
-
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX)) / FrequencyX;
- NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ;
- int ClayFloor = m_SeaLevel - 6 + (int)(4.f * m_MesaFloor.CubicNoise2D(NoiseX, NoiseY));
- if (ClayFloor >= Top)
- {
- ClayFloor = Top - 1;
- }
-
- if (Top - m_SeaLevel < 5)
- {
- // Simple case: top is red sand, then hardened clay down to ClayFloor, then stone:
- a_ChunkDesc.SetBlockTypeMeta(a_RelX, Top, a_RelZ, E_BLOCK_SAND, E_META_SAND_RED);
- for (int y = Top - 1; y >= ClayFloor; y--)
- {
- a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_HARDENED_CLAY);
- }
- for (int y = ClayFloor - 1; y > 0; y--)
- {
- a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STONE);
- }
- a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
- return;
- }
-
- // Difficult case: use the mesa pattern and watch for overhangs:
- int NoiseArrayIdx = a_RelX + 17 * 257 * a_RelZ;
- int PatternIdx = cChunkDef::Height - (Top - ClayFloor); // We want the block at index ClayFloor to be pattern's 256th block (first stone)
- const sBlockInfo * Pattern = m_MesaPattern;
- bool HasHadWater = false;
- for (int y = Top; y > 0; y--)
- {
- int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y];
- if (y < HeightMapHeight)
- {
- // "ground" part, use the pattern:
- a_ChunkDesc.SetBlockTypeMeta(a_RelX, y, a_RelZ, Pattern[PatternIdx].BlockType, Pattern[PatternIdx].BlockMeta);
- PatternIdx++;
- continue;
- }
-
- if (y >= m_SeaLevel)
- {
- // "air" part, do nothing
- continue;
- }
-
- // "water" part, fill with water and choose new pattern for ocean floor, if not chosen already:
- PatternIdx = 0;
- a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
- if (HasHadWater)
- {
- continue;
- }
-
- // Select the ocean-floor pattern to use:
- Pattern = ChooseOceanFloorPattern(a_RelX, a_RelZ);
- HasHadWater = true;
- } // for y
- a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
-}
-
-
-
-
-
-const cDistortedHeightmap::sBlockInfo * cDistortedHeightmap::ChooseOceanFloorPattern(int a_RelX, int a_RelZ)
-{
- // Frequencies for the ocean floor selecting noise:
- const NOISE_DATATYPE FrequencyX = 3;
- const NOISE_DATATYPE FrequencyZ = 3;
-
- // Select the ocean-floor pattern to use:
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX)) / FrequencyX;
- NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ;
- NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
- if (Val < -0.95)
- {
- return patOFClay.Get();
- }
- else if (Val < 0)
- {
- return patOFSand.Get();
- }
- else
- {
- return patDirt.Get();
- }
-}
-
-
-
-
diff --git a/src/Generating/DistortedHeightmap.h b/src/Generating/DistortedHeightmap.h
index d073f29e4..79fc35542 100644
--- a/src/Generating/DistortedHeightmap.h
+++ b/src/Generating/DistortedHeightmap.h
@@ -11,7 +11,6 @@
#include "ComposableGenerator.h"
#include "HeiGen.h"
-#include "../Noise.h"
@@ -24,17 +23,9 @@
class cDistortedHeightmap :
- public cTerrainHeightGen,
- public cTerrainCompositionGen
+ public cTerrainShapeGen
{
public:
- /// Structure used for storing block patterns for columns
- struct sBlockInfo
- {
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- } ;
-
cDistortedHeightmap(int a_Seed, cBiomeGenPtr a_BiomeGen);
protected:
@@ -52,8 +43,6 @@ protected:
cPerlinNoise m_NoiseDistortX;
cPerlinNoise m_NoiseDistortZ;
- cNoise m_OceanFloorSelect; ///< Used for selecting between dirt and sand on the ocean floor
- cNoise m_MesaFloor; ///< Used for the floor of the clay blocks in mesa biomes
int m_SeaLevel;
NOISE_DATATYPE m_FrequencyX;
@@ -71,9 +60,9 @@ protected:
cTerrainHeightGenPtr m_UnderlyingHeiGen;
/** Cache for m_UnderlyingHeiGen. */
- cHeiGenCache m_HeightGen;
+ cHeiGenCache m_HeightGen;
- /// Heightmap for the current chunk, before distortion (from m_HeightGen). Used for optimization.
+ /** Heightmap for the current chunk, before distortion (from m_HeightGen). Used for optimization. */
cChunkDef::HeightMap m_CurChunkHeights;
// Per-biome terrain generator parameters:
@@ -88,54 +77,30 @@ protected:
NOISE_DATATYPE m_DistortAmpX[DIM_X * DIM_Z];
NOISE_DATATYPE m_DistortAmpZ[DIM_X * DIM_Z];
- /// True if Initialize() has been called. Used to initialize-once even with multiple init entrypoints (HeiGen / CompoGen)
+ /** True if Initialize() has been called. Used to initialize-once even with multiple init entrypoints (HeiGen / CompoGen). */
bool m_IsInitialized;
- /// The vertical pattern to be used for mesa biomes. Seed-dependant.
- /// One Height of pattern and one Height of stone to avoid checking pattern dimensions
- sBlockInfo m_MesaPattern[2 * cChunkDef::Height];
-
- /// Initializes m_MesaPattern with a reasonable pattern of stained clay / hardened clay, based on the seed
- void InitMesaPattern(int a_Seed);
-
- /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap)
+ /** Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap). */
void PrepareState(int a_ChunkX, int a_ChunkZ);
- /// Generates the m_DistortedHeightmap array for the current chunk
+ /** Generates the m_DistortedHeightmap array for the current chunk. */
void GenerateHeightArray(void);
- /// Calculates the heightmap value (before distortion) at the specified (floating-point) coords
+ /** Calculates the heightmap value (before distortion) at the specified (floating-point) coords. */
int GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z);
- /// Updates m_DistortAmpX/Z[] based on m_CurChunkX and m_CurChunkZ
+ /** Updates m_DistortAmpX/Z[] based on m_CurChunkX and m_CurChunkZ. */
void UpdateDistortAmps(void);
- /// Calculates the X and Z distortion amplitudes based on the neighbors' biomes
+ /** Calculates the X and Z distortion amplitudes based on the neighbors' biomes. */
void GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ);
- /// Reads the settings from the ini file. Skips reading if already initialized
+ /** Reads the settings from the ini file. Skips reading if already initialized. */
void Initialize(cIniFile & a_IniFile);
- /// Composes a single column in a_ChunkDesc. Chooses what to do based on the biome in that column
- void ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ);
- /// Fills the specified column with the specified pattern; restarts the pattern when air is reached,
- /// switches to ocean floor pattern if ocean is reached. Always adds bedrock at the very bottom.
- void FillColumnPattern(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ, const sBlockInfo * a_Pattern);
-
- /// Fills the specified column with mesa pattern, based on the column height
- void FillColumnMesa(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ);
-
- /// Returns the pattern to use for an ocean floor in the specified column
- const sBlockInfo * ChooseOceanFloorPattern(int a_RelX, int a_RelZ);
-
-
- // cTerrainHeightGen overrides:
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
- virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
-
- // cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
- virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
+ // cTerrainShapeGen overrides:
+ virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override;
+ virtual void InitializeShapeGen(cIniFile & a_IniFile) override;
} ;
diff --git a/src/Generating/DungeonRoomsFinisher.cpp b/src/Generating/DungeonRoomsFinisher.cpp
index 3f328868d..092e232ab 100644
--- a/src/Generating/DungeonRoomsFinisher.cpp
+++ b/src/Generating/DungeonRoomsFinisher.cpp
@@ -7,6 +7,7 @@
#include "DungeonRoomsFinisher.h"
#include "../FastRandom.h"
#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/MobSpawnerEntity.h"
@@ -57,6 +58,22 @@ public:
int SecondChestPos = (FirstChestPos + 2 + (rnd % (NumPositions - 3))) % NumPositions;
m_Chest1 = DecodeChestCoords(FirstChestPos, SizeX, SizeZ);
m_Chest2 = DecodeChestCoords(SecondChestPos, SizeX, SizeZ);
+
+ // Choose what the mobspawner will spawn.
+ // 25% chance for a spider, 25% for a skeleton and 50% chance to get a zombie spawer.
+ int MobType = (a_Noise.IntNoise3DInt(a_OriginX, m_FloorHeight, a_OriginZ) / 7) % 100;
+ if (MobType <= 25)
+ {
+ m_MonsterType = mtSkeleton;
+ }
+ else if (MobType <= 50)
+ {
+ m_MonsterType = mtSpider;
+ }
+ else
+ {
+ m_MonsterType = mtZombie;
+ }
}
protected:
@@ -76,9 +93,12 @@ protected:
/** The (absolute) coords of the second chest. The Y coord represents the chest's Meta value (facing). */
Vector3i m_Chest2;
+ /** The monster type for the mobspawner entity. */
+ eMonsterType m_MonsterType;
- /** Decodes the position index along the room walls into a proper 2D position for a chest. */
+ /** Decodes the position index along the room walls into a proper 2D position for a chest.
+ The Y coord of the returned vector specifies the chest's meta value*/
Vector3i DecodeChestCoords(int a_PosIdx, int a_SizeX, int a_SizeZ)
{
if (a_PosIdx < a_SizeX)
@@ -245,7 +265,9 @@ protected:
)
{
a_ChunkDesc.SetBlockTypeMeta(CenterX, b, CenterZ, E_BLOCK_MOB_SPAWNER, 0);
- // TODO: Set the spawned mob
+ cMobSpawnerEntity * MobSpawner = static_cast<cMobSpawnerEntity *>(a_ChunkDesc.GetBlockEntity(CenterX, b, CenterZ));
+ ASSERT((MobSpawner != nullptr) && (MobSpawner->GetBlockType() == E_BLOCK_MOB_SPAWNER));
+ MobSpawner->SetEntity(m_MonsterType);
}
}
} ;
@@ -258,9 +280,9 @@ protected:
////////////////////////////////////////////////////////////////////////////////
// cDungeonRoomsFinisher:
-cDungeonRoomsFinisher::cDungeonRoomsFinisher(cTerrainHeightGenPtr a_HeightGen, int a_Seed, int a_GridSize, int a_MaxSize, int a_MinSize, const AString & a_HeightDistrib) :
+cDungeonRoomsFinisher::cDungeonRoomsFinisher(cTerrainShapeGenPtr a_ShapeGen, int a_Seed, int a_GridSize, int a_MaxSize, int a_MinSize, const AString & a_HeightDistrib) :
super(a_Seed + 100, a_GridSize, a_GridSize, a_GridSize, a_GridSize, a_MaxSize, a_MaxSize, 1024),
- m_HeightGen(a_HeightGen),
+ m_ShapeGen(a_ShapeGen),
m_MaxHalfSize((a_MaxSize + 1) / 2),
m_MinHalfSize((a_MinSize + 1) / 2),
m_HeightProbability(cChunkDef::Height)
@@ -293,13 +315,21 @@ cDungeonRoomsFinisher::cStructurePtr cDungeonRoomsFinisher::CreateStructure(int
int ChunkX, ChunkZ;
int RelX = a_OriginX, RelY = 0, RelZ = a_OriginZ;
cChunkDef::AbsoluteToRelative(RelX, RelY, RelZ, ChunkX, ChunkZ);
- cChunkDef::HeightMap HeightMap;
- m_HeightGen->GenHeightMap(ChunkX, ChunkZ, HeightMap);
- int Height = cChunkDef::GetHeight(HeightMap, RelX, RelZ); // Max room height at {a_OriginX, a_OriginZ}
- Height = Clamp(m_HeightProbability.MapValue(rnd % m_HeightProbability.GetSum()), 10, Height - 5);
+ cChunkDesc::Shape shape;
+ m_ShapeGen->GenShape(ChunkX, ChunkZ, shape);
+ int height = 0;
+ int idx = RelX * 256 + RelZ * 16 * 256;
+ for (int y = 6; y < cChunkDef::Height; y++)
+ {
+ if (shape[idx + y] != 0)
+ {
+ continue;
+ }
+ height = Clamp(m_HeightProbability.MapValue(rnd % m_HeightProbability.GetSum()), 10, y - 5);
+ }
// Create the dungeon room descriptor:
- return cStructurePtr(new cDungeonRoom(a_GridX, a_GridZ, a_OriginX, a_OriginZ, HalfSizeX, HalfSizeZ, Height, m_Noise));
+ return cStructurePtr(new cDungeonRoom(a_GridX, a_GridZ, a_OriginX, a_OriginZ, HalfSizeX, HalfSizeZ, height, m_Noise));
}
diff --git a/src/Generating/DungeonRoomsFinisher.h b/src/Generating/DungeonRoomsFinisher.h
index 09dd0448a..e5828f989 100644
--- a/src/Generating/DungeonRoomsFinisher.h
+++ b/src/Generating/DungeonRoomsFinisher.h
@@ -23,15 +23,15 @@ class cDungeonRoomsFinisher :
public:
/** Creates a new dungeon room finisher.
- a_HeightGen is the underlying height generator, so that the rooms can always be placed under the terrain.
+ a_ShapeGen is the underlying terrain shape generator, so that the rooms can always be placed under the terrain.
a_MaxSize and a_MinSize are the maximum and minimum sizes of the room's internal (air) area, in blocks across.
a_HeightDistrib is the string defining the height distribution for the rooms (cProbabDistrib format). */
- cDungeonRoomsFinisher(cTerrainHeightGenPtr a_HeightGen, int a_Seed, int a_GridSize, int a_MaxSize, int a_MinSize, const AString & a_HeightDistrib);
+ cDungeonRoomsFinisher(cTerrainShapeGenPtr a_ShapeGen, int a_Seed, int a_GridSize, int a_MaxSize, int a_MinSize, const AString & a_HeightDistrib);
protected:
- /** The height gen that is used for limiting the rooms' Y coords */
- cTerrainHeightGenPtr m_HeightGen;
+ /** The shape gen that is used for limiting the rooms' Y coords */
+ cTerrainShapeGenPtr m_ShapeGen;
/** Maximum half-size (from center to wall) of the dungeon room's inner (air) area. Default is 3 (vanilla). */
int m_MaxHalfSize;
diff --git a/src/Generating/EndGen.cpp b/src/Generating/EndGen.cpp
index 0111d2fa3..89d6117bb 100644
--- a/src/Generating/EndGen.cpp
+++ b/src/Generating/EndGen.cpp
@@ -147,13 +147,14 @@ bool cEndGen::IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ)
-void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+void cEndGen::GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape)
{
+ // If the chunk is outside out range, fill the shape with zeroes:
if (IsChunkOutsideRange(a_ChunkX, a_ChunkZ))
{
- for (size_t i = 0; i < ARRAYCOUNT(a_HeightMap); i++)
+ for (size_t i = 0; i < ARRAYCOUNT(a_Shape); i++)
{
- a_HeightMap[i] = 0;
+ a_Shape[i] = 0;
}
return;
}
@@ -165,15 +166,14 @@ void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_
{
for (int x = 0; x < cChunkDef::Width; x++)
{
- cChunkDef::SetHeight(a_HeightMap, x, z, MaxY);
- for (int y = MaxY; y > 0; y--)
+ for (int y = 0; y < MaxY; y++)
{
- if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0)
- {
- cChunkDef::SetHeight(a_HeightMap, x, z, y);
- break;
- }
- } // for y
+ a_Shape[(x + 16 * z) * 256 + y] = (m_NoiseArray[y * 17 * 17 + z * 17 + z] > 0) ? 1 : 0;
+ }
+ for (int y = MaxY; y < cChunkDef::Height; y++)
+ {
+ a_Shape[(x + 16 * z) * 256 + y] = 0;
+ }
} // for x
} // for z
}
@@ -182,30 +182,18 @@ void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_
-void cEndGen::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+void cEndGen::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
- if (IsChunkOutsideRange(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()))
- {
- a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
- return;
- }
-
- PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
-
- int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1);
+ a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
- for (int y = MaxY; y > 0; y--)
+ for (int y = 0; y < cChunkDef::Height; y++)
{
- if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0)
- {
- a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_END_STONE, 0);
- }
- else
+ if (a_Shape[(x + 16 * z) * 256 + y] != 0)
{
- a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_END_STONE);
}
} // for y
} // for x
diff --git a/src/Generating/EndGen.h b/src/Generating/EndGen.h
index 322061810..f9e3f6e53 100644
--- a/src/Generating/EndGen.h
+++ b/src/Generating/EndGen.h
@@ -10,14 +10,14 @@
#pragma once
#include "ComposableGenerator.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
class cEndGen :
- public cTerrainHeightGen,
+ public cTerrainShapeGen,
public cTerrainCompositionGen
{
public:
@@ -59,10 +59,10 @@ protected:
/// Returns true if the chunk is outside of the island's dimensions
bool IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ);
- // cTerrainHeightGen overrides:
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+ // cTerrainShapeGen overrides:
+ virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override;
// cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ;
diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp
index 18f8ee2bc..e10cb00f8 100644
--- a/src/Generating/FinishGen.cpp
+++ b/src/Generating/FinishGen.cpp
@@ -10,7 +10,6 @@
#include "Globals.h"
#include "FinishGen.h"
-#include "../Noise.h"
#include "../BlockID.h"
#include "../Simulator/FluidSimulator.h" // for cFluidSimulator::CanWashAway()
#include "../Simulator/FireSimulator.h"
@@ -27,6 +26,8 @@
#define DEF_OVERWORLD_LAVA_SPRINGS "0, 0; 10, 5; 11, 45; 48, 2; 64, 1; 255, 0"
#define DEF_END_WATER_SPRINGS "0, 1; 255, 1"
#define DEF_END_LAVA_SPRINGS "0, 1; 255, 1"
+#define DEF_ANIMAL_SPAWN_PERCENT 10
+#define DEF_NO_ANIMALS 0
@@ -66,7 +67,7 @@ void cFinishGenNetherClumpFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
{
continue;
}
-
+
// Choose what block to use.
NOISE_DATATYPE BlockType = m_Noise.IntNoise3D((int) ChunkX, y, (int) ChunkZ);
if (BlockType < -0.7)
@@ -196,10 +197,15 @@ void cFinishGenTallGrass::GenFinish(cChunkDesc & a_ChunkDesc)
{
continue;
}
-
+
// Get the top block + 1. This is the place where the grass would finaly be placed:
int y = a_ChunkDesc.GetHeight(x, z) + 1;
+ if (y >= 255)
+ {
+ continue;
+ }
+
// Check if long grass can be placed:
if (
(a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) ||
@@ -277,7 +283,7 @@ bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_
{
return false;
}
-
+
// All conditions met, place a sugarcane here:
a_ChunkDesc.SetBlockType(a_RelX, a_RelY + 1, a_RelZ, E_BLOCK_SUGARCANE);
return true;
@@ -290,7 +296,7 @@ bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_
void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
{
// Generate small foliage (1-block):
-
+
// TODO: Update heightmap with 1-block-tall foliage
for (int z = 0; z < cChunkDef::Width; z++)
{
@@ -315,7 +321,7 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
// WEIRD, since we're using heightmap, so there should NOT be anything above it
continue;
}
-
+
const float xx = (float)BlockX;
float val1 = m_Noise.CubicNoise2D(xx * 0.1f, zz * 0.1f);
float val2 = m_Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f);
@@ -355,7 +361,7 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
}
break;
} // case E_BLOCK_GRASS
-
+
case E_BLOCK_SAND:
{
int y = Top + 1;
@@ -366,7 +372,8 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
(a_ChunkDesc.GetBlockType(x + 1, y, z) == E_BLOCK_AIR) &&
(a_ChunkDesc.GetBlockType(x - 1, y, z) == E_BLOCK_AIR) &&
(a_ChunkDesc.GetBlockType(x, y, z + 1) == E_BLOCK_AIR) &&
- (a_ChunkDesc.GetBlockType(x, y, z - 1) == E_BLOCK_AIR)
+ (a_ChunkDesc.GetBlockType(x, y, z - 1) == E_BLOCK_AIR) &&
+ IsDesertVariant(a_ChunkDesc.GetBiome(x, z))
)
{
a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_CACTUS);
@@ -387,6 +394,72 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
+bool cFinishGenSprinkleFoliage::IsDesertVariant(EMCSBiome a_Biome)
+{
+ return
+ (
+ (a_Biome == biDesertHills) ||
+ (a_Biome == biDesert) ||
+ (a_Biome == biDesertM)
+ );
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cFinishGenSoulsandRims
+
+void cFinishGenSoulsandRims::GenFinish(cChunkDesc & a_ChunkDesc)
+{
+ int ChunkX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int ChunkZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight();
+
+ for (int x = 0; x < 16; x++)
+ {
+ int xx = ChunkX + x;
+ for (int z = 0; z < 16; z++)
+ {
+ int zz = ChunkZ + z;
+
+ // Place soulsand rims when netherrack gets thin
+ for (int y = 2; y < MaxHeight - 2; y++)
+ {
+ // The current block is air. Let's bail ut.
+ BLOCKTYPE Block = a_ChunkDesc.GetBlockType(x, y, z);
+ if (Block == E_BLOCK_AIR)
+ {
+ continue;
+ }
+
+ if (
+ ((a_ChunkDesc.GetBlockType(x, y + 1, z) != E_BLOCK_AIR) &&
+ ( a_ChunkDesc.GetBlockType(x, y + 2, z) != E_BLOCK_AIR)) ||
+ ((a_ChunkDesc.GetBlockType(x, y - 1, z) != E_BLOCK_AIR) &&
+ ( a_ChunkDesc.GetBlockType(x, y - 2, z) != E_BLOCK_AIR))
+ )
+ {
+ continue;
+ }
+
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(xx)) / 32;
+ NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(zz)) / 32;
+ NOISE_DATATYPE CompBlock = m_Noise.CubicNoise3D(NoiseX, (float) (y) / 4, NoiseY);
+ if (CompBlock < 0)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SOULSAND);
+ }
+ }
+ }
+ }
+}
+
+
+
+
+
////////////////////////////////////////////////////////////////////////////////
// cFinishGenSnow:
@@ -407,7 +480,7 @@ void cFinishGenSnow::GenFinish(cChunkDesc & a_ChunkDesc)
case biFrozenOcean:
{
int Height = a_ChunkDesc.GetHeight(x, z);
- if (cBlockInfo::IsSnowable(a_ChunkDesc.GetBlockType(x, Height, z)))
+ if (cBlockInfo::IsSnowable(a_ChunkDesc.GetBlockType(x, Height, z)) && (Height < cChunkDef::Height - 1))
{
a_ChunkDesc.SetBlockType(x, Height + 1, z, E_BLOCK_SNOW);
a_ChunkDesc.SetHeight(x, z, Height + 1);
@@ -512,7 +585,7 @@ void cFinishGenSingleTopBlock::GenFinish(cChunkDesc & a_ChunkDesc)
}
int Height = a_ChunkDesc.GetHeight(x, z);
- if (Height >= cChunkDef::Height)
+ if (Height >= cChunkDef::Height - 1)
{
// Too high up
continue;
@@ -712,7 +785,7 @@ void cFinishGenPreSimulator::StationarizeFluid(
} // for y
} // for x
} // for z
-
+
// Turn fluid at the chunk edges into non-stationary fluid:
for (int y = 0; y < cChunkDef::Height; y++)
{
@@ -804,12 +877,12 @@ void cFinishGenFluidSprings::GenFinish(cChunkDesc & a_ChunkDesc)
// Not in this chunk
return;
}
-
+
// Get the height at which to try:
int Height = m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 1024, 256 * a_ChunkDesc.GetChunkZ()) / 11;
Height %= m_HeightDistribution.GetSum();
Height = m_HeightDistribution.MapValue(Height);
-
+
// Try adding the spring at the height, if unsuccessful, move lower:
for (int y = Height; y > 1; y--)
{
@@ -847,7 +920,7 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int
{
return false;
}
-
+
static const struct
{
int x, y, z;
@@ -878,7 +951,7 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int
{
return false;
}
-
+
// Has exactly one air neighbor, place a spring:
a_ChunkDesc.SetBlockTypeMeta(x, y, z, m_Fluid, 0);
return true;
@@ -887,3 +960,236 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int
+
+////////////////////////////////////////////////////////////////////////////////
+// cFinishGenPassiveMobs:
+
+cFinishGenPassiveMobs::cFinishGenPassiveMobs(int a_Seed, cIniFile & a_IniFile, eDimension a_Dimension) :
+ m_Noise(a_Seed)
+{
+ AString SectionName = "Animals";
+ int DefaultAnimalSpawnChunkPercentage = DEF_ANIMAL_SPAWN_PERCENT;
+ switch (a_Dimension)
+ {
+ case dimOverworld:
+ {
+ DefaultAnimalSpawnChunkPercentage = DEF_ANIMAL_SPAWN_PERCENT;
+ break;
+ }
+ case dimNether:
+ case dimEnd: // No nether or end animals (currently)
+ {
+ DefaultAnimalSpawnChunkPercentage = DEF_NO_ANIMALS;
+ break;
+ }
+ default:
+ {
+ ASSERT(!"Unhandled world dimension");
+ DefaultAnimalSpawnChunkPercentage = DEF_NO_ANIMALS;
+ break;
+ }
+ } // switch (dimension)
+ m_AnimalProbability = a_IniFile.GetValueSetI(SectionName, "AnimalSpawnChunkPercentage", DefaultAnimalSpawnChunkPercentage);
+ if ((m_AnimalProbability < 0) || (m_AnimalProbability > 100))
+ {
+ LOGWARNING("[Animals]: AnimalSpawnChunkPercentage is invalid, using the default of \"%d\".", DefaultAnimalSpawnChunkPercentage);
+ m_AnimalProbability = DefaultAnimalSpawnChunkPercentage;
+ }
+}
+
+
+
+
+
+void cFinishGenPassiveMobs::GenFinish(cChunkDesc & a_ChunkDesc)
+{
+ int chunkX = a_ChunkDesc.GetChunkX();
+ int chunkZ = a_ChunkDesc.GetChunkZ();
+ int ChanceRnd = (m_Noise.IntNoise2DInt(chunkX, chunkZ) / 7) % 100;
+ if (ChanceRnd > m_AnimalProbability)
+ {
+ return;
+ }
+
+ eMonsterType RandomMob = GetRandomMob(a_ChunkDesc);
+ if (RandomMob == mtInvalidType)
+ {
+ // No mobs here. Don't send an error, because if the biome was a desert it would return mtInvalidType as well.
+ return;
+ }
+
+ // Try spawning a pack center 10 times, should get roughly the same probability
+ for (int Tries = 0; Tries < 10; Tries++)
+ {
+ int PackCenterX = (m_Noise.IntNoise2DInt(chunkX + chunkZ, Tries) / 7) % cChunkDef::Width;
+ int PackCenterZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ + Tries) / 7) % cChunkDef::Width;
+ if (TrySpawnAnimals(a_ChunkDesc, PackCenterX, a_ChunkDesc.GetHeight(PackCenterX, PackCenterZ), PackCenterZ, RandomMob))
+ {
+ for (int i = 0; i < 3; i++)
+ {
+ int OffsetX = (m_Noise.IntNoise2DInt(chunkX + chunkZ + i, Tries) / 7) % cChunkDef::Width;
+ int OffsetZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ + Tries + i) / 7) % cChunkDef::Width;
+ TrySpawnAnimals(a_ChunkDesc, OffsetX, a_ChunkDesc.GetHeight(OffsetX, OffsetZ), OffsetZ, RandomMob);
+ }
+ return;
+
+ } // if pack center spawn successful
+ } // for tries
+}
+
+
+
+
+
+bool cFinishGenPassiveMobs::TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, eMonsterType AnimalToSpawn)
+{
+ if ((a_RelY >= cChunkDef::Height - 1) || (a_RelY <= 0))
+ {
+ return false;
+ }
+
+ BLOCKTYPE BlockAtHead = a_ChunkDesc.GetBlockType(a_RelX, a_RelY + 1, a_RelZ);
+ BLOCKTYPE BlockAtFeet = a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ);
+ BLOCKTYPE BlockUnderFeet = a_ChunkDesc.GetBlockType(a_RelX, a_RelY - 1, a_RelZ);
+
+ // Check block below (opaque, grass, water), and above (air)
+ if ((AnimalToSpawn == mtSquid) && (BlockAtFeet != E_BLOCK_WATER))
+ {
+ return false;
+ }
+ if (
+ (AnimalToSpawn != mtSquid) &&
+ (BlockAtHead != E_BLOCK_AIR) &&
+ (BlockAtFeet != E_BLOCK_AIR) &&
+ (!cBlockInfo::IsTransparent(BlockUnderFeet))
+ )
+ {
+ return false;
+ }
+ if (
+ (BlockUnderFeet != E_BLOCK_GRASS) &&
+ ((AnimalToSpawn == mtSheep) || (AnimalToSpawn == mtChicken) || (AnimalToSpawn == mtPig))
+ )
+ {
+ return false;
+ }
+ if ((AnimalToSpawn == mtMooshroom) && (BlockUnderFeet != E_BLOCK_MYCELIUM))
+ {
+ return false;
+ }
+
+ double AnimalX = static_cast<double>(a_ChunkDesc.GetChunkX() * cChunkDef::Width + a_RelX + 0.5);
+ double AnimalY = a_RelY;
+ double AnimalZ = static_cast<double>(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ + 0.5);
+
+ cMonster * NewMob = cMonster::NewMonsterFromType(AnimalToSpawn);
+ NewMob->SetPosition(AnimalX, AnimalY, AnimalZ);
+ a_ChunkDesc.GetEntities().push_back(NewMob);
+ LOGD("Spawning %s #%i at {%.02f, %.02f, %.02f}", NewMob->GetClass(), NewMob->GetUniqueID(), AnimalX, AnimalY, AnimalZ);
+
+ return true;
+}
+
+
+
+
+
+eMonsterType cFinishGenPassiveMobs::GetRandomMob(cChunkDesc & a_ChunkDesc)
+{
+
+ std::set<eMonsterType> ListOfSpawnables;
+ int chunkX = a_ChunkDesc.GetChunkX();
+ int chunkZ = a_ChunkDesc.GetChunkZ();
+ int x = (m_Noise.IntNoise2DInt(chunkX, chunkZ + 10) / 7) % cChunkDef::Width;
+ int z = (m_Noise.IntNoise2DInt(chunkX + chunkZ, chunkZ) / 7) % cChunkDef::Width;
+
+ // Check biomes first to get a list of animals
+ switch (a_ChunkDesc.GetBiome(x, z))
+ {
+ // No animals in deserts or non-overworld dimensions
+ case biNether:
+ case biEnd:
+ case biDesertHills:
+ case biDesert:
+ case biDesertM:
+ {
+ return mtInvalidType;
+ }
+
+ // Mooshroom only - no other mobs on mushroom islands
+ case biMushroomIsland:
+ case biMushroomShore:
+ {
+ return mtMooshroom;
+ }
+
+ // Add squid in ocean biomes
+ case biOcean:
+ case biFrozenOcean:
+ case biFrozenRiver:
+ case biRiver:
+ case biDeepOcean:
+ {
+ ListOfSpawnables.insert(mtSquid);
+ break;
+ }
+
+ // Add ocelots in jungle biomes
+ case biJungle:
+ case biJungleHills:
+ case biJungleEdge:
+ case biJungleM:
+ case biJungleEdgeM:
+ {
+ ListOfSpawnables.insert(mtOcelot);
+ break;
+ }
+
+ // Add horses in plains-like biomes
+ case biPlains:
+ case biSunflowerPlains:
+ case biSavanna:
+ case biSavannaPlateau:
+ case biSavannaM:
+ case biSavannaPlateauM:
+ {
+ ListOfSpawnables.insert(mtHorse);
+ break;
+ }
+
+ // Add wolves in forest and spruce forests
+ case biForest:
+ case biTaiga:
+ case biMegaTaiga:
+ case biColdTaiga:
+ case biColdTaigaM:
+ {
+ ListOfSpawnables.insert(mtWolf);
+ break;
+ }
+ // Nothing special about this biome
+ default:
+ {
+ break;
+ }
+ }
+ ListOfSpawnables.insert(mtChicken);
+ ListOfSpawnables.insert(mtCow);
+ ListOfSpawnables.insert(mtPig);
+ ListOfSpawnables.insert(mtSheep);
+
+ if (ListOfSpawnables.empty())
+ {
+ return mtInvalidType;
+ }
+
+ int RandMob = (m_Noise.IntNoise2DInt(chunkX - chunkZ + 2, chunkX + 5) / 7) % ListOfSpawnables.size();
+ auto MobIter = ListOfSpawnables.begin();
+ std::advance(MobIter, RandMob);
+
+ return *MobIter;
+}
+
+
+
+
diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h
index 4a08d70c8..ae6dee590 100644
--- a/src/Generating/FinishGen.h
+++ b/src/Generating/FinishGen.h
@@ -16,8 +16,9 @@
#include "ComposableGenerator.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
#include "../ProbabDistrib.h"
+#include "../Mobs/Monster.h"
@@ -117,19 +118,41 @@ protected:
+class cFinishGenSoulsandRims :
+ public cFinishGen
+{
+public:
+ cFinishGenSoulsandRims(int a_Seed) :
+ m_Noise(a_Seed)
+ {
+ }
+
+protected:
+ cNoise m_Noise;
+
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
class cFinishGenSprinkleFoliage :
public cFinishGen
{
public:
cFinishGenSprinkleFoliage(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {}
-
+
protected:
cNoise m_Noise;
int m_Seed;
-
+
/// Tries to place sugarcane at the coords specified, returns true if successful
bool TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ);
-
+
+ // Returns true is the specified biome is a desert or its variant
+ static bool IsDesertVariant(EMCSBiome a_biome);
+
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
@@ -167,31 +190,31 @@ public:
{
m_IsAllowedBelow[idx] = false;
}
-
+
// Load the allowed blocks into m_IsAllowedBelow
for (BlockList::iterator itr = a_AllowedBelow.begin(); itr != a_AllowedBelow.end(); ++itr)
{
m_IsAllowedBelow[*itr] = true;
}
-
+
// Initialize all the biome types.
for (size_t idx = 0; idx < ARRAYCOUNT(m_IsBiomeAllowed); ++idx)
{
m_IsBiomeAllowed[idx] = false;
}
-
+
// Load the allowed biomes into m_IsBiomeAllowed
for (BiomeList::iterator itr = a_Biomes.begin(); itr != a_Biomes.end(); ++itr)
{
m_IsBiomeAllowed[*itr] = true;
}
}
-
+
protected:
cNoise m_Noise;
BLOCKTYPE m_BlockType;
int m_Amount; ///< Relative amount of blocks to try adding. 1 = one block per 256 biome columns.
-
+
int GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap);
// Returns true if the given biome is a biome that is allowed.
@@ -206,7 +229,7 @@ protected:
return m_IsAllowedBelow[a_BlockBelow];
}
-
+
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
@@ -223,11 +246,11 @@ public:
m_Level(a_Level)
{
}
-
+
int GetLevel(void) const { return m_Level; }
protected:
int m_Level;
-
+
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
@@ -241,7 +264,7 @@ class cFinishGenPreSimulator :
{
public:
cFinishGenPreSimulator(bool a_PreSimulateFallingBlocks, bool a_PreSimulateWater, bool a_PreSimulateLava);
-
+
protected:
bool m_PreSimulateFallingBlocks;
@@ -253,7 +276,7 @@ protected:
cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
cChunkDef::HeightMap & a_HeightMap // Height map to update by the current data
);
-
+
/** For each fluid block:
- if all surroundings are of the same fluid, makes it stationary; otherwise makes it flowing (excl. top)
- all fluid on the chunk's edge is made flowing
@@ -278,7 +301,7 @@ class cFinishGenFluidSprings :
{
public:
cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, eDimension a_Dimension);
-
+
protected:
cNoise m_Noise;
@@ -289,10 +312,43 @@ protected:
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
- /// Tries to place a spring at the specified coords, checks neighbors. Returns true if successful
+ /** Tries to place a spring at the specified coords, checks neighbors. Returns true if successful. */
bool TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z);
} ;
+
+/** This class populates generated chunks with packs of biome-dependant animals
+Animals: cows, sheep, pigs, mooshrooms, squid, horses, wolves, ocelots */
+class cFinishGenPassiveMobs :
+ public cFinishGen
+{
+public:
+
+ cFinishGenPassiveMobs(int a_Seed, cIniFile & a_IniFile, eDimension a_Dimension);
+
+protected:
+
+ /** The noise used as the source of randomness */
+ cNoise m_Noise;
+
+ /** Chance, [0..100], that an animal pack will be generated in a chunk */
+ int m_AnimalProbability;
+
+
+ // cFinishGen override:
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
+
+ /** Returns false if an animal cannot spawn at given coords, else adds it to the chunk's entity list and returns true */
+ bool TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int x, int y, int z, eMonsterType AnimalToSpawn);
+
+ /** Picks a random animal from biome-dependant list for a random position in the chunk.
+ Returns the chosen mob type, or mtInvalid if no mob chosen. */
+ eMonsterType GetRandomMob(cChunkDesc & a_ChunkDesc);
+} ;
+
+
+
+
diff --git a/src/Generating/GridStructGen.h b/src/Generating/GridStructGen.h
index 03131fce9..b92fb2e9d 100644
--- a/src/Generating/GridStructGen.h
+++ b/src/Generating/GridStructGen.h
@@ -10,7 +10,7 @@
#pragma once
#include "ComposableGenerator.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp
index 28a5698e4..61d087c17 100644
--- a/src/Generating/HeiGen.cpp
+++ b/src/Generating/HeiGen.cpp
@@ -15,7 +15,6 @@
-
////////////////////////////////////////////////////////////////////////////////
// cHeiGenFlat:
@@ -133,15 +132,6 @@ void cHeiGenCache::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap
-void cHeiGenCache::InitializeHeightGen(cIniFile & a_IniFile)
-{
- m_HeiGenToCache->InitializeHeightGen(a_IniFile);
-}
-
-
-
-
-
bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height)
{
for (int i = 0; i < m_CacheSize; i++)
@@ -160,6 +150,51 @@ bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_Rel
////////////////////////////////////////////////////////////////////////////////
+// cHeiGenMultiCache:
+
+cHeiGenMultiCache::cHeiGenMultiCache(cTerrainHeightGenPtr a_HeiGenToCache, size_t a_SubCacheSize, size_t a_NumSubCaches):
+ m_NumSubCaches(a_NumSubCaches)
+{
+ // Create the individual sub-caches:
+ m_SubCaches.reserve(a_NumSubCaches);
+ for (size_t i = 0; i < a_NumSubCaches; i++)
+ {
+ m_SubCaches.push_back(std::make_shared<cHeiGenCache>(a_HeiGenToCache, a_SubCacheSize));
+ }
+}
+
+
+
+
+
+void cHeiGenMultiCache::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+{
+ // Get the subcache responsible for this chunk:
+ const size_t cacheIdx = ((size_t)a_ChunkX + m_CoeffZ * (size_t)a_ChunkZ) % m_NumSubCaches;
+
+ // Ask the subcache:
+ m_SubCaches[cacheIdx]->GenHeightMap(a_ChunkX, a_ChunkZ, a_HeightMap);
+}
+
+
+
+
+
+bool cHeiGenMultiCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height)
+{
+ // Get the subcache responsible for this chunk:
+ const size_t cacheIdx = ((size_t)a_ChunkX + m_CoeffZ * (size_t)a_ChunkZ) % m_NumSubCaches;
+
+ // Ask the subcache:
+ return m_SubCaches[cacheIdx]->GetHeightAt(a_ChunkX, a_ChunkZ, a_RelX, a_RelZ, a_Height);
+}
+
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
// cHeiGenClassic:
cHeiGenClassic::cHeiGenClassic(int a_Seed) :
@@ -750,39 +785,51 @@ cTerrainHeightGenPtr cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cB
}
a_CacheOffByDefault = false;
- cTerrainHeightGen * res = nullptr;
- if (NoCaseCompare(HeightGenName, "flat") == 0)
+ cTerrainHeightGenPtr res;
+ if (NoCaseCompare(HeightGenName, "Flat") == 0)
{
- res = new cHeiGenFlat;
+ res = std::make_shared<cHeiGenFlat>();
a_CacheOffByDefault = true; // We're generating faster than a cache would retrieve data
}
else if (NoCaseCompare(HeightGenName, "classic") == 0)
{
- res = new cHeiGenClassic(a_Seed);
+ res = std::make_shared<cHeiGenClassic>(a_Seed);
}
else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0)
{
- res = new cDistortedHeightmap(a_Seed, a_BiomeGen);
+ // Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it
+ // Return an empty pointer, the caller will create the proper generator:
+ return cTerrainHeightGenPtr();
}
else if (NoCaseCompare(HeightGenName, "End") == 0)
{
- res = new cEndGen(a_Seed);
+ // Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it
+ // Return an empty pointer, the caller will create the proper generator:
+ return cTerrainHeightGenPtr();
}
else if (NoCaseCompare(HeightGenName, "MinMax") == 0)
{
- res = new cHeiGenMinMax(a_Seed, a_BiomeGen);
+ res = std::make_shared<cHeiGenMinMax>(a_Seed, a_BiomeGen);
}
else if (NoCaseCompare(HeightGenName, "Mountains") == 0)
{
- res = new cHeiGenMountains(a_Seed);
+ res = std::make_shared<cHeiGenMountains>(a_Seed);
+ }
+ else if (NoCaseCompare(HeightGenName, "BiomalNoise3D") == 0)
+ {
+ // Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it
+ // Return an empty pointer, the caller will create the proper generator:
+ return cTerrainHeightGenPtr();
}
else if (NoCaseCompare(HeightGenName, "Noise3D") == 0)
{
- res = new cNoise3DComposable(a_Seed);
+ // Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it
+ // Return an empty pointer, the caller will create the proper generator:
+ return cTerrainHeightGenPtr();
}
- else if (NoCaseCompare(HeightGenName, "biomal") == 0)
+ else if (NoCaseCompare(HeightGenName, "Biomal") == 0)
{
- res = new cHeiGenBiomal(a_Seed, a_BiomeGen);
+ res = std::make_shared<cHeiGenBiomal>(a_Seed, a_BiomeGen);
/*
// Performance-testing:
@@ -801,15 +848,14 @@ cTerrainHeightGenPtr cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cB
{
// No match found, force-set the default and retry
LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str());
- a_IniFile.DeleteValue("Generator", "HeightGen");
a_IniFile.SetValue("Generator", "HeightGen", "Biomal");
return CreateHeightGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault);
}
// Read the settings:
res->InitializeHeightGen(a_IniFile);
-
- return cTerrainHeightGenPtr(res);
+
+ return res;
}
diff --git a/src/Generating/HeiGen.h b/src/Generating/HeiGen.h
index 6ae5ba362..62bb227c6 100644
--- a/src/Generating/HeiGen.h
+++ b/src/Generating/HeiGen.h
@@ -2,10 +2,12 @@
// HeiGen.h
/*
-Interfaces to the various height generators:
+Interfaces to the various height-based terrain shape generators:
- cHeiGenFlat
- cHeiGenClassic
- cHeiGenBiomal
+
+Also implements the heightmap cache
*/
@@ -15,32 +17,13 @@ Interfaces to the various height generators:
#pragma once
#include "ComposableGenerator.h"
-#include "../Noise.h"
-
-
-
-
-
-class cHeiGenFlat :
- public cTerrainHeightGen
-{
-public:
- cHeiGenFlat(void) : m_Height(5) {}
-
-protected:
-
- int m_Height;
-
- // cTerrainHeightGen overrides:
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
- virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
-} ;
+#include "../Noise/Noise.h"
-/// A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation
+/** A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation */
class cHeiGenCache :
public cTerrainHeightGen
{
@@ -50,15 +33,11 @@ public:
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
- virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
- /// Retrieves height at the specified point in the cache, returns true if found, false if not found
+ /** Retrieves height at the specified point in the cache, returns true if found, false if not found */
bool GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height);
protected:
-
- cTerrainHeightGenPtr m_HeiGenToCache;
-
struct sCacheData
{
int m_ChunkX;
@@ -66,6 +45,9 @@ protected:
cChunkDef::HeightMap m_HeightMap;
} ;
+ /** The terrain height generator that is being cached. */
+ cTerrainHeightGenPtr m_HeiGenToCache;
+
// To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data
int m_CacheSize;
int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array
@@ -81,6 +63,57 @@ protected:
+/** Caches heightmaps in multiple underlying caches to improve the distribution and lower the chain length. */
+class cHeiGenMultiCache:
+ public cTerrainHeightGen
+{
+public:
+ cHeiGenMultiCache(cTerrainHeightGenPtr a_HeightGenToCache, size_t a_SubCacheSize, size_t a_NumSubCaches);
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+
+ /** Retrieves height at the specified point in the cache, returns true if found, false if not found */
+ bool GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height);
+
+protected:
+ typedef SharedPtr<cHeiGenCache> cHeiGenCachePtr;
+ typedef std::vector<cHeiGenCachePtr> cHeiGenCachePtrs;
+
+
+ /** The coefficient used to turn Z coords into index (x + Coeff * z). */
+ static const size_t m_CoeffZ = 5;
+
+ /** Number of sub-caches, pulled out of m_SubCaches.size() for performance reasons. */
+ size_t m_NumSubCaches;
+
+ /** The individual sub-caches. */
+ cHeiGenCachePtrs m_SubCaches;
+};
+
+
+
+
+
+class cHeiGenFlat :
+ public cTerrainHeightGen
+{
+public:
+ cHeiGenFlat(void) : m_Height(5) {}
+
+protected:
+
+ int m_Height;
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+ virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
+} ;
+
+
+
+
+
class cHeiGenClassic :
public cTerrainHeightGen
{
@@ -137,7 +170,11 @@ public:
m_BiomeGen(a_BiomeGen)
{
}
-
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+ virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
+
protected:
typedef cChunkDef::BiomeMap BiomeNeighbors[3][3];
@@ -154,11 +191,8 @@ protected:
float m_BaseHeight;
} ;
static const sGenParam m_GenParam[256];
-
- // cTerrainHeightGen overrides:
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
- virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
-
+
+
NOISE_DATATYPE GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const BiomeNeighbors & a_BiomeNeighbors);
} ;
diff --git a/src/Generating/MineShafts.cpp b/src/Generating/MineShafts.cpp
index 55b3b64dd..65588ce4b 100644
--- a/src/Generating/MineShafts.cpp
+++ b/src/Generating/MineShafts.cpp
@@ -20,6 +20,7 @@ in a depth-first processing. Each of the descendants will branch randomly, if no
#include "MineShafts.h"
#include "../Cuboid.h"
#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/MobSpawnerEntity.h"
@@ -875,7 +876,9 @@ void cMineShaftCorridor::PlaceSpawner(cChunkDesc & a_ChunkDesc)
)
{
a_ChunkDesc.SetBlockTypeMeta(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ, E_BLOCK_MOB_SPAWNER, 0);
- // TODO: The spawner needs its accompanying cMobSpawnerEntity, when implemented
+ cMobSpawnerEntity * MobSpawner = static_cast<cMobSpawnerEntity *>(a_ChunkDesc.GetBlockEntity(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ));
+ ASSERT((MobSpawner != nullptr) && (MobSpawner->GetBlockType() == E_BLOCK_MOB_SPAWNER));
+ MobSpawner->SetEntity(mtCaveSpider);
}
}
diff --git a/src/Generating/MineShafts.h b/src/Generating/MineShafts.h
index 2850db571..efb11cfee 100644
--- a/src/Generating/MineShafts.h
+++ b/src/Generating/MineShafts.h
@@ -10,7 +10,6 @@
#pragma once
#include "GridStructGen.h"
-#include "../Noise.h"
diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp
index 5a4cb44cf..b43a1a6de 100644
--- a/src/Generating/Noise3DGenerator.cpp
+++ b/src/Generating/Noise3DGenerator.cpp
@@ -61,80 +61,110 @@ public:
-////////////////////////////////////////////////////////////////////////////////
-// cNoise3DGenerator:
-
-cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) :
- super(a_ChunkGenerator),
- m_Perlin(1000),
- m_Cubic(1000)
+#if 0
+// Perform speed test of the cInterpolNoise class
+static class cInterpolNoiseSpeedTest
{
- m_Perlin.AddOctave(1, (NOISE_DATATYPE)0.5);
- m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 1);
- m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 2);
-
- #if 0
- // DEBUG: Test the noise generation:
- // NOTE: In order to be able to run MCS with this code, you need to increase the default thread stack size
- // In MSVC, it is done in Project Settings -> Configuration Properties -> Linker -> System, set Stack reserve size to at least 64M
- m_SeaLevel = 62;
- m_HeightAmplification = 0;
- m_MidPoint = 75;
- m_FrequencyX = 4;
- m_FrequencyY = 4;
- m_FrequencyZ = 4;
- m_AirThreshold = 0.5;
-
- const int NumChunks = 4;
- NOISE_DATATYPE Noise[NumChunks][cChunkDef::Width * cChunkDef::Width * cChunkDef::Height];
- for (int x = 0; x < NumChunks; x++)
+public:
+ cInterpolNoiseSpeedTest(void)
{
- GenerateNoiseArray(x, 5, Noise[x]);
+ TestSpeed2D();
+ TestSpeed3D();
+ printf("InterpolNoise speed comparison finished.\n");
}
- // Save in XY cuts:
- cFile f1;
- if (f1.Open("Test_XY.grab", cFile::fmWrite))
+
+ /** Compare the speed of the 3D InterpolNoise vs 3D CubicNoise. */
+ void TestSpeed3D(void)
{
- for (int z = 0; z < cChunkDef::Width; z++)
+ printf("Evaluating 3D noise performance...\n");
+ static const int SIZE_X = 128;
+ static const int SIZE_Y = 128;
+ static const int SIZE_Z = 128;
+ static const NOISE_DATATYPE MUL = 80;
+ std::unique_ptr<NOISE_DATATYPE[]> arr(new NOISE_DATATYPE[SIZE_X * SIZE_Y * SIZE_Z]);
+ cTimer timer;
+
+ // Test the cInterpolNoise:
+ cInterpolNoise<Interp5Deg> interpNoise(1);
+ long long start = timer.GetNowTime();
+ for (int i = 0; i < 30; i++)
{
- for (int y = 0; y < cChunkDef::Height; y++)
- {
- for (int i = 0; i < NumChunks; i++)
- {
- int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height;
- unsigned char buf[cChunkDef::Width];
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++]))));
- }
- f1.Write(buf, cChunkDef::Width);
- }
- } // for y
- } // for z
- } // if (XY file open)
+ interpNoise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, MUL * i, MUL * i + MUL, 0, MUL, 0, MUL);
+ }
+ long long end = timer.GetNowTime();
+ printf("InterpolNoise took %.02f sec\n", static_cast<float>(end - start) / 1000);
+
+ // Test the cCubicNoise:
+ cCubicNoise cubicNoise(1);
+ start = timer.GetNowTime();
+ for (int i = 0; i < 30; i++)
+ {
+ cubicNoise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, MUL * i, MUL * i + MUL, 0, MUL, 0, MUL);
+ }
+ end = timer.GetNowTime();
+ printf("CubicNoise took %.02f sec\n", static_cast<float>(end - start) / 1000);
+ printf("3D noise performance comparison finished.\n");
+ }
+
- cFile f2;
- if (f2.Open("Test_XZ.grab", cFile::fmWrite))
+ /** Compare the speed of the 2D InterpolNoise vs 2D CubicNoise. */
+ void TestSpeed2D(void)
{
- for (int y = 0; y < cChunkDef::Height; y++)
+ printf("Evaluating 2D noise performance...\n");
+ static const int SIZE_X = 128;
+ static const int SIZE_Y = 128;
+ static const NOISE_DATATYPE MUL = 80;
+ std::unique_ptr<NOISE_DATATYPE[]> arr(new NOISE_DATATYPE[SIZE_X * SIZE_Y]);
+ cTimer timer;
+
+ // Test the cInterpolNoise:
+ cInterpolNoise<Interp5Deg> interpNoise(1);
+ long long start = timer.GetNowTime();
+ for (int i = 0; i < 500; i++)
{
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int i = 0; i < NumChunks; i++)
- {
- int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height;
- unsigned char buf[cChunkDef::Width];
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++]))));
- }
- f2.Write(buf, cChunkDef::Width);
- }
- } // for z
- } // for y
- } // if (XZ file open)
- #endif // 0
+ interpNoise.Generate2D(arr.get(), SIZE_X, SIZE_Y, MUL * i, MUL * i + MUL, 0, MUL);
+ }
+ long long end = timer.GetNowTime();
+ printf("InterpolNoise took %.02f sec\n", static_cast<float>(end - start) / 1000);
+
+ // Test the cCubicNoise:
+ cCubicNoise cubicNoise(1);
+ start = timer.GetNowTime();
+ for (int i = 0; i < 500; i++)
+ {
+ cubicNoise.Generate2D(arr.get(), SIZE_X, SIZE_Y, MUL * i, MUL * i + MUL, 0, MUL);
+ }
+ end = timer.GetNowTime();
+ printf("CubicNoise took %.02f sec\n", static_cast<float>(end - start) / 1000);
+ printf("2D noise performance comparison finished.\n");
+ }
+} g_InterpolNoiseSpeedTest;
+#endif
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cNoise3DGenerator:
+
+cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) :
+ super(a_ChunkGenerator),
+ m_Perlin(1000),
+ m_Cubic(1000)
+{
+ m_Perlin.AddOctave(1, 1);
+ m_Perlin.AddOctave(2, 0.5);
+ m_Perlin.AddOctave(4, 0.25);
+ m_Perlin.AddOctave(8, 0.125);
+ m_Perlin.AddOctave(16, 0.0625);
+
+ m_Cubic.AddOctave(1, 1);
+ m_Cubic.AddOctave(2, 0.5);
+ m_Cubic.AddOctave(4, 0.25);
+ m_Cubic.AddOctave(8, 0.125);
+ m_Cubic.AddOctave(16, 0.0625);
}
@@ -153,9 +183,9 @@ cNoise3DGenerator::~cNoise3DGenerator()
void cNoise3DGenerator::Initialize(cIniFile & a_IniFile)
{
// Params:
- m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
- m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0);
- m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75);
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "SeaLevel", 62);
+ m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0.1);
+ m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 68);
m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 8);
m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 8);
m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 8);
@@ -220,10 +250,10 @@ void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DAT
NOISE_DATATYPE NoiseW[DIM_X * DIM_Y * DIM_Z]; // Workspace that the noise calculation can use and trash
// Our noise array has different layout, XZY, instead of regular chunk's XYZ, that's why the coords are "renamed"
- NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / m_FrequencyX;
- NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((a_ChunkX + 1) * cChunkDef::Width) - 1) / m_FrequencyX;
- NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width)) / m_FrequencyZ;
- NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width) - 1) / m_FrequencyZ;
+ NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / m_FrequencyX;
+ NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((a_ChunkX + 1) * cChunkDef::Width)) / m_FrequencyX;
+ NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width)) / m_FrequencyZ;
+ NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width)) / m_FrequencyZ;
NOISE_DATATYPE StartY = 0;
NOISE_DATATYPE EndY = ((NOISE_DATATYPE)256) / m_FrequencyY;
@@ -233,23 +263,23 @@ void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DAT
// Precalculate a "height" array:
NOISE_DATATYPE Height[DIM_X * DIM_Z]; // Output for the cubic noise heightmap ("source")
- m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 25, EndX / 25, StartZ / 25, EndZ / 25);
+ m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 5, EndX / 5, StartZ / 5, EndZ / 5);
for (size_t i = 0; i < ARRAYCOUNT(Height); i++)
{
- Height[i] = std::abs(Height[i]) * m_HeightAmplification + 1;
+ Height[i] = Height[i] * m_HeightAmplification;
}
// Modify the noise by height data:
for (int y = 0; y < DIM_Y; y++)
{
- NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 20;
- AddHeight *= AddHeight * AddHeight;
+ NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 30;
+ // AddHeight *= AddHeight * AddHeight;
for (int z = 0; z < DIM_Z; z++)
{
NOISE_DATATYPE * CurRow = &(NoiseO[y * DIM_X + z * DIM_X * DIM_Y]);
for (int x = 0; x < DIM_X; x++)
{
- CurRow[x] += AddHeight / Height[x + DIM_X * z];
+ CurRow[x] += AddHeight + Height[x + DIM_X * z];
}
}
}
@@ -346,9 +376,10 @@ void cNoise3DGenerator::ComposeTerrain(cChunkDesc & a_ChunkDesc)
// cNoise3DComposable:
cNoise3DComposable::cNoise3DComposable(int a_Seed) :
- m_Noise1(a_Seed + 1000),
- m_Noise2(a_Seed + 2000),
- m_Noise3(a_Seed + 3000)
+ m_ChoiceNoise(a_Seed),
+ m_DensityNoiseA(a_Seed + 1),
+ m_DensityNoiseB(a_Seed + 2),
+ m_BaseNoise(a_Seed + 3)
{
}
@@ -359,13 +390,50 @@ cNoise3DComposable::cNoise3DComposable(int a_Seed) :
void cNoise3DComposable::Initialize(cIniFile & a_IniFile)
{
// Params:
- m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
- m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0);
+ // The defaults generate extreme hills terrain
+ m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0.045);
m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75);
- m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 10);
- m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 10);
- m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 10);
- m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5);
+ m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 40);
+ m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 40);
+ m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 40);
+ m_BaseFrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DBaseFrequencyX", 40);
+ m_BaseFrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DBaseFrequencyZ", 40);
+ m_ChoiceFrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DChoiceFrequencyX", 40);
+ m_ChoiceFrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DChoiceFrequencyY", 80);
+ m_ChoiceFrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DChoiceFrequencyZ", 40);
+ m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0);
+ int NumChoiceOctaves = a_IniFile.GetValueSetI("Generator", "Noise3DNumChoiceOctaves", 4);
+ int NumDensityOctaves = a_IniFile.GetValueSetI("Generator", "Noise3DNumDensityOctaves", 6);
+ int NumBaseOctaves = a_IniFile.GetValueSetI("Generator", "Noise3DNumBaseOctaves", 6);
+ NOISE_DATATYPE BaseNoiseAmplitude = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DBaseAmplitude", 1);
+
+ // Add octaves for the choice noise:
+ NOISE_DATATYPE wavlen = 1, ampl = 0.5;
+ for (int i = 0; i < NumChoiceOctaves; i++)
+ {
+ m_ChoiceNoise.AddOctave(wavlen, ampl);
+ wavlen = wavlen * 2;
+ ampl = ampl / 2;
+ }
+
+ // Add octaves for the density noises:
+ wavlen = 1, ampl = 1;
+ for (int i = 0; i < NumDensityOctaves; i++)
+ {
+ m_DensityNoiseA.AddOctave(wavlen, ampl);
+ m_DensityNoiseB.AddOctave(wavlen, ampl);
+ wavlen = wavlen * 2;
+ ampl = ampl / 2;
+ }
+
+ // Add octaves for the base noise:
+ wavlen = 1, ampl = BaseNoiseAmplitude;
+ for (int i = 0; i < NumBaseOctaves; i++)
+ {
+ m_BaseNoise.AddOctave(wavlen, ampl);
+ wavlen = wavlen * 2;
+ ampl = ampl / 2;
+ }
}
@@ -376,142 +444,266 @@ void cNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ)
{
if ((a_ChunkX == m_LastChunkX) && (a_ChunkZ == m_LastChunkZ))
{
- // The noise for this chunk is already generated in m_Noise
+ // The noise for this chunk is already generated in m_NoiseArray
return;
}
m_LastChunkX = a_ChunkX;
m_LastChunkZ = a_ChunkZ;
- // Upscaling parameters:
- const int UPSCALE_X = 8;
- const int UPSCALE_Y = 4;
- const int UPSCALE_Z = 8;
-
- // Precalculate a "height" array:
- NOISE_DATATYPE Height[17 * 17]; // x + 17 * z
- for (int z = 0; z < 17; z += UPSCALE_Z)
+ // Generate all the noises:
+ NOISE_DATATYPE ChoiceNoise[5 * 5 * 33];
+ NOISE_DATATYPE Workspace[5 * 5 * 33];
+ NOISE_DATATYPE DensityNoiseA[5 * 5 * 33];
+ NOISE_DATATYPE DensityNoiseB[5 * 5 * 33];
+ NOISE_DATATYPE BaseNoise[5 * 5];
+ NOISE_DATATYPE BlockX = static_cast<NOISE_DATATYPE>(a_ChunkX * cChunkDef::Width);
+ NOISE_DATATYPE BlockZ = static_cast<NOISE_DATATYPE>(a_ChunkZ * cChunkDef::Width);
+ // Note that we have to swap the X and Y coords, because noise generator uses [x + SizeX * y + SizeX * SizeY * z] ordering and we want "BlockY" to be "x":
+ m_ChoiceNoise.Generate3D (ChoiceNoise, 33, 5, 5, 0, 257 / m_ChoiceFrequencyY, BlockX / m_ChoiceFrequencyX, (BlockX + 17) / m_ChoiceFrequencyX, BlockZ / m_ChoiceFrequencyZ, (BlockZ + 17) / m_ChoiceFrequencyZ, Workspace);
+ m_DensityNoiseA.Generate3D(DensityNoiseA, 33, 5, 5, 0, 257 / m_FrequencyY, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
+ m_DensityNoiseB.Generate3D(DensityNoiseB, 33, 5, 5, 0, 257 / m_FrequencyY, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
+ m_BaseNoise.Generate2D (BaseNoise, 5, 5, BlockX / m_BaseFrequencyX, (BlockX + 17) / m_BaseFrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
+
+ // Calculate the final noise based on the partial noises:
+ for (int z = 0; z < 5; z++)
{
- NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ;
- for (int x = 0; x < 17; x += UPSCALE_X)
+ for (int x = 0; x < 5; x++)
{
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX;
- NOISE_DATATYPE val = std::abs(m_Noise1.CubicNoise2D(NoiseX / 5, NoiseZ / 5)) * m_HeightAmplification + 1;
- Height[x + 17 * z] = val * val * val;
+ NOISE_DATATYPE curBaseNoise = BaseNoise[x + 5 * z];
+ for (int y = 0; y < 33; y++)
+ {
+ NOISE_DATATYPE AddHeight = (static_cast<NOISE_DATATYPE>(y * 8) - m_MidPoint) * m_HeightAmplification;
+
+ // If "underground", make the terrain smoother by forcing the vertical linear gradient into steeper slope:
+ if (AddHeight < 0)
+ {
+ AddHeight *= 4;
+ }
+
+ // If too high, cut off any terrain:
+ if (y > 28)
+ {
+ AddHeight = AddHeight + static_cast<NOISE_DATATYPE>(y - 28) / 4;
+ }
+
+ // Decide between the two density noises:
+ int idx = 33 * x + 33 * 5 * z + y;
+ Workspace[idx] = ClampedLerp(DensityNoiseA[idx], DensityNoiseB[idx], 8 * (ChoiceNoise[idx] + 0.5f)) + AddHeight + curBaseNoise;
+ }
}
}
+ LinearUpscale3DArray<NOISE_DATATYPE>(Workspace, 33, 5, 5, m_NoiseArray, 8, 4, 4);
+}
+
+
+
+
- for (int y = 0; y < 257; y += UPSCALE_Y)
+void cNoise3DComposable::GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape)
+{
+ GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ);
+
+ // Translate the noise array into Shape:
+ for (int z = 0; z < cChunkDef::Width; z++)
{
- NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)y) / m_FrequencyY;
- NOISE_DATATYPE AddHeight = (y - m_MidPoint) / 20;
- AddHeight *= AddHeight * AddHeight;
- NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]);
- for (int z = 0; z < 17; z += UPSCALE_Z)
+ for (int x = 0; x < cChunkDef::Width; x++)
{
- NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ;
- for (int x = 0; x < 17; x += UPSCALE_X)
+ for (int y = 0; y < cChunkDef::Height; y++)
{
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX;
- CurFloor[x + 17 * z] = (
- m_Noise1.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * (NOISE_DATATYPE)0.5 +
- m_Noise2.CubicNoise3D(NoiseX / 2, NoiseY / 2, NoiseZ / 2) +
- m_Noise3.CubicNoise3D(NoiseX / 4, NoiseY / 4, NoiseZ / 4) * 2 +
- AddHeight / Height[x + 17 * z]
- );
+ a_Shape[y + x * 256 + z * 256 * 16] = (m_NoiseArray[y + 257 * x + 257 * 17 * z] > m_AirThreshold) ? 0 : 1;
}
- }
- // Linear-interpolate this XZ floor:
- LinearUpscale2DArrayInPlace<17, 17, UPSCALE_X, UPSCALE_Z>(CurFloor);
- }
+ } // for x
+ } // for z
+}
- // Finish the 3D linear interpolation by interpolating between each XZ-floors on the Y axis
- for (int y = 1; y < cChunkDef::Height; y++)
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cBiomalNoise3DComposable:
+
+cBiomalNoise3DComposable::cBiomalNoise3DComposable(int a_Seed, cBiomeGenPtr a_BiomeGen) :
+ m_ChoiceNoise(a_Seed),
+ m_DensityNoiseA(a_Seed + 1),
+ m_DensityNoiseB(a_Seed + 2),
+ m_BaseNoise(a_Seed + 3),
+ m_BiomeGen(a_BiomeGen),
+ m_LastChunkX(0x7fffffff), // Set impossible coords for the chunk so that it's always considered stale
+ m_LastChunkZ(0x7fffffff)
+{
+ // Generate the weight distribution for summing up neighboring biomes:
+ m_WeightSum = 0;
+ for (int z = 0; z <= AVERAGING_SIZE * 2; z++)
{
- if ((y % UPSCALE_Y) == 0)
- {
- // This is the interpolation source floor, already calculated
- continue;
- }
- int LoFloorY = (y / UPSCALE_Y) * UPSCALE_Y;
- int HiFloorY = LoFloorY + UPSCALE_Y;
- NOISE_DATATYPE * LoFloor = &(m_NoiseArray[LoFloorY * 17 * 17]);
- NOISE_DATATYPE * HiFloor = &(m_NoiseArray[HiFloorY * 17 * 17]);
- NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]);
- NOISE_DATATYPE Ratio = ((NOISE_DATATYPE)(y % UPSCALE_Y)) / UPSCALE_Y;
- int idx = 0;
- for (int z = 0; z < cChunkDef::Width; z++)
+ for (int x = 0; x <= AVERAGING_SIZE * 2; x++)
{
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- CurFloor[idx] = LoFloor[idx] + (HiFloor[idx] - LoFloor[idx]) * Ratio;
- idx += 1;
- }
- idx += 1; // Skipping one X column
+ m_Weight[z][x] = static_cast<NOISE_DATATYPE>((AVERAGING_SIZE - std::abs(AVERAGING_SIZE - x)) + (AVERAGING_SIZE - std::abs(AVERAGING_SIZE - z)));
+ m_WeightSum += m_Weight[z][x];
}
}
+}
+
+
+
+
- // The noise array is now fully interpolated
- /*
- // DEBUG: Output two images of the array, sliced by XY and XZ:
- cFile f1;
- if (f1.Open(Printf("Chunk_%d_%d_XY.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite))
+void cBiomalNoise3DComposable::Initialize(cIniFile & a_IniFile)
+{
+ // Params:
+ // The defaults generate extreme hills terrain
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "SeaLevel", 62);
+ m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DFrequencyX", 40);
+ m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DFrequencyY", 40);
+ m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DFrequencyZ", 40);
+ m_BaseFrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DBaseFrequencyX", 40);
+ m_BaseFrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DBaseFrequencyZ", 40);
+ m_ChoiceFrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DChoiceFrequencyX", 40);
+ m_ChoiceFrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DChoiceFrequencyY", 80);
+ m_ChoiceFrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DChoiceFrequencyZ", 40);
+ m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DAirThreshold", 0);
+ int NumChoiceOctaves = a_IniFile.GetValueSetI("Generator", "BiomalNoise3DNumChoiceOctaves", 4);
+ int NumDensityOctaves = a_IniFile.GetValueSetI("Generator", "BiomalNoise3DNumDensityOctaves", 6);
+ int NumBaseOctaves = a_IniFile.GetValueSetI("Generator", "BiomalNoise3DNumBaseOctaves", 6);
+ NOISE_DATATYPE BaseNoiseAmplitude = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DBaseAmplitude", 1);
+
+ // Add octaves for the choice noise:
+ NOISE_DATATYPE wavlen = 1, ampl = 0.5;
+ for (int i = 0; i < NumChoiceOctaves; i++)
{
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int y = 0; y < cChunkDef::Height; y++)
- {
- int idx = y * 17 * 17 + z * 17;
- unsigned char buf[16];
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++]))));
- }
- f1.Write(buf, 16);
- } // for y
- } // for z
- } // if (XY file open)
+ m_ChoiceNoise.AddOctave(wavlen, ampl);
+ wavlen = wavlen * 2;
+ ampl = ampl / 2;
+ }
- cFile f2;
- if (f2.Open(Printf("Chunk_%d_%d_XZ.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite))
+ // Add octaves for the density noises:
+ wavlen = 1, ampl = 1;
+ for (int i = 0; i < NumDensityOctaves; i++)
{
- for (int y = 0; y < cChunkDef::Height; y++)
+ m_DensityNoiseA.AddOctave(wavlen, ampl);
+ m_DensityNoiseB.AddOctave(wavlen, ampl);
+ wavlen = wavlen * 2;
+ ampl = ampl / 2;
+ }
+
+ // Add octaves for the base noise:
+ wavlen = 1, ampl = BaseNoiseAmplitude;
+ for (int i = 0; i < NumBaseOctaves; i++)
+ {
+ m_BaseNoise.AddOctave(wavlen, ampl);
+ wavlen = wavlen * 2;
+ ampl = ampl / 2;
+ }
+}
+
+
+
+
+
+void cBiomalNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ)
+{
+ if ((a_ChunkX == m_LastChunkX) && (a_ChunkZ == m_LastChunkZ))
+ {
+ // The noise for this chunk is already generated in m_NoiseArray
+ return;
+ }
+ m_LastChunkX = a_ChunkX;
+ m_LastChunkZ = a_ChunkZ;
+
+ // Calculate the parameters for the biomes:
+ ChunkParam MidPoint;
+ ChunkParam HeightAmp;
+ CalcBiomeParamArrays(a_ChunkX, a_ChunkZ, HeightAmp, MidPoint);
+
+ // Generate all the noises:
+ NOISE_DATATYPE ChoiceNoise[5 * 5 * 33];
+ NOISE_DATATYPE Workspace[5 * 5 * 33];
+ NOISE_DATATYPE DensityNoiseA[5 * 5 * 33];
+ NOISE_DATATYPE DensityNoiseB[5 * 5 * 33];
+ NOISE_DATATYPE BaseNoise[5 * 5];
+ NOISE_DATATYPE BlockX = static_cast<NOISE_DATATYPE>(a_ChunkX * cChunkDef::Width);
+ NOISE_DATATYPE BlockZ = static_cast<NOISE_DATATYPE>(a_ChunkZ * cChunkDef::Width);
+ // Note that we have to swap the X and Y coords, because noise generator uses [x + SizeX * y + SizeX * SizeY * z] ordering and we want "BlockY" to be "x":
+ m_ChoiceNoise.Generate3D (ChoiceNoise, 33, 5, 5, 0, 257 / m_ChoiceFrequencyY, BlockX / m_ChoiceFrequencyX, (BlockX + 17) / m_ChoiceFrequencyX, BlockZ / m_ChoiceFrequencyZ, (BlockZ + 17) / m_ChoiceFrequencyZ, Workspace);
+ m_DensityNoiseA.Generate3D(DensityNoiseA, 33, 5, 5, 0, 257 / m_FrequencyY, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
+ m_DensityNoiseB.Generate3D(DensityNoiseB, 33, 5, 5, 0, 257 / m_FrequencyY, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
+ m_BaseNoise.Generate2D (BaseNoise, 5, 5, BlockX / m_BaseFrequencyX, (BlockX + 17) / m_BaseFrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
+
+ // Calculate the final noise based on the partial noises:
+ for (int z = 0; z < 5; z++)
+ {
+ for (int x = 0; x < 5; x++)
{
- for (int z = 0; z < cChunkDef::Width; z++)
+ NOISE_DATATYPE curMidPoint = MidPoint[x + 5 * z];
+ NOISE_DATATYPE curHeightAmp = HeightAmp[x + 5 * z];
+ NOISE_DATATYPE curBaseNoise = BaseNoise[x + 5 * z];
+ for (int y = 0; y < 33; y++)
{
- int idx = y * 17 * 17 + z * 17;
- unsigned char buf[16];
- for (int x = 0; x < cChunkDef::Width; x++)
+ NOISE_DATATYPE AddHeight = (static_cast<NOISE_DATATYPE>(y * 8) - curMidPoint) * curHeightAmp;
+
+ // If "underground", make the terrain smoother by forcing the vertical linear gradient into steeper slope:
+ if (AddHeight < 0)
{
- buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++]))));
+ AddHeight *= 4;
}
- f2.Write(buf, 16);
- } // for z
- } // for y
- } // if (XZ file open)
- */
+ // If too high, cut off any terrain:
+ if (y > 28)
+ {
+ AddHeight = AddHeight + static_cast<NOISE_DATATYPE>(y - 28) / 4;
+ }
+
+ // Decide between the two density noises:
+ int idx = 33 * x + y + 33 * 5 * z;
+ Workspace[idx] = ClampedLerp(DensityNoiseA[idx], DensityNoiseB[idx], 8 * (ChoiceNoise[idx] + 0.5f)) + AddHeight + curBaseNoise;
+ }
+ }
+ }
+ LinearUpscale3DArray<NOISE_DATATYPE>(Workspace, 33, 5, 5, m_NoiseArray, 8, 4, 4);
}
-void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+void cBiomalNoise3DComposable::CalcBiomeParamArrays(int a_ChunkX, int a_ChunkZ, ChunkParam & a_HeightAmp, ChunkParam & a_MidPoint)
{
- GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ);
+ // Generate the 3*3 chunks of biomes around this chunk:
+ cChunkDef::BiomeMap neighborBiomes[3 * 3];
+ for (int z = 0; z < 3; z++)
+ {
+ for (int x = 0; x < 3; x++)
+ {
+ m_BiomeGen->GenBiomes(a_ChunkX + x - 1, a_ChunkZ + z - 1, neighborBiomes[x + 3 * z]);
+ }
+ }
- for (int z = 0; z < cChunkDef::Width; z++)
+ // Sum up the biome values:
+ for (int z = 0; z < 5; z++)
{
- for (int x = 0; x < cChunkDef::Width; x++)
+ for (int x = 0; x < 5; x++)
{
- cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel);
- for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--)
+ NOISE_DATATYPE totalHeightAmp = 0;
+ NOISE_DATATYPE totalMidPoint = 0;
+ // Add up the biomes around this point:
+ for (int relz = 0; relz <= AVERAGING_SIZE * 2; ++relz)
{
- if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold)
+ int colz = 16 + z * 4 + relz - AVERAGING_SIZE; // Biome Z coord relative to the neighborBiomes start
+ int neicellz = colz / 16; // Chunk Z coord relative to the neighborBiomes start
+ int neirelz = colz % 16; // Biome Z coord relative to cz in neighborBiomes
+ for (int relx = 0; relx <= AVERAGING_SIZE * 2; ++relx)
{
- cChunkDef::SetHeight(a_HeightMap, x, z, y);
- break;
- }
- } // for y
+ int colx = 16 + x * 4 + relx - AVERAGING_SIZE; // Biome X coord relative to the neighborBiomes start
+ int neicellx = colx / 16; // Chunk X coord relative to the neighborBiomes start
+ int neirelx = colx % 16; // Biome X coord relative to cz in neighborBiomes
+ EMCSBiome biome = cChunkDef::GetBiome(neighborBiomes[neicellx + neicellz * 3], neirelx, neirelz);
+ NOISE_DATATYPE heightAmp, midPoint;
+ GetBiomeParams(biome, heightAmp, midPoint);
+ totalHeightAmp += heightAmp * m_Weight[relz][relx];
+ totalMidPoint += midPoint * m_Weight[relz][relx];
+ } // for relx
+ } // for relz
+ a_HeightAmp[x + 5 * z] = totalHeightAmp / m_WeightSum;
+ a_MidPoint[x + 5 * z] = totalMidPoint / m_WeightSum;
} // for x
} // for z
}
@@ -520,52 +712,97 @@ void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::Hei
-void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+void cBiomalNoise3DComposable::GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE & a_HeightAmp, NOISE_DATATYPE & a_MidPoint)
{
- GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
+ switch (a_Biome)
+ {
+ case biBeach: a_HeightAmp = 0.2f; a_MidPoint = 60; break;
+ case biBirchForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biBirchForestHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biBirchForestHillsM: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biBirchForestM: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biColdBeach: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
+ case biColdTaiga: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biColdTaigaM: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biColdTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biDesertHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biDeepOcean: a_HeightAmp = 0.17f; a_MidPoint = 35; break;
+ case biDesert: a_HeightAmp = 0.29f; a_MidPoint = 62; break;
+ case biDesertM: a_HeightAmp = 0.29f; a_MidPoint = 62; break;
+ case biEnd: a_HeightAmp = 0.15f; a_MidPoint = 64; break;
+ case biExtremeHills: a_HeightAmp = 0.045f; a_MidPoint = 75; break;
+ case biExtremeHillsEdge: a_HeightAmp = 0.1f; a_MidPoint = 70; break;
+ case biExtremeHillsM: a_HeightAmp = 0.045f; a_MidPoint = 75; break;
+ case biExtremeHillsPlus: a_HeightAmp = 0.04f; a_MidPoint = 80; break;
+ case biExtremeHillsPlusM: a_HeightAmp = 0.04f; a_MidPoint = 80; break;
+ case biFlowerForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biForestHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biFrozenRiver: a_HeightAmp = 0.4f; a_MidPoint = 54; break;
+ case biFrozenOcean: a_HeightAmp = 0.12f; a_MidPoint = 45; break;
+ case biIceMountains: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biIcePlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
+ case biIcePlainsSpikes: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
+ case biJungle: a_HeightAmp = 0.1f; a_MidPoint = 63; break;
+ case biJungleEdge: a_HeightAmp = 0.15f; a_MidPoint = 62; break;
+ case biJungleEdgeM: a_HeightAmp = 0.15f; a_MidPoint = 62; break;
+ case biJungleHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biJungleM: a_HeightAmp = 0.1f; a_MidPoint = 63; break;
+ case biMegaSpruceTaiga: a_HeightAmp = 0.09f; a_MidPoint = 64; break;
+ case biMegaSpruceTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biMegaTaiga: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biMegaTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biMesa: a_HeightAmp = 0.09f; a_MidPoint = 61; break;
+ case biMesaBryce: a_HeightAmp = 0.15f; a_MidPoint = 61; break;
+ case biMesaPlateau: a_HeightAmp = 0.25f; a_MidPoint = 86; break;
+ case biMesaPlateauF: a_HeightAmp = 0.25f; a_MidPoint = 96; break;
+ case biMesaPlateauFM: a_HeightAmp = 0.25f; a_MidPoint = 96; break;
+ case biMesaPlateauM: a_HeightAmp = 0.25f; a_MidPoint = 86; break;
+ case biMushroomShore: a_HeightAmp = 0.075f; a_MidPoint = 60; break;
+ case biMushroomIsland: a_HeightAmp = 0.06f; a_MidPoint = 80; break;
+ case biNether: a_HeightAmp = 0.01f; a_MidPoint = 64; break;
+ case biOcean: a_HeightAmp = 0.12f; a_MidPoint = 45; break;
+ case biPlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
+ case biRiver: a_HeightAmp = 0.4f; a_MidPoint = 54; break;
+ case biRoofedForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biRoofedForestM: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biSavanna: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
+ case biSavannaM: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
+ case biSavannaPlateau: a_HeightAmp = 0.3f; a_MidPoint = 85; break;
+ case biSavannaPlateauM: a_HeightAmp = 0.012f; a_MidPoint = 105; break;
+ case biStoneBeach: a_HeightAmp = 0.075f; a_MidPoint = 60; break;
+ case biSunflowerPlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
+ case biSwampland: a_HeightAmp = 0.25f; a_MidPoint = 59; break;
+ case biSwamplandM: a_HeightAmp = 0.11f; a_MidPoint = 59; break;
+ case biTaiga: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biTaigaM: a_HeightAmp = 0.1f; a_MidPoint = 70; break;
+ case biTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ default:
+ {
+ // Make a crazy terrain so that it stands out
+ a_HeightAmp = 0.001f;
+ a_MidPoint = 90;
+ break;
+ }
+ }
+}
- a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
- // Make basic terrain composition:
+
+
+void cBiomalNoise3DComposable::GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape)
+{
+ GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ);
+
+ // Translate the noise array into Shape:
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
- int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
- bool HasHadWater = false;
- for (int y = LastAir; y < m_SeaLevel; y++)
+ for (int y = 0; y < cChunkDef::Height; y++)
{
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
+ a_Shape[y + x * 256 + z * 256 * 16] = (m_NoiseArray[y + 257 * x + 257 * 17 * z] > m_AirThreshold) ? 0 : 1;
}
- for (int y = LastAir - 1; y > 0; y--)
- {
- if (m_NoiseArray[x + 17 * z + 17 * 17 * y] > m_AirThreshold)
- {
- // "air" part
- LastAir = y;
- if (y < m_SeaLevel)
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
- HasHadWater = true;
- }
- continue;
- }
- // "ground" part:
- if (LastAir - y > 4)
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE);
- continue;
- }
- if (HasHadWater)
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND);
- }
- else
- {
- a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT);
- }
- } // for y
- a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
} // for x
} // for z
}
@@ -573,3 +810,4 @@ void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+
diff --git a/src/Generating/Noise3DGenerator.h b/src/Generating/Noise3DGenerator.h
index 42f61a854..35b1e4c94 100644
--- a/src/Generating/Noise3DGenerator.h
+++ b/src/Generating/Noise3DGenerator.h
@@ -1,7 +1,11 @@
// Noise3DGenerator.h
-// Generates terrain using 3D noise, rather than composing. Is a test.
+// Declares cNoise3DGenerator and cNoise3DComposable classes, representing 3D-noise-based generators.
+// They generate terrain shape by combining a lerp of two 3D noises with a vertical linear gradient
+// cNoise3DGenerator is obsolete and unmaintained.
+// cNoise3DComposable is used to test parameter combinations for single-biome worlds.
+
@@ -9,7 +13,8 @@
#pragma once
#include "ComposableGenerator.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
+#include "../Noise/InterpolNoise.h"
@@ -30,17 +35,20 @@ public:
protected:
// Linear interpolation step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively:
- static const int UPSCALE_X = 8;
- static const int UPSCALE_Y = 4;
- static const int UPSCALE_Z = 8;
+ static const int UPSCALE_X = 4;
+ static const int UPSCALE_Y = 8;
+ static const int UPSCALE_Z = 4;
// Linear interpolation buffer dimensions, calculated from the step sizes:
static const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X;
static const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y;
static const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z;
- cPerlinNoise m_Perlin; // The base 3D noise source for the actual composition
- cCubicNoise m_Cubic; // The noise used for heightmap directing
+ /** The base 3D noise source for the actual composition */
+ cOctavedNoise<cInterp5DegNoise> m_Perlin;
+
+ /** The noise used for heightmap directing. */
+ cOctavedNoise<cInterp5DegNoise> m_Cubic;
int m_SeaLevel;
NOISE_DATATYPE m_HeightAmplification;
@@ -65,8 +73,7 @@ protected:
class cNoise3DComposable :
- public cTerrainHeightGen,
- public cTerrainCompositionGen
+ public cTerrainShapeGen
{
public:
cNoise3DComposable(int a_Seed);
@@ -74,31 +81,135 @@ public:
void Initialize(cIniFile & a_IniFile);
protected:
- cNoise m_Noise1;
- cNoise m_Noise2;
- cNoise m_Noise3;
+ /** The 3D noise that is used to choose between density noise A and B. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_ChoiceNoise;
+
+ /** Density 3D noise, variant A. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_DensityNoiseA;
+
+ /** Density 3D noise, variant B. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_DensityNoiseB;
+
+ /** Heightmap-like noise used to provide variance for low-amplitude biomes. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_BaseNoise;
- int m_SeaLevel;
+ /** The main parameter of the generator, specifies the slope of the vertical linear gradient.
+ A higher value means a steeper slope and a smaller total amplitude of the generated terrain. */
NOISE_DATATYPE m_HeightAmplification;
- NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be
+
+ /** Where the vertical "center" of the noise should be, as block height. */
+ NOISE_DATATYPE m_MidPoint;
+
+ // Frequency of the 3D noise's first octave:
NOISE_DATATYPE m_FrequencyX;
NOISE_DATATYPE m_FrequencyY;
NOISE_DATATYPE m_FrequencyZ;
+
+ // Frequency of the base terrain noise:
+ NOISE_DATATYPE m_BaseFrequencyX;
+ NOISE_DATATYPE m_BaseFrequencyZ;
+
+ // Frequency of the choice noise:
+ NOISE_DATATYPE m_ChoiceFrequencyX;
+ NOISE_DATATYPE m_ChoiceFrequencyY;
+ NOISE_DATATYPE m_ChoiceFrequencyZ;
+
+ // Threshold for when the values are considered air:
NOISE_DATATYPE m_AirThreshold;
+ // Cache for the last calculated chunk (reused between heightmap and composition queries):
int m_LastChunkX;
int m_LastChunkZ;
NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y
- /// Generates the 3D noise array used for terrain generation, unless the LastChunk coords are equal to coords given
+ /** Generates the 3D noise array used for terrain generation (m_NoiseArray), unless the LastChunk coords are equal to coords given */
void GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ);
// cTerrainHeightGen overrides:
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+ virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override;
+ virtual void InitializeShapeGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
+} ;
+
+
+
+
+
+class cBiomalNoise3DComposable :
+ public cTerrainShapeGen
+{
+public:
+ cBiomalNoise3DComposable(int a_Seed, cBiomeGenPtr a_BiomeGen);
+
+ void Initialize(cIniFile & a_IniFile);
+
+protected:
+ /** Number of columns around the pixel to query for biomes for averaging. Must be less than or equal to 16. */
+ static const int AVERAGING_SIZE = 9;
+
+ /** Type used for a single parameter across the entire (downscaled) chunk. */
+ typedef NOISE_DATATYPE ChunkParam[5 * 5];
- // cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+
+ /** The noise that is used to choose between density noise A and B. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_ChoiceNoise;
+
+ /** Density 3D noise, variant A. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_DensityNoiseA;
+
+ /** Density 3D noise, variant B. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_DensityNoiseB;
+
+ /** Heightmap-like noise used to provide variance for low-amplitude biomes. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_BaseNoise;
+
+ /** The underlying biome generator. */
+ cBiomeGenPtr m_BiomeGen;
+
+ /** Block height of the sealevel, used for composing the terrain. */
+ int m_SeaLevel;
+
+ // Frequency of the 3D noise's first octave:
+ NOISE_DATATYPE m_FrequencyX;
+ NOISE_DATATYPE m_FrequencyY;
+ NOISE_DATATYPE m_FrequencyZ;
+
+ // Frequency of the base terrain noise:
+ NOISE_DATATYPE m_BaseFrequencyX;
+ NOISE_DATATYPE m_BaseFrequencyZ;
+
+ // Frequency of the choice noise:
+ NOISE_DATATYPE m_ChoiceFrequencyX;
+ NOISE_DATATYPE m_ChoiceFrequencyY;
+ NOISE_DATATYPE m_ChoiceFrequencyZ;
+
+ // Threshold for when the values are considered air:
+ NOISE_DATATYPE m_AirThreshold;
+
+ // Cache for the last calculated chunk (reused between heightmap and composition queries):
+ int m_LastChunkX;
+ int m_LastChunkZ;
+ NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // 257 * x + y + 257 * 17 * z
+
+ /** Weights for summing up neighboring biomes. */
+ NOISE_DATATYPE m_Weight[AVERAGING_SIZE * 2 + 1][AVERAGING_SIZE * 2 + 1];
+
+ /** The sum of m_Weight[]. */
+ NOISE_DATATYPE m_WeightSum;
+
+
+ /** Generates the 3D noise array used for terrain generation (m_NoiseArray), unless the LastChunk coords are equal to coords given */
+ void GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ);
+
+ /** Calculates the biome-related parameters for the chunk. */
+ void CalcBiomeParamArrays(int a_ChunkX, int a_ChunkZ, ChunkParam & a_HeightAmp, ChunkParam & a_MidPoint);
+
+ /** Returns the parameters for the specified biome. */
+ void GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE & a_HeightAmp, NOISE_DATATYPE & a_MidPoint);
+
+ // cTerrainShapeGen overrides:
+ virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override;
+ virtual void InitializeShapeGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
} ;
diff --git a/src/Generating/PieceGenerator.h b/src/Generating/PieceGenerator.h
index f06029280..43ffed7a2 100644
--- a/src/Generating/PieceGenerator.h
+++ b/src/Generating/PieceGenerator.h
@@ -20,7 +20,7 @@ Each uses a slightly different approach to generating:
#include "../Defines.h"
#include "../Cuboid.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
diff --git a/src/Generating/Ravines.h b/src/Generating/Ravines.h
index 3e41c5ce6..b11037433 100644
--- a/src/Generating/Ravines.h
+++ b/src/Generating/Ravines.h
@@ -10,7 +10,6 @@
#pragma once
#include "GridStructGen.h"
-#include "../Noise.h"
diff --git a/src/Generating/ShapeGen.cpp b/src/Generating/ShapeGen.cpp
new file mode 100644
index 000000000..45a9c3b93
--- /dev/null
+++ b/src/Generating/ShapeGen.cpp
@@ -0,0 +1,145 @@
+
+// ShapeGen.cpp
+
+// Implements the function to create a cTerrainShapeGen descendant based on INI file settings
+
+#include "Globals.h"
+#include "HeiGen.h"
+#include "../IniFile.h"
+#include "DistortedHeightmap.h"
+#include "EndGen.h"
+#include "Noise3DGenerator.h"
+#include "TwoHeights.h"
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cTerrainHeightToShapeGen:
+
+/** Converts old-style height-generators into new-style shape-generators. */
+class cTerrainHeightToShapeGen:
+ public cTerrainShapeGen
+{
+public:
+ cTerrainHeightToShapeGen(cTerrainHeightGenPtr a_HeightGen):
+ m_HeightGen(a_HeightGen)
+ {
+ }
+
+
+ // cTerrainShapeGen overrides:
+ virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override
+ {
+ // Generate the heightmap:
+ cChunkDef::HeightMap heightMap;
+ m_HeightGen->GenHeightMap(a_ChunkX, a_ChunkZ, heightMap);
+
+ // Convert from heightmap to shape:
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ HEIGHTTYPE height = cChunkDef::GetHeight(heightMap, x, z) + 1;
+ Byte * shapeColumn = &(a_Shape[(x + 16 * z) * 256]);
+ for (int y = 0; y < height; y++)
+ {
+ shapeColumn[y] = 1;
+ }
+ for (int y = height; y < cChunkDef::Height; y++)
+ {
+ shapeColumn[y] = 0;
+ }
+ } // for x
+ } // for z
+ }
+
+
+ virtual void InitializeShapeGen(cIniFile & a_IniFile) override
+ {
+ m_HeightGen->InitializeHeightGen(a_IniFile);
+ }
+
+protected:
+ /** The height generator being converted. */
+ cTerrainHeightGenPtr m_HeightGen;
+};
+
+typedef SharedPtr<cTerrainHeightToShapeGen> cTerrainHeightToShapeGenPtr;
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cTerrainShapeGen:
+
+cTerrainShapeGenPtr cTerrainShapeGen::CreateShapeGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault)
+{
+ AString shapeGenName = a_IniFile.GetValueSet("Generator", "ShapeGen", "");
+ if (shapeGenName.empty())
+ {
+ LOGWARN("[Generator] ShapeGen value not set in world.ini, using \"BiomalNoise3D\".");
+ shapeGenName = "BiomalNoise3D";
+ }
+
+ // If the shapegen is HeightMap, redirect to older HeightMap-based generators:
+ if (NoCaseCompare(shapeGenName, "HeightMap") == 0)
+ {
+ cTerrainHeightGenPtr heightGen = cTerrainHeightGen::CreateHeightGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault);
+ if (heightGen != nullptr)
+ {
+ return std::make_shared<cTerrainHeightToShapeGen>(heightGen);
+ }
+
+ // The height gen was not recognized; several heightgens were promoted to shape gens, so let's try them instead:
+ shapeGenName = a_IniFile.GetValue("Generator", "HeightGen", "");
+ if (shapeGenName.empty())
+ {
+ LOGWARNING("[Generator] ShapeGen set to HeightMap, but HeightGen not set. Reverting to \"BiomalNoise3D\".");
+ shapeGenName = "BiomalNoise3D";
+ a_IniFile.SetValue("Generator", "ShapeGen", shapeGenName);
+ }
+ }
+
+ // Choose the shape generator based on the name:
+ a_CacheOffByDefault = false;
+ cTerrainShapeGenPtr res;
+ if (NoCaseCompare(shapeGenName, "DistortedHeightmap") == 0)
+ {
+ res = std::make_shared<cDistortedHeightmap>(a_Seed, a_BiomeGen);
+ }
+ else if (NoCaseCompare(shapeGenName, "End") == 0)
+ {
+ res = std::make_shared<cEndGen>(a_Seed);
+ }
+ else if (NoCaseCompare(shapeGenName, "BiomalNoise3D") == 0)
+ {
+ res = std::make_shared<cBiomalNoise3DComposable>(a_Seed, a_BiomeGen);
+ }
+ else if (NoCaseCompare(shapeGenName, "Noise3D") == 0)
+ {
+ res = std::make_shared<cNoise3DComposable>(a_Seed);
+ }
+ else if (NoCaseCompare(shapeGenName, "TwoHeights") == 0)
+ {
+ res = CreateShapeGenTwoHeights(a_Seed, a_BiomeGen);
+ }
+ else
+ {
+ // No match found, force-set the default and retry
+ LOGWARN("Unknown ShapeGen \"%s\", using \"BiomalNoise3D\" instead.", shapeGenName.c_str());
+ a_IniFile.SetValue("Generator", "ShapeGen", "BiomalNoise3D");
+ return CreateShapeGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault);
+ }
+
+ // Read the settings:
+ res->InitializeShapeGen(a_IniFile);
+
+ return res;
+}
+
+
+
+
diff --git a/src/Generating/StructGen.cpp b/src/Generating/StructGen.cpp
index bdefcd8c1..2f685c808 100644
--- a/src/Generating/StructGen.cpp
+++ b/src/Generating/StructGen.cpp
@@ -37,10 +37,13 @@ void cStructGenTrees::GenFinish(cChunkDesc & a_ChunkDesc)
Dest = &WorkerDesc;
WorkerDesc.SetChunkCoords(BaseX, BaseZ);
+ // TODO: This may cause a lot of wasted calculations, instead of pulling data out of a single (cChunkDesc) cache
+
+ cChunkDesc::Shape workerShape;
m_BiomeGen->GenBiomes (BaseX, BaseZ, WorkerDesc.GetBiomeMap());
- m_HeightGen->GenHeightMap (BaseX, BaseZ, WorkerDesc.GetHeightMap());
- m_CompositionGen->ComposeTerrain(WorkerDesc);
- // TODO: Free the entity lists
+ m_ShapeGen->GenShape (BaseX, BaseZ, workerShape);
+ WorkerDesc.SetHeightFromShape (workerShape);
+ m_CompositionGen->ComposeTerrain(WorkerDesc, workerShape);
}
else
{
@@ -97,7 +100,7 @@ void cStructGenTrees::GenerateSingleTree(
int Height = a_ChunkDesc.GetHeight(x, z);
- if ((Height <= 0) || (Height > 240))
+ if ((Height <= 0) || (Height >= 230))
{
return;
}
@@ -125,6 +128,11 @@ void cStructGenTrees::GenerateSingleTree(
// Outside the chunk
continue;
}
+ if (itr->y >= 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);
switch (Block)
@@ -159,7 +167,7 @@ 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))
+ if ((itr->ChunkX == a_ChunkX) && (itr->ChunkZ == a_ChunkZ) && (itr->y < cChunkDef::Height))
{
// Inside this chunk, integrate into a_ChunkDesc:
switch (a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z))
@@ -390,7 +398,7 @@ void cStructGenLakes::GenFinish(cChunkDesc & a_ChunkDesc)
}
cBlockArea Lake;
- CreateLakeImage(ChunkX + x, ChunkZ + z, Lake);
+ CreateLakeImage(ChunkX + x, ChunkZ + z, a_ChunkDesc.GetMinHeight(), Lake);
int OfsX = Lake.GetOriginX() + x * cChunkDef::Width;
int OfsZ = Lake.GetOriginZ() + z * cChunkDef::Width;
@@ -404,25 +412,13 @@ void cStructGenLakes::GenFinish(cChunkDesc & a_ChunkDesc)
-void cStructGenLakes::CreateLakeImage(int a_ChunkX, int a_ChunkZ, cBlockArea & a_Lake)
+void cStructGenLakes::CreateLakeImage(int a_ChunkX, int a_ChunkZ, int a_MaxLakeHeight, cBlockArea & a_Lake)
{
a_Lake.Create(16, 8, 16);
a_Lake.Fill(cBlockArea::baTypes, E_BLOCK_SPONGE); // Sponge is the NOP blocktype for lake merging strategy
- // Find the minimum height in this chunk:
- cChunkDef::HeightMap HeightMap;
- m_HeiGen->GenHeightMap(a_ChunkX, a_ChunkZ, HeightMap);
- HEIGHTTYPE MinHeight = HeightMap[0];
- for (size_t i = 1; i < ARRAYCOUNT(HeightMap); i++)
- {
- if (HeightMap[i] < MinHeight)
- {
- MinHeight = HeightMap[i];
- }
- }
-
// Make a random position in the chunk by using a random 16 block XZ offset and random height up to chunk's max height minus 6
- MinHeight = std::max(MinHeight - 6, 2);
+ int MinHeight = std::max(a_MaxLakeHeight - 6, 2);
int Rnd = m_Noise.IntNoise3DInt(a_ChunkX, 128, a_ChunkZ) / 11;
// Random offset [-8 .. 8], with higher probability around 0; add up four three-bit-wide randoms [0 .. 28], divide and subtract to get range
int OffsetX = 4 * ((Rnd & 0x07) + ((Rnd & 0x38) >> 3) + ((Rnd & 0x1c0) >> 6) + ((Rnd & 0xe00) >> 9)) / 7 - 8;
diff --git a/src/Generating/StructGen.h b/src/Generating/StructGen.h
index 906fdd722..796abf0f5 100644
--- a/src/Generating/StructGen.h
+++ b/src/Generating/StructGen.h
@@ -14,7 +14,7 @@
#pragma once
#include "ComposableGenerator.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
@@ -24,11 +24,11 @@ class cStructGenTrees :
public cFinishGen
{
public:
- cStructGenTrees(int a_Seed, cBiomeGenPtr a_BiomeGen, cTerrainHeightGenPtr a_HeightGen, cTerrainCompositionGenPtr a_CompositionGen) :
+ cStructGenTrees(int a_Seed, cBiomeGenPtr a_BiomeGen, cTerrainShapeGenPtr a_ShapeGen, cTerrainCompositionGenPtr a_CompositionGen) :
m_Seed(a_Seed),
m_Noise(a_Seed),
m_BiomeGen(a_BiomeGen),
- m_HeightGen(a_HeightGen),
+ m_ShapeGen(a_ShapeGen),
m_CompositionGen(a_CompositionGen)
{}
@@ -37,12 +37,12 @@ protected:
int m_Seed;
cNoise m_Noise;
cBiomeGenPtr m_BiomeGen;
- cTerrainHeightGenPtr m_HeightGen;
+ cTerrainShapeGenPtr m_ShapeGen;
cTerrainCompositionGenPtr m_CompositionGen;
/** Generates and applies an image of a single tree.
- Parts of the tree inside the chunk are applied to a_BlockX.
- Parts of the tree outside the chunk are stored in a_OutsideX
+ Parts of the tree inside the chunk are applied to a_ChunkDesc.
+ Parts of the tree outside the chunk are stored in a_OutsideXYZ
*/
void GenerateSingleTree(
int a_ChunkX, int a_ChunkZ, int a_Seq,
@@ -51,7 +51,7 @@ protected:
sSetBlockVector & a_OutsideOther
) ;
- /// Applies an image into chunk blockdata; all blocks outside the chunk will be appended to a_Overflow
+ /** Applies an image into chunk blockdata; all blocks outside the chunk will be appended to a_Overflow. */
void ApplyTreeImage(
int a_ChunkX, int a_ChunkZ,
cChunkDesc & a_ChunkDesc,
@@ -124,27 +124,30 @@ class cStructGenLakes :
public cFinishGen
{
public:
- cStructGenLakes(int a_Seed, BLOCKTYPE a_Fluid, cTerrainHeightGenPtr a_HeiGen, int a_Probability) :
+ cStructGenLakes(int a_Seed, BLOCKTYPE a_Fluid, cTerrainShapeGenPtr a_ShapeGen, int a_Probability) :
m_Noise(a_Seed),
m_Seed(a_Seed),
m_Fluid(a_Fluid),
- m_HeiGen(a_HeiGen),
+ m_ShapeGen(a_ShapeGen),
m_Probability(a_Probability)
{
}
protected:
- cNoise m_Noise;
- int m_Seed;
- BLOCKTYPE m_Fluid;
- cTerrainHeightGenPtr m_HeiGen;
- int m_Probability; ///< Chance, 0 .. 100, of a chunk having the lake
+ cNoise m_Noise;
+ int m_Seed;
+ BLOCKTYPE m_Fluid;
+ cTerrainShapeGenPtr m_ShapeGen;
+
+ /** Chance, [0 .. 100], of a chunk having the lake. */
+ int m_Probability;
+
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
- /// Creates a lake image for the specified chunk into a_Lake
- void CreateLakeImage(int a_ChunkX, int a_ChunkZ, cBlockArea & a_Lake);
+ /** Creates a lake image for the specified chunk into a_Lake. */
+ void CreateLakeImage(int a_ChunkX, int a_ChunkZ, int a_MaxLakeHeight, cBlockArea & a_Lake);
} ;
diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp
index 7fd6d6f07..be8b0cd6b 100644
--- a/src/Generating/Trees.cpp
+++ b/src/Generating/Trees.cpp
@@ -61,7 +61,7 @@ static const sCoords BigO3[] =
static const sCoords BigO4[] = // Part of Big Jungle tree
{
- /* -4 */ {-2, -4}, {-1, -4}, {0, -4}, {1, -4}, {2, -4},
+ /* -4 */ {-2, -4}, {-1, -4}, {0, -4}, {1, -4}, {2, -4},
/* -3 */ {-3, -3}, {-2, -3}, {-1, -3}, {0, -3}, {1, -3}, {2, -3}, {3, -3},
/* -2 */ {-4, -2}, {-3, -2}, {-2, -2}, {-1, -2}, {0, -2}, {1, -2}, {2, -2}, {3, -2}, {4, -2},
/* -1 */ {-4, -1}, {-3, -1}, {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, {3, -1}, {4, -1},
@@ -361,7 +361,109 @@ void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a
void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
- // TODO
+ int Height = 7 + a_Noise.IntNoise3DInt(a_BlockX, a_BlockY, a_BlockZ) % 4;
+
+ // Array with possible directions for a branch to go to.
+ const Vector3d AvailableDirections[] =
+ {
+ { -1, 0, 0 }, { 0, 0, -1 },
+ { -1, 0, 1 }, { -1, 0, -1 },
+ { 1, 0, 1 }, { 1, 0, -1 },
+ { 1, 0, 0 }, { 0, 0, 1 },
+
+ { -0.5, 0, 0 }, { 0, 0, -0.5 },
+ { -0.5, 0, 0.5 }, { -0.5, 0, -0.5 },
+ { 0.5, 0, 0.5 }, { 0.5, 0, -0.5 },
+ { 0.5, 0, 0 }, { 0, 0, 0.5 },
+
+ { -1, 0.5, 0 }, { 0, 0.5, -1 },
+ { -1, 0.5, 1 }, { -1, 0.5, -1 },
+ { 1, 0.5, 1 }, { 1, 0.5, -1 },
+ { 1, 0.5, 0 }, { 0, 0.5, 1 },
+
+ { -0.5, 0.5, 0 }, { 0, 0.5, -0.5 },
+ { -0.5, 0.5, 0.5 }, { -0.5, 0.5, -0.5 },
+ { 0.5, 0.5, 0.5 }, { 0.5, 0.5, -0.5 },
+ { 0.5, 0.5, 0 }, { 0, 0.5, 0.5 },
+
+ };
+
+ // Create branches
+ for (int i = 4; i < Height; i++)
+ {
+ // Get a direction for the trunk to go to.
+ Vector3d BranchStartDirection = AvailableDirections[a_Noise.IntNoise3DInt(a_BlockX, a_BlockY + i, a_BlockZ) % ARRAYCOUNT(AvailableDirections)];
+ Vector3d BranchDirection = AvailableDirections[a_Noise.IntNoise3DInt(a_BlockX, a_BlockY / i, a_BlockZ) % ARRAYCOUNT(AvailableDirections)] / 3;
+
+ int BranchLength = 2 + a_Noise.IntNoise3DInt(a_BlockX * a_Seq, a_BlockY * a_Seq, a_BlockZ * a_Seq) % 3;
+ GetLargeAppleTreeBranch(a_BlockX, a_BlockY + i, a_BlockZ, BranchLength, BranchStartDirection, BranchDirection, a_BlockY + Height, a_Noise, a_LogBlocks);
+ }
+
+ // Place leaves around each log block
+ 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;
+
+ 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);
+ 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.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));
+ }
+
+ // Trunk:
+ for (int i = 0; i < Height; i++)
+ {
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE));
+ }
+}
+
+
+
+
+
+void GetLargeAppleTreeBranch(int a_BlockX, int a_BlockY, int a_BlockZ, int a_BranchLength, Vector3d a_StartDirection, Vector3d a_Direction, int a_TreeHeight, cNoise & a_Noise, sSetBlockVector & a_LogBlocks)
+{
+ Vector3d CurrentPos = Vector3d(a_BlockX, a_BlockY, a_BlockZ);
+ Vector3d Direction = a_StartDirection;
+ for (int i = 0; i < a_BranchLength; i++)
+ {
+ CurrentPos += Direction;
+ if (CurrentPos.y >= a_TreeHeight)
+ {
+ return;
+ }
+ Direction -= a_Direction;
+ Direction.clamp(-1.0, 1.0);
+ a_LogBlocks.push_back(sSetBlock(FloorC(CurrentPos.x), FloorC(CurrentPos.y), FloorC(CurrentPos.z), E_BLOCK_LOG, GetLogMetaFromDirection(E_META_LOG_APPLE, Direction)));
+ }
+}
+
+
+
+
+
+NIBBLETYPE GetLogMetaFromDirection(NIBBLETYPE a_BlockMeta, Vector3d a_Direction)
+{
+ a_Direction.abs();
+
+ if ((a_Direction.y > a_Direction.x) && (a_Direction.y > a_Direction.z))
+ {
+ return a_BlockMeta;
+ }
+ else if (a_Direction.x > a_Direction.z)
+ {
+ return a_BlockMeta + 4;
+ }
+ else
+ {
+ return a_BlockMeta + 8;
+ }
}
diff --git a/src/Generating/Trees.h b/src/Generating/Trees.h
index c9eb7de80..a2c8274f5 100644
--- a/src/Generating/Trees.h
+++ b/src/Generating/Trees.h
@@ -18,7 +18,7 @@ logs can overwrite others(leaves), but others shouldn't overwrite logs. This is
#pragma once
#include "../ChunkDef.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
@@ -62,6 +62,12 @@ void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a
/// Generates an image of a large (branching) apple tree
void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+/// Generates a branch for a large apple tree
+void GetLargeAppleTreeBranch(int a_BlockX, int a_BlockY, int a_BlockZ, int a_BranchLength, Vector3d a_StartDirection, Vector3d a_Direction, int a_TreeHeight, cNoise & a_Noise, sSetBlockVector & a_LogBlocks);
+
+/// Returns the meta for a log from the given direction
+NIBBLETYPE GetLogMetaFromDirection(NIBBLETYPE a_BlockMeta, Vector3d a_Direction);
+
/// Generates an image of a random birch tree
void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
diff --git a/src/Generating/TwoHeights.cpp b/src/Generating/TwoHeights.cpp
new file mode 100644
index 000000000..e75c301de
--- /dev/null
+++ b/src/Generating/TwoHeights.cpp
@@ -0,0 +1,121 @@
+
+// TwoHeights.cpp
+
+// Implements the cTwoHeights class representing the terrain shape generator using two switched heightmaps
+
+#include "Globals.h"
+#include "TwoHeights.h"
+#include "../Noise/InterpolNoise.h"
+#include "HeiGen.h"
+#include "../LinearUpscale.h"
+#include "../IniFile.h"
+
+
+
+
+
+class cTwoHeights:
+ public cTerrainShapeGen
+{
+ typedef cTerrainShapeGen super;
+public:
+ cTwoHeights(int a_Seed, cBiomeGenPtr a_BiomeGen):
+ m_Seed(a_Seed),
+ m_Choice(a_Seed),
+ m_HeightA(a_Seed + 1, a_BiomeGen),
+ m_HeightB(a_Seed + 2, a_BiomeGen)
+ {
+ }
+
+
+ // cTerrainShapeGen override:
+ virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override
+ {
+ // Generate the two heightmaps:
+ cChunkDef::HeightMap heightsA;
+ cChunkDef::HeightMap heightsB;
+ m_HeightA.GenHeightMap(a_ChunkX, a_ChunkZ, heightsA);
+ m_HeightB.GenHeightMap(a_ChunkX, a_ChunkZ, heightsB);
+
+ // Generate the choice noise:
+ NOISE_DATATYPE smallChoice[33 * 5 * 5];
+ NOISE_DATATYPE workspace[33 * 5 * 5];
+ NOISE_DATATYPE startX = 0;
+ NOISE_DATATYPE endX = 256 * m_FrequencyY;
+ NOISE_DATATYPE startY = a_ChunkX * cChunkDef::Width * m_FrequencyX;
+ NOISE_DATATYPE endY = (a_ChunkX * cChunkDef::Width + cChunkDef::Width + 1) * m_FrequencyX;
+ NOISE_DATATYPE startZ = a_ChunkZ * cChunkDef::Width * m_FrequencyZ;
+ NOISE_DATATYPE endZ = (a_ChunkZ * cChunkDef::Width + cChunkDef::Width + 1) * m_FrequencyZ;
+ m_Choice.Generate3D(smallChoice, 33, 5, 5, startX, endX, startY, endY, startZ, endZ, workspace);
+ NOISE_DATATYPE choice[257 * 17 * 17];
+ LinearUpscale3DArray(smallChoice, 33, 5, 5, choice, 8, 4, 4);
+
+ // Generate the shape:
+ int idxShape = 0;
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int idxChoice = 257 * 17 * z + 257 * x;
+ NOISE_DATATYPE heightA = static_cast<NOISE_DATATYPE>(cChunkDef::GetHeight(heightsA, x, z));
+ NOISE_DATATYPE heightB = static_cast<NOISE_DATATYPE>(cChunkDef::GetHeight(heightsB, x, z));
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ int height = static_cast<int>(ClampedLerp(heightA, heightB, choice[idxChoice++]));
+ a_Shape[idxShape++] = (y < height) ? 1 : 0;
+ }
+ } // for x
+ } // for z
+ }
+
+
+ virtual void InitializeShapeGen(cIniFile & a_IniFile)
+ {
+ m_FrequencyX = static_cast<NOISE_DATATYPE>(a_IniFile.GetValueSetF("Generator", "TwoHeightsFrequencyX", 40));
+ m_FrequencyY = static_cast<NOISE_DATATYPE>(a_IniFile.GetValueSetF("Generator", "TwoHeightsFrequencyY", 40));
+ m_FrequencyZ = static_cast<NOISE_DATATYPE>(a_IniFile.GetValueSetF("Generator", "TwoHeightsFrequencyZ", 40));
+
+ // Initialize the two underlying height generators from an empty INI file:
+ cIniFile empty;
+ m_HeightA.InitializeHeightGen(empty);
+ m_HeightB.InitializeHeightGen(empty);
+
+ // Add the choice octaves:
+ NOISE_DATATYPE freq = 0.001f;
+ NOISE_DATATYPE ampl = 1;
+ for (int i = 0; i < 4; i++)
+ {
+ m_Choice.AddOctave(freq, ampl);
+ freq = freq * 2;
+ ampl = ampl / 2;
+ }
+ }
+
+protected:
+ int m_Seed;
+
+ /** The noise used to decide between the two heightmaps. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_Choice;
+
+ /** The first height generator. */
+ cHeiGenBiomal m_HeightA;
+
+ /** The second height generator. */
+ cHeiGenBiomal m_HeightB;
+
+ /** The base frequencies for m_Choice in each of the world axis directions. */
+ NOISE_DATATYPE m_FrequencyX, m_FrequencyY, m_FrequencyZ;
+};
+
+
+
+
+
+cTerrainShapeGenPtr CreateShapeGenTwoHeights(int a_Seed, cBiomeGenPtr a_BiomeGen)
+{
+ return std::make_shared<cTwoHeights>(a_Seed, a_BiomeGen);
+}
+
+
+
+
diff --git a/src/Generating/TwoHeights.h b/src/Generating/TwoHeights.h
new file mode 100644
index 000000000..353598011
--- /dev/null
+++ b/src/Generating/TwoHeights.h
@@ -0,0 +1,23 @@
+
+// TwoHeights.h
+
+// Declares the function to create a new instance of the cTwoHeights terrain shape generator
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+
+
+
+
+/** Creates and returns a new instance of the cTwoHeights terrain shape generator.
+The instance must be Initialize()-d before it is used. */
+extern cTerrainShapeGenPtr CreateShapeGenTwoHeights(int a_Seed, cBiomeGenPtr a_BiomeGen);
+
+
+
+
diff --git a/src/Generating/VillageGen.cpp b/src/Generating/VillageGen.cpp
index a9b359b6a..f7d9a8316 100644
--- a/src/Generating/VillageGen.cpp
+++ b/src/Generating/VillageGen.cpp
@@ -18,8 +18,8 @@
/*
How village generating works:
-By descending from a cGridStructGen, a semi-random grid is generated. A village may be generated for each of
-the grid's cells. Each cell checks the biomes in an entire chunk around it, only generating a village if all
+By descending from a cGridStructGen, a semi-random (jitter) grid is generated. A village may be generated for each
+of the grid's cells. Each cell checks the biomes in an entire chunk around it, only generating a village if all
biomes are village-friendly. If yes, the entire village structure is built for that cell. If not, the cell
is left village-less.
@@ -125,7 +125,7 @@ public:
m_Noise(a_Seed),
m_MaxSize(a_MaxSize),
m_Density(a_Density),
- m_Borders(a_OriginX - a_MaxSize, 0, a_OriginZ - a_MaxSize, a_OriginX + a_MaxSize, 255, a_OriginZ + a_MaxSize),
+ m_Borders(a_OriginX - a_MaxSize, 0, a_OriginZ - a_MaxSize, a_OriginX + a_MaxSize, cChunkDef::Height - 1, a_OriginZ + a_MaxSize),
m_Prefabs(a_Prefabs),
m_HeightGen(a_HeightGen),
m_RoadBlock(a_RoadBlock),
diff --git a/src/Globals.h b/src/Globals.h
index eb59f499e..61f500db9 100644
--- a/src/Globals.h
+++ b/src/Globals.h
@@ -184,9 +184,9 @@ template class SizeChecker<UInt16, 2>;
// OS-dependent stuff:
#ifdef _WIN32
- #define WIN32_LEAN_AND_MEAN
- #define _WIN32_WINNT 0x501 // We want to target WinXP and higher
+ #define WIN32_LEAN_AND_MEAN
+ #define _WIN32_WINNT _WIN32_WINNT_WS03 // We want to target Windows XP with Service Pack 2 & Windows Server 2003 with Service Pack 1 and higher
#include <Windows.h>
#include <winsock2.h>
@@ -241,6 +241,7 @@ template class SizeChecker<UInt16, 2>;
// STL stuff:
+#include <chrono>
#include <vector>
#include <list>
#include <deque>
@@ -259,13 +260,12 @@ template class SizeChecker<UInt16, 2>;
#ifndef TEST_GLOBALS
// Common headers (part 1, without macros):
#include "StringUtils.h"
- #include "OSSupport/Sleep.h"
#include "OSSupport/CriticalSection.h"
#include "OSSupport/Semaphore.h"
#include "OSSupport/Event.h"
- #include "OSSupport/Thread.h"
#include "OSSupport/File.h"
#include "Logger.h"
+ #include "OSSupport/StackTrace.h"
#else
// Logging functions
void inline LOGERROR(const char* a_Format, ...) FORMATSTRING(1, 2);
@@ -351,14 +351,14 @@ void inline LOGD(const char* a_Format, ...)
#else
#ifdef _DEBUG
- #define ASSERT( x) ( !!(x) || ( LOGERROR("Assertion failed: %s, file %s, line %i", #x, __FILE__, __LINE__), assert(0), 0))
+ #define ASSERT( x) ( !!(x) || ( LOGERROR("Assertion failed: %s, file %s, line %i", #x, __FILE__, __LINE__), PrintStackTrace(), assert(0), 0))
#else
#define ASSERT(x) ((void)(x))
#endif
#endif
// Pretty much the same as ASSERT() but stays in Release builds
-#define VERIFY( x) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__), exit(1), 0))
+#define VERIFY( x) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__), PrintStackTrace(), exit(1), 0))
// Same as assert but in all Self test builds
#ifdef SELF_TEST
diff --git a/src/HTTPServer/HTTPFormParser.h b/src/HTTPServer/HTTPFormParser.h
index edc6d2471..d9d29d9bc 100644
--- a/src/HTTPServer/HTTPFormParser.h
+++ b/src/HTTPServer/HTTPFormParser.h
@@ -82,7 +82,7 @@ protected:
bool m_IsValid;
/// The parser for the multipart data, if used
- std::auto_ptr<cMultipartParser> m_MultipartParser;
+ std::unique_ptr<cMultipartParser> m_MultipartParser;
/// Name of the currently parsed part in multipart data
AString m_CurrentPartName;
diff --git a/src/Inventory.cpp b/src/Inventory.cpp
index 51ac32da9..fba6f4aea 100644
--- a/src/Inventory.cpp
+++ b/src/Inventory.cpp
@@ -507,7 +507,11 @@ bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size,
int MaxStackSize = cItemHandler::GetItemHandler(a_Item.m_ItemType)->GetMaxStackSize();
for (int i = 0; i < a_Size; i++)
{
- if (m_Slots[i + a_Offset].m_ItemType == a_Item.m_ItemType && m_Slots[i + a_Offset].m_ItemCount < MaxStackSize && m_Slots[i + a_Offset].m_ItemDamage == a_Item.m_ItemDamage)
+ if (
+ (m_Slots[i + a_Offset].m_ItemType == a_Item.m_ItemType) &&
+ (m_Slots[i + a_Offset].m_ItemCount < MaxStackSize) &&
+ (m_Slots[i + a_Offset].m_ItemDamage == a_Item.m_ItemDamage)
+ )
{
int NumFree = MaxStackSize - m_Slots[i + a_Offset].m_ItemCount;
if (NumFree >= a_Item.m_ItemCount)
@@ -533,7 +537,7 @@ bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size,
if (a_Mode > 0)
{
// If we got more left, find first empty slot
- for (int i = 0; i < a_Size && a_Item.m_ItemCount > 0; i++)
+ for (int i = 0; (i < a_Size) && (a_Item.m_ItemCount > 0); i++)
{
if (m_Slots[i + a_Offset].m_ItemType == -1)
{
diff --git a/src/ItemGrid.cpp b/src/ItemGrid.cpp
index 55fb36a17..d49ea9df1 100644
--- a/src/ItemGrid.cpp
+++ b/src/ItemGrid.cpp
@@ -6,7 +6,7 @@
#include "Globals.h"
#include "ItemGrid.h"
#include "Items/ItemHandler.h"
-#include "Noise.h"
+#include "Noise/Noise.h"
diff --git a/src/Logger.cpp b/src/Logger.cpp
index cb528e8ab..292622a46 100644
--- a/src/Logger.cpp
+++ b/src/Logger.cpp
@@ -45,12 +45,12 @@ void cLogger::LogSimple(AString a_Message, eLogLevel a_LogLevel)
AString Line;
#ifdef _DEBUG
- Printf(Line, "[%04lx|%02d:%02d:%02d] %s\n", cIsThread::GetCurrentID(), timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, a_Message.c_str());
+ Printf(Line, "[%04llx|%02d:%02d:%02d] %s\n", static_cast<UInt64>(std::hash<std::thread::id>()(std::this_thread::get_id())), timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, a_Message.c_str());
#else
Printf(Line, "[%02d:%02d:%02d] %s\n", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, a_Message.c_str());
#endif
-
+
cCSLock Lock(m_CriticalSection);
for (size_t i = 0; i < m_LogListeners.size(); i++)
{
diff --git a/src/MersenneTwister.h b/src/MersenneTwister.h
deleted file mode 100644
index e83a470e2..000000000
--- a/src/MersenneTwister.h
+++ /dev/null
@@ -1,456 +0,0 @@
-// MersenneTwister.h
-// Mersenne Twister random number generator -- a C++ class MTRand
-// Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus
-// Richard J. Wagner v1.1 28 September 2009 wagnerr@umich.edu
-
-// The Mersenne Twister is an algorithm for generating random numbers. It
-// was designed with consideration of the flaws in various other generators.
-// The period, 2^19937-1, and the order of equidistribution, 623 dimensions,
-// are far greater. The generator is also fast; it avoids multiplication and
-// division, and it benefits from caches and pipelines. For more information
-// see the inventors' web page at
-// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
-
-// Reference
-// M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally
-// Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on
-// Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30.
-
-// Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
-// Copyright (C) 2000 - 2009, Richard J. Wagner
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions
-// are met:
-//
-// 1. Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// 3. The names of its contributors may not be used to endorse or promote
-// products derived from this software without specific prior written
-// permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef MERSENNETWISTER_H
-#define MERSENNETWISTER_H
-
-// Not thread safe (unless auto-initialization is avoided and each thread has
-// its own MTRand object)
-
-#include <iostream>
-#include <climits>
-#include <cstdio>
-#include <ctime>
-#include <cmath>
-
-class MTRand {
-// Data
-public:
- typedef UInt32 uint32; // unsigned integer type, at least 32 bits
-
- enum { N = 624 }; // length of state vector
- enum { SAVE = N + 1 }; // length of array for save()
-
-protected:
- enum { M = 397 }; // period parameter
-
- uint32 state[N]; // internal state
- uint32 *pNext; // next value to get from state
- uint32 left; // number of values left before reload needed
-
-// Methods
-public:
- MTRand( const uint32 oneSeed ); // initialize with a simple uint32
- MTRand( uint32 *const bigSeed, uint32 const seedLength = N ); // or array
- MTRand(); // auto-initialize with /dev/urandom or time() and clock()
- MTRand( const MTRand& o ); // copy
-
- // Do NOT use for CRYPTOGRAPHY without securely hashing several returned
- // values together, otherwise the generator state can be learned after
- // reading 624 consecutive values.
-
- // Access to 32-bit random numbers
- uint32 randInt(); // integer in [0,2^32-1]
- uint32 randInt( const uint32 n ); // integer in [0,n] for n < 2^32
- double rand(); // real number in [0,1]
- double rand( const double n ); // real number in [0,n]
- double randExc(); // real number in [0,1)
- double randExc( const double n ); // real number in [0,n)
- double randDblExc(); // real number in (0,1)
- double randDblExc( const double n ); // real number in (0,n)
- double operator()(); // same as rand()
-
- // Access to 53-bit random numbers (capacity of IEEE double precision)
- double rand53(); // real number in [0,1)
-
- // Access to nonuniform random number distributions
- double randNorm( const double mean = 0.0, const double stddev = 1.0 );
-
- // Re-seeding functions with same behavior as initializers
- void seed( const uint32 oneSeed );
- void seed( uint32 *const bigSeed, const uint32 seedLength = N );
- void seed();
-
- // Saving and loading generator state
- void save( uint32* saveArray ) const; // to array of size SAVE
- void load( uint32 *const loadArray ); // from such array
- friend std::ostream& operator<<( std::ostream& os, const MTRand& mtrand );
- friend std::istream& operator>>( std::istream& is, MTRand& mtrand );
- MTRand& operator=( const MTRand& o );
-
-protected:
- void initialize( const uint32 oneSeed );
- void reload();
- uint32 hiBit( const uint32 u ) const { return u & 0x80000000UL; }
- uint32 loBit( const uint32 u ) const { return u & 0x00000001UL; }
- uint32 loBits( const uint32 u ) const { return u & 0x7fffffffUL; }
- uint32 mixBits( const uint32 u, const uint32 v ) const
- { return hiBit(u) | loBits(v); }
- uint32 magic( const uint32 u ) const
- { return loBit(u) ? 0x9908b0dfUL : 0x0UL; }
- uint32 twist( const uint32 m, const uint32 s0, const uint32 s1 ) const
- { return m ^ (mixBits(s0,s1)>>1) ^ magic(s1); }
- static uint32 hash( time_t t, clock_t c );
-};
-
-// Functions are defined in order of usage to assist inlining
-
-inline MTRand::uint32 MTRand::hash( time_t t, clock_t c )
-{
- // Get a uint32 from t and c
- // Better than uint32(x) in case x is floating point in [0,1]
- // Based on code by Lawrence Kirby (fred@genesis.demon.co.uk)
-
- static uint32 differ = 0; // guarantee time-based seeds will change
-
- uint32 h1 = 0;
- unsigned char *p = (unsigned char *) &t;
- for( size_t i = 0; i < sizeof(t); ++i )
- {
- h1 *= UCHAR_MAX + 2U;
- h1 += p[i];
- }
- uint32 h2 = 0;
- p = (unsigned char *) &c;
- for( size_t j = 0; j < sizeof(c); ++j )
- {
- h2 *= UCHAR_MAX + 2U;
- h2 += p[j];
- }
- return ( h1 + differ++ ) ^ h2;
-}
-
-inline void MTRand::initialize( const uint32 seed )
-{
- // Initialize generator state with seed
- // See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier.
- // In previous versions, most significant bits (MSBs) of the seed affect
- // only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto.
- uint32 *s = state;
- uint32 *r = state;
- uint32 i = 1;
- *s++ = seed & 0xffffffffUL;
- for( ; i < N; ++i )
- {
- *s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL;
- r++;
- }
-}
-
-inline void MTRand::reload()
-{
- // Generate N new values in state
- // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com)
- static const int MmN = int(M) - int(N); // in case enums are unsigned
- uint32 *p = state;
- int i;
- for( i = N - M; i--; ++p )
- *p = twist( p[M], p[0], p[1] );
- for( i = M; --i; ++p )
- *p = twist( p[MmN], p[0], p[1] );
- *p = twist( p[MmN], p[0], state[0] );
-
- left = N, pNext = state;
-}
-
-inline void MTRand::seed( const uint32 oneSeed )
-{
- // Seed the generator with a simple uint32
- initialize(oneSeed);
- reload();
-}
-
-inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength )
-{
- // Seed the generator with an array of uint32's
- // There are 2^19937-1 possible initial states. This function allows
- // all of those to be accessed by providing at least 19937 bits (with a
- // default seed length of N = 624 uint32's). Any bits above the lower 32
- // in each element are discarded.
- // Just call seed() if you want to get array from /dev/urandom
- initialize(19650218UL);
- uint32 i = 1;
- uint32 j = 0;
- uint32 k = ( static_cast<uint32>(N) > seedLength ? static_cast<uint32>(N) : seedLength );
- for( ; k; --k )
- {
- state[i] =
- state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1664525UL );
- state[i] += ( bigSeed[j] & 0xffffffffUL ) + j;
- state[i] &= 0xffffffffUL;
- ++i; ++j;
- if( i >= N ) { state[0] = state[N-1]; i = 1; }
- if( j >= seedLength ) j = 0;
- }
- for( k = N - 1; k; --k )
- {
- state[i] =
- state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL );
- state[i] -= i;
- state[i] &= 0xffffffffUL;
- ++i;
- if( i >= N ) { state[0] = state[N-1]; i = 1; }
- }
- state[0] = 0x80000000UL; // MSB is 1, assuring non-zero initial array
- reload();
-}
-
-inline void MTRand::seed()
-{
- // Seed the generator with an array from /dev/urandom if available
- // Otherwise use a hash of time() and clock() values
-
- // First try getting an array from /dev/urandom
-
- /* // Commented out by FakeTruth because doing this 200 times a tick is SUUUUPEERRR SLOW!!~~!\D5Ne
- FILE* urandom = fopen( "/dev/urandom", "rb" );
- if( urandom )
- {
- uint32 bigSeed[N];
- register uint32 *s = bigSeed;
- register int i = N;
- register bool success = true;
- while( success && i-- )
- success = fread( s++, sizeof(uint32), 1, urandom );
- fclose(urandom);
- if( success ) { seed( bigSeed, N ); return; }
- }
- */
-
- // Was not successful, so use time() and clock() instead
- seed( hash( time(NULL), clock() ) );
-}
-
-inline MTRand::MTRand( const uint32 oneSeed )
- { seed(oneSeed); }
-
-inline MTRand::MTRand( uint32 *const bigSeed, const uint32 seedLength )
- { seed(bigSeed,seedLength); }
-
-inline MTRand::MTRand()
- { seed(); }
-
-inline MTRand::MTRand( const MTRand& o )
-{
- const uint32 *t = o.state;
- uint32 *s = state;
- int i = N;
- for( ; i--; *s++ = *t++ ) {}
- left = o.left;
- pNext = &state[N-left];
-}
-
-inline MTRand::uint32 MTRand::randInt()
-{
- // Pull a 32-bit integer from the generator state
- // Every other access function simply transforms the numbers extracted here
-
- if( left == 0 ) reload();
- --left;
-
- uint32 s1;
- s1 = *pNext++;
- s1 ^= (s1 >> 11);
- s1 ^= (s1 << 7) & 0x9d2c5680UL;
- s1 ^= (s1 << 15) & 0xefc60000UL;
- return ( s1 ^ (s1 >> 18) );
-}
-
-inline MTRand::uint32 MTRand::randInt( const uint32 n )
-{
- // Find which bits are used in n
- // Optimized by Magnus Jonsson (magnus@smartelectronix.com)
- uint32 used = n;
- used |= used >> 1;
- used |= used >> 2;
- used |= used >> 4;
- used |= used >> 8;
- used |= used >> 16;
-
- // Draw numbers until one is found in [0,n]
- uint32 i;
- do
- i = randInt() & used; // toss unused bits to shorten search
- while( i > n );
- return i;
-}
-
-inline double MTRand::rand()
- { return double(randInt()) * (1.0/4294967295.0); }
-
-inline double MTRand::rand( const double n )
- { return rand() * n; }
-
-inline double MTRand::randExc()
- { return double(randInt()) * (1.0/4294967296.0); }
-
-inline double MTRand::randExc( const double n )
- { return randExc() * n; }
-
-inline double MTRand::randDblExc()
- { return ( double(randInt()) + 0.5 ) * (1.0/4294967296.0); }
-
-inline double MTRand::randDblExc( const double n )
- { return randDblExc() * n; }
-
-inline double MTRand::rand53()
-{
- uint32 a = randInt() >> 5, b = randInt() >> 6;
- return ( a * 67108864.0 + b ) * (1.0/9007199254740992.0); // by Isaku Wada
-}
-
-inline double MTRand::randNorm( const double mean, const double stddev )
-{
- // Return a real number from a normal (Gaussian) distribution with given
- // mean and standard deviation by polar form of Box-Muller transformation
- double x, y, r;
- do
- {
- x = 2.0 * rand() - 1.0;
- y = 2.0 * rand() - 1.0;
- r = x * x + y * y;
- }
- while ( r >= 1.0 || r == 0.0 );
- double s = sqrt( -2.0 * log(r) / r );
- return mean + x * s * stddev;
-}
-
-inline double MTRand::operator()()
-{
- return rand();
-}
-
-inline void MTRand::save( uint32* saveArray ) const
-{
- const uint32 *s = state;
- uint32 *sa = saveArray;
- int i = N;
- for( ; i--; *sa++ = *s++ ) {}
- *sa = left;
-}
-
-inline void MTRand::load( uint32 *const loadArray )
-{
- uint32 *s = state;
- uint32 *la = loadArray;
- int i = N;
- for( ; i--; *s++ = *la++ ) {}
- left = *la;
- pNext = &state[N-left];
-}
-
-inline std::ostream& operator<<( std::ostream& os, const MTRand& mtrand )
-{
- const MTRand::uint32 *s = mtrand.state;
- int i = mtrand.N;
- for( ; i--; os << *s++ << "\t" ) {}
- return os << mtrand.left;
-}
-
-inline std::istream& operator>>( std::istream& is, MTRand& mtrand )
-{
- MTRand::uint32 *s = mtrand.state;
- int i = mtrand.N;
- for( ; i--; is >> *s++ ) {}
- is >> mtrand.left;
- mtrand.pNext = &mtrand.state[mtrand.N-mtrand.left];
- return is;
-}
-
-inline MTRand& MTRand::operator=( const MTRand& o )
-{
- if( this == &o ) return (*this);
- const uint32 *t = o.state;
- uint32 *s = state;
- int i = N;
- for( ; i--; *s++ = *t++ ) {}
- left = o.left;
- pNext = &state[N-left];
- return (*this);
-}
-
-#endif // MERSENNETWISTER_H
-
-// Change log:
-//
-// v0.1 - First release on 15 May 2000
-// - Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus
-// - Translated from C to C++
-// - Made completely ANSI compliant
-// - Designed convenient interface for initialization, seeding, and
-// obtaining numbers in default or user-defined ranges
-// - Added automatic seeding from /dev/urandom or time() and clock()
-// - Provided functions for saving and loading generator state
-//
-// v0.2 - Fixed bug which reloaded generator one step too late
-//
-// v0.3 - Switched to clearer, faster reload() code from Matthew Bellew
-//
-// v0.4 - Removed trailing newline in saved generator format to be consistent
-// with output format of built-in types
-//
-// v0.5 - Improved portability by replacing static const int's with enum's and
-// clarifying return values in seed(); suggested by Eric Heimburg
-// - Removed MAXINT constant; use 0xffffffffUL instead
-//
-// v0.6 - Eliminated seed overflow when uint32 is larger than 32 bits
-// - Changed integer [0,n] generator to give better uniformity
-//
-// v0.7 - Fixed operator precedence ambiguity in reload()
-// - Added access for real numbers in (0,1) and (0,n)
-//
-// v0.8 - Included time.h header to properly support time_t and clock_t
-//
-// v1.0 - Revised seeding to match 26 Jan 2002 update of Nishimura and Matsumoto
-// - Allowed for seeding with arrays of any length
-// - Added access for real numbers in [0,1) with 53-bit resolution
-// - Added access for real numbers from normal (Gaussian) distributions
-// - Increased overall speed by optimizing twist()
-// - Doubled speed of integer [0,n] generation
-// - Fixed out-of-range number generation on 64-bit machines
-// - Improved portability by substituting literal constants for long enum's
-// - Changed license from GNU LGPL to BSD
-//
-// v1.1 - Corrected parameter label in randNorm from "variance" to "stddev"
-// - Changed randNorm algorithm from basic to polar form for efficiency
-// - Updated includes from deprecated <xxxx.h> to standard <cxxxx> forms
-// - Cleaned declarations and definitions to please Intel compiler
-// - Revised twist() operator to work on ones'-complement machines
-// - Fixed reload() function to work when N and M are unsigned
-// - Added copy constructor and copy operator from Salvador Espana
diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp
index e461fae4c..ee9e569a7 100644
--- a/src/MobSpawner.cpp
+++ b/src/MobSpawner.cpp
@@ -61,7 +61,7 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
{
std::set<eMonsterType> allowedMobs;
- if (a_Biome == biMushroomIsland || a_Biome == biMushroomShore)
+ if ((a_Biome == biMushroomIsland) || (a_Biome == biMushroomShore))
{
addIfAllowed(mtMooshroom, allowedMobs);
}
@@ -84,7 +84,7 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
addIfAllowed(mtCreeper, allowedMobs);
addIfAllowed(mtSquid, allowedMobs);
- if (a_Biome != biDesert && a_Biome != biBeach && a_Biome != biOcean)
+ if ((a_Biome != biDesert) && (a_Biome != biBeach) && (a_Biome != biOcean))
{
addIfAllowed(mtSheep, allowedMobs);
addIfAllowed(mtPig, allowedMobs);
@@ -93,11 +93,11 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
addIfAllowed(mtEnderman, allowedMobs);
addIfAllowed(mtSlime, allowedMobs); // MG TODO : much more complicated rule
- if (a_Biome == biForest || a_Biome == biForestHills || a_Biome == biTaiga || a_Biome == biTaigaHills)
+ if ((a_Biome == biForest) || (a_Biome == biForestHills) || (a_Biome == biTaiga) || (a_Biome == biTaigaHills))
{
addIfAllowed(mtWolf, allowedMobs);
}
- else if (a_Biome == biJungle || a_Biome == biJungleHills)
+ else if ((a_Biome == biJungle) || (a_Biome == biJungleHills))
{
addIfAllowed(mtOcelot, allowedMobs);
}
@@ -126,8 +126,9 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, eMonsterType a_MobType, EMCSBiome a_Biome)
{
+ cFastRandom Random;
BLOCKTYPE TargetBlock = E_BLOCK_AIR;
- if (m_AllowedTypes.find(a_MobType) != m_AllowedTypes.end() && a_Chunk->UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, TargetBlock))
+ if (a_Chunk->UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, TargetBlock))
{
if ((a_RelY + 1 > cChunkDef::Height) || (a_RelY - 1 < 0))
{
@@ -177,7 +178,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
(BlockBelow == E_BLOCK_GRASS) || (BlockBelow == E_BLOCK_LEAVES) || (BlockBelow == E_BLOCK_NEW_LEAVES)
) &&
(a_RelY >= 62) &&
- (m_Random.NextInt(3, a_Biome) != 0)
+ (Random.NextInt(3, a_Biome) != 0)
);
}
@@ -238,7 +239,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
(!cBlockInfo::IsTransparent(BlockBelow)) &&
(SkyLight <= 7) &&
(BlockLight <= 7) &&
- (m_Random.NextInt(2, a_Biome) == 0)
+ (Random.NextInt(2, a_Biome) == 0)
);
}
@@ -262,7 +263,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
(TargetBlock == E_BLOCK_AIR) &&
(BlockAbove == E_BLOCK_AIR) &&
(!cBlockInfo::IsTransparent(BlockBelow)) &&
- (m_Random.NextInt(20, a_Biome) == 0)
+ (Random.NextInt(20, a_Biome) == 0)
);
}
@@ -284,6 +285,19 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
)
);
}
+
+ case mtMooshroom:
+ {
+ return (
+ (TargetBlock == E_BLOCK_AIR) &&
+ (BlockAbove == E_BLOCK_AIR) &&
+ (BlockBelow == E_BLOCK_MYCELIUM) &&
+ (
+ (a_Biome == biMushroomShore) ||
+ (a_Biome == biMushroomIsland)
+ )
+ );
+ }
default:
{
@@ -322,8 +336,8 @@ cMonster* cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY,
// Make sure we are looking at the right chunk to spawn in
a_Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
-
- if (CanSpawnHere(a_Chunk, a_RelX, a_RelY, a_RelZ, m_MobType, a_Biome))
+
+ if ((m_AllowedTypes.find(m_MobType) != m_AllowedTypes.end()) && CanSpawnHere(a_Chunk, a_RelX, a_RelY, a_RelZ, m_MobType, a_Biome))
{
cMonster * newMob = cMonster::NewMonsterFromType(m_MobType);
if (newMob)
diff --git a/src/MobSpawner.h b/src/MobSpawner.h
index 6b3a913ec..e8b8f191b 100644
--- a/src/MobSpawner.h
+++ b/src/MobSpawner.h
@@ -51,10 +51,10 @@ public :
typedef const std::set<cMonster *> tSpawnedContainer;
tSpawnedContainer & getSpawned(void);
-protected :
- // return true if specified type of mob can spawn on specified block
- bool CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, eMonsterType a_MobType, EMCSBiome a_Biome);
+ /** Returns true if specified type of mob can spawn on specified block */
+ static bool CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, eMonsterType a_MobType, EMCSBiome a_Biome);
+protected :
// return a random type that can spawn on specified biome.
// returns E_ENTITY_TYPE_DONOTUSE if none is possible
eMonsterType ChooseMobType(EMCSBiome a_Biome);
@@ -62,7 +62,6 @@ protected :
// add toAdd inside toAddIn, if toAdd is in m_AllowedTypes
void addIfAllowed(eMonsterType toAdd, std::set<eMonsterType> & toAddIn);
-protected :
cMonster::eFamily m_MonsterFamily;
std::set<eMonsterType> m_AllowedTypes;
bool m_NewPack;
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp
index 41ef26e2a..7ca7a9d66 100644
--- a/src/Mobs/AggressiveMonster.cpp
+++ b/src/Mobs/AggressiveMonster.cpp
@@ -75,7 +75,9 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk)
}
if (m_Target == nullptr)
+ {
return;
+ }
cTracer LineOfSight(GetWorld());
Vector3d AttackDirection(m_Target->GetPosition() - GetPosition());
diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp
index 16869c79d..1fa9d2c37 100644
--- a/src/Mobs/Blaze.cpp
+++ b/src/Mobs/Blaze.cpp
@@ -34,7 +34,7 @@ void cBlaze::Attack(float a_Dt)
{
m_AttackInterval += a_Dt * m_AttackRate;
- if (m_Target != nullptr && m_AttackInterval > 3.0)
+ if ((m_Target != nullptr) && (m_AttackInterval > 3.0))
{
// Setting this higher gives us more wiggle room for attackrate
Vector3d Speed = GetLookVector() * 20;
diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp
index c65c0d29a..fc8de8362 100644
--- a/src/Mobs/Ghast.cpp
+++ b/src/Mobs/Ghast.cpp
@@ -36,7 +36,7 @@ void cGhast::Attack(float a_Dt)
{
m_AttackInterval += a_Dt * m_AttackRate;
- if (m_Target != nullptr && m_AttackInterval > 3.0)
+ if ((m_Target != nullptr) && (m_AttackInterval > 3.0))
{
// Setting this higher gives us more wiggle room for attackrate
Vector3d Speed = GetLookVector() * 20;
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index 23b4d9f45..7b8f763af 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -9,7 +9,6 @@
#include "../Entities/Player.h"
#include "../Entities/ExpOrb.h"
#include "../MonsterConfig.h"
-#include "../MersenneTwister.h"
#include "../Chunk.h"
#include "../FastRandom.h"
@@ -21,57 +20,49 @@
/** Map for eType <-> string
Needs to be alpha-sorted by the strings, because binary search is used in StringToMobType()
The strings need to be lowercase (for more efficient comparisons in StringToMobType())
+m_VanillaName is the name that vanilla use for this mob.
*/
static const struct
{
eMonsterType m_Type;
const char * m_lcName;
+ const char * m_VanillaName;
} g_MobTypeNames[] =
{
- {mtBat, "bat"},
- {mtBlaze, "blaze"},
- {mtCaveSpider, "cavespider"},
- {mtChicken, "chicken"},
- {mtCow, "cow"},
- {mtCreeper, "creeper"},
- {mtEnderman, "enderman"},
- {mtEnderDragon, "enderdragon"},
- {mtGhast, "ghast"},
- {mtHorse, "horse"},
- {mtIronGolem, "irongolem"},
- {mtMagmaCube, "magmacube"},
- {mtMooshroom, "mooshroom"},
- {mtOcelot, "ocelot"},
- {mtPig, "pig"},
- {mtSheep, "sheep"},
- {mtSilverfish, "silverfish"},
- {mtSkeleton, "skeleton"},
- {mtSlime, "slime"},
- {mtSnowGolem, "snowgolem"},
- {mtSpider, "spider"},
- {mtSquid, "squid"},
- {mtVillager, "villager"},
- {mtWitch, "witch"},
- {mtWither, "wither"},
- {mtWolf, "wolf"},
- {mtZombie, "zombie"},
- {mtZombiePigman, "zombiepigman"},
+ {mtBat, "bat", "Bat"},
+ {mtBlaze, "blaze", "Blaze"},
+ {mtCaveSpider, "cavespider", "CaveSpider"},
+ {mtChicken, "chicken", "Chicken"},
+ {mtCow, "cow", "Cow"},
+ {mtCreeper, "creeper", "Creeper"},
+ {mtEnderman, "enderman", "Enderman"},
+ {mtEnderDragon, "enderdragon", "EnderDragon"},
+ {mtGhast, "ghast", "Ghast"},
+ {mtHorse, "horse", "EntityHorse"},
+ {mtIronGolem, "irongolem", "VillagerGolem"},
+ {mtMagmaCube, "magmacube", "LavaSlime"},
+ {mtMooshroom, "mooshroom", "MushroomCow"},
+ {mtOcelot, "ocelot", "Ozelot"},
+ {mtPig, "pig", "Pig"},
+ {mtSheep, "sheep", "Sheep"},
+ {mtSilverfish, "silverfish", "Silverfish"},
+ {mtSkeleton, "skeleton", "Skeleton"},
+ {mtSlime, "slime", "Slime"},
+ {mtSnowGolem, "snowgolem", "SnowMan"},
+ {mtSpider, "spider", "Spider"},
+ {mtSquid, "squid", "Squid"},
+ {mtVillager, "villager", "Villager"},
+ {mtWitch, "witch", "Witch"},
+ {mtWither, "wither", "WitherBoss"},
+ {mtWolf, "wolf", "Wolf"},
+ {mtZombie, "zombie", "Zombie"},
+ {mtZombiePigman, "zombiepigman", "PigZombie"},
} ;
-eMonsterType StringToMobType(const AString & a_MobString)
-{
- LOGWARNING("%s: Function is obsolete, use cMonster::StringToMobType() instead", __FUNCTION__);
- return cMonster::StringToMobType(a_MobString);
-}
-
-
-
-
-
////////////////////////////////////////////////////////////////////////////////
// cMonster:
@@ -160,7 +151,7 @@ void cMonster::TickPathFinding()
BLOCKTYPE BlockAtYP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 1, gCrossCoords[i].z + PosZ);
BLOCKTYPE BlockAtYPP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 2, gCrossCoords[i].z + PosZ);
int LowestY = FindFirstNonAirBlockPosition(gCrossCoords[i].x + PosX, gCrossCoords[i].z + PosZ);
- BLOCKTYPE BlockAtLowestY = m_World->GetBlock(gCrossCoords[i].x + PosX, LowestY, gCrossCoords[i].z + PosZ);
+ BLOCKTYPE BlockAtLowestY = (LowestY >= cChunkDef::Height) ? E_BLOCK_AIR : m_World->GetBlock(gCrossCoords[i].x + PosX, LowestY, gCrossCoords[i].z + PosZ);
if (
(!cBlockInfo::IsSolid(BlockAtY)) &&
@@ -275,7 +266,9 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk)
}
if ((m_Target != nullptr) && m_Target->IsDestroyed())
+ {
m_Target = nullptr;
+ }
// Burning in daylight
HandleDaylightBurning(a_Chunk);
@@ -453,7 +446,7 @@ int cMonster::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ)
}
else
{
- while (cBlockInfo::IsSolid(m_World->GetBlock((int)floor(a_PosX), PosY, (int)floor(a_PosZ))) && (PosY < cChunkDef::Height))
+ while ((PosY < cChunkDef::Height) && cBlockInfo::IsSolid(m_World->GetBlock((int)floor(a_PosX), PosY, (int)floor(a_PosZ))))
{
PosY++;
}
@@ -784,39 +777,47 @@ AString cMonster::MobTypeToString(eMonsterType a_MobType)
-eMonsterType cMonster::StringToMobType(const AString & a_Name)
+AString cMonster::MobTypeToVanillaName(eMonsterType a_MobType)
{
- AString lcName = StrToLower(a_Name);
-
- // Binary-search for the lowercase name:
- int lo = 0, hi = ARRAYCOUNT(g_MobTypeNames) - 1;
- while (hi - lo > 1)
+ // Mob types aren't sorted, so we need to search linearly:
+ for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++)
{
- int mid = (lo + hi) / 2;
- int res = strcmp(g_MobTypeNames[mid].m_lcName, lcName.c_str());
- if (res == 0)
- {
- return g_MobTypeNames[mid].m_Type;
- }
- if (res < 0)
- {
- lo = mid;
- }
- else
+ if (g_MobTypeNames[i].m_Type == a_MobType)
{
- hi = mid;
+ return g_MobTypeNames[i].m_VanillaName;
}
}
- // Range has collapsed to at most two elements, compare each:
- if (strcmp(g_MobTypeNames[lo].m_lcName, lcName.c_str()) == 0)
+
+ // Not found:
+ return "";
+}
+
+
+
+
+
+eMonsterType cMonster::StringToMobType(const AString & a_Name)
+{
+ AString lcName = StrToLower(a_Name);
+
+ // Search MCServer name:
+ for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++)
{
- return g_MobTypeNames[lo].m_Type;
+ if (strcmp(g_MobTypeNames[i].m_lcName, lcName.c_str()) == 0)
+ {
+ return g_MobTypeNames[i].m_Type;
+ }
}
- if ((lo != hi) && (strcmp(g_MobTypeNames[hi].m_lcName, lcName.c_str()) == 0))
+
+ // Not found. Search Vanilla name:
+ for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++)
{
- return g_MobTypeNames[hi].m_Type;
+ if (strcmp(StrToLower(g_MobTypeNames[i].m_VanillaName).c_str(), lcName.c_str()) == 0)
+ {
+ return g_MobTypeNames[i].m_Type;
+ }
}
-
+
// Not found:
return mtInvalidType;
}
@@ -1028,22 +1029,34 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel)
MTRand r1;
if (r1.randInt() % 200 < ((m_DropChanceHelmet * 200) + (a_LootingLevel * 2)))
{
- if (!GetEquippedHelmet().IsEmpty()) a_Drops.push_back(GetEquippedHelmet());
+ if (!GetEquippedHelmet().IsEmpty())
+ {
+ a_Drops.push_back(GetEquippedHelmet());
+ }
}
if (r1.randInt() % 200 < ((m_DropChanceChestplate * 200) + (a_LootingLevel * 2)))
{
- if (!GetEquippedChestplate().IsEmpty()) a_Drops.push_back(GetEquippedChestplate());
+ if (!GetEquippedChestplate().IsEmpty())
+ {
+ a_Drops.push_back(GetEquippedChestplate());
+ }
}
if (r1.randInt() % 200 < ((m_DropChanceLeggings * 200) + (a_LootingLevel * 2)))
{
- if (!GetEquippedLeggings().IsEmpty()) a_Drops.push_back(GetEquippedLeggings());
+ if (!GetEquippedLeggings().IsEmpty())
+ {
+ a_Drops.push_back(GetEquippedLeggings());
+ }
}
if (r1.randInt() % 200 < ((m_DropChanceBoots * 200) + (a_LootingLevel * 2)))
{
- if (!GetEquippedBoots().IsEmpty()) a_Drops.push_back(GetEquippedBoots());
+ if (!GetEquippedBoots().IsEmpty())
+ {
+ a_Drops.push_back(GetEquippedBoots());
+ }
}
}
@@ -1056,7 +1069,10 @@ void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, short a_LootingLevel)
MTRand r1;
if (r1.randInt() % 200 < ((m_DropChanceWeapon * 200) + (a_LootingLevel * 2)))
{
- if (!GetEquippedWeapon().IsEmpty()) a_Drops.push_back(GetEquippedWeapon());
+ if (!GetEquippedWeapon().IsEmpty())
+ {
+ a_Drops.push_back(GetEquippedWeapon());
+ }
}
}
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
index f5ae2cb4d..f04e45ac6 100644
--- a/src/Mobs/Monster.h
+++ b/src/Mobs/Monster.h
@@ -64,7 +64,7 @@ public:
virtual bool ReachedDestination(void);
// tolua_begin
- eMonsterType GetMobType(void) const {return m_MobType; }
+ eMonsterType GetMobType(void) const { return m_MobType; }
eFamily GetMobFamily(void) const;
// tolua_end
@@ -133,16 +133,19 @@ public:
If it's false, you only see the name when you sight the mob. If it's true, you always see the custom name. */
void SetCustomNameAlwaysVisible(bool a_CustomNameAlwaysVisible);
- /// Translates MobType enum to a string, empty string if unknown
+ /** Translates MobType enum to a string, empty string if unknown */
static AString MobTypeToString(eMonsterType a_MobType);
- /// Translates MobType string to the enum, mtInvalidType if not recognized
+ /** Translates MobType enum to the vanilla name of the mob, empty string if unknown. */
+ static AString MobTypeToVanillaName(eMonsterType a_MobType);
+
+ /** Translates MobType string to the enum, mtInvalidType if not recognized */
static eMonsterType StringToMobType(const AString & a_MobTypeName);
- /// Returns the mob family based on the type
+ /** Returns the mob family based on the type */
static eFamily FamilyFromType(eMonsterType a_MobType);
- /// Returns the spawn delay (number of game ticks between spawn attempts) for the given mob family
+ /** Returns the spawn delay (number of game ticks between spawn attempts) for the given mob family */
static int GetSpawnDelay(cMonster::eFamily a_MobFamily);
// tolua_end
@@ -169,10 +172,12 @@ protected:
/** Stores if mobile is currently moving towards the ultimate, final destination */
bool m_bMovingToDestination;
- /** Finds the first non-air block position (not the highest, as cWorld::GetHeight does)
- If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1
- If current Y is solid, goes up to find first nonsolid block, and returns that */
+ /** Finds the lowest non-air block position (not the highest, as cWorld::GetHeight does)
+ If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1
+ If current Y is solid, goes up to find first nonsolid block, and returns that.
+ If no suitable position is found, returns cChunkDef::Height. */
int FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ);
+
/** Returns if a monster can actually reach a given height by jumping or walking */
inline bool IsNextYPosReachable(int a_PosY)
{
diff --git a/src/Mobs/MonsterTypes.h b/src/Mobs/MonsterTypes.h
index 852eb3446..dc6dd3992 100644
--- a/src/Mobs/MonsterTypes.h
+++ b/src/Mobs/MonsterTypes.h
@@ -2,6 +2,7 @@
#pragma once
/// This identifies individual monster type, as well as their network type-ID
+
// tolua_begin
enum eMonsterType
{
@@ -38,15 +39,6 @@ enum eMonsterType
mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN,
} ;
-
-
-
-
-/** Translates a mob string ("ocelot") to mobtype (mtOcelot).
-OBSOLETE, use cMonster::StringToMobType() instead.
-Implemented in Monster.cpp. */
-extern eMonsterType StringToMobType(const AString & a_MobString);
-
// tolua_end
diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp
index 55a4412ca..1e4c35acd 100644
--- a/src/Mobs/Pig.cpp
+++ b/src/Mobs/Pig.cpp
@@ -49,17 +49,17 @@ void cPig::OnRightClicked(cPlayer & a_Player)
a_Player.Detach();
return;
}
-
+
if (m_Attachee->IsPlayer())
{
// Another player is already sitting in here, cannot attach
return;
}
-
+
// Detach whatever is sitting in this pig now:
m_Attachee->Detach();
}
-
+
// Attach the player to this pig
a_Player.AttachTo(this);
}
@@ -98,3 +98,23 @@ void cPig::Tick(float a_Dt, cChunk & a_Chunk)
+
+bool cPig::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ if (!super::DoTakeDamage(a_TDI))
+ {
+ return false;
+ }
+
+ if (a_TDI.DamageType == dtLightning)
+ {
+ Destroy();
+ m_World->SpawnMob(GetPosX(), GetPosY(), GetPosZ(), mtZombiePigman);
+ return true;
+ }
+ return true;
+}
+
+
+
+
diff --git a/src/Mobs/Pig.h b/src/Mobs/Pig.h
index 953850b3a..0e026933a 100644
--- a/src/Mobs/Pig.h
+++ b/src/Mobs/Pig.h
@@ -17,6 +17,9 @@ public:
CLASS_PROTODEF(cPig)
+ // cEntity overrides
+ virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
+
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;
diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp
index f17bc307c..da5ddc670 100644
--- a/src/Mobs/Skeleton.cpp
+++ b/src/Mobs/Skeleton.cpp
@@ -71,7 +71,7 @@ void cSkeleton::Attack(float a_Dt)
{
m_AttackInterval += a_Dt * m_AttackRate;
- if (m_Target != nullptr && m_AttackInterval > 3.0)
+ if ((m_Target != nullptr) && (m_AttackInterval > 3.0))
{
// Setting this higher gives us more wiggle room for attackrate
Vector3d Speed = GetLookVector() * 20;
diff --git a/src/Mobs/SnowGolem.cpp b/src/Mobs/SnowGolem.cpp
index 76334d970..8c4178beb 100644
--- a/src/Mobs/SnowGolem.cpp
+++ b/src/Mobs/SnowGolem.cpp
@@ -38,7 +38,7 @@ void cSnowGolem::Tick(float a_Dt, cChunk & a_Chunk)
{
BLOCKTYPE BlockBelow = m_World->GetBlock((int) floor(GetPosX()), (int) floor(GetPosY()) - 1, (int) floor(GetPosZ()));
BLOCKTYPE Block = m_World->GetBlock((int) floor(GetPosX()), (int) floor(GetPosY()), (int) floor(GetPosZ()));
- if (Block == E_BLOCK_AIR && cBlockInfo::IsSolid(BlockBelow))
+ if ((Block == E_BLOCK_AIR) && cBlockInfo::IsSolid(BlockBelow))
{
m_World->SetBlock((int) floor(GetPosX()), (int) floor(GetPosY()), (int) floor(GetPosZ()), E_BLOCK_SNOW, 0);
}
diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp
index 5c9999a59..963595347 100644
--- a/src/Mobs/Villager.cpp
+++ b/src/Mobs/Villager.cpp
@@ -37,6 +37,13 @@ bool cVillager::DoTakeDamage(TakeDamageInfo & a_TDI)
m_World->BroadcastEntityStatus(*this, esVillagerAngry);
}
}
+
+ if (a_TDI.DamageType == dtLightning)
+ {
+ Destroy();
+ m_World->SpawnMob(GetPosX(), GetPosY(), GetPosZ(), mtWitch);
+ return true;
+ }
return true;
}
diff --git a/src/Mobs/Witch.cpp b/src/Mobs/Witch.cpp
index 747a11f97..a3cadbaa0 100644
--- a/src/Mobs/Witch.cpp
+++ b/src/Mobs/Witch.cpp
@@ -2,6 +2,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Witch.h"
+#include "FastRandom.h"
diff --git a/src/Mobs/Witch.h b/src/Mobs/Witch.h
index 03691fef1..8230e1f98 100644
--- a/src/Mobs/Witch.h
+++ b/src/Mobs/Witch.h
@@ -2,7 +2,6 @@
#pragma once
#include "AggressiveMonster.h"
-#include "../MersenneTwister.h"
diff --git a/src/Noise/CMakeLists.txt b/src/Noise/CMakeLists.txt
new file mode 100644
index 000000000..e1837500f
--- /dev/null
+++ b/src/Noise/CMakeLists.txt
@@ -0,0 +1,21 @@
+
+cmake_minimum_required (VERSION 2.6)
+project (MCServer)
+
+include_directories ("${PROJECT_SOURCE_DIR}/../")
+
+SET (SRCS
+ Noise.cpp
+)
+
+SET (HDRS
+ Noise.h
+ OctavedNoise.h
+ RidgedNoise.h
+)
+
+if(NOT MSVC)
+ add_library(Noise ${SRCS} ${HDRS})
+
+ target_link_libraries(Noise OSSupport)
+endif()
diff --git a/src/Noise/InterpolNoise.h b/src/Noise/InterpolNoise.h
new file mode 100644
index 000000000..683b54563
--- /dev/null
+++ b/src/Noise/InterpolNoise.h
@@ -0,0 +1,524 @@
+
+// InterpolNoise.h
+
+// Implements the cInterpolNoise class template representing a noise that interpolates the values between integer coords from a single set of neighbors
+
+
+
+
+
+#pragma once
+
+#include "Noise.h"
+
+#define FAST_FLOOR(x) (((x) < 0) ? (((int)x) - 1) : ((int)x))
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cInterpolCell2D:
+
+template <typename T>
+class cInterpolCell2D
+{
+public:
+ cInterpolCell2D(
+ const cNoise & a_Noise, ///< Noise to use for generating the random values
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
+ int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
+ const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values
+ const NOISE_DATATYPE * a_FracY ///< Pointer to the attay that stores the Y fractional values
+ ):
+ m_Noise(a_Noise),
+ m_WorkRnds(&m_Workspace1),
+ m_CurFloorX(0),
+ m_CurFloorY(0),
+ m_Array(a_Array),
+ m_SizeX(a_SizeX),
+ m_SizeY(a_SizeY),
+ m_FracX(a_FracX),
+ m_FracY(a_FracY)
+ {
+ }
+
+
+ /** Generates part of the output noise array using the current m_WorkRnds[] values */
+ void Generate(
+ int a_FromX, int a_ToX,
+ int a_FromY, int a_ToY
+ )
+ {
+ for (int y = a_FromY; y < a_ToY; y++)
+ {
+ NOISE_DATATYPE Interp[2];
+ NOISE_DATATYPE FracY = T::coeff(m_FracY[y]);
+ Interp[0] = Lerp((*m_WorkRnds)[0][0], (*m_WorkRnds)[0][1], FracY);
+ Interp[1] = Lerp((*m_WorkRnds)[1][0], (*m_WorkRnds)[1][1], FracY);
+ int idx = y * m_SizeX + a_FromX;
+ for (int x = a_FromX; x < a_ToX; x++)
+ {
+ m_Array[idx++] = Lerp(Interp[0], Interp[1], T::coeff(m_FracX[x]));
+ } // for x
+ } // for y
+ }
+
+
+ /** Initializes m_WorkRnds[] with the specified values of the noise at the specified integral coords. */
+ void InitWorkRnds(int a_FloorX, int a_FloorY)
+ {
+ m_CurFloorX = a_FloorX;
+ m_CurFloorY = a_FloorY;
+ (*m_WorkRnds)[0][0] = m_Noise.IntNoise2D(m_CurFloorX, m_CurFloorY);
+ (*m_WorkRnds)[0][1] = m_Noise.IntNoise2D(m_CurFloorX, m_CurFloorY + 1);
+ (*m_WorkRnds)[1][0] = m_Noise.IntNoise2D(m_CurFloorX + 1, m_CurFloorY);
+ (*m_WorkRnds)[1][1] = m_Noise.IntNoise2D(m_CurFloorX + 1, m_CurFloorY + 1);
+ }
+
+
+ /** Updates m_WorkRnds[] for the new integral coords */
+ void Move(int a_NewFloorX, int a_NewFloorY)
+ {
+ // Swap the doublebuffer:
+ int OldFloorX = m_CurFloorX;
+ int OldFloorY = m_CurFloorY;
+ Workspace * OldWorkRnds = m_WorkRnds;
+ m_WorkRnds = (m_WorkRnds == &m_Workspace1) ? &m_Workspace2 : &m_Workspace1;
+
+ // Reuse as much of the old workspace as possible:
+ // TODO: Try out if simply calculating all 4 elements each time is faster than this monster loop
+ int DiffX = OldFloorX - a_NewFloorX;
+ int DiffY = OldFloorY - a_NewFloorY;
+ for (int x = 0; x < 2; x++)
+ {
+ int cx = a_NewFloorX + x;
+ int OldX = x - DiffX; // Where would this X be in the old grid?
+ for (int y = 0; y < 2; y++)
+ {
+ int cy = a_NewFloorY + y;
+ int OldY = y - DiffY; // Where would this Y be in the old grid?
+ if ((OldX >= 0) && (OldX < 2) && (OldY >= 0) && (OldY < 2))
+ {
+ (*m_WorkRnds)[x][y] = (*OldWorkRnds)[OldX][OldY];
+ }
+ else
+ {
+ (*m_WorkRnds)[x][y] = (NOISE_DATATYPE)m_Noise.IntNoise2D(cx, cy);
+ }
+ }
+ }
+ m_CurFloorX = a_NewFloorX;
+ m_CurFloorY = a_NewFloorY;
+ }
+
+protected:
+ typedef NOISE_DATATYPE Workspace[2][2];
+
+ /** The noise used for generating the values at integral coords. */
+ const cNoise & m_Noise;
+
+ /** The current random values; points to either m_Workspace1 or m_Workspace2 (doublebuffering) */
+ Workspace * m_WorkRnds;
+
+ /** Buffer 1 for workspace doublebuffering, used in Move() */
+ Workspace m_Workspace1;
+
+ /** Buffer 2 for workspace doublebuffering, used in Move() */
+ Workspace m_Workspace2;
+
+ /** Coords of the currently calculated m_WorkRnds[]. */
+ int m_CurFloorX, m_CurFloorY;
+
+ /** The output array to generate into. */
+ NOISE_DATATYPE * m_Array;
+
+ /** Dimensions of the output array. */
+ int m_SizeX, m_SizeY;
+
+ /** Arrays holding the fractional values of the coords in each direction. */
+ const NOISE_DATATYPE * m_FracX;
+ const NOISE_DATATYPE * m_FracY;
+} ;
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cInterpolCell3D:
+
+/** Holds a cache of the last calculated integral noise values and interpolates between them en masse.
+Provides a massive optimization for cInterpolNoise.
+Works by calculating multiple noise values (that have the same integral noise coords) at once. The underlying noise values
+needn't be recalculated for these values, only the interpolation is done within the unit cube. */
+template <typename T>
+class cInterpolCell3D
+{
+public:
+ cInterpolCell3D(
+ const cNoise & a_Noise, ///< Noise to use for generating the random values
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
+ int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
+ const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values
+ const NOISE_DATATYPE * a_FracY, ///< Pointer to the attay that stores the Y fractional values
+ const NOISE_DATATYPE * a_FracZ ///< Pointer to the array that stores the Z fractional values
+ ):
+ m_Noise(a_Noise),
+ m_WorkRnds(&m_Workspace1),
+ m_CurFloorX(0),
+ m_CurFloorY(0),
+ m_CurFloorZ(0),
+ m_Array(a_Array),
+ m_SizeX(a_SizeX),
+ m_SizeY(a_SizeY),
+ m_SizeZ(a_SizeZ),
+ m_FracX(a_FracX),
+ m_FracY(a_FracY),
+ m_FracZ(a_FracZ)
+ {
+ }
+
+
+ /** Generates part of the output array using current m_WorkRnds[]. */
+ void Generate(
+ int a_FromX, int a_ToX,
+ int a_FromY, int a_ToY,
+ int a_FromZ, int a_ToZ
+ )
+ {
+ for (int z = a_FromZ; z < a_ToZ; z++)
+ {
+ int idxZ = z * m_SizeX * m_SizeY;
+ NOISE_DATATYPE Interp2[2][2];
+ NOISE_DATATYPE FracZ = T::coeff(m_FracZ[z]);
+ for (int x = 0; x < 2; x++)
+ {
+ for (int y = 0; y < 2; y++)
+ {
+ Interp2[x][y] = Lerp((*m_WorkRnds)[x][y][0], (*m_WorkRnds)[x][y][1], FracZ);
+ }
+ }
+ for (int y = a_FromY; y < a_ToY; y++)
+ {
+ NOISE_DATATYPE Interp[2];
+ NOISE_DATATYPE FracY = T::coeff(m_FracY[y]);
+ Interp[0] = Lerp(Interp2[0][0], Interp2[0][1], FracY);
+ Interp[1] = Lerp(Interp2[1][0], Interp2[1][1], FracY);
+ int idx = idxZ + y * m_SizeX + a_FromX;
+ for (int x = a_FromX; x < a_ToX; x++)
+ {
+ m_Array[idx++] = Lerp(Interp[0], Interp[1], T::coeff(m_FracX[x]));
+ } // for x
+ } // for y
+ } // for z
+ }
+
+
+ /** Initializes m_WorkRnds[] with the specified Floor values. */
+ void InitWorkRnds(int a_FloorX, int a_FloorY, int a_FloorZ)
+ {
+ m_CurFloorX = a_FloorX;
+ m_CurFloorY = a_FloorY;
+ m_CurFloorZ = a_FloorZ;
+ (*m_WorkRnds)[0][0][0] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX, m_CurFloorY, m_CurFloorZ);
+ (*m_WorkRnds)[0][0][1] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX, m_CurFloorY, m_CurFloorZ + 1);
+ (*m_WorkRnds)[0][1][0] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX, m_CurFloorY + 1, m_CurFloorZ);
+ (*m_WorkRnds)[0][1][1] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX, m_CurFloorY + 1, m_CurFloorZ + 1);
+ (*m_WorkRnds)[1][0][0] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX + 1, m_CurFloorY, m_CurFloorZ);
+ (*m_WorkRnds)[1][0][1] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX + 1, m_CurFloorY, m_CurFloorZ + 1);
+ (*m_WorkRnds)[1][1][0] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX + 1, m_CurFloorY + 1, m_CurFloorZ);
+ (*m_WorkRnds)[1][1][1] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX + 1, m_CurFloorY + 1, m_CurFloorZ + 1);
+ }
+
+
+ /** Updates m_WorkRnds[] for the new Floor values. */
+ void Move(int a_NewFloorX, int a_NewFloorY, int a_NewFloorZ)
+ {
+ // Swap the doublebuffer:
+ int OldFloorX = m_CurFloorX;
+ int OldFloorY = m_CurFloorY;
+ int OldFloorZ = m_CurFloorZ;
+ Workspace * OldWorkRnds = m_WorkRnds;
+ m_WorkRnds = (m_WorkRnds == &m_Workspace1) ? &m_Workspace2 : &m_Workspace1;
+
+ // Reuse as much of the old workspace as possible:
+ // TODO: Try out if simply calculating all 8 elements each time is faster than this monster loop
+ int DiffX = OldFloorX - a_NewFloorX;
+ int DiffY = OldFloorY - a_NewFloorY;
+ int DiffZ = OldFloorZ - a_NewFloorZ;
+ for (int x = 0; x < 2; x++)
+ {
+ int cx = a_NewFloorX + x;
+ int OldX = x - DiffX; // Where would this X be in the old grid?
+ for (int y = 0; y < 2; y++)
+ {
+ int cy = a_NewFloorY + y;
+ int OldY = y - DiffY; // Where would this Y be in the old grid?
+ for (int z = 0; z < 2; z++)
+ {
+ int cz = a_NewFloorZ + z;
+ int OldZ = z - DiffZ;
+ if ((OldX >= 0) && (OldX < 2) && (OldY >= 0) && (OldY < 2) && (OldZ >= 0) && (OldZ < 2))
+ {
+ (*m_WorkRnds)[x][y][z] = (*OldWorkRnds)[OldX][OldY][OldZ];
+ }
+ else
+ {
+ (*m_WorkRnds)[x][y][z] = (NOISE_DATATYPE)m_Noise.IntNoise3D(cx, cy, cz);
+ }
+ } // for z
+ } // for y
+ } // for x
+ m_CurFloorX = a_NewFloorX;
+ m_CurFloorY = a_NewFloorY;
+ m_CurFloorZ = a_NewFloorZ;
+ }
+
+protected:
+ typedef NOISE_DATATYPE Workspace[2][2][2];
+
+ /** The noise used for generating the values at integral coords. */
+ const cNoise & m_Noise;
+
+ /** The current random values; points to either m_Workspace1 or m_Workspace2 (doublebuffering) */
+ Workspace * m_WorkRnds;
+
+ /** Buffer 1 for workspace doublebuffering, used in Move() */
+ Workspace m_Workspace1;
+
+ /** Buffer 2 for workspace doublebuffering, used in Move() */
+ Workspace m_Workspace2;
+
+ /** The integral coords of the currently calculated WorkRnds[] */
+ int m_CurFloorX, m_CurFloorY, m_CurFloorZ;
+
+ /** The output array where the noise is calculated. */
+ NOISE_DATATYPE * m_Array;
+
+ /** Dimensions of the output array. */
+ int m_SizeX, m_SizeY, m_SizeZ;
+
+ /** Arrays holding the fractional values of the coords in each direction. */
+ const NOISE_DATATYPE * m_FracX;
+ const NOISE_DATATYPE * m_FracY;
+ const NOISE_DATATYPE * m_FracZ;
+} ;
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cInterpolNoise:
+
+template <typename T>
+class cInterpolNoise
+{
+ /** Maximum size, for each direction, of the generated array. */
+ static const int MAX_SIZE = 256;
+
+public:
+ cInterpolNoise(int a_Seed):
+ m_Noise(a_Seed)
+ {
+ }
+
+
+ /** Sets a new seed for the generators. Relays the seed to the underlying noise. */
+ void SetSeed(int a_Seed)
+ {
+ m_Noise.SetSeed(a_Seed);
+ }
+
+
+ /** Fills a 2D array with the values of the noise. */
+ void Generate2D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
+ int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction
+ ) const
+ {
+ ASSERT(a_SizeX > 0);
+ ASSERT(a_SizeY > 0);
+ ASSERT(a_SizeX < MAX_SIZE);
+ ASSERT(a_SizeY < MAX_SIZE);
+ ASSERT(a_StartX < a_EndX);
+ ASSERT(a_StartY < a_EndY);
+
+ // Calculate the integral and fractional parts of each coord:
+ int FloorX[MAX_SIZE];
+ int FloorY[MAX_SIZE];
+ NOISE_DATATYPE FracX[MAX_SIZE];
+ NOISE_DATATYPE FracY[MAX_SIZE];
+ int SameX[MAX_SIZE];
+ int SameY[MAX_SIZE];
+ int NumSameX, NumSameY;
+ CalcFloorFrac(a_SizeX, a_StartX, a_EndX, FloorX, FracX, SameX, NumSameX);
+ CalcFloorFrac(a_SizeY, a_StartY, a_EndY, FloorY, FracY, SameY, NumSameY);
+
+ cInterpolCell2D<T> Cell(m_Noise, a_Array, a_SizeX, a_SizeY, FracX, FracY);
+
+ Cell.InitWorkRnds(FloorX[0], FloorY[0]);
+
+ // Calculate query values using Cell:
+ int FromY = 0;
+ for (int y = 0; y < NumSameY; y++)
+ {
+ int ToY = FromY + SameY[y];
+ int FromX = 0;
+ int CurFloorY = FloorY[FromY];
+ for (int x = 0; x < NumSameX; x++)
+ {
+ int ToX = FromX + SameX[x];
+ Cell.Generate(FromX, ToX, FromY, ToY);
+ Cell.Move(FloorX[ToX], CurFloorY);
+ FromX = ToX;
+ } // for x
+ Cell.Move(FloorX[0], FloorY[ToY]);
+ FromY = ToY;
+ } // for y
+ }
+
+
+ /** Fills a 3D array with the values of the noise. */
+ void Generate3D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
+ int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
+ NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Z direction
+ ) const
+ {
+ // Check params:
+ ASSERT(a_SizeX > 1);
+ ASSERT(a_SizeY > 1);
+
+ ASSERT(a_SizeX < MAX_SIZE);
+ ASSERT(a_SizeY < MAX_SIZE);
+ ASSERT(a_SizeZ < MAX_SIZE);
+ ASSERT(a_StartX < a_EndX);
+ ASSERT(a_StartY < a_EndY);
+ ASSERT(a_StartZ < a_EndZ);
+
+ // Calculate the integral and fractional parts of each coord:
+ int FloorX[MAX_SIZE];
+ int FloorY[MAX_SIZE];
+ int FloorZ[MAX_SIZE];
+ NOISE_DATATYPE FracX[MAX_SIZE];
+ NOISE_DATATYPE FracY[MAX_SIZE];
+ NOISE_DATATYPE FracZ[MAX_SIZE];
+ int SameX[MAX_SIZE];
+ int SameY[MAX_SIZE];
+ int SameZ[MAX_SIZE];
+ int NumSameX, NumSameY, NumSameZ;
+ CalcFloorFrac(a_SizeX, a_StartX, a_EndX, FloorX, FracX, SameX, NumSameX);
+ CalcFloorFrac(a_SizeY, a_StartY, a_EndY, FloorY, FracY, SameY, NumSameY);
+ CalcFloorFrac(a_SizeZ, a_StartZ, a_EndZ, FloorZ, FracZ, SameZ, NumSameZ);
+
+ cInterpolCell3D<T> Cell(
+ m_Noise, a_Array,
+ a_SizeX, a_SizeY, a_SizeZ,
+ FracX, FracY, FracZ
+ );
+
+ Cell.InitWorkRnds(FloorX[0], FloorY[0], FloorZ[0]);
+
+ // Calculate query values using Cell:
+ int FromZ = 0;
+ for (int z = 0; z < NumSameZ; z++)
+ {
+ int ToZ = FromZ + SameZ[z];
+ int CurFloorZ = FloorZ[FromZ];
+ int FromY = 0;
+ for (int y = 0; y < NumSameY; y++)
+ {
+ int ToY = FromY + SameY[y];
+ int CurFloorY = FloorY[FromY];
+ int FromX = 0;
+ for (int x = 0; x < NumSameX; x++)
+ {
+ int ToX = FromX + SameX[x];
+ Cell.Generate(FromX, ToX, FromY, ToY, FromZ, ToZ);
+ Cell.Move(FloorX[ToX], CurFloorY, CurFloorZ);
+ FromX = ToX;
+ }
+ Cell.Move(FloorX[0], FloorY[ToY], CurFloorZ);
+ FromY = ToY;
+ } // for y
+ Cell.Move(FloorX[0], FloorY[0], FloorZ[ToZ]);
+ FromZ = ToZ;
+ } // for z
+ }
+
+protected:
+
+ /** The noise used for the underlying value generation. */
+ cNoise m_Noise;
+
+
+ /** Calculates the integral and fractional parts along one axis.
+ a_Floor will receive the integral parts (array of a_Size ints).
+ a_Frac will receive the fractional parts (array of a_Size floats).
+ a_Same will receive the counts of items that have the same integral parts (array of up to a_Size ints).
+ a_NumSame will receive the count of a_Same elements (total count of different integral parts). */
+ void CalcFloorFrac(
+ int a_Size,
+ NOISE_DATATYPE a_Start, NOISE_DATATYPE a_End,
+ int * a_Floor, NOISE_DATATYPE * a_Frac,
+ int * a_Same, int & a_NumSame
+ ) const
+ {
+ ASSERT(a_Size > 0);
+
+ // Calculate the floor and frac values:
+ NOISE_DATATYPE val = a_Start;
+ NOISE_DATATYPE dif = (a_End - a_Start) / (a_Size - 1);
+ for (int i = 0; i < a_Size; i++)
+ {
+ a_Floor[i] = FAST_FLOOR(val);
+ a_Frac[i] = val - a_Floor[i];
+ val += dif;
+ }
+
+ // Mark up the same floor values into a_Same / a_NumSame:
+ int CurFloor = a_Floor[0];
+ int LastSame = 0;
+ a_NumSame = 0;
+ for (int i = 1; i < a_Size; i++)
+ {
+ if (a_Floor[i] != CurFloor)
+ {
+ a_Same[a_NumSame] = i - LastSame;
+ LastSame = i;
+ a_NumSame += 1;
+ CurFloor = a_Floor[i];
+ }
+ } // for i - a_Floor[]
+ if (LastSame < a_Size)
+ {
+ a_Same[a_NumSame] = a_Size - LastSame;
+ a_NumSame += 1;
+ }
+ }
+};
+
+
+
+
+
+/** A fifth-degree curve for interpolating.
+Implemented as a functor for better chance of inlining. */
+struct Interp5Deg
+{
+ static NOISE_DATATYPE coeff(NOISE_DATATYPE a_Val)
+ {
+ return a_Val * a_Val * a_Val * (a_Val * (a_Val * 6 - 15) + 10);
+ }
+};
+
+typedef cInterpolNoise<Interp5Deg> cInterp5DegNoise;
+
+
+
diff --git a/src/Noise.cpp b/src/Noise/Noise.cpp
index 8fcfe2920..0249ab6c1 100644
--- a/src/Noise.cpp
+++ b/src/Noise/Noise.cpp
@@ -9,10 +9,110 @@
+#if 0
+/** cImprovedPerlin noise test suite:
+- Generate a rather large 2D and 3D noise array and output it to a file
+- Compare performance of cCubicNoise and cImprovedNoise, both in single-value and 3D-array usages */
+static class cImprovedPerlinNoiseTest
+{
+public:
+ cImprovedPerlinNoiseTest(void)
+ {
+ printf("Performing Improved Perlin Noise tests...\n");
+ TestImage();
+ TestSpeed();
+ TestSpeedArr();
+ printf("Improved Perlin Noise tests complete.\n");
+ }
+
+
+ /** Tests the noise by generating 2D and 3D images and dumping them to files. */
+ void TestImage(void)
+ {
+ static const int SIZE_X = 256;
+ static const int SIZE_Y = 256;
+ static const int SIZE_Z = 16;
+
+ cImprovedNoise noise(1);
+ std::unique_ptr<NOISE_DATATYPE[]> arr(new NOISE_DATATYPE[SIZE_X * SIZE_Y * SIZE_Z]);
+ noise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, 0, 14, 0, 14, 0, 14);
+ Debug3DNoise(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, "ImprovedPerlinNoiseTest3D", 128);
+ noise.Generate2D(arr.get(), SIZE_X, SIZE_Y, 0, 14, 15, 28);
+ Debug2DNoise(arr.get(), SIZE_X, SIZE_Y, "ImprovedPerlinNoiseTest2D", 128);
+ }
+
+
+ /** Tests the speeds of cImprovedPerlin and cCubicNoise when generating individual values. */
+ void TestSpeed(void)
+ {
+ cImprovedNoise improvedNoise(1);
+ cNoise noise(1);
+ cTimer timer;
+
+ // Measure the improvedNoise:
+ NOISE_DATATYPE sum = 0;
+ long long start = timer.GetNowTime();
+ for (int i = 0; i < 100000000; i++)
+ {
+ sum += improvedNoise.GetValueAt(i, 0, -i);
+ }
+ long long finish = timer.GetNowTime();
+ printf("cImprovedNoise took %.2f seconds; total is %f.\n", static_cast<float>(finish - start) / 1000.0f, sum);
+
+ // Measure the cubicNoise:
+ sum = 0;
+ start = timer.GetNowTime();
+ for (int i = 0; i < 100000000; i++)
+ {
+ sum += noise.IntNoise3D(i, 0, -i);
+ }
+ finish = timer.GetNowTime();
+ printf("cCubicNoise took %.2f seconds; total is %f.\n", static_cast<float>(finish - start) / 1000.0f, sum);
+ }
+
+
+ /** Tests the speeds of cImprovedPerlin and cCubicNoise when generating arrays. */
+ void TestSpeedArr(void)
+ {
+ static const int SIZE_X = 256;
+ static const int SIZE_Y = 256;
+ static const int SIZE_Z = 16;
+
+ std::unique_ptr<NOISE_DATATYPE[]> arr(new NOISE_DATATYPE[SIZE_X * SIZE_Y * SIZE_Z]);
+ cTimer timer;
+ cImprovedNoise improvedNoise(1);
+ cCubicNoise cubicNoise(1);
+
+ // Measure the improvedNoise:
+ long long start = timer.GetNowTime();
+ for (int i = 0; i < 40; i++)
+ {
+ improvedNoise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, 0, 14, 0, 14, 0, 14);
+ }
+ long long finish = timer.GetNowTime();
+ printf("cImprovedNoise(arr) took %.2f seconds.\n", static_cast<float>(finish - start) / 1000.0f);
+
+ // Measure the cubicNoise:
+ start = timer.GetNowTime();
+ for (int i = 0; i < 40; i++)
+ {
+ cubicNoise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, 0, 14, 0, 14, 0, 14);
+ }
+ finish = timer.GetNowTime();
+ printf("cCubicNoise(arr) took %.2f seconds.\n", static_cast<float>(finish - start) / 1000.0f);
+ }
+} g_Test;
+
+#endif
+
+
+
+
+
////////////////////////////////////////////////////////////////////////////////
// Globals:
-void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase)
+void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff)
{
const int BUF_SIZE = 512;
ASSERT(a_SizeX <= BUF_SIZE); // Just stretch it, if needed
@@ -29,7 +129,7 @@ void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int
unsigned char buf[BUF_SIZE];
for (int x = 0; x < a_SizeX; x++)
{
- buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++]))));
+ buf[x] = static_cast<unsigned char>(Clamp((int)(128 + a_Coeff * a_Noise[idx++]), 0, 255));
}
f1.Write(buf, a_SizeX);
} // for y
@@ -50,7 +150,7 @@ void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int
unsigned char buf[BUF_SIZE];
for (int x = 0; x < a_SizeX; x++)
{
- buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++]))));
+ buf[x] = static_cast<unsigned char>(Clamp((int)(128 + a_Coeff * a_Noise[idx++]), 0, 255));
}
f2.Write(buf, a_SizeX);
} // for z
@@ -65,7 +165,7 @@ void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int
-void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase)
+void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff)
{
const int BUF_SIZE = 512;
ASSERT(a_SizeX <= BUF_SIZE); // Just stretch it, if needed
@@ -79,7 +179,7 @@ void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, cons
unsigned char buf[BUF_SIZE];
for (int x = 0; x < a_SizeX; x++)
{
- buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++]))));
+ buf[x] = static_cast<unsigned char>(Clamp((int)(128 + a_Coeff * a_Noise[idx++]), 0, 255));
}
f1.Write(buf, a_SizeX);
} // for y
@@ -594,13 +694,6 @@ NOISE_DATATYPE cNoise::CubicNoise3D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOIS
////////////////////////////////////////////////////////////////////////////////
// cCubicNoise:
-#ifdef _DEBUG
- int cCubicNoise::m_NumSingleX = 0;
- int cCubicNoise::m_NumSingleXY = 0;
- int cCubicNoise::m_NumSingleY = 0;
- int cCubicNoise::m_NumCalls = 0;
-#endif // _DEBUG
-
cCubicNoise::cCubicNoise(int a_Seed) :
m_Noise(a_Seed)
{
@@ -639,23 +732,6 @@ void cCubicNoise::Generate2D(
Cell.InitWorkRnds(FloorX[0], FloorY[0]);
- #ifdef _DEBUG
- // Statistics on the noise-space coords:
- if (NumSameX == 1)
- {
- m_NumSingleX++;
- if (NumSameY == 1)
- {
- m_NumSingleXY++;
- }
- }
- if (NumSameY == 1)
- {
- m_NumSingleY++;
- }
- m_NumCalls++;
- #endif // _DEBUG
-
// Calculate query values using Cell:
int FromY = 0;
for (int y = 0; y < NumSameY; y++)
@@ -792,101 +868,28 @@ void cCubicNoise::CalcFloorFrac(
////////////////////////////////////////////////////////////////////////////////
-// cPerlinNoise:
-
-cPerlinNoise::cPerlinNoise(void) :
- m_Seed(0)
-{
-}
-
-
-
+// cImprovedNoise:
-
-cPerlinNoise::cPerlinNoise(int a_Seed) :
- m_Seed(a_Seed)
-{
-}
-
-
-
-
-
-void cPerlinNoise::SetSeed(int a_Seed)
-{
- m_Seed = a_Seed;
-}
-
-
-
-
-
-void cPerlinNoise::AddOctave(float a_Frequency, float a_Amplitude)
-{
- m_Octaves.push_back(cOctave(m_Seed * ((int)m_Octaves.size() + 4) * 4 + 1024, a_Frequency, a_Amplitude));
-}
-
-
-
-
-
-void cPerlinNoise::Generate2D(
- NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
- int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
- NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
- NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
- NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
-) const
+cImprovedNoise::cImprovedNoise(int a_Seed)
{
- if (m_Octaves.empty())
- {
- // No work to be done
- ASSERT(!"Perlin: No octaves to generate!");
- return;
- }
-
- bool ShouldFreeWorkspace = (a_Workspace == nullptr);
- int ArrayCount = a_SizeX * a_SizeY;
- if (ShouldFreeWorkspace)
- {
- a_Workspace = new NOISE_DATATYPE[ArrayCount];
- }
-
- // Generate the first octave directly into array:
- const cOctave & FirstOctave = m_Octaves.front();
-
- FirstOctave.m_Noise.Generate2D(
- a_Workspace, a_SizeX, a_SizeY,
- a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
- a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency
- );
- NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
- for (int i = 0; i < ArrayCount; i++)
+ // Initialize the permutations with identity:
+ for (int i = 0; i < 256; i++)
{
- a_Array[i] = a_Workspace[i] * Amplitude;
+ m_Perm[i] = i;
}
-
- // Add each octave:
- for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
+
+ // Randomize the permutation table - swap each element with a random other element:
+ cNoise noise(a_Seed);
+ for (int i = 0; i < 256; i++)
{
- // Generate cubic noise for the octave:
- itr->m_Noise.Generate2D(
- a_Workspace, a_SizeX, a_SizeY,
- a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
- a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency
- );
- // Add the cubic noise into the output:
- NOISE_DATATYPE Amplitude = itr->m_Amplitude;
- for (int i = 0; i < ArrayCount; i++)
- {
- a_Array[i] += a_Workspace[i] * Amplitude;
- }
+ int rnd = (noise.IntNoise1DInt(i) / 7) % 256;
+ std::swap(m_Perm[i], m_Perm[rnd]);
}
-
- if (ShouldFreeWorkspace)
+
+ // Copy the lower 256 entries into upper 256 entries:
+ for (int i = 0; i < 256; i++)
{
- delete[] a_Workspace;
- a_Workspace = nullptr;
+ m_Perm[i + 256] = m_Perm[i];
}
}
@@ -894,239 +897,132 @@ void cPerlinNoise::Generate2D(
-void cPerlinNoise::Generate3D(
- NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
- int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
- NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
- NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
- NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
- NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
+void cImprovedNoise::Generate2D(
+ NOISE_DATATYPE * a_Array,
+ int a_SizeX, int a_SizeY,
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX,
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY
) const
{
- if (m_Octaves.empty())
- {
- // No work to be done
- ASSERT(!"Perlin: No octaves to generate!");
- return;
- }
-
- bool ShouldFreeWorkspace = (a_Workspace == nullptr);
- int ArrayCount = a_SizeX * a_SizeY * a_SizeZ;
- if (ShouldFreeWorkspace)
+ size_t idx = 0;
+ for (int y = 0; y < a_SizeY; y++)
{
- a_Workspace = new NOISE_DATATYPE[ArrayCount];
- }
-
- // Generate the first octave directly into array:
- const cOctave & FirstOctave = m_Octaves.front();
-
- FirstOctave.m_Noise.Generate3D(
- a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
- a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
- a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency,
- a_StartZ * FirstOctave.m_Frequency, a_EndZ * FirstOctave.m_Frequency
- );
- NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
- for (int i = 0; i < ArrayCount; i++)
- {
- a_Array[i] = a_Workspace[i] * Amplitude;
- }
-
- // Add each octave:
- for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
- {
- // Generate cubic noise for the octave:
- itr->m_Noise.Generate3D(
- a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
- a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
- a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency,
- a_StartZ * itr->m_Frequency, a_EndZ * itr->m_Frequency
- );
- // Add the cubic noise into the output:
- NOISE_DATATYPE Amplitude = itr->m_Amplitude;
- for (int i = 0; i < ArrayCount; i++)
+ NOISE_DATATYPE ratioY = static_cast<NOISE_DATATYPE>(y) / (a_SizeY - 1);
+ NOISE_DATATYPE noiseY = Lerp(a_StartY, a_EndY, ratioY);
+ int noiseYInt = FAST_FLOOR(noiseY);
+ int yCoord = noiseYInt & 255;
+ NOISE_DATATYPE noiseYFrac = noiseY - noiseYInt;
+ NOISE_DATATYPE fadeY = Fade(noiseYFrac);
+ for (int x = 0; x < a_SizeX; x++)
{
- a_Array[i] += a_Workspace[i] * Amplitude;
- }
- }
-
- if (ShouldFreeWorkspace)
- {
- delete[] a_Workspace;
- a_Workspace = nullptr;
- }
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cRidgedMultiNoise:
-
-cRidgedMultiNoise::cRidgedMultiNoise(void) :
- m_Seed(0)
-{
-}
-
-
-
-
-
-cRidgedMultiNoise::cRidgedMultiNoise(int a_Seed) :
- m_Seed(a_Seed)
-{
-}
-
-
-
-
-
-void cRidgedMultiNoise::SetSeed(int a_Seed)
-{
- m_Seed = a_Seed;
-}
-
-
-
-
-
-void cRidgedMultiNoise::AddOctave(float a_Frequency, float a_Amplitude)
-{
- m_Octaves.push_back(cOctave(m_Seed * ((int)m_Octaves.size() + 4) * 4 + 1024, a_Frequency, a_Amplitude));
+ NOISE_DATATYPE ratioX = static_cast<NOISE_DATATYPE>(x) / (a_SizeX - 1);
+ NOISE_DATATYPE noiseX = Lerp(a_StartX, a_EndX, ratioX);
+ int noiseXInt = FAST_FLOOR(noiseX);
+ int xCoord = noiseXInt & 255;
+ NOISE_DATATYPE noiseXFrac = noiseX - noiseXInt;
+ NOISE_DATATYPE fadeX = Fade(noiseXFrac);
+
+ // Hash the coordinates:
+ int A = m_Perm[xCoord] + yCoord;
+ int AA = m_Perm[A];
+ int AB = m_Perm[A + 1];
+ int B = m_Perm[xCoord + 1] + yCoord;
+ int BA = m_Perm[B];
+ int BB = m_Perm[B + 1];
+
+ // Lerp the gradients:
+ a_Array[idx++] = Lerp(
+ Lerp(Grad(m_Perm[AA], noiseXFrac, noiseYFrac, 0), Grad(m_Perm[BA], noiseXFrac - 1, noiseYFrac, 0), fadeX),
+ Lerp(Grad(m_Perm[AB], noiseXFrac, noiseYFrac - 1, 0), Grad(m_Perm[BB], noiseXFrac - 1, noiseYFrac - 1, 0), fadeX),
+ fadeY
+ );
+ } // for x
+ } // for y
}
-void cRidgedMultiNoise::Generate2D(
- NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
- int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
- NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
- NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
- NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
+void cImprovedNoise::Generate3D(
+ NOISE_DATATYPE * a_Array,
+ int a_SizeX, int a_SizeY, int a_SizeZ,
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX,
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY,
+ NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ
) const
{
- if (m_Octaves.empty())
- {
- // No work to be done
- ASSERT(!"RidgedMulti: No octaves to generate!");
- return;
- }
-
- bool ShouldFreeWorkspace = (a_Workspace == nullptr);
- int ArrayCount = a_SizeX * a_SizeY;
- if (ShouldFreeWorkspace)
- {
- a_Workspace = new NOISE_DATATYPE[ArrayCount];
- }
-
- // Generate the first octave directly into array:
- const cOctave & FirstOctave = m_Octaves.front();
-
- FirstOctave.m_Noise.Generate2D(
- a_Workspace, a_SizeX, a_SizeY,
- a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
- a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency
- );
- NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
- for (int i = 0; i < ArrayCount; i++)
+ size_t idx = 0;
+ for (int z = 0; z < a_SizeZ; z++)
{
- a_Array[i] = fabs(a_Workspace[i] * Amplitude);
- }
-
- // Add each octave:
- for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
- {
- // Generate cubic noise for the octave:
- itr->m_Noise.Generate2D(
- a_Workspace, a_SizeX, a_SizeY,
- a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
- a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency
- );
- // Add the cubic noise into the output:
- NOISE_DATATYPE Amplitude = itr->m_Amplitude;
- for (int i = 0; i < ArrayCount; i++)
+ NOISE_DATATYPE ratioZ = static_cast<NOISE_DATATYPE>(z) / (a_SizeZ - 1);
+ NOISE_DATATYPE noiseZ = Lerp(a_StartZ, a_EndZ, ratioZ);
+ int noiseZInt = FAST_FLOOR(noiseZ);
+ int zCoord = noiseZInt & 255;
+ NOISE_DATATYPE noiseZFrac = noiseZ - noiseZInt;
+ NOISE_DATATYPE fadeZ = Fade(noiseZFrac);
+ for (int y = 0; y < a_SizeY; y++)
{
- a_Array[i] += fabs(a_Workspace[i] * Amplitude);
- }
- }
-
- if (ShouldFreeWorkspace)
- {
- delete[] a_Workspace;
- a_Workspace = nullptr;
- }
+ NOISE_DATATYPE ratioY = static_cast<NOISE_DATATYPE>(y) / (a_SizeY - 1);
+ NOISE_DATATYPE noiseY = Lerp(a_StartY, a_EndY, ratioY);
+ int noiseYInt = FAST_FLOOR(noiseY);
+ int yCoord = noiseYInt & 255;
+ NOISE_DATATYPE noiseYFrac = noiseY - noiseYInt;
+ NOISE_DATATYPE fadeY = Fade(noiseYFrac);
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ NOISE_DATATYPE ratioX = static_cast<NOISE_DATATYPE>(x) / (a_SizeX - 1);
+ NOISE_DATATYPE noiseX = Lerp(a_StartX, a_EndX, ratioX);
+ int noiseXInt = FAST_FLOOR(noiseX);
+ int xCoord = noiseXInt & 255;
+ NOISE_DATATYPE noiseXFrac = noiseX - noiseXInt;
+ NOISE_DATATYPE fadeX = Fade(noiseXFrac);
+
+ // Hash the coordinates:
+ int A = m_Perm[xCoord] + yCoord;
+ int AA = m_Perm[A] + zCoord;
+ int AB = m_Perm[A + 1] + zCoord;
+ int B = m_Perm[xCoord + 1] + yCoord;
+ int BA = m_Perm[B] + zCoord;
+ int BB = m_Perm[B + 1] + zCoord;
+
+ // Lerp the gradients:
+ // TODO: This may be optimized by swapping the coords and recalculating most lerps only "once every x"
+ a_Array[idx++] = Lerp(
+ Lerp(
+ Lerp(Grad(m_Perm[AA], noiseXFrac, noiseYFrac, noiseZFrac), Grad(m_Perm[BA], noiseXFrac - 1, noiseYFrac, noiseZFrac), fadeX),
+ Lerp(Grad(m_Perm[AB], noiseXFrac, noiseYFrac - 1, noiseZFrac), Grad(m_Perm[BB], noiseXFrac - 1, noiseYFrac - 1, noiseZFrac), fadeX),
+ fadeY
+ ),
+ Lerp(
+ Lerp(Grad(m_Perm[AA + 1], noiseXFrac, noiseYFrac, noiseZFrac - 1), Grad(m_Perm[BA + 1], noiseXFrac - 1, noiseYFrac, noiseZFrac - 1), fadeX),
+ Lerp(Grad(m_Perm[AB + 1], noiseXFrac, noiseYFrac - 1, noiseZFrac - 1), Grad(m_Perm[BB + 1], noiseXFrac - 1, noiseYFrac - 1, noiseZFrac - 1), fadeX),
+ fadeY
+ ),
+ fadeZ
+ );
+ } // for x
+ } // for y
+ } // for z
}
-void cRidgedMultiNoise::Generate3D(
- NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
- int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
- NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
- NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
- NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
- NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
-) const
+NOISE_DATATYPE cImprovedNoise::GetValueAt(int a_X, int a_Y, int a_Z)
{
- if (m_Octaves.empty())
- {
- // No work to be done
- ASSERT(!"RidgedMulti: No octaves to generate!");
- return;
- }
-
- bool ShouldFreeWorkspace = (a_Workspace == nullptr);
- int ArrayCount = a_SizeX * a_SizeY * a_SizeZ;
- if (ShouldFreeWorkspace)
- {
- a_Workspace = new NOISE_DATATYPE[ArrayCount];
- }
-
- // Generate the first octave directly into array:
- const cOctave & FirstOctave = m_Octaves.front();
-
- FirstOctave.m_Noise.Generate3D(
- a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
- a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
- a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency,
- a_StartZ * FirstOctave.m_Frequency, a_EndZ * FirstOctave.m_Frequency
- );
- NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
- for (int i = 0; i < ArrayCount; i++)
- {
- a_Array[i] = a_Workspace[i] * Amplitude;
- }
-
- // Add each octave:
- for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
- {
- // Generate cubic noise for the octave:
- itr->m_Noise.Generate3D(
- a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
- a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
- a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency,
- a_StartZ * itr->m_Frequency, a_EndZ * itr->m_Frequency
- );
- // Add the cubic noise into the output:
- NOISE_DATATYPE Amplitude = itr->m_Amplitude;
- for (int i = 0; i < ArrayCount; i++)
- {
- a_Array[i] += a_Workspace[i] * Amplitude;
- }
- }
-
- if (ShouldFreeWorkspace)
- {
- delete[] a_Workspace;
- a_Workspace = nullptr;
- }
+ // Hash the coordinates:
+ a_X = a_X & 255;
+ a_Y = a_Y & 255;
+ a_Z = a_Z & 255;
+ int A = m_Perm[a_X] + a_Y;
+ int AA = m_Perm[A] + a_Z;
+
+ return Grad(m_Perm[AA], 1, 1, 1);
}
+
diff --git a/src/Noise.h b/src/Noise/Noise.h
index b7a90d5b7..323194bfd 100644
--- a/src/Noise.h
+++ b/src/Noise/Noise.h
@@ -7,22 +7,11 @@
#include <cmath>
+/** The datatype used by all the noise generators. */
+typedef float NOISE_DATATYPE;
-
-
-
-// Some settings
-#define NOISE_DATATYPE float
-
-
-
-
-
-#ifdef _MSC_VER
- #define INLINE __forceinline
-#else
- #define INLINE inline
-#endif
+#include "OctavedNoise.h"
+#include "RidgedNoise.h"
@@ -35,20 +24,20 @@ public:
cNoise(const cNoise & a_Noise);
// The following functions, if not marked INLINE, are about 20 % slower
- INLINE NOISE_DATATYPE IntNoise1D(int a_X) const;
- INLINE NOISE_DATATYPE IntNoise2D(int a_X, int a_Y) const;
- INLINE NOISE_DATATYPE IntNoise3D(int a_X, int a_Y, int a_Z) const;
+ inline NOISE_DATATYPE IntNoise1D(int a_X) const;
+ inline NOISE_DATATYPE IntNoise2D(int a_X, int a_Y) const;
+ inline NOISE_DATATYPE IntNoise3D(int a_X, int a_Y, int a_Z) const;
// Return a float number in the specified range:
- INLINE NOISE_DATATYPE IntNoise2DInRange(int a_X, int a_Y, float a_Min, float a_Max) const
+ inline NOISE_DATATYPE IntNoise2DInRange(int a_X, int a_Y, float a_Min, float a_Max) const
{
return a_Min + std::abs(IntNoise2D(a_X, a_Y)) * (a_Max - a_Min);
}
// Note: These functions have a mod8-irregular chance - each of the mod8 remainders has different chance of occurrence. Divide by 8 to rectify.
- INLINE int IntNoise1DInt(int a_X) const;
- INLINE int IntNoise2DInt(int a_X, int a_Y) const;
- INLINE int IntNoise3DInt(int a_X, int a_Y, int a_Z) const;
+ inline int IntNoise1DInt(int a_X) const;
+ inline int IntNoise2DInt(int a_X, int a_Y) const;
+ inline int IntNoise3DInt(int a_X, int a_Y, int a_Z) const;
NOISE_DATATYPE LinearNoise1D(NOISE_DATATYPE a_X) const;
NOISE_DATATYPE CosineNoise1D(NOISE_DATATYPE a_X) const;
@@ -61,9 +50,9 @@ public:
void SetSeed(int a_Seed) { m_Seed = a_Seed; }
- INLINE static NOISE_DATATYPE CubicInterpolate (NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct);
- INLINE static NOISE_DATATYPE CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
- INLINE static NOISE_DATATYPE LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
+ inline static NOISE_DATATYPE CubicInterpolate (NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct);
+ inline static NOISE_DATATYPE CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
+ inline static NOISE_DATATYPE LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
private:
int m_Seed;
@@ -76,19 +65,15 @@ private:
class cCubicNoise
{
public:
- static const int MAX_SIZE = 512; ///< Maximum size of each dimension of the query arrays.
+ /** Maximum size of each dimension of the query arrays. */
+ static const int MAX_SIZE = 512;
+ /** Creates a new instance with the specified seed. */
cCubicNoise(int a_Seed);
- void Generate1D(
- NOISE_DATATYPE * a_Array, ///< Array to generate into
- int a_SizeX, ///< Count of the array
- NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX ///< Noise-space coords of the array
- ) const;
-
-
+ /** Fills a 2D array with the values of the noise. */
void Generate2D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
@@ -97,6 +82,7 @@ public:
) const;
+ /** Fills a 3D array with the values of the noise. */
void Generate3D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
@@ -106,163 +92,87 @@ public:
) const;
protected:
- typedef NOISE_DATATYPE Workspace1D[4];
- typedef NOISE_DATATYPE Workspace2D[4][4];
-
- cNoise m_Noise; // Used for integral rnd values
-
- #ifdef _DEBUG
- // Statistics on the noise-space coords:
- static int m_NumSingleX;
- static int m_NumSingleXY;
- static int m_NumSingleY;
- static int m_NumCalls;
- #endif // _DEBUG
- /// Calculates the integral and fractional parts along one axis.
+ /** Noise used for integral random values. */
+ cNoise m_Noise;
+
+
+ /** Calculates the integral and fractional parts along one axis.
+ a_Floor will receive the integral parts (array of a_Size ints).
+ a_Frac will receive the fractional parts (array of a_Size floats).
+ a_Same will receive the counts of items that have the same integral parts (array of up to a_Size ints).
+ a_NumSame will receive the count of a_Same elements (total count of different integral parts). */
void CalcFloorFrac(
int a_Size,
NOISE_DATATYPE a_Start, NOISE_DATATYPE a_End,
int * a_Floor, NOISE_DATATYPE * a_Frac,
int * a_Same, int & a_NumSame
) const;
-
- void UpdateWorkRnds2DX(
- Workspace2D & a_WorkRnds,
- Workspace1D & a_Interps,
- int a_LastFloorX, int a_NewFloorX,
- int a_FloorY,
- NOISE_DATATYPE a_FractionY
- ) const;
} ;
-class cPerlinNoise
+/** Improved noise, as described by Ken Perlin: http://mrl.nyu.edu/~perlin/paper445.pdf
+Implementation adapted from Perlin's Java implementation: http://mrl.nyu.edu/~perlin/noise/ */
+class cImprovedNoise
{
public:
- cPerlinNoise(void);
- cPerlinNoise(int a_Seed);
-
-
- void SetSeed(int a_Seed);
-
- void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude);
-
- void Generate1D(
- NOISE_DATATYPE * a_Array, ///< Array to generate into
- int a_SizeX, ///< Count of the array
- NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array
- NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
- ) const;
-
-
+ /** Constructs a new instance of the noise obbject.
+ Note that this operation is quite expensive (the permutation array being constructed). */
+ cImprovedNoise(int a_Seed);
+
+
+ /** Fills a 2D array with the values of the noise. */
void Generate2D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
- NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
- NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction
) const;
+ /** Fills a 3D array with the values of the noise. */
void Generate3D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
- NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
- NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
+ NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Z direction
) const;
-
+
+ /** Returns the value at the specified integral coords. Used for raw speed measurement. */
+ NOISE_DATATYPE GetValueAt(int a_X, int a_Y, int a_Z);
+
protected:
- class cOctave
+
+ /** The permutation table used by the noise function. Initialized using seed. */
+ int m_Perm[512];
+
+
+ /** Calculates the fade curve, 6 * t^5 - 15 * t^4 + 10 * t^3. */
+ inline static NOISE_DATATYPE Fade(NOISE_DATATYPE a_T)
{
- public:
- cCubicNoise m_Noise;
-
- NOISE_DATATYPE m_Frequency; // Coord multiplier
- NOISE_DATATYPE m_Amplitude; // Value multiplier
-
- cOctave(int a_Seed, NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude) :
- m_Noise(a_Seed),
- m_Frequency(a_Frequency),
- m_Amplitude(a_Amplitude)
- {
- }
- } ;
-
- typedef std::vector<cOctave> cOctaves;
-
- int m_Seed;
- cOctaves m_Octaves;
-} ;
+ return a_T * a_T * a_T * (a_T * (a_T * 6 - 15) + 10);
+ }
+ /** Returns the gradient value based on the hash. */
+ inline static NOISE_DATATYPE Grad(int a_Hash, NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOISE_DATATYPE a_Z)
+ {
+ int hash = a_Hash % 16;
+ NOISE_DATATYPE u = (hash < 8) ? a_X : a_Y;
+ NOISE_DATATYPE v = (hash < 4) ? a_Y : (((hash == 12) || (hash == 14)) ? a_X : a_Z);
+ return (((hash & 1) == 0) ? u : -u) + (((hash & 2) == 0) ? v : -v);
+ }
+};
-class cRidgedMultiNoise
-{
-public:
- cRidgedMultiNoise(void);
- cRidgedMultiNoise(int a_Seed);
-
-
- void SetSeed(int a_Seed);
-
- void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude);
-
- void Generate1D(
- NOISE_DATATYPE * a_Array, ///< Array to generate into
- int a_SizeX, ///< Count of the array
- NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array
- NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
- ) const;
-
-
- void Generate2D(
- NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
- int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
- NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
- NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
- NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
- ) const;
-
-
- void Generate3D(
- NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
- int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
- NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
- NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
- NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
- NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
- ) const;
-
-protected:
- class cOctave
- {
- public:
- cCubicNoise m_Noise;
-
- NOISE_DATATYPE m_Frequency; // Coord multiplier
- NOISE_DATATYPE m_Amplitude; // Value multiplier
-
- cOctave(int a_Seed, NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude) :
- m_Noise(a_Seed),
- m_Frequency(a_Frequency),
- m_Amplitude(a_Amplitude)
- {
- }
- } ;
-
- typedef std::vector<cOctave> cOctaves;
-
- int m_Seed;
- cOctaves m_Octaves;
-} ;
+
+typedef cOctavedNoise<cCubicNoise> cPerlinNoise;
+typedef cOctavedNoise<cRidgedNoise<cCubicNoise>> cRidgedMultiNoise;
@@ -376,8 +286,46 @@ NOISE_DATATYPE cNoise::LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B,
////////////////////////////////////////////////////////////////////////////////
// Global functions:
-extern void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase);
-extern void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase);
+/** Exports the noise array into a file.
+a_Coeff specifies the value that each array value is multiplied by before being converted into a byte. */
+extern void Debug2DNoise(const NOISE_DATATYPE * a_Array, int a_SizeX, int a_SizeY, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff = 32);
+
+/** Exports the noise array into a set of files, ordered by XY and XZ.
+a_Coeff specifies the value that each array value is multiplied by before being converted into a byte. */
+extern void Debug3DNoise(const NOISE_DATATYPE * a_Array, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff = 32);
+
+
+
+
+/** Linearly interpolates between two values.
+Assumes that a_Ratio is in range [0, 1]. */
+inline NOISE_DATATYPE Lerp(NOISE_DATATYPE a_Val1, NOISE_DATATYPE a_Val2, NOISE_DATATYPE a_Ratio)
+{
+ return a_Val1 + (a_Val2 - a_Val1) * a_Ratio;
+}
+
+
+
+
+
+/** Linearly interpolates between two values, clamping the ratio to [0, 1] first. */
+inline NOISE_DATATYPE ClampedLerp(NOISE_DATATYPE a_Val1, NOISE_DATATYPE a_Val2, NOISE_DATATYPE a_Ratio)
+{
+ if (a_Ratio < 0)
+ {
+ return a_Val1;
+ }
+ if (a_Ratio > 1)
+ {
+ return a_Val2;
+ }
+ return Lerp(a_Val1, a_Val2, a_Ratio);
+}
+
+
+
+
+
diff --git a/src/Noise/OctavedNoise.h b/src/Noise/OctavedNoise.h
new file mode 100644
index 000000000..efb9a0167
--- /dev/null
+++ b/src/Noise/OctavedNoise.h
@@ -0,0 +1,196 @@
+
+// OctavedNoise.h
+
+// Implements the cOctavedNoise class template representing a noise generator that layers several octaves of another noise
+
+
+
+
+
+#pragma once
+
+
+
+
+
+template <typename N>
+class cOctavedNoise
+{
+public:
+ cOctavedNoise(int a_Seed = 0):
+ m_Seed(a_Seed)
+ {
+ }
+
+
+ /** Sets a new seed for the generators. Relays the seed to all underlying octaves. */
+ void SetSeed(int a_Seed)
+ {
+ m_Seed = a_Seed;
+ for (auto oct: m_Octaves)
+ {
+ oct->SetSeed(a_Seed);
+ }
+ }
+
+
+ /** Adds a new octave to the list of octaves that compose this noise. */
+ void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude)
+ {
+ m_Octaves.emplace_back(m_Seed, a_Frequency, a_Amplitude);
+ }
+
+
+ /** Fills a 2D array with the values of the noise. */
+ void Generate2D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
+ int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
+ NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash.
+ ) const
+ {
+ // Check that state is alright:
+ if (m_Octaves.empty())
+ {
+ ASSERT(!"cOctavedNoise: No octaves to generate!");
+ return;
+ }
+
+ // Allocate the workspace on the heap, if it wasn't given:
+ std::unique_ptr<NOISE_DATATYPE[]> workspaceHeap;
+ if (a_Workspace == nullptr)
+ {
+ workspaceHeap.reset(new NOISE_DATATYPE[a_SizeX * a_SizeY]);
+ a_Workspace = workspaceHeap.get();
+ }
+
+ // Generate the first octave directly into array:
+ int ArrayCount = a_SizeX * a_SizeY;
+ {
+ const cOctave & FirstOctave = m_Octaves.front();
+ FirstOctave.m_Noise.Generate2D(
+ a_Workspace, a_SizeX, a_SizeY,
+ a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
+ a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency
+ );
+ NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
+ for (int i = 0; i < ArrayCount; i++)
+ {
+ a_Array[i] = a_Workspace[i] * Amplitude;
+ }
+ }
+
+ // Add each octave:
+ for (auto itr = m_Octaves.cbegin() + 1, end = m_Octaves.cend(); itr != end; ++itr)
+ {
+ // Generate the noise for the octave:
+ itr->m_Noise.Generate2D(
+ a_Workspace, a_SizeX, a_SizeY,
+ a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
+ a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency
+ );
+ // Add it into the output:
+ NOISE_DATATYPE Amplitude = itr->m_Amplitude;
+ for (int i = 0; i < ArrayCount; i++)
+ {
+ a_Array[i] += a_Workspace[i] * Amplitude;
+ }
+ } // for itr - m_Octaves[]
+ }
+
+
+ /** Fills a 3D array with the values of the noise. */
+ void Generate3D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
+ int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
+ NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
+ NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash, same size as a_Array
+ ) const
+ {
+ // Check that state is alright:
+ if (m_Octaves.empty())
+ {
+ ASSERT(!"cOctavedNoise: No octaves to generate!");
+ return;
+ }
+
+ // Allocate the workspace on the heap, if it wasn't given:
+ std::unique_ptr<NOISE_DATATYPE[]> workspaceHeap;
+ if (a_Workspace == nullptr)
+ {
+ workspaceHeap.reset(new NOISE_DATATYPE[a_SizeX * a_SizeY * a_SizeZ]);
+ a_Workspace = workspaceHeap.get();
+ }
+
+ // Generate the first octave directly into array:
+ int ArrayCount = a_SizeX * a_SizeY * a_SizeZ;
+ {
+ const cOctave & FirstOctave = m_Octaves.front();
+ FirstOctave.m_Noise.Generate3D(
+ a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
+ a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
+ a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency,
+ a_StartZ * FirstOctave.m_Frequency, a_EndZ * FirstOctave.m_Frequency
+ );
+ NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
+ for (int i = 0; i < ArrayCount; i++)
+ {
+ a_Array[i] = a_Workspace[i] * Amplitude;
+ }
+ }
+
+ // Add each octave:
+ for (auto itr = m_Octaves.cbegin() + 1, end = m_Octaves.cend(); itr != end; ++itr)
+ {
+ // Generate the noise for the octave:
+ itr->m_Noise.Generate3D(
+ a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
+ a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
+ a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency,
+ a_StartZ * itr->m_Frequency, a_EndZ * itr->m_Frequency
+ );
+ // Add it into the output:
+ NOISE_DATATYPE Amplitude = itr->m_Amplitude;
+ for (int i = 0; i < ArrayCount; i++)
+ {
+ a_Array[i] += a_Workspace[i] * Amplitude;
+ }
+ } // for itr - m_Octaves[]
+ }
+
+protected:
+ /** Stores information and state for one octave of the noise. */
+ class cOctave
+ {
+ public:
+ N m_Noise;
+
+ /** Coord multiplier. */
+ NOISE_DATATYPE m_Frequency;
+
+ /** Value multiplier. */
+ NOISE_DATATYPE m_Amplitude;
+
+ cOctave(int a_Seed, NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude) :
+ m_Noise(a_Seed),
+ m_Frequency(a_Frequency),
+ m_Amplitude(a_Amplitude)
+ {
+ }
+ } ;
+ typedef std::vector<cOctave> cOctaves;
+
+
+ /** The seed used by the underlying generators. */
+ int m_Seed;
+
+ /** The octaves that compose this noise. */
+ cOctaves m_Octaves;
+};
+
+
+
+
diff --git a/src/Noise/RidgedNoise.h b/src/Noise/RidgedNoise.h
new file mode 100644
index 000000000..f59a0512f
--- /dev/null
+++ b/src/Noise/RidgedNoise.h
@@ -0,0 +1,91 @@
+
+// RidgedNoise.h
+
+// Implements the cRidgedNoise template class that generates ridged noise based on another noise provider.
+
+
+
+
+
+#pragma once
+
+
+
+
+
+template <typename N>
+class cRidgedNoise
+{
+public:
+ /** Creates a new instance with the seed set to 0. */
+ cRidgedNoise(void):
+ m_Noise(0)
+ {
+ }
+
+
+ /** Creates a new instance with the specified seed. */
+ cRidgedNoise(int a_Seed):
+ m_Noise(a_Seed)
+ {
+ }
+
+
+ /** Sets the seed for the underlying noise. */
+ void SetSeed(int a_Seed)
+ {
+ m_Noise.SetSeed(a_Seed);
+ }
+
+
+ /** Fills a 2D array with the values of the noise. */
+ void Generate2D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
+ int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction
+ ) const
+ {
+ int ArrayCount = a_SizeX * a_SizeY;
+ m_Noise.Generate2D(
+ a_Array, a_SizeX, a_SizeY,
+ a_StartX, a_EndX,
+ a_StartY, a_EndY
+ );
+ for (int i = 0; i < ArrayCount; i++)
+ {
+ a_Array[i] = std::abs(a_Array[i]);
+ }
+ }
+
+
+ /** Fills a 3D array with the values of the noise. */
+ void Generate3D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
+ int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
+ NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Z direction
+ ) const
+ {
+ int ArrayCount = a_SizeX * a_SizeY * a_SizeZ;
+ m_Noise.Generate2D(
+ a_Array, a_SizeX, a_SizeY, a_SizeZ,
+ a_StartX, a_EndX,
+ a_StartY, a_EndY,
+ a_StartZ, a_EndZ
+ );
+ for (int i = 0; i < ArrayCount; i++)
+ {
+ a_Array[i] = std::abs(a_Array[i]);
+ }
+ }
+
+protected:
+ N m_Noise;
+} ;
+
+
+
+
+
diff --git a/src/OSSupport/CMakeLists.txt b/src/OSSupport/CMakeLists.txt
index c3eabeef6..e943ceb18 100644
--- a/src/OSSupport/CMakeLists.txt
+++ b/src/OSSupport/CMakeLists.txt
@@ -13,11 +13,10 @@ SET (SRCS
IsThread.cpp
ListenThread.cpp
Semaphore.cpp
- Sleep.cpp
Socket.cpp
SocketThreads.cpp
- Thread.cpp
- Timer.cpp)
+ StackTrace.cpp
+)
SET (HDRS
CriticalSection.h
@@ -29,11 +28,10 @@ SET (HDRS
ListenThread.h
Queue.h
Semaphore.h
- Sleep.h
Socket.h
SocketThreads.h
- Thread.h
- Timer.h)
+ StackTrace.h
+)
if(NOT MSVC)
add_library(OSSupport ${SRCS} ${HDRS})
diff --git a/src/OSSupport/CriticalSection.cpp b/src/OSSupport/CriticalSection.cpp
index 5dfc8b5f9..13a3e4d9f 100644
--- a/src/OSSupport/CriticalSection.cpp
+++ b/src/OSSupport/CriticalSection.cpp
@@ -1,6 +1,5 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-#include "IsThread.h"
@@ -9,41 +8,12 @@
////////////////////////////////////////////////////////////////////////////////
// cCriticalSection:
+#ifdef _DEBUG
cCriticalSection::cCriticalSection()
{
- #ifdef _WIN32
- InitializeCriticalSection(&m_CriticalSection);
- #else
- pthread_mutexattr_init(&m_Attributes);
- pthread_mutexattr_settype(&m_Attributes, PTHREAD_MUTEX_RECURSIVE);
-
- if (pthread_mutex_init(&m_CriticalSection, &m_Attributes) != 0)
- {
- LOGERROR("Could not initialize Critical Section!");
- }
- #endif
-
- #ifdef _DEBUG
- m_IsLocked = 0;
- #endif // _DEBUG
-}
-
-
-
-
-
-cCriticalSection::~cCriticalSection()
-{
- #ifdef _WIN32
- DeleteCriticalSection(&m_CriticalSection);
- #else
- if (pthread_mutex_destroy(&m_CriticalSection) != 0)
- {
- LOGWARNING("Could not destroy Critical Section!");
- }
- pthread_mutexattr_destroy(&m_Attributes);
- #endif
+ m_IsLocked = 0;
}
+#endif // _DEBUG
@@ -51,15 +21,11 @@ cCriticalSection::~cCriticalSection()
void cCriticalSection::Lock()
{
- #ifdef _WIN32
- EnterCriticalSection(&m_CriticalSection);
- #else
- pthread_mutex_lock(&m_CriticalSection);
- #endif
+ m_Mutex.lock();
#ifdef _DEBUG
m_IsLocked += 1;
- m_OwningThreadID = cIsThread::GetCurrentID();
+ m_OwningThreadID = std::this_thread::get_id();
#endif // _DEBUG
}
@@ -74,11 +40,7 @@ void cCriticalSection::Unlock()
m_IsLocked -= 1;
#endif // _DEBUG
- #ifdef _WIN32
- LeaveCriticalSection(&m_CriticalSection);
- #else
- pthread_mutex_unlock(&m_CriticalSection);
- #endif
+ m_Mutex.unlock();
}
@@ -97,7 +59,7 @@ bool cCriticalSection::IsLocked(void)
bool cCriticalSection::IsLockedByCurrentThread(void)
{
- return ((m_IsLocked > 0) && (m_OwningThreadID == cIsThread::GetCurrentID()));
+ return ((m_IsLocked > 0) && (m_OwningThreadID == std::this_thread::get_id()));
}
#endif // _DEBUG
diff --git a/src/OSSupport/CriticalSection.h b/src/OSSupport/CriticalSection.h
index c3c6e57f0..17fcdfc12 100644
--- a/src/OSSupport/CriticalSection.h
+++ b/src/OSSupport/CriticalSection.h
@@ -1,5 +1,7 @@
#pragma once
+#include <mutex>
+#include <thread>
@@ -8,8 +10,6 @@
class cCriticalSection
{
public:
- cCriticalSection(void);
- ~cCriticalSection();
void Lock(void);
void Unlock(void);
@@ -17,6 +17,7 @@ public:
// IsLocked/IsLockedByCurrentThread are only used in ASSERT statements, but because of the changes with ASSERT they must always be defined
// The fake versions (in Release) will not effect the program in any way
#ifdef _DEBUG
+ cCriticalSection(void);
bool IsLocked(void);
bool IsLockedByCurrentThread(void);
#else
@@ -27,15 +28,10 @@ public:
private:
#ifdef _DEBUG
int m_IsLocked; // Number of times this CS is locked
- unsigned long m_OwningThreadID;
+ std::thread::id m_OwningThreadID;
#endif // _DEBUG
- #ifdef _WIN32
- CRITICAL_SECTION m_CriticalSection;
- #else // _WIN32
- pthread_mutex_t m_CriticalSection;
- pthread_mutexattr_t m_Attributes;
- #endif // else _WIN32
+ std::recursive_mutex m_Mutex;
} ALIGN_8;
diff --git a/src/OSSupport/IsThread.cpp b/src/OSSupport/IsThread.cpp
index 1a436623a..94bed1f56 100644
--- a/src/OSSupport/IsThread.cpp
+++ b/src/OSSupport/IsThread.cpp
@@ -5,49 +5,40 @@
// This class will eventually suupersede the old cThread class
#include "Globals.h"
-
#include "IsThread.h"
-// When in MSVC, the debugger provides "thread naming" by catching special exceptions. Interface here:
#if defined(_MSC_VER) && defined(_DEBUG)
-//
-// Usage: SetThreadName (-1, "MainThread");
-//
-
-// Code adapted from MSDN: http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
-
-const DWORD MS_VC_EXCEPTION = 0x406D1388;
-
-#pragma pack(push, 8)
-typedef struct tagTHREADNAME_INFO
-{
- DWORD dwType; // Must be 0x1000.
- LPCSTR szName; // Pointer to name (in user addr space).
- DWORD dwThreadID; // Thread ID (-1 = caller thread).
- DWORD dwFlags; // Reserved for future use, must be zero.
-} THREADNAME_INFO;
-#pragma pack(pop)
-
-static void SetThreadName(DWORD dwThreadID, const char * threadName)
-{
- THREADNAME_INFO info;
- info.dwType = 0x1000;
- info.szName = threadName;
- info.dwThreadID = dwThreadID;
- info.dwFlags = 0;
+ // Code adapted from MSDN: http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
- __try
- {
- RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info);
- }
- __except (EXCEPTION_EXECUTE_HANDLER)
+ const DWORD MS_VC_EXCEPTION = 0x406D1388;
+ #pragma pack(push, 8)
+ struct THREADNAME_INFO
+ {
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ DWORD dwThreadID; // Thread ID (-1 = caller thread).
+ DWORD dwFlags; // Reserved for future use, must be zero.
+ };
+ #pragma pack(pop)
+
+ /** Sets the name of a thread with the specified ID
+ (When in MSVC, the debugger provides "thread naming" by catching special exceptions)
+ */
+ static void SetThreadName(std::thread * a_Thread, const char * a_ThreadName)
{
+ THREADNAME_INFO info { 0x1000, a_ThreadName, GetThreadId(a_Thread->native_handle()), 0 };
+ __try
+ {
+ RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
}
-}
#endif // _MSC_VER && _DEBUG
@@ -57,13 +48,9 @@ static void SetThreadName(DWORD dwThreadID, const char * threadName)
////////////////////////////////////////////////////////////////////////////////
// cIsThread:
-cIsThread::cIsThread(const AString & iThreadName) :
+cIsThread::cIsThread(const AString & a_ThreadName) :
m_ShouldTerminate(false),
- m_ThreadName(iThreadName),
- #ifdef _WIN32
- m_ThreadID(0),
- #endif
- m_Handle(NULL_HANDLE)
+ m_ThreadName(a_ThreadName)
{
}
@@ -83,35 +70,24 @@ cIsThread::~cIsThread()
bool cIsThread::Start(void)
{
- ASSERT(m_Handle == NULL_HANDLE); // Has already started one thread?
- #ifdef _WIN32
- // Create the thread suspended, so that the mHandle variable is valid in the thread procedure
- m_ThreadID = 0;
- m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &m_ThreadID);
- if (m_Handle == NULL)
- {
- LOGERROR("ERROR: Could not create thread \"%s\", GLE = %u!", m_ThreadName.c_str(), (unsigned)GetLastError());
- return false;
- }
- ResumeThread(m_Handle);
-
- #if defined(_DEBUG) && defined(_MSC_VER)
- // Thread naming is available only in MSVC
- if (!m_ThreadName.empty())
- {
- SetThreadName(m_ThreadID, m_ThreadName.c_str());
- }
- #endif // _DEBUG and _MSC_VER
-
- #else // _WIN32
- if (pthread_create(&m_Handle, NULL, thrExecute, this))
+ try
+ {
+ m_Thread = std::thread(&cIsThread::Execute, this);
+
+ #if defined (_MSC_VER) && defined(_DEBUG)
+ if (!m_ThreadName.empty())
{
- LOGERROR("ERROR: Could not create thread \"%s\", !", m_ThreadName.c_str());
- return false;
+ SetThreadName(&m_Thread, m_ThreadName.c_str());
}
- #endif // else _WIN32
+ #endif
- return true;
+ return true;
+ }
+ catch (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;
+ }
}
@@ -120,10 +96,12 @@ bool cIsThread::Start(void)
void cIsThread::Stop(void)
{
- if (m_Handle == NULL_HANDLE)
+ if (!m_Thread.joinable())
{
+ // The thread hasn't been started or has already been joined
return;
}
+
m_ShouldTerminate = true;
Wait();
}
@@ -134,59 +112,23 @@ void cIsThread::Stop(void)
bool cIsThread::Wait(void)
{
- if (m_Handle == NULL_HANDLE)
+ LOGD("Waiting for thread %s to finish", m_ThreadName.c_str());
+ if (m_Thread.joinable())
{
- return true;
+ try
+ {
+ m_Thread.join();
+ return true;
+ }
+ catch (std::system_error & a_Exception)
+ {
+ LOGERROR("cIsThread::Wait error %i: could not join thread %s; %s", a_Exception.code().value(), m_ThreadName.c_str(), a_Exception.code().message().c_str());
+ return false;
+ }
}
-
- #ifdef LOGD // ProtoProxy doesn't have LOGD
- LOGD("Waiting for thread %s to finish", m_ThreadName.c_str());
- #endif // LOGD
-
- #ifdef _WIN32
- int res = WaitForSingleObject(m_Handle, INFINITE);
- m_Handle = NULL;
-
- #ifdef LOGD // ProtoProxy doesn't have LOGD
- LOGD("Thread %s finished", m_ThreadName.c_str());
- #endif // LOGD
-
- return (res == WAIT_OBJECT_0);
- #else // _WIN32
- int res = pthread_join(m_Handle, NULL);
- m_Handle = NULL_HANDLE;
-
- #ifdef LOGD // ProtoProxy doesn't have LOGD
- LOGD("Thread %s finished", m_ThreadName.c_str());
- #endif // LOGD
-
- return (res == 0);
- #endif // else _WIN32
-}
-
-
-
-
-unsigned long cIsThread::GetCurrentID(void)
-{
- #ifdef _WIN32
- return (unsigned long) GetCurrentThreadId();
- #else
- return (unsigned long) pthread_self();
- #endif
-}
-
-
-
-
-bool cIsThread::IsCurrentThread(void) const
-{
- #ifdef _WIN32
- return (GetCurrentThreadId() == m_ThreadID);
- #else
- return (m_Handle == pthread_self());
- #endif
+ LOGD("Thread %s finished", m_ThreadName.c_str());
+ return true;
}
diff --git a/src/OSSupport/IsThread.h b/src/OSSupport/IsThread.h
index 5c8d28d2d..131c6950e 100644
--- a/src/OSSupport/IsThread.h
+++ b/src/OSSupport/IsThread.h
@@ -16,8 +16,7 @@ In the descending class' constructor call the Start() method to start the thread
#pragma once
-#ifndef CISTHREAD_H_INCLUDED
-#define CISTHREAD_H_INCLUDED
+#include <thread>
@@ -33,7 +32,7 @@ protected:
volatile bool m_ShouldTerminate;
public:
- cIsThread(const AString & iThreadName);
+ cIsThread(const AString & a_ThreadName);
virtual ~cIsThread();
/// Starts the thread; returns without waiting for the actual start
@@ -45,56 +44,14 @@ public:
/// Waits for the thread to finish. Doesn't signalize the ShouldTerminate flag
bool Wait(void);
- /// Returns the OS-dependent thread ID for the caller's thread
- static unsigned long GetCurrentID(void);
-
/** Returns true if the thread calling this function is the thread contained within this object. */
- bool IsCurrentThread(void) const;
+ bool IsCurrentThread(void) const { return std::this_thread::get_id() == m_Thread.get_id(); }
protected:
AString m_ThreadName;
-
- // Value used for "no handle":
- #ifdef _WIN32
- #define NULL_HANDLE NULL
- #else
- #define NULL_HANDLE 0
- #endif
-
- #ifdef _WIN32
-
- DWORD m_ThreadID;
- HANDLE m_Handle;
-
- static DWORD __stdcall thrExecute(LPVOID a_Param)
- {
- // Create a window so that the thread can be identified by 3rd party tools:
- HWND IdentificationWnd = CreateWindowA("STATIC", ((cIsThread *)a_Param)->m_ThreadName.c_str(), 0, 0, 0, 0, WS_OVERLAPPED, NULL, NULL, NULL, NULL);
-
- // Run the thread:
- ((cIsThread *)a_Param)->Execute();
-
- // Destroy the identification window:
- DestroyWindow(IdentificationWnd);
-
- return 0;
- }
-
- #else // _WIN32
-
- pthread_t m_Handle;
-
- static void * thrExecute(void * a_Param)
- {
- (static_cast<cIsThread *>(a_Param))->Execute();
- return NULL;
- }
-
- #endif // else _WIN32
+ std::thread m_Thread;
} ;
-
-#endif // CISTHREAD_H_INCLUDED
diff --git a/src/OSSupport/Sleep.cpp b/src/OSSupport/Sleep.cpp
deleted file mode 100644
index 297d668d7..000000000
--- a/src/OSSupport/Sleep.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#ifndef _WIN32
- #include <unistd.h>
-#endif
-
-
-
-
-
-void cSleep::MilliSleep( unsigned int a_MilliSeconds)
-{
-#ifdef _WIN32
- Sleep(a_MilliSeconds); // Don't tick too much
-#else
- usleep(a_MilliSeconds*1000);
-#endif
-}
diff --git a/src/OSSupport/Sleep.h b/src/OSSupport/Sleep.h
deleted file mode 100644
index 57d29682c..000000000
--- a/src/OSSupport/Sleep.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#pragma once
-
-class cSleep
-{
-public:
- static void MilliSleep( unsigned int a_MilliSeconds);
-};
diff --git a/src/OSSupport/Socket.h b/src/OSSupport/Socket.h
index e4ec895cb..9ec8c1111 100644
--- a/src/OSSupport/Socket.h
+++ b/src/OSSupport/Socket.h
@@ -74,7 +74,7 @@ public:
inline static bool IsSocketError(int a_ReturnedValue)
{
#ifdef _WIN32
- return (a_ReturnedValue == SOCKET_ERROR || a_ReturnedValue == 0);
+ return ((a_ReturnedValue == SOCKET_ERROR) || (a_ReturnedValue == 0));
#else
return (a_ReturnedValue <= 0);
#endif
diff --git a/src/OSSupport/SocketThreads.cpp b/src/OSSupport/SocketThreads.cpp
index 7a3ef4274..153d6ed1d 100644
--- a/src/OSSupport/SocketThreads.cpp
+++ b/src/OSSupport/SocketThreads.cpp
@@ -86,7 +86,8 @@ void cSocketThreads::RemoveClient(const cCallback * a_Client)
}
} // for itr - m_Threads[]
- ASSERT(!"Removing an unknown client");
+ // This client wasn't found.
+ // It's not an error, because it may have been removed by a different thread in the meantime.
}
@@ -491,10 +492,17 @@ void cSocketThreads::cSocketThread::ReadFromSockets(fd_set * a_Read)
{
case sSlot::ssNormal:
{
- // Notify the callback that the remote has closed the socket; keep the slot
- m_Slots[i].m_Client->SocketClosed();
+ // Close the socket on our side:
m_Slots[i].m_State = sSlot::ssRemoteClosed;
m_Slots[i].m_Socket.CloseSocket();
+
+ // Notify the callback that the remote has closed the socket, *after* removing the socket:
+ cCallback * client = m_Slots[i].m_Client;
+ m_Slots[i] = m_Slots[--m_NumSlots];
+ if (client != nullptr)
+ {
+ client->SocketClosed();
+ }
break;
}
case sSlot::ssWritingRestOut:
diff --git a/src/OSSupport/StackTrace.cpp b/src/OSSupport/StackTrace.cpp
new file mode 100644
index 000000000..a56568457
--- /dev/null
+++ b/src/OSSupport/StackTrace.cpp
@@ -0,0 +1,44 @@
+
+// StackTrace.cpp
+
+// Implements the functions to print current stack traces
+
+#include "Globals.h"
+#include "StackTrace.h"
+#ifdef _WIN32
+ #include "../StackWalker.h"
+#else
+ #include <execinfo.h>
+ #include <unistd.h>
+#endif
+
+
+
+
+
+void PrintStackTrace(void)
+{
+ #ifdef _WIN32
+ // Reuse the StackWalker from the LeakFinder project already bound to MCS
+ // Define a subclass of the StackWalker that outputs everything to stdout
+ class PrintingStackWalker :
+ public StackWalker
+ {
+ virtual void OnOutput(LPCSTR szText) override
+ {
+ puts(szText);
+ }
+ } sw;
+ sw.ShowCallstack();
+ #else
+ // 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));
+ backtrace_symbols_fd(stackTrace, numItems, STDERR_FILENO);
+ #endif
+}
+
+
+
+
diff --git a/src/OSSupport/StackTrace.h b/src/OSSupport/StackTrace.h
new file mode 100644
index 000000000..228a00077
--- /dev/null
+++ b/src/OSSupport/StackTrace.h
@@ -0,0 +1,15 @@
+
+// StackTrace.h
+
+// Declares the functions to print current stack trace
+
+
+
+
+
+/** Prints the stacktrace for the current thread. */
+extern void PrintStackTrace(void);
+
+
+
+
diff --git a/src/OSSupport/Thread.cpp b/src/OSSupport/Thread.cpp
deleted file mode 100644
index faaccce96..000000000
--- a/src/OSSupport/Thread.cpp
+++ /dev/null
@@ -1,137 +0,0 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-
-
-
-
-// When in MSVC, the debugger provides "thread naming" by catching special exceptions. Interface here:
-#ifdef _MSC_VER
-//
-// Usage: SetThreadName (-1, "MainThread");
-//
-
-// Code adapted from MSDN: http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
-
-const DWORD MS_VC_EXCEPTION = 0x406D1388;
-
-#pragma pack(push, 8)
-typedef struct tagTHREADNAME_INFO
-{
- DWORD dwType; // Must be 0x1000.
- LPCSTR szName; // Pointer to name (in user addr space).
- DWORD dwThreadID; // Thread ID (-1 = caller thread).
- DWORD dwFlags; // Reserved for future use, must be zero.
-} THREADNAME_INFO;
-#pragma pack(pop)
-
-static void SetThreadName(DWORD dwThreadID, const char * threadName)
-{
- THREADNAME_INFO info;
- info.dwType = 0x1000;
- info.szName = threadName;
- info.dwThreadID = dwThreadID;
- info.dwFlags = 0;
-
- __try
- {
- RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info);
- }
- __except (EXCEPTION_EXECUTE_HANDLER)
- {
- }
-}
-#endif // _MSC_VER
-
-
-
-
-
-cThread::cThread( ThreadFunc a_ThreadFunction, void* a_Param, const char* a_ThreadName /* = 0 */)
- : m_ThreadFunction( a_ThreadFunction)
- , m_Param( a_Param)
- , m_Event( new cEvent())
- , m_StopEvent( 0)
-{
- if (a_ThreadName)
- {
- m_ThreadName.assign(a_ThreadName);
- }
-}
-
-
-
-
-
-cThread::~cThread()
-{
- delete m_Event;
- m_Event = NULL;
-
- if (m_StopEvent)
- {
- m_StopEvent->Wait();
- delete m_StopEvent;
- m_StopEvent = NULL;
- }
-}
-
-
-
-
-
-void cThread::Start( bool a_bWaitOnDelete /* = true */)
-{
- if (a_bWaitOnDelete)
- m_StopEvent = new cEvent();
-
-#ifndef _WIN32
- pthread_t SndThread;
- if (pthread_create( &SndThread, NULL, MyThread, this))
- LOGERROR("ERROR: Could not create thread!");
-#else
- DWORD ThreadID = 0;
- HANDLE hThread = CreateThread(NULL // security
- , 0 // stack size
- , (LPTHREAD_START_ROUTINE) MyThread // function name
- , this // parameters
- , 0 // flags
- , &ThreadID); // thread id
- CloseHandle( hThread);
-
- #ifdef _MSC_VER
- if (!m_ThreadName.empty())
- {
- SetThreadName(ThreadID, m_ThreadName.c_str());
- }
- #endif // _MSC_VER
-#endif
-
- // Wait until thread has actually been created
- m_Event->Wait();
-}
-
-
-
-
-
-#ifdef _WIN32
-unsigned long cThread::MyThread(void* a_Param)
-#else
-void *cThread::MyThread( void *a_Param)
-#endif
-{
- cThread* self = (cThread*)a_Param;
- cEvent* StopEvent = self->m_StopEvent;
-
- ThreadFunc* ThreadFunction = self->m_ThreadFunction;
- void* ThreadParam = self->m_Param;
-
- // Set event to let other thread know this thread has been created and it's safe to delete the cThread object
- self->m_Event->Set();
-
- ThreadFunction( ThreadParam);
-
- if (StopEvent) StopEvent->Set();
- return 0;
-}
diff --git a/src/OSSupport/Thread.h b/src/OSSupport/Thread.h
deleted file mode 100644
index 7ee352c82..000000000
--- a/src/OSSupport/Thread.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-
-class cThread
-{
-public:
- typedef void (ThreadFunc)(void*);
- cThread( ThreadFunc a_ThreadFunction, void* a_Param, const char* a_ThreadName = 0);
- ~cThread();
-
- void Start( bool a_bWaitOnDelete = true);
- void WaitForThread();
-private:
- ThreadFunc* m_ThreadFunction;
-
-#ifdef _WIN32
- static unsigned long MyThread(void* a_Param);
-#else
- static void *MyThread( void *lpParam);
-#endif
-
- void* m_Param;
- cEvent* m_Event;
- cEvent* m_StopEvent;
-
- AString m_ThreadName;
-};
diff --git a/src/OSSupport/Timer.cpp b/src/OSSupport/Timer.cpp
deleted file mode 100644
index fd838dd0d..000000000
--- a/src/OSSupport/Timer.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "Timer.h"
-
-
-
-
-
-
-cTimer::cTimer(void)
-{
- #ifdef _WIN32
- QueryPerformanceFrequency(&m_TicksPerSecond);
- #endif
-}
-
-
-
-
-
-long long cTimer::GetNowTime(void)
-{
- #ifdef _WIN32
- LARGE_INTEGER now;
- QueryPerformanceCounter(&now);
- return ((now.QuadPart * 1000) / m_TicksPerSecond.QuadPart);
- #else
- struct timeval now;
- gettimeofday(&now, NULL);
- return (long long)now.tv_sec * 1000 + (long long)now.tv_usec / 1000;
- #endif
-}
-
-
-
-
diff --git a/src/OSSupport/Timer.h b/src/OSSupport/Timer.h
deleted file mode 100644
index a059daa41..000000000
--- a/src/OSSupport/Timer.h
+++ /dev/null
@@ -1,32 +0,0 @@
-
-// Timer.h
-
-// Declares the cTimer class representing an OS-independent of retrieving current time with msec accuracy
-
-
-
-
-
-#pragma once
-
-
-
-
-
-class cTimer
-{
-public:
- cTimer(void);
-
- // Returns the current time expressed in milliseconds
- long long GetNowTime(void);
-private:
-
- #ifdef _WIN32
- LARGE_INTEGER m_TicksPerSecond;
- #endif
-} ;
-
-
-
-
diff --git a/src/ProbabDistrib.cpp b/src/ProbabDistrib.cpp
index 7a5869dcc..c34c75982 100644
--- a/src/ProbabDistrib.cpp
+++ b/src/ProbabDistrib.cpp
@@ -5,7 +5,7 @@
#include "Globals.h"
#include "ProbabDistrib.h"
-#include "MersenneTwister.h"
+#include "FastRandom.h"
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index 1d108ce9c..1e5fe5586 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -42,6 +42,7 @@ Implements the 1.7.x protocol classes:
#include "../BlockEntities/BeaconEntity.h"
#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/MobHeadEntity.h"
+#include "../BlockEntities/MobSpawnerEntity.h"
#include "../BlockEntities/FlowerPotEntity.h"
#include "Bindings/PluginManager.h"
@@ -2662,6 +2663,18 @@ void cProtocol172::cPacketizer::WriteBlockEntity(const cBlockEntity & a_BlockEnt
Writer.AddString("id", "FlowerPot"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
break;
}
+ case E_BLOCK_MOB_SPAWNER:
+ {
+ cMobSpawnerEntity & MobSpawnerEntity = (cMobSpawnerEntity &)a_BlockEntity;
+
+ Writer.AddInt("x", MobSpawnerEntity.GetPosX());
+ Writer.AddInt("y", MobSpawnerEntity.GetPosY());
+ Writer.AddInt("z", MobSpawnerEntity.GetPosZ());
+ Writer.AddString("EntityId", cMonster::MobTypeToVanillaName(MobSpawnerEntity.GetEntity()));
+ Writer.AddShort("Delay", MobSpawnerEntity.GetSpawnDelay());
+ Writer.AddString("id", "MobSpawner");
+ break;
+ }
default: break;
}
@@ -3134,4 +3147,3 @@ void cProtocol176::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
-
diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp
index 8170a494f..ce580d73e 100644
--- a/src/Protocol/Protocol18x.cpp
+++ b/src/Protocol/Protocol18x.cpp
@@ -41,6 +41,7 @@ Implements the 1.8.x protocol classes:
#include "../BlockEntities/BeaconEntity.h"
#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/MobHeadEntity.h"
+#include "../BlockEntities/MobSpawnerEntity.h"
#include "../BlockEntities/FlowerPotEntity.h"
#include "Bindings/PluginManager.h"
@@ -2972,6 +2973,18 @@ void cProtocol180::cPacketizer::WriteBlockEntity(const cBlockEntity & a_BlockEnt
Writer.AddString("id", "FlowerPot"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
break;
}
+ case E_BLOCK_MOB_SPAWNER:
+ {
+ cMobSpawnerEntity & MobSpawnerEntity = (cMobSpawnerEntity &)a_BlockEntity;
+
+ Writer.AddInt("x", MobSpawnerEntity.GetPosX());
+ Writer.AddInt("y", MobSpawnerEntity.GetPosY());
+ Writer.AddInt("z", MobSpawnerEntity.GetPosZ());
+ Writer.AddString("EntityId", cMonster::MobTypeToVanillaName(MobSpawnerEntity.GetEntity()));
+ Writer.AddShort("Delay", MobSpawnerEntity.GetSpawnDelay());
+ Writer.AddString("id", "MobSpawner");
+ break;
+ }
default: break;
}
diff --git a/src/Root.cpp b/src/Root.cpp
index 24c1a4cc8..865b2a213 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -16,7 +16,6 @@
#include "Protocol/ProtocolRecognizer.h" // for protocol version constants
#include "CommandOutput.h"
#include "DeadlockDetect.h"
-#include "OSSupport/Timer.h"
#include "LoggerListeners.h"
#include "BuildInfo.h"
#include "IniFile.h"
@@ -42,7 +41,6 @@ cRoot* cRoot::s_Root = nullptr;
cRoot::cRoot(void) :
m_pDefaultWorld(nullptr),
- m_InputThread(nullptr),
m_Server(nullptr),
m_MonsterConfig(nullptr),
m_CraftingRecipes(nullptr),
@@ -68,26 +66,24 @@ cRoot::~cRoot()
-void cRoot::InputThread(void * a_Params)
+void cRoot::InputThread(cRoot & a_Params)
{
- cRoot & self = *(cRoot*)a_Params;
-
cLogCommandOutputCallback Output;
- while (!self.m_bStop && !self.m_bRestart && !m_TerminateEventRaised && std::cin.good())
+ while (!a_Params.m_bStop && !a_Params.m_bRestart && !m_TerminateEventRaised && std::cin.good())
{
AString Command;
std::getline(std::cin, Command);
if (!Command.empty())
{
- self.ExecuteConsoleCommand(TrimString(Command), Output);
+ a_Params.ExecuteConsoleCommand(TrimString(Command), Output);
}
}
if (m_TerminateEventRaised || !std::cin.good())
{
// We have come here because the std::cin has received an EOF / a terminate signal has been sent, and the server is still running; stop the server:
- self.m_bStop = true;
+ a_Params.m_bStop = true;
}
}
@@ -120,9 +116,7 @@ void cRoot::Start(void)
m_bStop = false;
while (!m_bStop)
{
- cTimer Time;
- long long mseconds = Time.GetNowTime();
-
+ auto BeginTime = std::chrono::steady_clock::now();
m_bRestart = false;
LoadGlobalSettings();
@@ -154,7 +148,7 @@ void cRoot::Start(void)
m_WebAdmin->Init();
LOGD("Loading settings...");
- m_RankManager = new cRankManager();
+ m_RankManager.reset(new cRankManager());
m_RankManager->Initialize(m_MojangAPI);
m_CraftingRecipes = new cCraftingRecipes;
m_FurnaceRecipe = new cFurnaceRecipe();
@@ -191,21 +185,25 @@ void cRoot::Start(void)
#if !defined(ANDROID_NDK)
LOGD("Starting InputThread...");
- m_InputThread = new cThread( InputThread, this, "cRoot::InputThread");
- m_InputThread->Start( false); // We should NOT wait? Otherwise we can't stop the server from other threads than the input thread
+ try
+ {
+ m_InputThread = std::thread(InputThread, std::ref(*this));
+ m_InputThread.detach();
+ }
+ catch (std::system_error & a_Exception)
+ {
+ LOGERROR("cRoot::Start (std::thread) error %i: could not construct input thread; %s", a_Exception.code().value(), a_Exception.what());
+ }
#endif
- long long finishmseconds = Time.GetNowTime();
- finishmseconds -= mseconds;
-
- LOG("Startup complete, took %lld ms!", finishmseconds);
+ LOG("Startup complete, took %lld ms!", 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
while (!m_bStop && !m_bRestart && !m_TerminateEventRaised) // These are modified by external threads
{
- cSleep::MilliSleep(1000);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
}
if (m_TerminateEventRaised)
@@ -213,10 +211,6 @@ void cRoot::Start(void)
m_bStop = true;
}
- #if !defined(ANDROID_NDK)
- delete m_InputThread; m_InputThread = nullptr;
- #endif
-
// Stop the server:
m_WebAdmin->Stop();
LOG("Shutting down server...");
@@ -633,6 +627,22 @@ 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++)
+ {
+ if (itr->second->DoWithPlayerByUUID(a_PlayerUUID, a_Callback))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
AString cRoot::GetProtocolVersionTextFromInt(int a_ProtocolVersion)
{
return cProtocolRecognizer::GetVersionTextFromInt(a_ProtocolVersion);
diff --git a/src/Root.h b/src/Root.h
index 020a6cff0..e70b284f9 100644
--- a/src/Root.h
+++ b/src/Root.h
@@ -6,6 +6,7 @@
#include "HTTPServer/HTTPServer.h"
#include "Defines.h"
#include "RankManager.h"
+#include <thread>
@@ -86,7 +87,7 @@ public:
cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export
cAuthenticator & GetAuthenticator (void) { return m_Authenticator; }
cMojangAPI & GetMojangAPI (void) { return m_MojangAPI; }
- cRankManager * GetRankManager (void) { return m_RankManager; }
+ cRankManager * GetRankManager (void) { return m_RankManager.get(); }
/** Queues a console command for execution through the cServer class.
The command will be executed in the tick thread
@@ -126,6 +127,9 @@ public:
/// Finds a player from a partial or complete player name and calls the callback - case-insensitive
bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
+ /** Finds the player over his uuid and calls the callback */
+ bool DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
+
// tolua_begin
/// Sends a chat message to all connected clients (in all worlds)
@@ -174,7 +178,7 @@ private:
cCriticalSection m_CSPendingCommands;
cCommandQueue m_PendingCommands;
- cThread * m_InputThread;
+ std::thread m_InputThread;
cServer * m_Server;
cMonsterConfig * m_MonsterConfig;
@@ -185,7 +189,9 @@ private:
cPluginManager * m_PluginManager;
cAuthenticator m_Authenticator;
cMojangAPI m_MojangAPI;
- cRankManager * m_RankManager;
+
+ std::unique_ptr<cRankManager> m_RankManager;
+
cHTTPServer m_HTTPServer;
bool m_bStop;
@@ -207,10 +213,10 @@ private:
/// Does the actual work of executing a command
void DoExecuteConsoleCommand(const AString & a_Cmd);
-
- static void InputThread(void* a_Params);
static cRoot* s_Root;
+
+ static void InputThread(cRoot & a_Params);
}; // tolua_export
diff --git a/src/Server.cpp b/src/Server.cpp
index bbb5ecff3..d6163df7e 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -4,7 +4,6 @@
#include "Server.h"
#include "ClientHandle.h"
-#include "OSSupport/Timer.h"
#include "Mobs/Monster.h"
#include "OSSupport/Socket.h"
#include "Root.h"
@@ -20,8 +19,6 @@
#include "Protocol/ProtocolRecognizer.h"
#include "CommandOutput.h"
-#include "MersenneTwister.h"
-
#include "IniFile.h"
#include "Vector3.h"
@@ -75,22 +72,20 @@ cServer::cTickThread::cTickThread(cServer & a_Server) :
void cServer::cTickThread::Execute(void)
{
- cTimer Timer;
-
- long long msPerTick = 50;
- long long LastTime = Timer.GetNowTime();
+ auto LastTime = std::chrono::steady_clock::now();
+ static const auto msPerTick = std::chrono::milliseconds(50);
while (!m_ShouldTerminate)
{
- long long NowTime = Timer.GetNowTime();
- float DeltaTime = (float)(NowTime-LastTime);
- m_ShouldTerminate = !m_Server.Tick(DeltaTime);
- long long TickTime = Timer.GetNowTime() - NowTime;
+ auto NowTime = std::chrono::steady_clock::now();
+ auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(NowTime - LastTime).count();
+ m_ShouldTerminate = !m_Server.Tick(static_cast<float>(msec));
+ auto TickTime = std::chrono::steady_clock::now() - NowTime;
if (TickTime < msPerTick)
{
// Stretch tick time until it's at least msPerTick
- cSleep::MilliSleep((unsigned int)(msPerTick - TickTime));
+ std::this_thread::sleep_for(msPerTick - TickTime);
}
LastTime = NowTime;
diff --git a/src/Simulator/FluidSimulator.cpp b/src/Simulator/FluidSimulator.cpp
index 9c8453d04..ecd74ee52 100644
--- a/src/Simulator/FluidSimulator.cpp
+++ b/src/Simulator/FluidSimulator.cpp
@@ -133,8 +133,10 @@ Direction cFluidSimulator::GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a
/*
Disabled because of causing problems and being useless atm
char BlockBelow = m_World.GetBlock(a_X, a_Y - 1, a_Z); // If there is nothing or fluid below it -> dominating flow is down :D
- if (BlockBelow == E_BLOCK_AIR || IsAllowedBlock(BlockBelow))
+ if ((BlockBelow == E_BLOCK_AIR) || IsAllowedBlock(BlockBelow))
+ {
return Y_MINUS;
+ }
*/
NIBBLETYPE LowestPoint = m_World.GetBlockMeta(a_X, a_Y, a_Z); // Current Block Meta so only lower points will be counted
@@ -182,7 +184,9 @@ Direction cFluidSimulator::GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a
}
if (LowestPoint == m_World.GetBlockMeta(a_X, a_Y, a_Z))
+ {
return NONE;
+ }
if (a_X - X > 0)
{
diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp
index 34f2da682..5febf5d6c 100644
--- a/src/StringUtils.cpp
+++ b/src/StringUtils.cpp
@@ -416,24 +416,30 @@ static bool isLegalUTF8(const unsigned char * source, int length)
{
default: return false;
// Everything else falls through when "true"...
- case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
- case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 4: if (((a = (*--srcptr)) < 0x80) || (a > 0xbf)) return false;
+ case 3: if (((a = (*--srcptr)) < 0x80) || (a > 0xbf)) return false;
case 2:
{
- if ((a = (*--srcptr)) > 0xBF) return false;
+ if ((a = (*--srcptr)) > 0xbf)
+ {
+ return false;
+ }
switch (*source)
{
// no fall-through in this inner switch
- case 0xE0: if (a < 0xA0) return false; break;
- case 0xED: if (a > 0x9F) return false; break;
- case 0xF0: if (a < 0x90) return false; break;
- case 0xF4: if (a > 0x8F) return false; break;
+ case 0xe0: if (a < 0xa0) return false; break;
+ case 0xed: if (a > 0x9f) return false; break;
+ case 0xf0: if (a < 0x90) return false; break;
+ case 0xf4: if (a > 0x8f) return false; break;
default: if (a < 0x80) return false;
}
}
- case 1: if (*source >= 0x80 && *source < 0xC2) return false;
+ case 1: if ((*source >= 0x80) && (*source < 0xc2)) return false;
+ }
+ if (*source > 0xf4)
+ {
+ return false;
}
- if (*source > 0xF4) return false;
return true;
}
@@ -446,11 +452,11 @@ AString UTF8ToRawBEUTF16(const char * a_UTF8, size_t a_UTF8Length)
AString UTF16;
UTF16.reserve(a_UTF8Length * 3);
- const unsigned char * source = (const unsigned char*)a_UTF8;
+ const unsigned char * source = (const unsigned char *)a_UTF8;
const unsigned char * sourceEnd = source + a_UTF8Length;
const int halfShift = 10; // used for shifting by 10 bits
const unsigned int halfBase = 0x0010000UL;
- const unsigned int halfMask = 0x3FFUL;
+ const unsigned int halfMask = 0x3ffUL;
while (source < sourceEnd)
{
@@ -481,7 +487,7 @@ AString UTF8ToRawBEUTF16(const char * a_UTF8, size_t a_UTF8Length)
if (ch <= UNI_MAX_BMP)
{
// Target is a character <= 0xFFFF
- if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)
+ if ((ch >= UNI_SUR_HIGH_START) && (ch <= UNI_SUR_LOW_END))
{
// UTF-16 surrogate values are illegal in UTF-32
ch = ' ';
@@ -520,7 +526,10 @@ are equivalent to the following loop:
{
ch += *source++;
--tmpBytesToRead;
- if (tmpBytesToRead) ch <<= 6;
+ if (tmpBytesToRead)
+ {
+ ch <<= 6;
+ }
} while (tmpBytesToRead > 0);
}
---------------------------------------------------------------------
@@ -723,15 +732,15 @@ AString ReplaceAllCharOccurrences(const AString & a_String, char a_From, char a_
/// Converts one Hex character in a Base64 encoding into the data value
static inline int UnBase64(char c)
{
- if (c >='A' && c <= 'Z')
+ if ((c >='A') && (c <= 'Z'))
{
return c - 'A';
}
- if (c >='a' && c <= 'z')
+ if ((c >='a') && (c <= 'z'))
{
return c - 'a' + 26;
}
- if (c >= '0' && c <= '9')
+ if ((c >= '0') && (c <= '9'))
{
return c - '0' + 52;
}
diff --git a/src/Tracer.cpp b/src/Tracer.cpp
index e125c6aa4..e604f4a5b 100644
--- a/src/Tracer.cpp
+++ b/src/Tracer.cpp
@@ -14,15 +14,15 @@
-cTracer::cTracer(cWorld * a_World)
- : m_World(a_World)
+cTracer::cTracer(cWorld * a_World):
+ m_World(a_World)
{
- m_NormalTable[0].Set(-1, 0, 0);
- m_NormalTable[1].Set( 0, 0, -1);
- m_NormalTable[2].Set( 1, 0, 0);
- m_NormalTable[3].Set( 0, 0, 1);
- m_NormalTable[4].Set( 0, 1, 0);
- m_NormalTable[5].Set( 0, -1, 0);
+ m_NormalTable[0].Set(-1, 0, 0);
+ m_NormalTable[1].Set( 0, 0, -1);
+ m_NormalTable[2].Set( 1, 0, 0);
+ m_NormalTable[3].Set( 0, 0, 1);
+ m_NormalTable[4].Set( 0, 1, 0);
+ m_NormalTable[5].Set( 0, -1, 0);
}
@@ -37,10 +37,16 @@ cTracer::~cTracer()
-float cTracer::SigNum( float a_Num)
+float cTracer::SigNum(float a_Num)
{
- if (a_Num < 0.f) return -1.f;
- if (a_Num > 0.f) return 1.f;
+ if (a_Num < 0.f)
+ {
+ return -1.f;
+ }
+ if (a_Num > 0.f)
+ {
+ return 1.f;
+ }
return 0.f;
}
@@ -59,7 +65,10 @@ void cTracer::SetValues(const Vector3f & a_Start, const Vector3f & a_Direction)
step.z = (int) SigNum(dir.z);
// normalize the direction vector
- if (dir.SqrLength() > 0.f) dir.Normalize();
+ if (dir.SqrLength() > 0.f)
+ {
+ dir.Normalize();
+ }
// how far we must move in the ray direction before
// we encounter a new voxel in x-direction
@@ -127,7 +136,7 @@ void cTracer::SetValues(const Vector3f & a_Start, const Vector3f & a_Direction)
-bool cTracer::Trace( const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance, bool a_LineOfSight)
+bool cTracer::Trace(const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance, bool a_LineOfSight)
{
if ((a_Start.y < 0) || (a_Start.y >= cChunkDef::Height))
{
@@ -249,35 +258,42 @@ int LinesCross(float x0, float y0, float x1, float y1, float x2, float y2, float
{
// float linx, liny;
- float d=(x1-x0)*(y3-y2)-(y1-y0)*(x3-x2);
- if (std::abs(d)<0.001) {return 0;}
- float AB=((y0-y2)*(x3-x2)-(x0-x2)*(y3-y2))/d;
- if (AB>=0.0 && AB<=1.0)
+ float d = (x1 - x0) * (y3 - y2) - (y1 - y0) * (x3 - x2);
+ if (std::abs(d) < 0.001)
{
- float CD=((y0-y2)*(x1-x0)-(x0-x2)*(y1-y0))/d;
- if (CD>=0.0 && CD<=1.0)
+ return 0;
+ }
+
+ float AB = ((y0 - y2) * (x3 - x2) - (x0 - x2) * (y3 - y2)) / d;
+ if ((AB >= 0.0) && (AB <= 1.0))
+ {
+ float CD = ((y0 - y2) * (x1 - x0) - (x0 - x2) * (y1 - y0)) / d;
+ if ((CD >= 0.0) && (CD <= 1.0))
{
- // linx=x0+AB*(x1-x0);
- // liny=y0+AB*(y1-y0);
+ // linx = x0 + AB * (x1 - x0);
+ // liny = y0 + AB * (y1 - y0);
return 1;
}
}
return 0;
}
+
+
+
// intersect3D_SegmentPlane(): intersect a segment and a plane
// Input: a_Ray = a segment, and a_Plane = a plane = {Point V0; Vector n;}
// Output: *I0 = the intersect point (when it exists)
// Return: 0 = disjoint (no intersection)
// 1 = intersection in the unique point *I0
// 2 = the segment lies in the plane
-int cTracer::intersect3D_SegmentPlane( const Vector3f & a_Origin, const Vector3f & a_End, const Vector3f & a_PlanePos, const Vector3f & a_PlaneNormal)
+int cTracer::intersect3D_SegmentPlane(const Vector3f & a_Origin, const Vector3f & a_End, const Vector3f & a_PlanePos, const Vector3f & a_PlaneNormal)
{
Vector3f u = a_End - a_Origin; // a_Ray.P1 - S.P0;
Vector3f w = a_Origin - a_PlanePos; // S.P0 - Pn.V0;
- float D = a_PlaneNormal.Dot( u); // dot(Pn.n, u);
- float N = -(a_PlaneNormal.Dot( w)); // -dot(a_Plane.n, w);
+ float D = a_PlaneNormal.Dot(u); // dot(Pn.n, u);
+ float N = -(a_PlaneNormal.Dot(w)); // -dot(a_Plane.n, w);
const float EPSILON = 0.0001f;
if (fabs(D) < EPSILON)
@@ -294,12 +310,12 @@ int cTracer::intersect3D_SegmentPlane( const Vector3f & a_Origin, const Vector3f
// they are not parallel
// compute intersect param
float sI = N / D;
- if (sI < 0 || sI > 1)
+ if ((sI < 0) || (sI > 1))
{
return 0; // no intersection
}
- // Vector3f I ( a_Ray->GetOrigin() + sI * u);// S.P0 + sI * u; // compute segment intersect point
+ // Vector3f I (a_Ray->GetOrigin() + sI * u);// S.P0 + sI * u; // compute segment intersect point
RealHit = a_Origin + u * sI;
return 1;
}
@@ -311,9 +327,9 @@ int cTracer::intersect3D_SegmentPlane( const Vector3f & a_Origin, const Vector3f
int cTracer::GetHitNormal(const Vector3f & start, const Vector3f & end, const Vector3i & a_BlockPos)
{
Vector3i SmallBlockPos = a_BlockPos;
- char BlockID = m_World->GetBlock( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z);
+ char BlockID = m_World->GetBlock(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z);
- if (BlockID == E_BLOCK_AIR || IsBlockWater(BlockID))
+ if ((BlockID == E_BLOCK_AIR) || IsBlockWater(BlockID))
{
return 0;
}
@@ -324,86 +340,86 @@ int cTracer::GetHitNormal(const Vector3f & start, const Vector3f & end, const Ve
Vector3f Look = (end - start);
Look.Normalize();
- float dot = Look.Dot( Vector3f(-1, 0, 0)); // first face normal is x -1
+ float dot = Look.Dot(Vector3f(-1, 0, 0)); // first face normal is x -1
if (dot < 0)
{
- int Lines = LinesCross( start.x, start.y, end.x, end.y, BlockPos.x, BlockPos.y, BlockPos.x, BlockPos.y + 1);
+ int Lines = LinesCross(start.x, start.y, end.x, end.y, BlockPos.x, BlockPos.y, BlockPos.x, BlockPos.y + 1);
if (Lines == 1)
{
- Lines = LinesCross( start.x, start.z, end.x, end.z, BlockPos.x, BlockPos.z, BlockPos.x, BlockPos.z + 1);
+ Lines = LinesCross(start.x, start.z, end.x, end.z, BlockPos.x, BlockPos.z, BlockPos.x, BlockPos.z + 1);
if (Lines == 1)
{
- intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(-1, 0, 0));
+ intersect3D_SegmentPlane(start, end, BlockPos, Vector3f(-1, 0, 0));
return 1;
}
}
}
- dot = Look.Dot( Vector3f(0, 0, -1)); // second face normal is z -1
+ dot = Look.Dot(Vector3f(0, 0, -1)); // second face normal is z -1
if (dot < 0)
{
- int Lines = LinesCross( start.z, start.y, end.z, end.y, BlockPos.z, BlockPos.y, BlockPos.z, BlockPos.y + 1);
+ int Lines = LinesCross(start.z, start.y, end.z, end.y, BlockPos.z, BlockPos.y, BlockPos.z, BlockPos.y + 1);
if (Lines == 1)
{
- Lines = LinesCross( start.z, start.x, end.z, end.x, BlockPos.z, BlockPos.x, BlockPos.z, BlockPos.x + 1);
+ Lines = LinesCross(start.z, start.x, end.z, end.x, BlockPos.z, BlockPos.x, BlockPos.z, BlockPos.x + 1);
if (Lines == 1)
{
- intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(0, 0, -1));
+ intersect3D_SegmentPlane(start, end, BlockPos, Vector3f(0, 0, -1));
return 2;
}
}
}
- dot = Look.Dot( Vector3f(1, 0, 0)); // third face normal is x 1
+ dot = Look.Dot(Vector3f(1, 0, 0)); // third face normal is x 1
if (dot < 0)
{
- int Lines = LinesCross( start.x, start.y, end.x, end.y, BlockPos.x + 1, BlockPos.y, BlockPos.x + 1, BlockPos.y + 1);
+ int Lines = LinesCross(start.x, start.y, end.x, end.y, BlockPos.x + 1, BlockPos.y, BlockPos.x + 1, BlockPos.y + 1);
if (Lines == 1)
{
- Lines = LinesCross( start.x, start.z, end.x, end.z, BlockPos.x + 1, BlockPos.z, BlockPos.x + 1, BlockPos.z + 1);
+ Lines = LinesCross(start.x, start.z, end.x, end.z, BlockPos.x + 1, BlockPos.z, BlockPos.x + 1, BlockPos.z + 1);
if (Lines == 1)
{
- intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(1, 0, 0), Vector3f(1, 0, 0));
+ intersect3D_SegmentPlane(start, end, BlockPos + Vector3f(1, 0, 0), Vector3f(1, 0, 0));
return 3;
}
}
}
- dot = Look.Dot( Vector3f(0, 0, 1)); // fourth face normal is z 1
+ dot = Look.Dot(Vector3f(0, 0, 1)); // fourth face normal is z 1
if (dot < 0)
{
- int Lines = LinesCross( start.z, start.y, end.z, end.y, BlockPos.z + 1, BlockPos.y, BlockPos.z + 1, BlockPos.y + 1);
+ int Lines = LinesCross(start.z, start.y, end.z, end.y, BlockPos.z + 1, BlockPos.y, BlockPos.z + 1, BlockPos.y + 1);
if (Lines == 1)
{
- Lines = LinesCross( start.z, start.x, end.z, end.x, BlockPos.z + 1, BlockPos.x, BlockPos.z + 1, BlockPos.x + 1);
+ Lines = LinesCross(start.z, start.x, end.z, end.x, BlockPos.z + 1, BlockPos.x, BlockPos.z + 1, BlockPos.x + 1);
if (Lines == 1)
{
- intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(0, 0, 1), Vector3f(0, 0, 1));
+ intersect3D_SegmentPlane(start, end, BlockPos + Vector3f(0, 0, 1), Vector3f(0, 0, 1));
return 4;
}
}
}
- dot = Look.Dot( Vector3f(0, 1, 0)); // fifth face normal is y 1
+ dot = Look.Dot(Vector3f(0, 1, 0)); // fifth face normal is y 1
if (dot < 0)
{
- int Lines = LinesCross( start.y, start.x, end.y, end.x, BlockPos.y + 1, BlockPos.x, BlockPos.y + 1, BlockPos.x + 1);
+ int Lines = LinesCross(start.y, start.x, end.y, end.x, BlockPos.y + 1, BlockPos.x, BlockPos.y + 1, BlockPos.x + 1);
if (Lines == 1)
{
- Lines = LinesCross( start.y, start.z, end.y, end.z, BlockPos.y + 1, BlockPos.z, BlockPos.y + 1, BlockPos.z + 1);
+ Lines = LinesCross(start.y, start.z, end.y, end.z, BlockPos.y + 1, BlockPos.z, BlockPos.y + 1, BlockPos.z + 1);
if (Lines == 1)
{
- intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(0, 1, 0), Vector3f(0, 1, 0));
+ intersect3D_SegmentPlane(start, end, BlockPos + Vector3f(0, 1, 0), Vector3f(0, 1, 0));
return 5;
}
}
}
- dot = Look.Dot( Vector3f(0, -1, 0)); // sixth face normal is y -1
+ dot = Look.Dot(Vector3f(0, -1, 0)); // sixth face normal is y -1
if (dot < 0)
{
- int Lines = LinesCross( start.y, start.x, end.y, end.x, BlockPos.y, BlockPos.x, BlockPos.y, BlockPos.x + 1);
+ int Lines = LinesCross(start.y, start.x, end.y, end.x, BlockPos.y, BlockPos.x, BlockPos.y, BlockPos.x + 1);
if (Lines == 1)
{
- Lines = LinesCross( start.y, start.z, end.y, end.z, BlockPos.y, BlockPos.z, BlockPos.y, BlockPos.z + 1);
+ Lines = LinesCross(start.y, start.z, end.y, end.z, BlockPos.y, BlockPos.z, BlockPos.y, BlockPos.z + 1);
if (Lines == 1)
{
- intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(0, -1, 0));
+ intersect3D_SegmentPlane(start, end, BlockPos, Vector3f(0, -1, 0));
return 6;
}
}
diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp
index 9113ec343..e784569d9 100644
--- a/src/UI/SlotArea.cpp
+++ b/src/UI/SlotArea.cpp
@@ -1167,7 +1167,7 @@ void cSlotAreaAnvil::UpdateResult(cPlayer & a_Player)
{
m_MaximumCost = 39;
}
- if (m_MaximumCost >= 40 && !a_Player.IsGameModeCreative())
+ if ((m_MaximumCost >= 40) && !a_Player.IsGameModeCreative())
{
Input.Empty();
}
@@ -2322,7 +2322,7 @@ cItem * cSlotAreaTemporary::GetPlayerSlots(cPlayer & a_Player)
{
return nullptr;
}
- return &(itr->second[0]);
+ return itr->second.data();
}
diff --git a/src/Vector3.h b/src/Vector3.h
index 1854e42e3..1e4a1f5d9 100644
--- a/src/Vector3.h
+++ b/src/Vector3.h
@@ -93,6 +93,21 @@ public:
return x * a_Rhs.x + y * a_Rhs.y + z * a_Rhs.z;
}
+ inline void abs()
+ {
+ x = (x < 0) ? -x : x;
+ y = (y < 0) ? -y : y;
+ z = (z < 0) ? -z : z;
+ }
+
+ // We can't use a capital letter, because we wouldn't be able to call the normal Clamp function.
+ inline void clamp(T a_Min, T a_Max)
+ {
+ x = Clamp(x, a_Min, a_Max);
+ y = Clamp(y, a_Min, a_Max);
+ z = Clamp(z, a_Min, a_Max);
+ }
+
inline Vector3<T> Cross(const Vector3<T> & a_Rhs) const
{
return Vector3<T>(
diff --git a/src/VoronoiMap.h b/src/VoronoiMap.h
index dfb11e9ce..56022849e 100644
--- a/src/VoronoiMap.h
+++ b/src/VoronoiMap.h
@@ -9,7 +9,7 @@
#pragma once
-#include "Noise.h"
+#include "Noise/Noise.h"
diff --git a/src/World.cpp b/src/World.cpp
index 2e079d447..e73dcb915 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -11,7 +11,6 @@
#include "IniFile.h"
#include "ChunkMap.h"
#include "Generating/ChunkDesc.h"
-#include "OSSupport/Timer.h"
#include "SetChunkData.h"
// Serializers
@@ -45,7 +44,6 @@
#include "MobCensus.h"
#include "MobSpawner.h"
-#include "MersenneTwister.h"
#include "Generating/Trees.h"
#include "Bindings/PluginManager.h"
#include "Blocks/BlockHandler.h"
@@ -74,102 +72,134 @@ const int TIME_SPAWN_DIVISOR = 148;
////////////////////////////////////////////////////////////////////////////////
-// cWorldLoadProgress:
+// cSpawnPrepare:
-/// A simple thread that displays the progress of world loading / saving in cWorld::InitializeSpawn()
-class cWorldLoadProgress :
- public cIsThread
+/** Generates and lights the spawn area of the world. Runs as a separate thread. */
+class cSpawnPrepare:
+ public cIsThread,
+ public cChunkCoordCallback
{
+ typedef cIsThread super;
+
public:
- cWorldLoadProgress(cWorld * a_World) :
- cIsThread("cWorldLoadProgress"),
- m_World(a_World)
- {
+ cSpawnPrepare(cWorld & a_World, int a_SpawnChunkX, int a_SpawnChunkZ, int a_PrepareDistance):
+ super("SpawnPrepare"),
+ m_World(a_World),
+ m_SpawnChunkX(a_SpawnChunkX),
+ m_SpawnChunkZ(a_SpawnChunkZ),
+ m_PrepareDistance(a_PrepareDistance),
+ m_MaxIdx(a_PrepareDistance * a_PrepareDistance),
+ m_NumPrepared(0),
+ m_LastReportChunkCount(0)
+ {
+ // Start the thread:
Start();
+
+ // Wait for start confirmation, so that the thread can be waited-upon after the constructor returns:
+ m_EvtStarted.Wait();
}
-
- void Stop(void)
- {
- m_ShouldTerminate = true;
- Wait();
- }
-
-protected:
- cWorld * m_World;
-
+
+ // cIsThread override:
virtual void Execute(void) override
{
- for (;;)
+ // Confirm thread start:
+ m_EvtStarted.Set();
+
+ // Queue the initial chunks:
+ m_MaxIdx = m_PrepareDistance * m_PrepareDistance;
+ int maxQueue = std::min(m_MaxIdx - 1, 100); // Number of chunks to queue at once
+ m_NextIdx = maxQueue;
+ m_LastReportTime = std::chrono::steady_clock::now();
+ for (int i = 0; i < maxQueue; i++)
{
- LOG("" SIZE_T_FMT " chunks to load, %d chunks to generate",
- m_World->GetStorage().GetLoadQueueLength(),
- m_World->GetGenerator().GetQueueLength()
- );
-
- // Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish
- for (int i = 0; i < 20; i++)
- {
- cSleep::MilliSleep(100);
- if (m_ShouldTerminate)
- {
- return;
- }
- }
- } // for (-ever)
+ int chunkX, chunkZ;
+ DecodeChunkCoords(i, chunkX, chunkZ);
+ m_World.GetLightingThread().QueueChunk(chunkX, chunkZ, this);
+ } // for i
+
+ // Wait for the lighting thread to prepare everything. Event is set in the Call() callback:
+ m_EvtFinished.Wait();
}
-
-} ;
+protected:
+ cWorld & m_World;
+ int m_SpawnChunkX;
+ int m_SpawnChunkZ;
+ int m_PrepareDistance;
+ /** The index of the next chunk to be queued in the lighting thread. */
+ int m_NextIdx;
+ /** The maximum index of the prepared chunks. Queueing stops when m_NextIdx reaches this number. */
+ int m_MaxIdx;
+ /** Total number of chunks already finished preparing. Preparation finishes when this number reaches m_MaxIdx. */
+ int m_NumPrepared;
-////////////////////////////////////////////////////////////////////////////////
-// cWorldLightingProgress:
+ /** Event used to signal that the thread has started. */
+ cEvent m_EvtStarted;
-/// A simple thread that displays the progress of world lighting in cWorld::InitializeSpawn()
-class cWorldLightingProgress :
- public cIsThread
-{
-public:
- cWorldLightingProgress(cLightingThread * a_Lighting) :
- cIsThread("cWorldLightingProgress"),
- m_Lighting(a_Lighting)
- {
- Start();
- }
-
- void Stop(void)
+ /** Event used to signal that the preparation is finished. */
+ cEvent m_EvtFinished;
+
+ /** The timestamp of the last progress report emitted. */
+ std::chrono::steady_clock::time_point m_LastReportTime;
+
+ /** Number of chunks prepared when the last progress report was emitted. */
+ int m_LastReportChunkCount;
+
+ // cChunkCoordCallback override:
+ virtual void Call(int a_ChunkX, int a_ChunkZ)
{
- m_ShouldTerminate = true;
- Wait();
+ // Check if this was the last chunk:
+ m_NumPrepared += 1;
+ if (m_NumPrepared >= m_MaxIdx)
+ {
+ m_EvtFinished.Set();
+ // Must return here, because "this" may have gotten deleted by the previous line
+ return;
+ }
+
+ // Queue another chunk, if appropriate:
+ if (m_NextIdx < m_MaxIdx)
+ {
+ int chunkX, chunkZ;
+ DecodeChunkCoords(m_NextIdx, chunkX, chunkZ);
+ m_World.GetLightingThread().QueueChunk(chunkX, chunkZ, this);
+ m_NextIdx += 1;
+ }
+
+ // Report progress every 1 second:
+ auto Now = std::chrono::steady_clock::now();
+ if (Now - m_LastReportTime > std::chrono::seconds(1))
+ {
+ float PercentDone = static_cast<float>(m_NumPrepared * 100) / m_MaxIdx;
+ float ChunkSpeed = static_cast<float>((m_NumPrepared - m_LastReportChunkCount) * 1000) / std::chrono::duration_cast<std::chrono::milliseconds>(Now - m_LastReportTime).count();
+ LOG("Preparing spawn (%s): %.02f%% (%d/%d; %.02f chunks/s)",
+ m_World.GetName().c_str(), PercentDone, m_NumPrepared, m_MaxIdx, ChunkSpeed
+ );
+ m_LastReportTime = Now;
+ m_LastReportChunkCount = m_NumPrepared;
+ }
}
-
-protected:
- cLightingThread * m_Lighting;
-
- virtual void Execute(void) override
+
+ /** Decodes the index into chunk coords. Provides the specific chunk ordering. */
+ void DecodeChunkCoords(int a_Idx, int & a_ChunkX, int & a_ChunkZ)
{
- for (;;)
+ // A zigzag pattern from the top to bottom, each row alternating between forward-x and backward-x:
+ int z = a_Idx / m_PrepareDistance;
+ int x = a_Idx % m_PrepareDistance;
+ if ((z & 1) == 0)
{
- LOG("" SIZE_T_FMT " chunks remaining to light", m_Lighting->GetQueueLength()
- );
-
- // Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish
- for (int i = 0; i < 20; i++)
- {
- cSleep::MilliSleep(100);
- if (m_ShouldTerminate)
- {
- return;
- }
- }
- } // for (-ever)
+ // Reverse every second row:
+ x = m_PrepareDistance - 1 - x;
+ }
+ a_ChunkZ = m_SpawnChunkZ + z - m_PrepareDistance / 2;
+ a_ChunkX = m_SpawnChunkX + x - m_PrepareDistance / 2;
}
-
-} ;
+};
@@ -202,23 +232,22 @@ cWorld::cTickThread::cTickThread(cWorld & a_World) :
void cWorld::cTickThread::Execute(void)
{
- cTimer Timer;
-
- const Int64 msPerTick = 50;
- Int64 LastTime = Timer.GetNowTime();
+ auto LastTime = std::chrono::steady_clock::now();
+ static const auto msPerTick = std::chrono::milliseconds(50);
+ auto TickTime = std::chrono::steady_clock::duration(50);
- Int64 TickDuration = 50;
while (!m_ShouldTerminate)
{
- Int64 NowTime = Timer.GetNowTime();
- float DeltaTime = (float)(NowTime - LastTime);
- m_World.Tick(DeltaTime, (int)TickDuration);
- TickDuration = Timer.GetNowTime() - NowTime;
+ 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;
- if (TickDuration < msPerTick)
+ if (TickTime < msPerTick)
{
// Stretch tick time until it's at least msPerTick
- cSleep::MilliSleep((unsigned int)(msPerTick - TickDuration));
+ std::this_thread::sleep_for(msPerTick - TickTime);
}
LastTime = NowTime;
@@ -283,6 +312,7 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin
m_bCommandBlocksEnabled(true),
m_bUseChatPrefixes(false),
m_TNTShrapnelLevel(slNone),
+ m_MaxViewDistance(12),
m_Scoreboard(this),
m_MapManager(this),
m_GeneratorCallbacks(*this),
@@ -316,6 +346,10 @@ cWorld::~cWorld()
Serializer.Save();
m_MapManager.SaveMapData();
+
+ // Explicitly destroy the chunkmap, so that it's guaranteed to be destroyed before the other internals
+ // This fixes crashes on stopping the server, because chunk destructor deletes entities and those access the world.
+ m_ChunkMap.reset();
}
@@ -431,53 +465,9 @@ void cWorld::InitializeSpawn(void)
IniFile.ReadFile(m_IniFileName);
int ViewDist = IniFile.GetValueSetI("SpawnPosition", "PregenerateDistance", DefaultViewDist);
IniFile.WriteFile(m_IniFileName);
-
- LOG("Preparing spawn area in world \"%s\", %d x %d chunks, total %d chunks...", m_WorldName.c_str(), ViewDist, ViewDist, ViewDist * ViewDist);
- for (int x = 0; x < ViewDist; x++)
- {
- for (int z = 0; z < ViewDist; z++)
- {
- m_ChunkMap->TouchChunk(x + ChunkX-(ViewDist - 1) / 2, z + ChunkZ-(ViewDist - 1) / 2); // Queue the chunk in the generator / loader
- }
- }
-
- {
- // Display progress during this process:
- cWorldLoadProgress Progress(this);
-
- // Wait for the loader to finish loading
- m_Storage.WaitForLoadQueueEmpty();
-
- // Wait for the generator to finish generating
- m_Generator.WaitForQueueEmpty();
- // Wait for the loader to finish saving
- m_Storage.WaitForSaveQueueEmpty();
-
- Progress.Stop();
- }
-
- // Light all chunks that have been newly generated:
- LOG("Lighting spawn area in world \"%s\"...", m_WorldName.c_str());
-
- for (int x = 0; x < ViewDist; x++)
- {
- int ChX = x + ChunkX-(ViewDist - 1) / 2;
- for (int z = 0; z < ViewDist; z++)
- {
- int ChZ = z + ChunkZ-(ViewDist - 1) / 2;
- if (!m_ChunkMap->IsChunkLighted(ChX, ChZ))
- {
- m_Lighting.QueueChunk(ChX, ChZ); // Queue the chunk in the lighting thread
- }
- } // for z
- } // for x
-
- {
- cWorldLightingProgress Progress(&m_Lighting);
- m_Lighting.WaitForQueueEmpty();
- Progress.Stop();
- }
+ cSpawnPrepare prep(*this, ChunkX, ChunkZ, ViewDist);
+ prep.Wait();
#ifdef TEST_LINEBLOCKTRACER
// DEBUG: Test out the cLineBlockTracer class by tracing a few lines:
@@ -561,6 +551,8 @@ void cWorld::Start(void)
m_BroadcastDeathMessages = IniFile.GetValueSetB("Broadcasting", "BroadcastDeathMessages", true);
m_BroadcastAchievementMessages = IniFile.GetValueSetB("Broadcasting", "BroadcastAchievementMessages", true);
+ SetMaxViewDistance(IniFile.GetValueSetI("SpawnPosition", "MaxViewDistance", 12));
+
// Try to find the "SpawnPosition" key and coord values in the world configuration, set the flag if found
int KeyNum = IniFile.FindKey("SpawnPosition");
m_IsSpawnExplicitlySet =
@@ -618,7 +610,7 @@ void cWorld::Start(void)
}
// Adjust the enum-backed variables into their respective bounds:
- m_GameMode = (eGameMode) Clamp(GameMode, (int)gmSurvival, (int)gmAdventure);
+ m_GameMode = (eGameMode) Clamp(GameMode, (int)gmSurvival, (int)gmSpectator);
m_TNTShrapnelLevel = (eShrapnelLevel)Clamp(TNTShrapnelLevel, (int)slNone, (int)slAll);
m_Weather = (eWeather) Clamp(Weather, (int)wSunny, (int)wStorm);
@@ -738,28 +730,32 @@ void cWorld::InitialiseGeneratorDefaults(cIniFile & a_IniFile)
{
case dimEnd:
{
- a_IniFile.GetValueSet("Generator", "BiomeGen", "Constant");
- a_IniFile.GetValueSet("Generator", "ConstantBiome", "End");
- a_IniFile.GetValueSet("Generator", "HeightGen", "Biomal");
+ a_IniFile.GetValueSet("Generator", "Generator", "Composable");
+ a_IniFile.GetValueSet("Generator", "BiomeGen", "Constant");
+ a_IniFile.GetValueSet("Generator", "ConstantBiome", "End");
+ a_IniFile.GetValueSet("Generator", "ShapeGen", "End");
a_IniFile.GetValueSet("Generator", "CompositionGen", "End");
break;
}
case dimOverworld:
{
- a_IniFile.GetValueSet("Generator", "BiomeGen", "MultiStepMap");
- a_IniFile.GetValueSet("Generator", "HeightGen", "DistortedHeightmap");
- a_IniFile.GetValueSet("Generator", "CompositionGen", "DistortedHeightmap");
- a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, PreSimulator");
+ a_IniFile.GetValueSet("Generator", "Generator", "Composable");
+ a_IniFile.GetValueSet("Generator", "BiomeGen", "Grown");
+ a_IniFile.GetValueSet("Generator", "ShapeGen", "BiomalNoise3D");
+ a_IniFile.GetValueSet("Generator", "CompositionGen", "Biomal");
+ a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, PreSimulator, Animals");
break;
}
case dimNether:
{
- a_IniFile.GetValueSet("Generator", "BiomeGen", "Constant");
- a_IniFile.GetValueSet("Generator", "ConstantBiome", "Nether");
- a_IniFile.GetValueSet("Generator", "HeightGen", "Flat");
- a_IniFile.GetValueSet("Generator", "FlatHeight", "128");
- a_IniFile.GetValueSet("Generator", "CompositionGen", "Nether");
- a_IniFile.GetValueSet("Generator", "Finishers", "WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherForts, PreSimulator");
+ a_IniFile.GetValueSet("Generator", "Generator", "Composable");
+ a_IniFile.GetValueSet("Generator", "BiomeGen", "Constant");
+ a_IniFile.GetValueSet("Generator", "ConstantBiome", "Nether");
+ a_IniFile.GetValueSet("Generator", "ShapeGen", "HeightMap");
+ a_IniFile.GetValueSet("Generator", "HeightGen", "Flat");
+ a_IniFile.GetValueSet("Generator", "FlatHeight", "128");
+ a_IniFile.GetValueSet("Generator", "CompositionGen", "Nether");
+ a_IniFile.GetValueSet("Generator", "Finishers", "SoulsandRims, WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherForts, PreSimulator");
a_IniFile.GetValueSet("Generator", "BottomLavaHeight", "30");
break;
}
@@ -1809,7 +1805,7 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double
a_FlyAwaySpeed /= 100; // Pre-divide, so that we don't have to divide each time inside the loop
for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr)
{
- if (!IsValidItem(itr->m_ItemType) || itr->m_ItemType == E_BLOCK_AIR)
+ if (!IsValidItem(itr->m_ItemType) || (itr->m_ItemType == E_BLOCK_AIR))
{
// Don't spawn pickup if item isn't even valid; should prevent client crashing too
continue;
@@ -1835,7 +1831,7 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double
{
for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr)
{
- if (!IsValidItem(itr->m_ItemType) || itr->m_ItemType == E_BLOCK_AIR)
+ if (!IsValidItem(itr->m_ItemType) || (itr->m_ItemType == E_BLOCK_AIR))
{
continue;
}
@@ -2680,7 +2676,7 @@ bool cWorld::ForEachPlayer(cPlayerListCallback & a_Callback)
bool cWorld::DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback)
{
- // Calls the callback for each player in the list
+ // Calls the callback for the specified player in the list
cCSLock Lock(m_CSPlayers);
for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
@@ -2729,6 +2725,23 @@ bool cWorld::FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCa
+bool cWorld::DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback)
+{
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ if ((*itr)->GetUUID() == a_PlayerUUID)
+ {
+ return a_Callback.Item(*itr);
+ }
+ }
+ return false;
+}
+
+
+
+
+
// TODO: This interface is dangerous!
cPlayer * cWorld::FindClosestPlayer(const Vector3d & a_Pos, float a_SightLimit, bool a_CheckLineOfSight)
{
@@ -2909,25 +2922,19 @@ bool cWorld::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AStrin
AString Line2(a_Line2);
AString Line3(a_Line3);
AString Line4(a_Line4);
+
if (cRoot::Get()->GetPluginManager()->CallHookUpdatingSign(*this, a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4, a_Player))
{
return false;
}
+
if (m_ChunkMap->SetSignLines(a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4))
{
cRoot::Get()->GetPluginManager()->CallHookUpdatedSign(*this, a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4, a_Player);
return true;
}
- return false;
-}
-
-
-
-
-bool cWorld::UpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player)
-{
- return SetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player);
+ return false;
}
@@ -3119,6 +3126,11 @@ bool cWorld::HasEntity(int a_UniqueID)
}
// Check if the entity is in the chunkmap:
+ if (m_ChunkMap.get() == nullptr)
+ {
+ // Chunkmap has already been destroyed, there are no entities anymore.
+ return false;
+ }
return m_ChunkMap->HasEntity(a_UniqueID);
}
diff --git a/src/World.h b/src/World.h
index ec6dcadde..d64be208e 100644
--- a/src/World.h
+++ b/src/World.h
@@ -10,7 +10,6 @@
#define MAX_PLAYERS 65535
#include "Simulator/SimulatorManager.h"
-#include "MersenneTwister.h"
#include "ChunkMap.h"
#include "WorldStorage/WorldStorage.h"
#include "Generating/ChunkGenerator.h"
@@ -26,6 +25,8 @@
#include "MapManager.h"
#include "Blocks/WorldInterface.h"
#include "Blocks/BroadcastInterface.h"
+#include "FastRandom.h"
+#include "ClientHandle.h"
@@ -314,7 +315,8 @@ public:
/** Calls the callback for each player in the list; returns true if all players processed, false if the callback aborted by returning true */
virtual bool ForEachPlayer(cPlayerListCallback & a_Callback) override; // >> EXPORTED IN MANUALBINDINGS <<
- /** Calls the callback for the player of the given name; returns true if the player was found and the callback called, false if player not found. Callback return ignored */
+ /** Calls the callback for the player of the given name; returns true if the player was found and the callback called, false if player not found.
+ Callback return value is ignored. If there are multiple players of the same name, only (random) one is processed by the callback. */
bool DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
/** Finds a player from a partial or complete player name and calls the callback - case-insensitive */
@@ -323,6 +325,9 @@ public:
// TODO: This interface is dangerous - rewrite to DoWithClosestPlayer(pos, sight, action)
cPlayer * FindClosestPlayer(const Vector3d & a_Pos, float a_SightLimit, bool a_CheckLineOfSight = true);
+ /** Finds the player over his uuid and calls the callback */
+ bool DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
+
void SendPlayerList(cPlayer * a_DestPlayer); // Sends playerlist to the player
/** Adds the entity into its appropriate chunk; takes ownership of the entity ptr.
@@ -374,11 +379,8 @@ public:
/** Marks the chunk as failed-to-load: */
void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ);
- /** Sets the sign text, asking plugins for permission first. a_Player is the player who this change belongs to, may be nullptr. Returns true if sign text changed. Same as UpdateSign() */
+ /** Sets the sign text, asking plugins for permission first. a_Player is the player who this change belongs to, may be nullptr. Returns true if sign text changed. */
bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player = nullptr); // Exported in ManualBindings.cpp
-
- /** Sets the sign text, asking plugins for permission first. a_Player is the player who this change belongs to, may be nullptr. Returns true if sign text changed. Same as SetSignLines() */
- bool UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player = nullptr); // Exported in ManualBindings.cpp
/** Sets the command block command. Returns true if command changed. */
bool SetCommandBlockCommand(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Command); // tolua_export
@@ -646,6 +648,12 @@ public:
eShrapnelLevel GetTNTShrapnelLevel(void) const { return m_TNTShrapnelLevel; }
void SetTNTShrapnelLevel(eShrapnelLevel a_Flag) { m_TNTShrapnelLevel = a_Flag; }
+ int GetMaxViewDistance(void) const { return m_MaxViewDistance; }
+ void SetMaxViewDistance(int a_MaxViewDistance)
+ {
+ m_MaxViewDistance = Clamp(a_MaxViewDistance, cClientHandle::MIN_VIEW_DISTANCE, cClientHandle::MAX_VIEW_DISTANCE);
+ }
+
bool ShouldUseChatPrefixes(void) const { return m_bUseChatPrefixes; }
void SetShouldUseChatPrefixes(bool a_Flag) { m_bUseChatPrefixes = a_Flag; }
@@ -689,6 +697,8 @@ public:
inline size_t GetStorageLoadQueueLength(void) { return m_Storage.GetLoadQueueLength(); } // tolua_export
inline size_t GetStorageSaveQueueLength(void) { return m_Storage.GetSaveQueueLength(); } // tolua_export
+ cLightingThread & GetLightingThread(void) { return m_Lighting; }
+
void InitializeSpawn(void);
/** Starts threads that belong to this world */
@@ -961,6 +971,9 @@ private:
*/
eShrapnelLevel m_TNTShrapnelLevel;
+ /** The maximum view distance that a player can have in this world. */
+ int m_MaxViewDistance;
+
/** Name of the nether world */
AString m_NetherWorldName;
diff --git a/src/WorldStorage/FireworksSerializer.cpp b/src/WorldStorage/FireworksSerializer.cpp
index ecb600483..91a5b8b63 100644
--- a/src/WorldStorage/FireworksSerializer.cpp
+++ b/src/WorldStorage/FireworksSerializer.cpp
@@ -22,11 +22,11 @@ void cFireworkItem::WriteToNBTCompound(const cFireworkItem & a_FireworkItem, cFa
a_Writer.AddByte("Type", a_FireworkItem.m_Type);
if (!a_FireworkItem.m_Colours.empty())
{
- a_Writer.AddIntArray("Colors", &(a_FireworkItem.m_Colours[0]), a_FireworkItem.m_Colours.size());
+ a_Writer.AddIntArray("Colors", a_FireworkItem.m_Colours.data(), a_FireworkItem.m_Colours.size());
}
if (!a_FireworkItem.m_FadeColours.empty())
{
- a_Writer.AddIntArray("FadeColors", &(a_FireworkItem.m_FadeColours[0]), a_FireworkItem.m_FadeColours.size());
+ a_Writer.AddIntArray("FadeColors", a_FireworkItem.m_FadeColours.data(), a_FireworkItem.m_FadeColours.size());
}
a_Writer.EndCompound();
a_Writer.EndList();
@@ -41,11 +41,11 @@ void cFireworkItem::WriteToNBTCompound(const cFireworkItem & a_FireworkItem, cFa
a_Writer.AddByte("Type", a_FireworkItem.m_Type);
if (!a_FireworkItem.m_Colours.empty())
{
- a_Writer.AddIntArray("Colors", &(a_FireworkItem.m_Colours[0]), a_FireworkItem.m_Colours.size());
+ a_Writer.AddIntArray("Colors", a_FireworkItem.m_Colours.data(), a_FireworkItem.m_Colours.size());
}
if (!a_FireworkItem.m_FadeColours.empty())
{
- a_Writer.AddIntArray("FadeColors", &(a_FireworkItem.m_FadeColours[0]), a_FireworkItem.m_FadeColours.size());
+ a_Writer.AddIntArray("FadeColors", a_FireworkItem.m_FadeColours.data(), a_FireworkItem.m_FadeColours.size());
}
a_Writer.EndCompound();
break;
diff --git a/src/WorldStorage/MapSerializer.cpp b/src/WorldStorage/MapSerializer.cpp
index 4a913c81a..f2d35b318 100644
--- a/src/WorldStorage/MapSerializer.cpp
+++ b/src/WorldStorage/MapSerializer.cpp
@@ -112,7 +112,7 @@ void cMapSerializer::SaveMapToNBT(cFastNBTWriter & a_Writer)
a_Writer.AddInt("zCenter", m_Map->GetCenterZ());
const cMap::cColorList & Data = m_Map->GetData();
- a_Writer.AddByteArray("colors", (char *) &Data[0], Data.size());
+ a_Writer.AddByteArray("colors", (char *)Data.data(), Data.size());
a_Writer.EndCompound();
}
@@ -190,7 +190,7 @@ bool cMapSerializer::LoadMapFromNBT(const cParsedNBT & a_NBT)
CurrLine = a_NBT.FindChildByName(Data, "colors");
if ((CurrLine >= 0) && (a_NBT.GetType(CurrLine) == TAG_ByteArray))
{
- memcpy(&m_Map->m_Data[0], a_NBT.GetData(CurrLine), NumPixels);
+ memcpy(m_Map->m_Data.data(), a_NBT.GetData(CurrLine), NumPixels);
}
return true;
diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp
index 4daa8cc7b..432e122b5 100644
--- a/src/WorldStorage/NBTChunkSerializer.cpp
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -18,6 +18,7 @@
#include "../BlockEntities/FurnaceEntity.h"
#include "../BlockEntities/HopperEntity.h"
#include "../BlockEntities/JukeboxEntity.h"
+#include "../BlockEntities/MobSpawnerEntity.h"
#include "../BlockEntities/NoteEntity.h"
#include "../BlockEntities/SignEntity.h"
#include "../BlockEntities/MobHeadEntity.h"
@@ -290,6 +291,20 @@ void cNBTChunkSerializer::AddJukeboxEntity(cJukeboxEntity * a_Jukebox)
+void cNBTChunkSerializer::AddMobSpawnerEntity(cMobSpawnerEntity * a_MobSpawner)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_MobSpawner, "MobSpawner");
+ m_Writer.AddShort("Entity", static_cast<short>(a_MobSpawner->GetEntity()));
+ m_Writer.AddString("EntityId", cMonster::MobTypeToVanillaName(a_MobSpawner->GetEntity()));
+ m_Writer.AddShort("Delay", a_MobSpawner->GetSpawnDelay());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
void cNBTChunkSerializer::AddNoteEntity(cNoteEntity * a_Note)
{
m_Writer.BeginCompound("");
@@ -914,6 +929,7 @@ void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity)
case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break;
case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break;
case E_BLOCK_LIT_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break;
+ case E_BLOCK_MOB_SPAWNER: AddMobSpawnerEntity ((cMobSpawnerEntity *) a_Entity); break;
case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break;
case E_BLOCK_SIGN_POST: AddSignEntity ((cSignEntity *) a_Entity); break;
case E_BLOCK_TRAPPED_CHEST: AddChestEntity ((cChestEntity *) a_Entity, a_Entity->GetBlockType()); break;
diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h
index 5ffab8cc5..4c066b9af 100644
--- a/src/WorldStorage/NBTChunkSerializer.h
+++ b/src/WorldStorage/NBTChunkSerializer.h
@@ -32,6 +32,7 @@ class cJukeboxEntity;
class cNoteEntity;
class cSignEntity;
class cMobHeadEntity;
+class cMobSpawnerEntity;
class cFlowerPotEntity;
class cFallingBlock;
class cMinecart;
@@ -94,19 +95,20 @@ protected:
void AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum = 0);
// Block entities:
- void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID);
- void AddBeaconEntity (cBeaconEntity * a_Entity);
- void AddChestEntity (cChestEntity * a_Entity, BLOCKTYPE a_ChestType);
- void AddDispenserEntity(cDispenserEntity * a_Entity);
- void AddDropperEntity (cDropperEntity * a_Entity);
- void AddFurnaceEntity (cFurnaceEntity * a_Furnace);
- void AddHopperEntity (cHopperEntity * a_Entity);
- void AddJukeboxEntity (cJukeboxEntity * a_Jukebox);
- void AddNoteEntity (cNoteEntity * a_Note);
- void AddSignEntity (cSignEntity * a_Sign);
- void AddMobHeadEntity (cMobHeadEntity * a_MobHead);
+ void AddBasicTileEntity (cBlockEntity * a_Entity, const char * a_EntityTypeID);
+ void AddBeaconEntity (cBeaconEntity * a_Entity);
+ void AddChestEntity (cChestEntity * a_Entity, BLOCKTYPE a_ChestType);
+ void AddDispenserEntity (cDispenserEntity * a_Entity);
+ void AddDropperEntity (cDropperEntity * a_Entity);
+ void AddFurnaceEntity (cFurnaceEntity * a_Furnace);
+ void AddHopperEntity (cHopperEntity * a_Entity);
+ void AddJukeboxEntity (cJukeboxEntity * a_Jukebox);
+ void AddMobSpawnerEntity (cMobSpawnerEntity * a_MobSpawner);
+ void AddNoteEntity (cNoteEntity * a_Note);
+ void AddSignEntity (cSignEntity * a_Sign);
+ void AddMobHeadEntity (cMobHeadEntity * a_MobHead);
void AddCommandBlockEntity(cCommandBlockEntity * a_CmdBlock);
- void AddFlowerPotEntity(cFlowerPotEntity * a_FlowerPot);
+ void AddFlowerPotEntity (cFlowerPotEntity * a_FlowerPot);
// Entities:
void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName);
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index 0c77b4d67..5a0932bbe 100644
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -28,6 +28,7 @@
#include "../BlockEntities/NoteEntity.h"
#include "../BlockEntities/SignEntity.h"
#include "../BlockEntities/MobHeadEntity.h"
+#include "../BlockEntities/MobSpawnerEntity.h"
#include "../BlockEntities/FlowerPotEntity.h"
#include "../Mobs/Monster.h"
@@ -630,7 +631,7 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
// Load the proper BlockEntity type based on the block type:
BLOCKTYPE BlockType = cChunkDef::GetBlock(a_BlockTypes, RelX, RelY, RelZ);
NIBBLETYPE BlockMeta = cChunkDef::GetNibble(a_BlockMetas, RelX, RelY, RelZ);
- std::auto_ptr<cBlockEntity> be(LoadBlockEntityFromNBT(a_NBT, Child, x, y, z, BlockType, BlockMeta));
+ std::unique_ptr<cBlockEntity> be(LoadBlockEntityFromNBT(a_NBT, Child, x, y, z, BlockType, BlockMeta));
if (be.get() == nullptr)
{
continue;
@@ -664,6 +665,7 @@ cBlockEntity * cWSSAnvil::LoadBlockEntityFromNBT(const cParsedNBT & a_NBT, int a
case E_BLOCK_HOPPER: return LoadHopperFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_JUKEBOX: return LoadJukeboxFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_LIT_FURNACE: return LoadFurnaceFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_LIT_FURNACE, a_BlockMeta);
+ case E_BLOCK_MOB_SPAWNER: return LoadMobSpawnerFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_NOTE_BLOCK: return LoadNoteBlockFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_SIGN_POST: return LoadSignFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SIGN_POST);
case E_BLOCK_TRAPPED_CHEST: return LoadChestFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_TRAPPED_CHEST);
@@ -856,7 +858,7 @@ cBlockEntity * cWSSAnvil::LoadBeaconFromNBT(const cParsedNBT & a_NBT, int a_TagI
return nullptr;
}
- std::auto_ptr<cBeaconEntity> Beacon(new cBeaconEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
+ std::unique_ptr<cBeaconEntity> Beacon(new cBeaconEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int CurrentLine = a_NBT.FindChildByName(a_TagIdx, "Levels");
if (CurrentLine >= 0)
@@ -906,7 +908,7 @@ cBlockEntity * cWSSAnvil::LoadChestFromNBT(const cParsedNBT & a_NBT, int a_TagId
{
return nullptr; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this
}
- std::auto_ptr<cChestEntity> Chest(new cChestEntity(a_BlockX, a_BlockY, a_BlockZ, m_World, a_ChestBlockType));
+ std::unique_ptr<cChestEntity> Chest(new cChestEntity(a_BlockX, a_BlockY, a_BlockZ, m_World, a_ChestBlockType));
LoadItemGridFromNBT(Chest->GetContents(), a_NBT, Items);
return Chest.release();
}
@@ -923,7 +925,7 @@ cBlockEntity * cWSSAnvil::LoadCommandBlockFromNBT(const cParsedNBT & a_NBT, int
return nullptr;
}
- std::auto_ptr<cCommandBlockEntity> CmdBlock(new cCommandBlockEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
+ std::unique_ptr<cCommandBlockEntity> CmdBlock(new cCommandBlockEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int currentLine = a_NBT.FindChildByName(a_TagIdx, "Command");
if (currentLine >= 0)
@@ -965,7 +967,7 @@ cBlockEntity * cWSSAnvil::LoadDispenserFromNBT(const cParsedNBT & a_NBT, int a_T
{
return nullptr; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this
}
- std::auto_ptr<cDispenserEntity> Dispenser(new cDispenserEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
+ std::unique_ptr<cDispenserEntity> Dispenser(new cDispenserEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
LoadItemGridFromNBT(Dispenser->GetContents(), a_NBT, Items);
return Dispenser.release();
}
@@ -987,7 +989,7 @@ cBlockEntity * cWSSAnvil::LoadDropperFromNBT(const cParsedNBT & a_NBT, int a_Tag
{
return nullptr; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this
}
- std::auto_ptr<cDropperEntity> Dropper(new cDropperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
+ std::unique_ptr<cDropperEntity> Dropper(new cDropperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
LoadItemGridFromNBT(Dropper->GetContents(), a_NBT, Items);
return Dropper.release();
}
@@ -1004,7 +1006,7 @@ cBlockEntity * cWSSAnvil::LoadFlowerPotFromNBT(const cParsedNBT & a_NBT, int a_T
return nullptr;
}
- std::auto_ptr<cFlowerPotEntity> FlowerPot(new cFlowerPotEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
+ std::unique_ptr<cFlowerPotEntity> FlowerPot(new cFlowerPotEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
short ItemType = 0, ItemData = 0;
int currentLine = a_NBT.FindChildByName(a_TagIdx, "Item");
@@ -1041,7 +1043,7 @@ cBlockEntity * cWSSAnvil::LoadFurnaceFromNBT(const cParsedNBT & a_NBT, int a_Tag
return nullptr; // Make it an empty furnace - the chunk loader will provide an empty cFurnaceEntity for this
}
- std::auto_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, m_World));
+ std::unique_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, m_World));
// Load slots:
for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child))
@@ -1085,6 +1087,54 @@ cBlockEntity * cWSSAnvil::LoadFurnaceFromNBT(const cParsedNBT & a_NBT, int a_Tag
+cBlockEntity * cWSSAnvil::LoadMobSpawnerFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // Check if the data has a proper type:
+ if (!CheckBlockEntityType(a_NBT, a_TagIdx, "MobSpawner"))
+ {
+ return nullptr;
+ }
+
+ std::unique_ptr<cMobSpawnerEntity> MobSpawner(new cMobSpawnerEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
+
+ // Load entity (MCServer worlds):
+ int Type = a_NBT.FindChildByName(a_TagIdx, "Entity");
+ if ((Type >= 0) && (a_NBT.GetType(Type) == TAG_Short))
+ {
+ short MonsterType = a_NBT.GetShort(Type);
+ if ((MonsterType >= 50) && (MonsterType <= 120))
+ {
+ MobSpawner->SetEntity(static_cast<eMonsterType>(MonsterType));
+ }
+ }
+ else
+ {
+ // Load entity (vanilla worlds):
+ Type = a_NBT.FindChildByName(a_TagIdx, "EntityId");
+ if ((Type >= 0) && (a_NBT.GetType(Type) == TAG_String))
+ {
+ eMonsterType MonsterType = cMonster::StringToMobType(a_NBT.GetString(Type));
+ if (MonsterType != eMonsterType::mtInvalidType)
+ {
+ MobSpawner->SetEntity(MonsterType);
+ }
+ }
+ }
+
+ // Load delay:
+ int Delay = a_NBT.FindChildByName(a_TagIdx, "Delay");
+ if ((Delay >= 0) && (a_NBT.GetType(Delay) == TAG_Short))
+ {
+ MobSpawner->SetSpawnDelay(a_NBT.GetShort(Delay));
+ }
+
+ return MobSpawner.release();
+}
+
+
+
+
+
cBlockEntity * cWSSAnvil::LoadHopperFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
// Check if the data has a proper type:
@@ -1098,7 +1148,7 @@ cBlockEntity * cWSSAnvil::LoadHopperFromNBT(const cParsedNBT & a_NBT, int a_TagI
{
return nullptr; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this
}
- std::auto_ptr<cHopperEntity> Hopper(new cHopperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
+ std::unique_ptr<cHopperEntity> Hopper(new cHopperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
LoadItemGridFromNBT(Hopper->GetContents(), a_NBT, Items);
return Hopper.release();
}
@@ -1115,7 +1165,7 @@ cBlockEntity * cWSSAnvil::LoadJukeboxFromNBT(const cParsedNBT & a_NBT, int a_Tag
return nullptr;
}
- std::auto_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
+ std::unique_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int Record = a_NBT.FindChildByName(a_TagIdx, "Record");
if (Record >= 0)
{
@@ -1136,7 +1186,7 @@ cBlockEntity * cWSSAnvil::LoadMobHeadFromNBT(const cParsedNBT & a_NBT, int a_Tag
return nullptr;
}
- std::auto_ptr<cMobHeadEntity> MobHead(new cMobHeadEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
+ std::unique_ptr<cMobHeadEntity> MobHead(new cMobHeadEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int currentLine = a_NBT.FindChildByName(a_TagIdx, "SkullType");
if (currentLine >= 0)
@@ -1171,7 +1221,7 @@ cBlockEntity * cWSSAnvil::LoadNoteBlockFromNBT(const cParsedNBT & a_NBT, int a_T
return nullptr;
}
- std::auto_ptr<cNoteEntity> NoteBlock(new cNoteEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
+ std::unique_ptr<cNoteEntity> NoteBlock(new cNoteEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int note = a_NBT.FindChildByName(a_TagIdx, "note");
if (note >= 0)
{
@@ -1192,7 +1242,7 @@ cBlockEntity * cWSSAnvil::LoadSignFromNBT(const cParsedNBT & a_NBT, int a_TagIdx
return nullptr;
}
- std::auto_ptr<cSignEntity> Sign(new cSignEntity(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, m_World));
+ std::unique_ptr<cSignEntity> Sign(new cSignEntity(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, m_World));
int currentLine = a_NBT.FindChildByName(a_TagIdx, "Text1");
if (currentLine >= 0)
@@ -1445,7 +1495,7 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a
void cWSSAnvil::LoadBoatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cBoat> Boat(new cBoat(0, 0, 0));
+ std::unique_ptr<cBoat> Boat(new cBoat(0, 0, 0));
if (!LoadEntityBaseFromNBT(*Boat.get(), a_NBT, a_TagIdx))
{
return;
@@ -1459,7 +1509,7 @@ void cWSSAnvil::LoadBoatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_N
void cWSSAnvil::LoadEnderCrystalFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cEnderCrystal> EnderCrystal(new cEnderCrystal(0, 0, 0));
+ std::unique_ptr<cEnderCrystal> EnderCrystal(new cEnderCrystal(0, 0, 0));
if (!LoadEntityBaseFromNBT(*EnderCrystal.get(), a_NBT, a_TagIdx))
{
return;
@@ -1476,12 +1526,15 @@ void cWSSAnvil::LoadFallingBlockFromNBT(cEntityList & a_Entities, const cParsedN
int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "TileID");
int MetaIdx = a_NBT.FindChildByName(a_TagIdx, "Data");
- if ((TypeIdx < 0) || (MetaIdx < 0)) { return; }
+ if ((TypeIdx < 0) || (MetaIdx < 0))
+ {
+ return;
+ }
int Type = a_NBT.GetInt(TypeIdx);
NIBBLETYPE Meta = (NIBBLETYPE)a_NBT.GetByte(MetaIdx);
- std::auto_ptr<cFallingBlock> FallingBlock(new cFallingBlock(Vector3i(0, 0, 0), Type, Meta));
+ std::unique_ptr<cFallingBlock> FallingBlock(new cFallingBlock(Vector3i(0, 0, 0), Type, Meta));
if (!LoadEntityBaseFromNBT(*FallingBlock.get(), a_NBT, a_TagIdx))
{
return;
@@ -1495,7 +1548,7 @@ void cWSSAnvil::LoadFallingBlockFromNBT(cEntityList & a_Entities, const cParsedN
void cWSSAnvil::LoadMinecartRFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cRideableMinecart> Minecart(new cRideableMinecart(0, 0, 0, cItem(), 1)); // TODO: Load the block and the height
+ std::unique_ptr<cRideableMinecart> Minecart(new cRideableMinecart(0, 0, 0, cItem(), 1)); // TODO: Load the block and the height
if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx))
{
return;
@@ -1514,7 +1567,7 @@ void cWSSAnvil::LoadMinecartCFromNBT(cEntityList & a_Entities, const cParsedNBT
{
return; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this
}
- std::auto_ptr<cMinecartWithChest> Minecart(new cMinecartWithChest(0, 0, 0));
+ std::unique_ptr<cMinecartWithChest> Minecart(new cMinecartWithChest(0, 0, 0));
if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx))
{
return;
@@ -1541,7 +1594,7 @@ void cWSSAnvil::LoadMinecartCFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadMinecartFFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cMinecartWithFurnace> Minecart(new cMinecartWithFurnace(0, 0, 0));
+ std::unique_ptr<cMinecartWithFurnace> Minecart(new cMinecartWithFurnace(0, 0, 0));
if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx))
{
return;
@@ -1558,7 +1611,7 @@ void cWSSAnvil::LoadMinecartFFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadMinecartTFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cMinecartWithTNT> Minecart(new cMinecartWithTNT(0, 0, 0));
+ std::unique_ptr<cMinecartWithTNT> Minecart(new cMinecartWithTNT(0, 0, 0));
if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx))
{
return;
@@ -1575,7 +1628,7 @@ void cWSSAnvil::LoadMinecartTFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadMinecartHFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cMinecartWithHopper> Minecart(new cMinecartWithHopper(0, 0, 0));
+ std::unique_ptr<cMinecartWithHopper> Minecart(new cMinecartWithHopper(0, 0, 0));
if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx))
{
return;
@@ -1604,7 +1657,7 @@ void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a
return;
}
- std::auto_ptr<cPickup> Pickup(new cPickup(0, 0, 0, Item, false)); // Pickup delay doesn't matter, just say false
+ std::unique_ptr<cPickup> Pickup(new cPickup(0, 0, 0, Item, false)); // Pickup delay doesn't matter, just say false
if (!LoadEntityBaseFromNBT(*Pickup.get(), a_NBT, a_TagIdx))
{
return;
@@ -1626,7 +1679,7 @@ void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a
void cWSSAnvil::LoadTNTFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cTNTEntity> TNT(new cTNTEntity(0.0, 0.0, 0.0, 0));
+ std::unique_ptr<cTNTEntity> TNT(new cTNTEntity(0.0, 0.0, 0.0, 0));
if (!LoadEntityBaseFromNBT(*TNT.get(), a_NBT, a_TagIdx))
{
return;
@@ -1648,7 +1701,7 @@ void cWSSAnvil::LoadTNTFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB
void cWSSAnvil::LoadExpOrbFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cExpOrb> ExpOrb(new cExpOrb(0.0, 0.0, 0.0, 0));
+ std::unique_ptr<cExpOrb> ExpOrb(new cExpOrb(0.0, 0.0, 0.0, 0));
if (!LoadEntityBaseFromNBT(*ExpOrb.get(), a_NBT, a_TagIdx))
{
return;
@@ -1747,7 +1800,7 @@ void cWSSAnvil::LoadItemFrameFromNBT(cEntityList & a_Entities, const cParsedNBT
return;
}
- std::auto_ptr<cItemFrame> ItemFrame(new cItemFrame(BLOCK_FACE_NONE, 0.0, 0.0, 0.0));
+ std::unique_ptr<cItemFrame> ItemFrame(new cItemFrame(BLOCK_FACE_NONE, 0.0, 0.0, 0.0));
if (!LoadEntityBaseFromNBT(*ItemFrame.get(), a_NBT, a_TagIdx))
{
return;
@@ -1772,7 +1825,7 @@ void cWSSAnvil::LoadItemFrameFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cArrowEntity> Arrow(new cArrowEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
+ std::unique_ptr<cArrowEntity> Arrow(new cArrowEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadProjectileBaseFromNBT(*Arrow.get(), a_NBT, a_TagIdx))
{
return;
@@ -1807,9 +1860,10 @@ void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
int InBlockZIdx = a_NBT.FindChildByName(a_TagIdx, "zTile");
if ((InBlockXIdx > 0) && (InBlockYIdx > 0) && (InBlockZIdx > 0))
{
- if (a_NBT.GetType(InBlockXIdx) == a_NBT.GetType(InBlockYIdx) == a_NBT.GetType(InBlockZIdx))
+ eTagType typeX = a_NBT.GetType(InBlockXIdx);
+ if ((typeX == a_NBT.GetType(InBlockYIdx)) && (typeX == a_NBT.GetType(InBlockZIdx)))
{
- switch (a_NBT.GetType(InBlockXIdx))
+ switch (typeX)
{
case TAG_Int:
{
@@ -1823,6 +1877,11 @@ void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
Arrow->SetBlockHit(Vector3i((int)a_NBT.GetShort(InBlockXIdx), (int)a_NBT.GetShort(InBlockYIdx), (int)a_NBT.GetShort(InBlockZIdx)));
break;
}
+ default:
+ {
+ // No hit block, the arrow is still flying?
+ break;
+ }
}
}
}
@@ -1836,7 +1895,7 @@ void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadSplashPotionFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cSplashPotionEntity> SplashPotion(new cSplashPotionEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0), cItem()));
+ std::unique_ptr<cSplashPotionEntity> SplashPotion(new cSplashPotionEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0), cItem()));
if (!LoadProjectileBaseFromNBT(*SplashPotion.get(), a_NBT, a_TagIdx))
{
return;
@@ -1860,7 +1919,7 @@ void cWSSAnvil::LoadSplashPotionFromNBT(cEntityList & a_Entities, const cParsedN
void cWSSAnvil::LoadSnowballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cThrownSnowballEntity> Snowball(new cThrownSnowballEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
+ std::unique_ptr<cThrownSnowballEntity> Snowball(new cThrownSnowballEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadProjectileBaseFromNBT(*Snowball.get(), a_NBT, a_TagIdx))
{
return;
@@ -1876,7 +1935,7 @@ void cWSSAnvil::LoadSnowballFromNBT(cEntityList & a_Entities, const cParsedNBT &
void cWSSAnvil::LoadEggFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cThrownEggEntity> Egg(new cThrownEggEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
+ std::unique_ptr<cThrownEggEntity> Egg(new cThrownEggEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadProjectileBaseFromNBT(*Egg.get(), a_NBT, a_TagIdx))
{
return;
@@ -1892,7 +1951,7 @@ void cWSSAnvil::LoadEggFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB
void cWSSAnvil::LoadFireballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cGhastFireballEntity> Fireball(new cGhastFireballEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
+ std::unique_ptr<cGhastFireballEntity> Fireball(new cGhastFireballEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadProjectileBaseFromNBT(*Fireball.get(), a_NBT, a_TagIdx))
{
return;
@@ -1908,7 +1967,7 @@ void cWSSAnvil::LoadFireballFromNBT(cEntityList & a_Entities, const cParsedNBT &
void cWSSAnvil::LoadFireChargeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cFireChargeEntity> FireCharge(new cFireChargeEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
+ std::unique_ptr<cFireChargeEntity> FireCharge(new cFireChargeEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadProjectileBaseFromNBT(*FireCharge.get(), a_NBT, a_TagIdx))
{
return;
@@ -1924,7 +1983,7 @@ void cWSSAnvil::LoadFireChargeFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cThrownEnderPearlEntity> Enderpearl(new cThrownEnderPearlEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
+ std::unique_ptr<cThrownEnderPearlEntity> Enderpearl(new cThrownEnderPearlEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadProjectileBaseFromNBT(*Enderpearl.get(), a_NBT, a_TagIdx))
{
return;
@@ -1940,7 +1999,7 @@ void cWSSAnvil::LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cPar
void cWSSAnvil::LoadBatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cBat> Monster(new cBat());
+ std::unique_ptr<cBat> Monster(new cBat());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -1960,7 +2019,7 @@ void cWSSAnvil::LoadBatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB
void cWSSAnvil::LoadBlazeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cBlaze> Monster(new cBlaze());
+ std::unique_ptr<cBlaze> Monster(new cBlaze());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -1980,7 +2039,7 @@ void cWSSAnvil::LoadBlazeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadCaveSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cCaveSpider> Monster(new cCaveSpider());
+ std::unique_ptr<cCaveSpider> Monster(new cCaveSpider());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2000,7 +2059,7 @@ void cWSSAnvil::LoadCaveSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadChickenFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cChicken> Monster(new cChicken());
+ std::unique_ptr<cChicken> Monster(new cChicken());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2020,7 +2079,7 @@ void cWSSAnvil::LoadChickenFromNBT(cEntityList & a_Entities, const cParsedNBT &
void cWSSAnvil::LoadCowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cCow> Monster(new cCow());
+ std::unique_ptr<cCow> Monster(new cCow());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2040,7 +2099,7 @@ void cWSSAnvil::LoadCowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB
void cWSSAnvil::LoadCreeperFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cCreeper> Monster(new cCreeper());
+ std::unique_ptr<cCreeper> Monster(new cCreeper());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2060,7 +2119,7 @@ void cWSSAnvil::LoadCreeperFromNBT(cEntityList & a_Entities, const cParsedNBT &
void cWSSAnvil::LoadEnderDragonFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cEnderDragon> Monster(new cEnderDragon());
+ std::unique_ptr<cEnderDragon> Monster(new cEnderDragon());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2080,7 +2139,7 @@ void cWSSAnvil::LoadEnderDragonFromNBT(cEntityList & a_Entities, const cParsedNB
void cWSSAnvil::LoadEndermanFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cEnderman> Monster(new cEnderman());
+ std::unique_ptr<cEnderman> Monster(new cEnderman());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2100,7 +2159,7 @@ void cWSSAnvil::LoadEndermanFromNBT(cEntityList & a_Entities, const cParsedNBT &
void cWSSAnvil::LoadGhastFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cGhast> Monster(new cGhast());
+ std::unique_ptr<cGhast> Monster(new cGhast());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2120,7 +2179,7 @@ void cWSSAnvil::LoadGhastFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadGiantFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cGiant> Monster(new cGiant());
+ std::unique_ptr<cGiant> Monster(new cGiant());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2140,17 +2199,19 @@ void cWSSAnvil::LoadGiantFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadHorseFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "Type");
+ int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "Type");
int ColorIdx = a_NBT.FindChildByName(a_TagIdx, "Color");
int StyleIdx = a_NBT.FindChildByName(a_TagIdx, "Style");
-
- if ((TypeIdx < 0) || (ColorIdx < 0) || (StyleIdx < 0)) { return; }
+ if ((TypeIdx < 0) || (ColorIdx < 0) || (StyleIdx < 0))
+ {
+ return;
+ }
int Type = a_NBT.GetInt(TypeIdx);
int Color = a_NBT.GetInt(ColorIdx);
int Style = a_NBT.GetInt(StyleIdx);
- std::auto_ptr<cHorse> Monster(new cHorse(Type, Color, Style, 1));
+ std::unique_ptr<cHorse> Monster(new cHorse(Type, Color, Style, 1));
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
@@ -2171,7 +2232,7 @@ void cWSSAnvil::LoadHorseFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadIronGolemFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cIronGolem> Monster(new cIronGolem());
+ std::unique_ptr<cIronGolem> Monster(new cIronGolem());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2200,7 +2261,7 @@ void cWSSAnvil::LoadMagmaCubeFromNBT(cEntityList & a_Entities, const cParsedNBT
int Size = a_NBT.GetInt(SizeIdx);
- std::auto_ptr<cMagmaCube> Monster(new cMagmaCube(Size));
+ std::unique_ptr<cMagmaCube> Monster(new cMagmaCube(Size));
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2220,7 +2281,7 @@ void cWSSAnvil::LoadMagmaCubeFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadMooshroomFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cMooshroom> Monster(new cMooshroom());
+ std::unique_ptr<cMooshroom> Monster(new cMooshroom());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2240,7 +2301,7 @@ void cWSSAnvil::LoadMooshroomFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadOcelotFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cOcelot> Monster(new cOcelot());
+ std::unique_ptr<cOcelot> Monster(new cOcelot());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2260,7 +2321,7 @@ void cWSSAnvil::LoadOcelotFromNBT(cEntityList & a_Entities, const cParsedNBT & a
void cWSSAnvil::LoadPigFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cPig> Monster(new cPig());
+ std::unique_ptr<cPig> Monster(new cPig());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2287,7 +2348,7 @@ void cWSSAnvil::LoadSheepFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
Color = (int)a_NBT.GetByte(ColorIdx);
}
- std::auto_ptr<cSheep> Monster(new cSheep(Color));
+ std::unique_ptr<cSheep> Monster(new cSheep(Color));
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2313,7 +2374,7 @@ void cWSSAnvil::LoadSheepFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadSilverfishFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cSilverfish> Monster(new cSilverfish());
+ std::unique_ptr<cSilverfish> Monster(new cSilverfish());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2334,12 +2395,14 @@ void cWSSAnvil::LoadSilverfishFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadSkeletonFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "SkeletonType");
-
- if (TypeIdx < 0) { return; }
+ if (TypeIdx < 0)
+ {
+ return;
+ }
bool Type = ((a_NBT.GetByte(TypeIdx) == 1) ? true : false);
- std::auto_ptr<cSkeleton> Monster(new cSkeleton(Type));
+ std::unique_ptr<cSkeleton> Monster(new cSkeleton(Type));
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2368,7 +2431,7 @@ void cWSSAnvil::LoadSlimeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
int Size = a_NBT.GetInt(SizeIdx);
- std::auto_ptr<cSlime> Monster(new cSlime(Size));
+ std::unique_ptr<cSlime> Monster(new cSlime(Size));
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2388,7 +2451,7 @@ void cWSSAnvil::LoadSlimeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadSnowGolemFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cSnowGolem> Monster(new cSnowGolem());
+ std::unique_ptr<cSnowGolem> Monster(new cSnowGolem());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2408,7 +2471,7 @@ void cWSSAnvil::LoadSnowGolemFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cSpider> Monster(new cSpider());
+ std::unique_ptr<cSpider> Monster(new cSpider());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2428,7 +2491,7 @@ void cWSSAnvil::LoadSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT & a
void cWSSAnvil::LoadSquidFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cSquid> Monster(new cSquid());
+ std::unique_ptr<cSquid> Monster(new cSquid());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2449,12 +2512,14 @@ void cWSSAnvil::LoadSquidFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadVillagerFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "Profession");
-
- if (TypeIdx < 0) { return; }
+ if (TypeIdx < 0)
+ {
+ return;
+ }
int Type = a_NBT.GetInt(TypeIdx);
- std::auto_ptr<cVillager> Monster(new cVillager(cVillager::eVillagerType(Type)));
+ std::unique_ptr<cVillager> Monster(new cVillager(cVillager::eVillagerType(Type)));
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2474,7 +2539,7 @@ void cWSSAnvil::LoadVillagerFromNBT(cEntityList & a_Entities, const cParsedNBT &
void cWSSAnvil::LoadWitchFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cWitch> Monster(new cWitch());
+ std::unique_ptr<cWitch> Monster(new cWitch());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2494,7 +2559,7 @@ void cWSSAnvil::LoadWitchFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadWitherFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cWither> Monster(new cWither());
+ std::unique_ptr<cWither> Monster(new cWither());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2520,7 +2585,7 @@ void cWSSAnvil::LoadWitherFromNBT(cEntityList & a_Entities, const cParsedNBT & a
void cWSSAnvil::LoadWolfFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cWolf> Monster(new cWolf());
+ std::unique_ptr<cWolf> Monster(new cWolf());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2574,12 +2639,14 @@ void cWSSAnvil::LoadWolfFromNBT(cEntityList & a_Entities, const cParsedNBT & a_N
void cWSSAnvil::LoadZombieFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
int IsVillagerIdx = a_NBT.FindChildByName(a_TagIdx, "IsVillager");
-
- if (IsVillagerIdx < 0) { return; }
+ if (IsVillagerIdx < 0)
+ {
+ return;
+ }
bool IsVillagerZombie = ((a_NBT.GetByte(IsVillagerIdx) == 1) ? true : false);
- std::auto_ptr<cZombie> Monster(new cZombie(IsVillagerZombie));
+ std::unique_ptr<cZombie> Monster(new cZombie(IsVillagerZombie));
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@@ -2599,7 +2666,7 @@ void cWSSAnvil::LoadZombieFromNBT(cEntityList & a_Entities, const cParsedNBT & a
void cWSSAnvil::LoadPigZombieFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cZombiePigman> Monster(new cZombiePigman());
+ std::unique_ptr<cZombiePigman> Monster(new cZombiePigman());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h
index 9c579a617..7a98a9a04 100644
--- a/src/WorldStorage/WSSAnvil.h
+++ b/src/WorldStorage/WSSAnvil.h
@@ -148,6 +148,7 @@ protected:
cBlockEntity * LoadDropperFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * LoadFlowerPotFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * LoadFurnaceFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+ cBlockEntity * LoadMobSpawnerFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * LoadHopperFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * LoadJukeboxFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * LoadMobHeadFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
diff --git a/src/XMLParser.h b/src/XMLParser.h
index 5b53e55cf..ea341bce6 100644
--- a/src/XMLParser.h
+++ b/src/XMLParser.h
@@ -105,11 +105,11 @@ public:
Destroy ();
// If the encoding or seperator are empty, then nullptr
- if (pszEncoding != nullptr && pszEncoding [0] == 0)
+ if ((pszEncoding != nullptr) && (pszEncoding[0] == 0))
{
pszEncoding = nullptr;
}
- if (pszSep != nullptr && pszSep [0] == 0)
+ if ((pszSep != nullptr) && (pszSep[0] == 0))
{
pszSep = nullptr;
}
@@ -147,7 +147,7 @@ public:
bool Parse (const char *pszBuffer, int nLength, bool fIsFinal = true)
{
assert (m_p != nullptr);
- return XML_Parse (m_p, pszBuffer, nLength, fIsFinal) != 0;
+ return (XML_Parse (m_p, pszBuffer, nLength, fIsFinal) != 0);
}
// @cmember Parse internal buffer
@@ -254,7 +254,9 @@ protected:
XML_SetDefaultHandlerExpand (m_p, fEnable ? DefaultHandler : nullptr);
}
else
+ {
XML_SetDefaultHandler (m_p, fEnable ? DefaultHandler : nullptr);
+ }
}
// @cmember Enable/Disable external entity ref handler
@@ -402,11 +404,17 @@ public:
{
XML_expat_version v = XML_ExpatVersionInfo ();
if (pnMajor)
+ {
*pnMajor = v .major;
+ }
if (pnMinor)
+ {
*pnMinor = v .minor;
+ }
if (pnMicro)
+ {
*pnMicro = v .micro;
+ }
}
// @cmember Get last error string
diff --git a/src/main.cpp b/src/main.cpp
index 463c54c28..d4adc1ed9 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -61,6 +61,7 @@ void NonCtrlHandler(int a_Signal)
std::signal(SIGSEGV, SIG_DFL);
LOGERROR(" D: | MCServer has encountered an error and needs to close");
LOGERROR("Details | SIGSEGV: Segmentation fault");
+ PrintStackTrace();
abort();
}
case SIGABRT:
@@ -71,6 +72,7 @@ void NonCtrlHandler(int a_Signal)
std::signal(a_Signal, SIG_DFL);
LOGERROR(" D: | MCServer has encountered an error and needs to close");
LOGERROR("Details | SIGABRT: Server self-terminated due to an internal fault");
+ PrintStackTrace();
abort();
}
case SIGINT:
@@ -137,6 +139,9 @@ LONG WINAPI LastChanceExceptionFilter(__in struct _EXCEPTION_POINTERS * a_Except
g_WriteMiniDump(GetCurrentProcess(), GetCurrentProcessId(), dumpFile, g_DumpFlags, (a_ExceptionInfo) ? &ExcInformation : nullptr, nullptr, nullptr);
CloseHandle(dumpFile);
+ // Print the stack trace for the basic debugging:
+ PrintStackTrace();
+
// Revert to old stack:
_asm
{
@@ -158,9 +163,9 @@ BOOL CtrlHandler(DWORD fdwCtrlType)
cRoot::m_TerminateEventRaised = true;
LOGD("Terminate event raised from the Windows CtrlHandler");
- if (fdwCtrlType == CTRL_CLOSE_EVENT) // Console window closed via 'x' button, Windows will try to close immediately, therefore...
+ while (!g_ServerTerminated)
{
- while (!g_ServerTerminated) { cSleep::MilliSleep(100); } // Delay as much as possible to try to get the server to shut down cleanly
+ std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Delay as much as possible to try to get the server to shut down cleanly
}
return TRUE;
@@ -182,7 +187,7 @@ int main( int argc, char **argv)
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
InitLeakFinder();
#endif
-
+
// Magic code to produce dump-files on Windows if the server crashes:
#if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER)
HINSTANCE hDbgHelp = LoadLibrary("DBGHELP.DLL");