1
0

Merge remote-tracking branch 'origin/master' into c++11Events

This commit is contained in:
Mattes D 2014-12-07 18:15:23 +01:00
commit 8ad1afcc1b
178 changed files with 5922 additions and 3977 deletions

View File

@ -6,6 +6,15 @@ compiler:
before_install: before_install:
- if [ "$TRAVIS_MCSERVER_BUILD_TYPE" == "COVERAGE" ]; then sudo pip install cpp_coveralls; fi - if [ "$TRAVIS_MCSERVER_BUILD_TYPE" == "COVERAGE" ]; then sudo pip install cpp_coveralls; fi
# g++4.8
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
- sudo apt-get update -qq
install:
# g++4.8
- sudo apt-get install -qq g++-4.8
- export CXX="g++-4.8"
# Build MCServer # Build MCServer
script: ./CIbuild.sh script: ./CIbuild.sh

View File

@ -10,6 +10,7 @@ Howaner
keyboard keyboard
Lapayo Lapayo
Luksor Luksor
M10360
marmot21 marmot21
Masy98 Masy98
mborland mborland
@ -17,6 +18,7 @@ mgueydan
MikeHunsinger MikeHunsinger
mtilden mtilden
nesco nesco
p-mcgowan
rs2k rs2k
SamJBarney SamJBarney
Sofapriester Sofapriester

View File

@ -534,6 +534,7 @@ end
GetUUID = { Params = "", Return = "string", Notes = "Returns the authentication-based UUID of the client. This UUID should be used to identify the player when persisting any player-related data. Returns a 32-char UUID (no dashes)" }, GetUUID = { Params = "", Return = "string", Notes = "Returns the authentication-based UUID of the client. This UUID should be used to identify the player when persisting any player-related data. Returns a 32-char UUID (no dashes)" },
GetUsername = { Params = "", Return = "string", Notes = "Returns the username that the client has provided" }, GetUsername = { Params = "", Return = "string", Notes = "Returns the username that the client has provided" },
GetViewDistance = { Params = "", Return = "number", Notes = "Returns the viewdistance (number of chunks loaded for the player in each direction)" }, GetViewDistance = { Params = "", Return = "number", Notes = "Returns the viewdistance (number of chunks loaded for the player in each direction)" },
GetRequestedViewDistance = { Params = "", Return = "number", Notes = "Returns the view distance that the player request, not the used view distance." },
HasPluginChannel = { Params = "ChannelName", Return = "bool", Notes = "Returns true if the client has registered to receive messages on the specified plugin channel." }, HasPluginChannel = { Params = "ChannelName", Return = "bool", Notes = "Returns true if the client has registered to receive messages on the specified plugin channel." },
IsUUIDOnline = { Params = "UUID", Return = "bool", Notes = "(STATIC) Returns true if the UUID is generated by online auth, false if it is an offline-generated UUID. We use Version-3 UUIDs for offline UUIDs, online UUIDs are Version-4, thus we can tell them apart. Accepts both 32-char and 36-char UUIDs (with and without dashes). If the string given is not a valid UUID, returns false."}, IsUUIDOnline = { Params = "UUID", Return = "bool", Notes = "(STATIC) Returns true if the UUID is generated by online auth, false if it is an offline-generated UUID. We use Version-3 UUIDs for offline UUIDs, online UUIDs are Version-4, thus we can tell them apart. Accepts both 32-char and 36-char UUIDs (with and without dashes). If the string given is not a valid UUID, returns false."},
Kick = { Params = "Reason", Return = "", Notes = "Kicks the user with the specified reason" }, Kick = { Params = "Reason", Return = "", Notes = "Kicks the user with the specified reason" },
@ -614,7 +615,7 @@ function OnPlayerJoined(a_Player)
-- Send an example composite chat message to the player: -- Send an example composite chat message to the player:
a_Player:SendMessage(cCompositeChat() a_Player:SendMessage(cCompositeChat()
:AddTextPart("Hello, ") :AddTextPart("Hello, ")
:AddUrlPart(a_Player:GetName(), "www.mc-server.org", "u@2") -- Colored underlined link :AddUrlPart(a_Player:GetName(), "http://www.mc-server.org", "u@2") -- Colored underlined link
:AddSuggestCommandPart(", and welcome.", "/help", "u") -- Underlined suggest-command :AddSuggestCommandPart(", and welcome.", "/help", "u") -- Underlined suggest-command
:AddRunCommandPart(" SetDay", "/time set 0") -- Regular text that will execute command when clicked :AddRunCommandPart(" SetDay", "/time set 0") -- Regular text that will execute command when clicked
:SetMessageType(mtJoin) -- It is a join-message :SetMessageType(mtJoin) -- It is a join-message
@ -1667,13 +1668,14 @@ a_Player:OpenWindow(Window);
SetCustomName = { Params = "string", Return = "", Notes = "Sets the custom name of the monster. You see the name over the monster. If you want to disable the custom name, simply set an empty string." }, SetCustomName = { Params = "string", Return = "", Notes = "Sets the custom name of the monster. You see the name over the monster. If you want to disable the custom name, simply set an empty string." },
IsCustomNameAlwaysVisible = { Params = "", Return = "bool", Notes = "Is the custom name of this monster always visible? If not, you only see the name when you sight the mob." }, IsCustomNameAlwaysVisible = { Params = "", Return = "bool", Notes = "Is the custom name of this monster always visible? If not, you only see the name when you sight the mob." },
SetCustomNameAlwaysVisible = { Params = "bool", Return = "", Notes = "Sets the custom name visiblity of this monster. If it's false, you only see the name when you sight the mob. If it's true, you always see the custom name." }, SetCustomNameAlwaysVisible = { Params = "bool", Return = "", Notes = "Sets the custom name visiblity of this monster. If it's false, you only see the name when you sight the mob. If it's true, you always see the custom name." },
FamilyFromType = { Params = "{{cMonster#MobType|MobType}}", Return = "{{cMonster#MobFamily|MobFamily}}", Notes = "(STATIC) Returns the mob family ({{cMonster#MobFamily|mfXXX}} constants) based on the mob type ({{cMonster#MobType|mtXXX}} constants)" }, FamilyFromType = { Params = "{{Globals#MobType|MobType}}", Return = "{{cMonster#MobFamily|MobFamily}}", Notes = "(STATIC) Returns the mob family ({{cMonster#MobFamily|mfXXX}} constants) based on the mob type ({{Globals#MobType|mtXXX}} constants)" },
GetMobFamily = { Params = "", Return = "{{cMonster#MobFamily|MobFamily}}", Notes = "Returns this mob's family ({{cMonster#MobFamily|mfXXX}} constant)" }, GetMobFamily = { Params = "", Return = "{{cMonster#MobFamily|MobFamily}}", Notes = "Returns this mob's family ({{cMonster#MobFamily|mfXXX}} constant)" },
GetMobType = { Params = "", Return = "{{cMonster#MobType|MobType}}", Notes = "Returns the type of this mob ({{cMonster#MobType|mtXXX}} constant)" }, GetMobType = { Params = "", Return = "{{Globals#MobType|MobType}}", Notes = "Returns the type of this mob ({{Globals#MobType|mtXXX}} constant)" },
GetSpawnDelay = { Params = "{{cMonster#MobFamily|MobFamily}}", Return = "number", Notes = "(STATIC) Returns the spawn delay - the number of game ticks between spawn attempts - for the specified mob family." }, GetSpawnDelay = { Params = "{{cMonster#MobFamily|MobFamily}}", Return = "number", Notes = "(STATIC) Returns the spawn delay - the number of game ticks between spawn attempts - for the specified mob family." },
MobTypeToString = { Params = "{{cMonster#MobType|MobType}}", Return = "string", Notes = "(STATIC) Returns the string representing the given mob type ({{cMonster#MobType|mtXXX}} constant), or empty string if unknown type." }, MobTypeToString = { Params = "{{Globals#MobType|MobType}}", Return = "string", Notes = "(STATIC) Returns the string representing the given mob type ({{Globals#MobType|mtXXX}} constant), or empty string if unknown type." },
MobTypeToVanillaName = { Params = "{{Globals#MobType|MobType}}", Return = "string", Notes = "(STATIC) Returns the vanilla name of the given mob type, or empty string if unknown type." },
MoveToPosition = { Params = "Position", Return = "", Notes = "Moves mob to the specified position" }, MoveToPosition = { Params = "Position", Return = "", Notes = "Moves mob to the specified position" },
StringToMobType = { Params = "string", Return = "{{cMonster#MobType|MobType}}", Notes = "(STATIC) Returns the mob type ({{cMonster#MobType|mtXXX}} constant) parsed from the string type (\"creeper\"), or mtInvalidType if unrecognized." }, StringToMobType = { Params = "string", Return = "{{Globals#MobType|MobType}}", Notes = "(STATIC) Returns the mob type ({{Globals#MobType|mtXXX}} constant) parsed from the string type (\"creeper\"), or mtInvalidType if unrecognized." },
GetRelativeWalkSpeed = { Params = "", Return = "number", Notes = "Returns the relative walk speed of this mob. Standard is 1.0" }, GetRelativeWalkSpeed = { Params = "", Return = "number", Notes = "Returns the relative walk speed of this mob. Standard is 1.0" },
SetRelativeWalkSpeed = { Params = "number", Return = "", Notes = "Sets the relative walk speed of this mob. Standard is 1.0" }, SetRelativeWalkSpeed = { Params = "number", Return = "", Notes = "Sets the relative walk speed of this mob. Standard is 1.0" },
}, },
@ -1724,13 +1726,6 @@ a_Player:OpenWindow(Window);
Mobs are divided into families. The following constants are used for individual family types: Mobs are divided into families. The following constants are used for individual family types:
]], ]],
}, },
MobType =
{
Include = "mt.*",
TextBefore = [[
The following constants are used for distinguishing between the individual mob types:
]],
},
}, },
Inherits = "cPawn", Inherits = "cPawn",
}, -- cMonster }, -- cMonster
@ -2137,6 +2132,7 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
BroadcastChatWarning = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtWarning. Use for concerning events, such as plugin reload etc." }, BroadcastChatWarning = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtWarning. Use for concerning events, such as plugin reload etc." },
CreateAndInitializeWorld = { Params = "WorldName", Return = "{{cWorld|cWorld}}", Notes = "Creates a new world and initializes it. If there is a world whith the same name it returns nil.<br/><br/><b>NOTE</b>This function is currently unsafe, do not use!" }, CreateAndInitializeWorld = { Params = "WorldName", Return = "{{cWorld|cWorld}}", Notes = "Creates a new world and initializes it. If there is a world whith the same name it returns nil.<br/><br/><b>NOTE</b>This function is currently unsafe, do not use!" },
FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "", Notes = "Calls the given callback function for all players with names partially (or fully) matching the name string provided." }, FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "", Notes = "Calls the given callback function for all players with names partially (or fully) matching the name string provided." },
DoWithPlayerByUUID = { Params = "PlayerUUID, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is the player with the uuid, calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found." },
ForEachPlayer = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each player. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|cPlayer}})</pre>" }, ForEachPlayer = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each player. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|cPlayer}})</pre>" },
ForEachWorld = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each world. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cWorld|cWorld}})</pre>" }, ForEachWorld = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each world. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cWorld|cWorld}})</pre>" },
Get = { Params = "", Return = "Root object", Notes = "(STATIC)This function returns the cRoot object." }, Get = { Params = "", Return = "Root object", Notes = "(STATIC)This function returns the cRoot object." },
@ -2406,6 +2402,7 @@ end
{ Params = "{{Vector3i|BlockCoords}}, BlockType, BlockMeta", Return = "", Notes = "Sets the block at the specified coords, without waking up the simulators or replacing the block entities for the previous block type. Do not use if the block being replaced has a block entity tied to it!" }, { Params = "{{Vector3i|BlockCoords}}, BlockType, BlockMeta", Return = "", Notes = "Sets the block at the specified coords, without waking up the simulators or replacing the block entities for the previous block type. Do not use if the block being replaced has a block entity tied to it!" },
}, },
FindAndDoWithPlayer = { Params = "PlayerNameHint, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a player of a name similar to the specified name (weighted-match), calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found. Note that the name matching is very loose, so it is a good idea to check the player name in the callback function." }, FindAndDoWithPlayer = { Params = "PlayerNameHint, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a player of a name similar to the specified name (weighted-match), calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found. Note that the name matching is very loose, so it is a good idea to check the player name in the callback function." },
DoWithPlayerByUUID = { Params = "PlayerUUID, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is the player with the uuid, calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found." },
ForEachBlockEntityInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each block entity in the chunk. Returns true if all block entities in the chunk have been processed (including when there are zero block entities), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next block entity, or true to abort the enumeration. Use {{tolua}}.cast() to cast the Callback's BlockEntity parameter to the correct {{cBlockEntity}} descendant." }, ForEachBlockEntityInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each block entity in the chunk. Returns true if all block entities in the chunk have been processed (including when there are zero block entities), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next block entity, or true to abort the enumeration. Use {{tolua}}.cast() to cast the Callback's BlockEntity parameter to the correct {{cBlockEntity}} descendant." },
ForEachChestInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each chest in the chunk. Returns true if all chests in the chunk have been processed (including when there are zero chests), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cChestEntity|ChestEntity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next chest, or true to abort the enumeration." }, ForEachChestInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each chest in the chunk. Returns true if all chests in the chunk have been processed (including when there are zero chests), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cChestEntity|ChestEntity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next chest, or true to abort the enumeration." },
ForEachEntity = { Params = "CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each entity in the loaded world. Returns true if all the entities have been processed (including when there are zero entities), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next entity, or true to abort the enumeration." }, ForEachEntity = { Params = "CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each entity in the loaded world. Returns true if all the entities have been processed (including when there are zero entities), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next entity, or true to abort the enumeration." },
@ -2497,7 +2494,7 @@ end
SetCommandBlocksEnabled = { Params = "IsEnabled (bool)", Return = "", Notes = "Sets whether command blocks should be enabled on the (entire) server." }, SetCommandBlocksEnabled = { Params = "IsEnabled (bool)", Return = "", Notes = "Sets whether command blocks should be enabled on the (entire) server." },
SetShouldUseChatPrefixes = { Params = "", Return = "ShouldUse (bool)", Notes = "Sets whether coloured chat prefixes such as [INFO] is used with the SendMessageXXX() or BroadcastChatXXX(), or simply the entire message is coloured in the respective colour." }, SetShouldUseChatPrefixes = { Params = "", Return = "ShouldUse (bool)", Notes = "Sets whether coloured chat prefixes such as [INFO] is used with the SendMessageXXX() or BroadcastChatXXX(), or simply the entire message is coloured in the respective colour." },
ShouldUseChatPrefixes = { Params = "", Return = "bool", Notes = "Returns whether coloured chat prefixes are prepended to chat messages or the entire message is simply coloured." }, ShouldUseChatPrefixes = { Params = "", Return = "bool", Notes = "Returns whether coloured chat prefixes are prepended to chat messages or the entire message is simply coloured." },
SetSignLines = { Params = "X, Y, Z, Line1, Line2, Line3, Line4, [{{cPlayer|Player}}]", Return = "", Notes = "Sets the sign text at the specified coords. The sign-updating hooks are called for the change. The Player parameter is used to indicate the player from whom the change has come, it may be nil. Same as UpdateSign()." }, SetSignLines = { Params = "X, Y, Z, Line1, Line2, Line3, Line4, [{{cPlayer|Player}}]", Return = "", Notes = "Sets the sign text at the specified coords. The sign-updating hooks are called for the change. The Player parameter is used to indicate the player from whom the change has come, it may be nil." },
SetTicksUntilWeatherChange = { Params = "NumTicks", Return = "", Notes = "Sets the number of ticks after which the weather will be changed." }, SetTicksUntilWeatherChange = { Params = "NumTicks", Return = "", Notes = "Sets the number of ticks after which the weather will be changed." },
SetTimeOfDay = { Params = "TimeOfDayTicks", Return = "", Notes = "Sets the time of day, expressed as number of ticks past sunrise, in the range 0 .. 24000." }, SetTimeOfDay = { Params = "TimeOfDayTicks", Return = "", Notes = "Sets the time of day, expressed as number of ticks past sunrise, in the range 0 .. 24000." },
SetWeather = { Params = "Weather", Return = "", Notes = "Sets the current weather (wSunny, wRain, wStorm) and resets the TicksUntilWeatherChange to the default value for the new weather. The normal weather-changing hooks are called for the change." }, SetWeather = { Params = "Weather", Return = "", Notes = "Sets the current weather (wSunny, wRain, wStorm) and resets the TicksUntilWeatherChange to the default value for the new weather. The normal weather-changing hooks are called for the change." },
@ -2512,7 +2509,7 @@ end
SpawnExperienceOrb = { Params = "X, Y, Z, Reward", Return = "EntityID", Notes = "Spawns an {{cExpOrb|experience orb}} at the specified coords, with the given reward" }, SpawnExperienceOrb = { Params = "X, Y, Z, Reward", Return = "EntityID", Notes = "Spawns an {{cExpOrb|experience orb}} at the specified coords, with the given reward" },
SpawnPrimedTNT = { Params = "X, Y, Z, FuseTicks, InitialVelocityCoeff", Return = "", Notes = "Spawns a {{cTNTEntity|primed TNT entity}} at the specified coords, with the given fuse ticks. The entity gets a random speed multiplied by the InitialVelocityCoeff, 1 being the default value." }, SpawnPrimedTNT = { Params = "X, Y, Z, FuseTicks, InitialVelocityCoeff", Return = "", Notes = "Spawns a {{cTNTEntity|primed TNT entity}} at the specified coords, with the given fuse ticks. The entity gets a random speed multiplied by the InitialVelocityCoeff, 1 being the default value." },
TryGetHeight = { Params = "BlockX, BlockZ", Return = "IsValid, Height", Notes = "Returns true and height of the highest non-air block if the chunk is loaded, or false otherwise." }, TryGetHeight = { Params = "BlockX, BlockZ", Return = "IsValid, Height", Notes = "Returns true and height of the highest non-air block if the chunk is loaded, or false otherwise." },
UpdateSign = { Params = "X, Y, Z, Line1, Line2, Line3, Line4, [{{cPlayer|Player}}]", Return = "", Notes = "Sets the sign text at the specified coords. The sign-updating hooks are called for the change. The Player parameter is used to indicate the player from whom the change has come, it may be nil. Same as SetSignLiens()" }, UpdateSign = { Params = "X, Y, Z, Line1, Line2, Line3, Line4, [{{cPlayer|Player}}]", Return = "", Notes = "(<b>DEPRECATED</b>) Please use SetSignLines()." },
UseBlockEntity = { Params = "{{cPlayer|Player}}, BlockX, BlockY, BlockZ", Return = "", Notes = "Makes the specified Player use the block entity at the specified coords (open chest UI, etc.) If the cords are in an unloaded chunk or there's no block entity, ignores the call." }, UseBlockEntity = { Params = "{{cPlayer|Player}}, BlockX, BlockY, BlockZ", Return = "", Notes = "Makes the specified Player use the block entity at the specified coords (open chest UI, etc.) If the cords are in an unloaded chunk or there's no block entity, ignores the call." },
WakeUpSimulators = { Params = "BlockX, BlockY, BlockZ", Return = "", Notes = "Wakes up the simulators for the specified block." }, WakeUpSimulators = { Params = "BlockX, BlockY, BlockZ", Return = "", Notes = "Wakes up the simulators for the specified block." },
WakeUpSimulatorsInArea = { Params = "MinBlockX, MaxBlockX, MinBlockY, MaxBlockY, MinBlockZ, MaxBlockZ", Return = "", Notes = "Wakes up the simulators for all the blocks in the specified area (edges inclusive)." }, WakeUpSimulatorsInArea = { Params = "MinBlockX, MaxBlockX, MinBlockY, MaxBlockY, MinBlockZ, MaxBlockZ", Return = "", Notes = "Wakes up the simulators for all the blocks in the specified area (edges inclusive)." },
@ -2568,7 +2565,7 @@ World:ForEachEntity(
return; return;
end end
local Monster = tolua.cast(a_Entity, "cMonster"); -- Get the cMonster out of cEntity, now that we know the entity represents one. local Monster = tolua.cast(a_Entity, "cMonster"); -- Get the cMonster out of cEntity, now that we know the entity represents one.
if (Monster:GetMobType() == cMonster.mtSpider) then if (Monster:GetMobType() == mtSpider) then
Monster:TeleportToCoords(Monster:GetPosX(), Monster:GetPosY() + 100, Monster:GetPosZ()); Monster:TeleportToCoords(Monster:GetPosX(), Monster:GetPosY() + 100, Monster:GetPosZ());
end end
end end
@ -2925,7 +2922,7 @@ end
StringToDamageType = {Params = "string", Return = "{{Globals#DamageType|DamageType}}", Notes = "Converts a string representation to a {{Globals#DamageType|DamageType}} enumerated value."}, StringToDamageType = {Params = "string", Return = "{{Globals#DamageType|DamageType}}", Notes = "Converts a string representation to a {{Globals#DamageType|DamageType}} enumerated value."},
StringToDimension = {Params = "string", Return = "{{Globals#WorldDimension|Dimension}}", Notes = "Converts a string representation to a {{Globals#WorldDimension|Dimension}} enumerated value"}, StringToDimension = {Params = "string", Return = "{{Globals#WorldDimension|Dimension}}", Notes = "Converts a string representation to a {{Globals#WorldDimension|Dimension}} enumerated value"},
StringToItem = {Params = "string, {{cItem|cItem}}", Return = "bool", Notes = "Parses the given string and sets the item; returns true if successful"}, StringToItem = {Params = "string, {{cItem|cItem}}", Return = "bool", Notes = "Parses the given string and sets the item; returns true if successful"},
StringToMobType = {Params = "string", Return = "{{cMonster#MobType|MobType}}", Notes = "Converts a string representation to a {{cMonster#MobType|MobType}} enumerated value"}, StringToMobType = {Params = "string", Return = "{{Globals#MobType|MobType}}", Notes = "<b>DEPRECATED!</b> Please use cMonster:StringToMobType(). Converts a string representation to a {{Globals#MobType|MobType}} enumerated value"},
StripColorCodes = {Params = "string", Return = "string", Notes = "Removes all control codes used by MC for colors and styles"}, StripColorCodes = {Params = "string", Return = "string", Notes = "Removes all control codes used by MC for colors and styles"},
TrimString = {Params = "string", Return = "string", Notes = "Trims whitespace at both ends of the string"}, TrimString = {Params = "string", Return = "string", Notes = "Trims whitespace at both ends of the string"},
md5 = {Params = "string", Return = "string", Notes = "converts a string to an md5 hash"}, md5 = {Params = "string", Return = "string", Notes = "converts a string to an md5 hash"},
@ -3011,6 +3008,13 @@ end
gmXXX constants, the eGameMode_ constants are deprecated and will be removed from the API. gmXXX constants, the eGameMode_ constants are deprecated and will be removed from the API.
]], ]],
}, },
MobType =
{
Include = { "^mt.*" },
TextBefore = [[
The following constants are used for distinguishing between the individual mob types:
]],
},
Weather = Weather =
{ {
Include = { "^eWeather_.*", "wSunny", "wRain", "wStorm", "wThunderstorm" }, Include = { "^eWeather_.*", "wSunny", "wRain", "wStorm", "wThunderstorm" },

View File

@ -231,6 +231,43 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(),
}, },
}, -- cJukeboxEntity }, -- cJukeboxEntity
cMobHeadEntity =
{
Desc = [[
This class represents a mob head block entity in the world.
]],
Inherits = "cBlockEntity",
Functions =
{
SetType = { Params = "eMobHeadType", Return = "", Notes = "Set the type of the mob head" },
SetRotation = { Params = "eMobHeadRotation", Return = "", Notes = "Sets the rotation of the mob head" },
SetOwner = { Params = "string", Return = "", Notes = "Set the player name for mob heads with player type" },
GetType = { Params = "", Return = "eMobHeadType", Notes = "Returns the type of the mob head" },
GetRotation = { Params = "", Return = "eMobHeadRotation", Notes = "Returns the rotation of the mob head" },
GetOwner = { Params = "", Return = "string", Notes = "Returns the player name of the mob head" },
},
}, -- cMobHeadEntity
cMobSpawnerEntity =
{
Desc = [[
This class represents a mob spawner block entity in the world.
]],
Inherits = "cBlockEntity",
Functions =
{
UpdateActiveState = { Params = "", Return = "", Notes = "Upate the active flag from the mob spawner. This function will called every 5 seconds from the Tick() function." },
ResetTimer = { Params = "", Return = "", Notes = "Sets the spawn delay to a new random value." },
SpawnEntity = { Params = "", Return = "", Notes = "Spawns the entity. This function automaticly change the spawn delay!" },
GetEntity = { Params = "", Return = "{{Globals#MobType|MobType}}", Notes = "Returns the entity type that will be spawn by this mob spawner." },
SetEntity = { Params = "{{Globals#MobType|MobType}}", Return = "", Notes = "Sets the entity type who will be spawn by this mob spawner." },
GetSpawnDelay = { Params = "", Return = "number", Notes = "Returns the spawn delay. This is the tick delay that is needed to spawn new monsters." },
SetSpawnDelay = { Params = "number", Return = "", Notes = "Sets the spawn delay." },
GetNearbyPlayersNum = { Params = "", Return = "number", Notes = "Returns the amount of the nearby players in a 16-block radius." },
GetNearbyMonsterNum = { Params = "", Return = "number", Notes = "Returns the amount of this monster type in a 8-block radius (Y: 4-block radius)." },
},
}, -- cMobSpawnerEntity
cNoteEntity = cNoteEntity =
{ {
Desc = [[ Desc = [[

@ -1 +1 @@
Subproject commit 4702471943511f641458c7e8e89b430a723f43ea Subproject commit 4183d6cfb2049d0757d811a65bd4e75ed78b9f41

View File

@ -444,7 +444,18 @@ local function BuildPermissions(a_PluginInfo)
Permissions[info.Permission] = Permission Permissions[info.Permission] = Permission
-- Add the command to the list of commands using this permission: -- Add the command to the list of commands using this permission:
Permission.CommandsAffected = Permission.CommandsAffected or {} Permission.CommandsAffected = Permission.CommandsAffected or {}
table.insert(Permission.CommandsAffected, CommandString) -- First, make sure that we don't already have this command in the list,
-- it may have already been present in a_PluginInfo
local NewCommand = true
for _, existCmd in ipairs(Permission.CommandsAffected) do
if CommandString == existCmd then
NewCommand = false
break
end
end
if NewCommand then
table.insert(Permission.CommandsAffected, CommandString)
end
end end
-- Process the command param combinations for permissions: -- Process the command param combinations for permissions:

View File

@ -51,6 +51,7 @@ local function MultiCommandHandler(a_Split, a_Player, a_CmdString, a_CmdInfo, a_
return a_CmdInfo.Handler(a_Split, a_Player); return a_CmdInfo.Handler(a_Split, a_Player);
end end
-- Let the player know they need to give a subcommand: -- Let the player know they need to give a subcommand:
assert(type(a_CmdInfo.Subcommands) == "table", "Info.lua error: There is no handler for command \"" .. a_CmdString .. "\" and there are no subcommands defined at level " .. a_Level)
ListSubcommands(a_Player, a_CmdInfo.Subcommands, a_CmdString); ListSubcommands(a_Player, a_CmdInfo.Subcommands, a_CmdString);
return true; return true;
end end

View File

@ -75,7 +75,6 @@ Wool = String, 1:1, 1:2, 2:1, 2:2
TNT = Gunpowder, 1:1, 3:1, 2:2, 1:3, 3:3 | Sand, 2:1, 1:2, 3:2, 2:3 TNT = Gunpowder, 1:1, 3:1, 2:2, 1:3, 3:3 | Sand, 2:1, 1:2, 3:2, 2:3
PillarQuartzBlock = QuartzSlab, 1:1, 1:2 PillarQuartzBlock = QuartzSlab, 1:1, 1:2
ChiseledQuartzBlock, 2 = QuartzBlock, 1:1, 1:2 ChiseledQuartzBlock, 2 = QuartzBlock, 1:1, 1:2
CoalBlock = Coal, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
HayBale = Wheat, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 HayBale = Wheat, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
SnowBlock = SnowBall, 1:1, 1:2, 2:1, 2:2 SnowBlock = SnowBall, 1:1, 1:2, 2:1, 2:2
ClayBlock = Clay, 1:1, 1:2, 2:1, 2:2 ClayBlock = Clay, 1:1, 1:2, 2:1, 2:2

View File

@ -52,7 +52,7 @@ RawBeef = Steak
RawChicken = CookedChicken RawChicken = CookedChicken
Clay = Brick Clay = Brick
ClayBlock = HardenedClay ClayBlock = HardenedClay
TallGrass = NetherBrickItem Netherrack = NetherBrickItem
RawFish = CookedFish RawFish = CookedFish
Log = CharCoal Log = CharCoal
Cactus = GreenDye Cactus = GreenDye
@ -90,6 +90,25 @@ RawMutton = CookedMutton
! CoalBlock = 16000 # -> 800 sec ! CoalBlock = 16000 # -> 800 sec
! BlazeRod = 2400 # -> 120 sec ! BlazeRod = 2400 # -> 120 sec
! NoteBlock = 300 # -> 15 sec ! NoteBlock = 300 # -> 15 sec
! HugeRedMushroom = 300 # -> 15 sec
! HugeBrownMushroom = 300 # -> 15 sec
! Banner = 300 # -> 15 sec
! BlackBanner = 300 # -> 15 sec
! RedBanner = 300 # -> 15 sec
! GreenBanner = 300 # -> 15 sec
! BrownBanner = 300 # -> 15 sec
! BlueBanner = 300 # -> 15 sec
! PurpleBanner = 300 # -> 15 sec
! CyanBanner = 300 # -> 15 sec
! SilverBanner = 300 # -> 15 sec
! GrayBanner = 300 # -> 15 sec
! PinkBanner = 300 # -> 15 sec
! LimeBanner = 300 # -> 15 sec
! YellowBanner = 300 # -> 15 sec
! LightBlueBanner = 300 # -> 15 sec
! MagentaBanner = 300 # -> 15 sec
! OrangeBanner = 300 # -> 15 sec
! WhiteBanner = 300 # -> 15 sec
! DaylightSensor = 300 # -> 15 sec ! DaylightSensor = 300 # -> 15 sec
! FenceGate = 300 # -> 15 sec ! FenceGate = 300 # -> 15 sec
! SpruceFenceGate = 300 # -> 15 sec ! SpruceFenceGate = 300 # -> 15 sec

View File

@ -9,6 +9,8 @@ body {
background: #fff; background: #fff;
width: 100%; width: 100%;
min-width: 100%; min-width: 100%;
height:100%;
min-height:100%;
overflow-y: scroll; overflow-y: scroll;
overflow-x: hidden; overflow-x: hidden;
} }
@ -48,7 +50,8 @@ h1 {
margin: 0 auto; margin: 0 auto;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
margin-top: 125px; margin-top: 25px;
margin-bottom: 25px;
} }
.contention { .contention {
@ -58,10 +61,10 @@ h1 {
margin: 0; margin: 0;
font-family: Tahoma,Verdana,Arial,Sans-Serif; font-family: Tahoma,Verdana,Arial,Sans-Serif;
font-size: 13px; font-size: 13px;
margin-bottom:75px;
} }
.push10 { .push25 {
padding-bottom: 75px;
} }
#panel ul.menu { #panel ul.menu {
@ -93,10 +96,6 @@ h1 {
padding: 7px; padding: 7px;
} }
#footer {
z-index: 99999;
}
#footer ul.menu { #footer ul.menu {
margin: 0; margin: 0;
padding: 0; padding: 0;
@ -154,13 +153,14 @@ h1 {
} }
#footer { #footer {
position: fixed; position: fixed;
left: 0; left:0;
bottom: 0; bottom:0;
height: 60px; height: 61px;
width: 100%; width: 100%;
background: #999; background: #999;
border-top: 1px #000 solid; border-top: 1px #000 solid;
border-bottom: 1px #000 solid;
} }
* html #footer { * html #footer {
@ -234,10 +234,6 @@ table {
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
} }
.padtopp {
padding-top: 25px;
}
table { table {
color: #000; color: #000;
font-size: 13px; font-size: 13px;
@ -319,8 +315,6 @@ select {
} }
.pagehead { .pagehead {
position: fixed;
z-index: 99999;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;

View File

@ -1,4 +1,4 @@
/* Copyright Justin S and MCServer Team, licensed under CC-BY-SA 3.0 */ <!-- Copyright Justin S and MCServer Team, licensed under CC-BY-SA 3.0 */ -->
<html> <html>
<head> <head>
<title>MCServer WebAdmin - Login</title> <title>MCServer WebAdmin - Login</title>
@ -61,7 +61,6 @@
</div> </div>
<div class="lower"> <div class="lower">
<div class="wrapper"> <div class="wrapper">
<span id="current_time"><strong>Current time:</strong> <script type="text/javascript">document.write('Time: <strong><span id="date-time">',new Date().toLocaleString(),"</span></strong>");if(document.getElementById){onload=function(){setInterval("document.getElementById ('date-time').firstChild.data = new Date().toLocaleString()",50)}};</script></span>
<span id="copyright">Copyright © <a href="http://www.mc-server.org" target="_blank">MCServer Team</a> 2014.</span> <span id="copyright">Copyright © <a href="http://www.mc-server.org" target="_blank">MCServer Team</a> 2014.</span>
</div> </div>
</div> </div>

View File

@ -1 +0,0 @@

View File

@ -81,7 +81,7 @@ function ShowPage(WebAdmin, TemplateRequest)
end end
Output([[ Output([[
/* Copyright Justin S and MCServer Team, licensed under CC-BY-SA 3.0 */ <!-- Copyright Justin S and MCServer Team, licensed under CC-BY-SA 3.0 -->
<html> <html>
<head> <head>
<title>]] .. Title .. [[</title> <title>]] .. Title .. [[</title>
@ -90,7 +90,7 @@ function ShowPage(WebAdmin, TemplateRequest)
<link rel="icon" href="/favicon.ico"> <link rel="icon" href="/favicon.ico">
</head> </head>
<body> <body>
<div class="contention"> <div class="contention push25">
<div class="pagehead"> <div class="pagehead">
<div class="row1"> <div class="row1">
<div class="wrapper"> <div class="wrapper">
@ -110,27 +110,27 @@ function ShowPage(WebAdmin, TemplateRequest)
</div> </div>
</div> </div>
</div> </div>
<div class="row2 push10"> <div class="row2">
<div class="wrapper padtopp"> <div class="wrapper">
<table width="100%" border="0" align="center"> <table width="100%" border="0" align="center">
<tbody> <tbody>
<tr> <tr>
<td width="180" valign="top"> <td width="180" valign="top">
<table border="0" cellspacing="0" cellpadding="5" class="tborder"> <table border="0" cellspacing="0" cellpadding="5" class="tborder">
<tbody> <tbody>
<tr> <tr>
<td class="thead"><strong>Menu</strong></td> <td class="thead"><strong>Menu</strong></td>
</tr> </tr>
<tr> <tr>
<td class="trow1 smalltext"><a href=']] .. BaseURL .. [[' class='usercp_nav_item usercp_nav_home'>Home</a></td> <td class="trow1 smalltext"><a href=']] .. BaseURL .. [[' class='usercp_nav_item usercp_nav_home'>Home</a></td>
</tr> </tr>
<tr> <tr>
<td class="tcat"><div><span class="smalltext"><strong><font color="#000">Server Management</font></strong></span></div></td> <td class="tcat"><div><span class="smalltext"><strong><font color="#000">Server Management</font></strong></span></div></td>
</tr> </tr>
</tbody> </tbody>
<tbody style="" id="usercppms_e"> <tbody style="" id="usercppms_e">
<tr> <tr>
<td class="trow1 smalltext"> <td class="trow1 smalltext">
]]) ]])
@ -173,7 +173,6 @@ function ShowPage(WebAdmin, TemplateRequest)
</table> </table>
</div> </div>
</div> </div>
</div>
<div id="footer"> <div id="footer">
<div class="upper"> <div class="upper">
<div class="wrapper"> <div class="wrapper">
@ -188,11 +187,11 @@ function ShowPage(WebAdmin, TemplateRequest)
</div> </div>
<div class="lower"> <div class="lower">
<div class="wrapper"> <div class="wrapper">
<span id="current_time"><strong>Current time:</strong> <script type="text/javascript">document.write('Time: <strong><span id="date-time">',new Date().toLocaleString(),"</span></strong>");if(document.getElementById){onload=function(){setInterval("document.getElementById ('date-time').firstChild.data = new Date().toLocaleString()",50)}};</script></span>
<span id="copyright">Copyright © <a href="http://www.mc-server.org" target="_blank">MCServer Team</a> 2014.</span> <span id="copyright">Copyright © <a href="http://www.mc-server.org" target="_blank">MCServer Team</a> 2014.</span>
</div> </div>
</div> </div>
</div> </div>
</div>
</body> </body>
</html> </html>
]]) ]])

View File

@ -1,4 +1,4 @@
MCServer [![Build Status](http://img.shields.io/travis/mc-server/MCServer.svg)](https://travis-ci.org/mc-server/MCServer) [![Coverity Scan Build Status](https://scan.coverity.com/projects/1930/badge.svg)](https://scan.coverity.com/projects/1930) [![tip for next commit](http://tip4commit.com/projects/74.svg)](http://tip4commit.com/projects/74) MCServer [![Build Status](http://img.shields.io/travis/mc-server/MCServer/master.svg?style=flat)](https://travis-ci.org/mc-server/MCServer) [![Coverity Scan Build Status](https://scan.coverity.com/projects/1930/badge.svg)](https://scan.coverity.com/projects/1930) [![weekly tips](http://img.shields.io/gratipay/cuberite_team.svg?style=flat)](http://gratipay.com/cuberite_team) [![tip for next commit](http://tip4commit.com/projects/74.svg)](http://tip4commit.com/projects/74)
======== ========
MCServer is a Minecraft server that is written in C++ and designed to be efficient with memory and CPU, as well as having a flexible Lua Plugin API. MCServer is a Minecraft server that is written in C++ and designed to be efficient with memory and CPU, as well as having a flexible Lua Plugin API.

View File

@ -64,18 +64,12 @@ macro(set_flags)
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
OUTPUT_VARIABLE GCC_VERSION) OUTPUT_VARIABLE GCC_VERSION)
endif() endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND (NOT GCC_VERSION VERSION_GREATER 4.6)) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++0x") set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++11")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++0x") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++0x")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++11")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
endif()
#on os x clang adds pthread for us but we need to add it for gcc #on os x clang adds pthread for us but we need to add it for gcc
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
add_flags_cxx("-stdlib=libc++") add_flags_cxx("-stdlib=libc++")
@ -94,18 +88,11 @@ macro(set_flags)
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
OUTPUT_VARIABLE GCC_VERSION) OUTPUT_VARIABLE GCC_VERSION)
endif() endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND (NOT GCC_VERSION VERSION_GREATER 4.6)) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++0x") set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++11")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++0x") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++0x")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++11")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
endif()
# We use a signed char (fixes #640 on RasPi) # We use a signed char (fixes #640 on RasPi)
add_flags_cxx("-fsigned-char") add_flags_cxx("-fsigned-char")

View File

@ -53,7 +53,6 @@ set(SHARED_OSS_SRC
../../src/OSSupport/File.cpp ../../src/OSSupport/File.cpp
../../src/OSSupport/GZipFile.cpp ../../src/OSSupport/GZipFile.cpp
../../src/OSSupport/IsThread.cpp ../../src/OSSupport/IsThread.cpp
../../src/OSSupport/Timer.cpp
) )
set(SHARED_OSS_HDR set(SHARED_OSS_HDR
@ -62,7 +61,6 @@ set(SHARED_OSS_HDR
../../src/OSSupport/File.h ../../src/OSSupport/File.h
../../src/OSSupport/GZipFile.h ../../src/OSSupport/GZipFile.h
../../src/OSSupport/IsThread.h ../../src/OSSupport/IsThread.h
../../src/OSSupport/Timer.h
) )
flatten_files(SHARED_SRC) flatten_files(SHARED_SRC)

View File

@ -46,6 +46,7 @@ set(SHARED_HDR
../../src/ByteBuffer.h ../../src/ByteBuffer.h
../../src/StringUtils.h ../../src/StringUtils.h
) )
flatten_files(SHARED_SRC) flatten_files(SHARED_SRC)
flatten_files(SHARED_HDR) flatten_files(SHARED_HDR)
source_group("Shared" FILES ${SHARED_SRC} ${SHARED_HDR}) source_group("Shared" FILES ${SHARED_SRC} ${SHARED_HDR})
@ -54,15 +55,21 @@ set(SHARED_OSS_SRC
../../src/OSSupport/CriticalSection.cpp ../../src/OSSupport/CriticalSection.cpp
../../src/OSSupport/File.cpp ../../src/OSSupport/File.cpp
../../src/OSSupport/IsThread.cpp ../../src/OSSupport/IsThread.cpp
../../src/OSSupport/Timer.cpp ../../src/OSSupport/StackTrace.cpp
) )
set(SHARED_OSS_HDR set(SHARED_OSS_HDR
../../src/OSSupport/CriticalSection.h ../../src/OSSupport/CriticalSection.h
../../src/OSSupport/File.h ../../src/OSSupport/File.h
../../src/OSSupport/IsThread.h ../../src/OSSupport/IsThread.h
../../src/OSSupport/Timer.h ../../src/OSSupport/StackTrace.h
) )
if(WIN32)
list (APPEND SHARED_OSS_SRC ../../src/StackWalker.cpp)
list (APPEND SHARED_OSS_HDR ../../src/StackWalker.h)
endif()
flatten_files(SHARED_OSS_SRC) flatten_files(SHARED_OSS_SRC)
flatten_files(SHARED_OSS_HDR) flatten_files(SHARED_OSS_HDR)

View File

@ -57,14 +57,20 @@ set(SHARED_OSS_SRC
../../src/OSSupport/CriticalSection.cpp ../../src/OSSupport/CriticalSection.cpp
../../src/OSSupport/File.cpp ../../src/OSSupport/File.cpp
../../src/OSSupport/IsThread.cpp ../../src/OSSupport/IsThread.cpp
../../src/OSSupport/Timer.cpp ../../src/OSSupport/StackTrace.cpp
) )
set(SHARED_OSS_HDR set(SHARED_OSS_HDR
../../src/OSSupport/CriticalSection.h ../../src/OSSupport/CriticalSection.h
../../src/OSSupport/File.h ../../src/OSSupport/File.h
../../src/OSSupport/IsThread.h ../../src/OSSupport/IsThread.h
../../src/OSSupport/Timer.h ../../src/OSSupport/StackTrace.h
) )
if(WIN32)
list (APPEND SHARED_OSS_SRC ../../src/StackWalker.cpp)
list (APPEND SHARED_OSS_HDR ../../src/StackWalker.h)
endif()
flatten_files(SHARED_SRC) flatten_files(SHARED_SRC)
flatten_files(SHARED_HDR) flatten_files(SHARED_HDR)
flatten_files(SHARED_OSS_SRC) flatten_files(SHARED_OSS_SRC)

View File

@ -189,7 +189,7 @@ cConnection::cConnection(SOCKET a_ClientSocket, cServer & a_Server) :
m_Server(a_Server), m_Server(a_Server),
m_ClientSocket(a_ClientSocket), m_ClientSocket(a_ClientSocket),
m_ServerSocket(-1), m_ServerSocket(-1),
m_BeginTick(m_Timer.GetNowTime()), m_BeginTick(std::chrono::steady_clock::now()),
m_ClientState(csUnencrypted), m_ClientState(csUnencrypted),
m_ServerState(csUnencrypted), m_ServerState(csUnencrypted),
m_Nonce(0), m_Nonce(0),
@ -436,7 +436,8 @@ bool cConnection::RelayFromClient(void)
double cConnection::GetRelativeTime(void) double cConnection::GetRelativeTime(void)
{ {
return (double)(m_Timer.GetNowTime() - m_BeginTick) / 1000; Int64 msec = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_BeginTick).count();
return static_cast<double>(msec) / 1000;
} }

View File

@ -10,7 +10,6 @@
#pragma once #pragma once
#include "ByteBuffer.h" #include "ByteBuffer.h"
#include "OSSupport/Timer.h"
#include "PolarSSL++/AesCfb128Decryptor.h" #include "PolarSSL++/AesCfb128Decryptor.h"
#include "PolarSSL++/AesCfb128Encryptor.h" #include "PolarSSL++/AesCfb128Encryptor.h"
@ -37,8 +36,7 @@ class cConnection
SOCKET m_ClientSocket; SOCKET m_ClientSocket;
SOCKET m_ServerSocket; SOCKET m_ServerSocket;
cTimer m_Timer; std::chrono::steady_clock::time_point m_BeginTick; // Tick when the relative time was first retrieved (used for GetRelativeTime())
long long m_BeginTick; // Tick when the relative time was first retrieved (used for GetRelativeTime())
enum eConnectionState enum eConnectionState
{ {

View File

@ -17,7 +17,7 @@ SOURCES += \
BiomeView.cpp \ BiomeView.cpp \
../../src/Generating/BioGen.cpp \ ../../src/Generating/BioGen.cpp \
../../src/VoronoiMap.cpp \ ../../src/VoronoiMap.cpp \
../../src/Noise.cpp \ ../../src/Noise/Noise.cpp \
../../src/StringUtils.cpp \ ../../src/StringUtils.cpp \
../../src/LoggerListeners.cpp \ ../../src/LoggerListeners.cpp \
../../src/Logger.cpp \ ../../src/Logger.cpp \
@ -25,7 +25,9 @@ SOURCES += \
../../src/OSSupport/File.cpp \ ../../src/OSSupport/File.cpp \
../../src/OSSupport/CriticalSection.cpp \ ../../src/OSSupport/CriticalSection.cpp \
../../src/OSSupport/IsThread.cpp \ ../../src/OSSupport/IsThread.cpp \
../../src/OSSupport/StackTrace.cpp \
../../src/BiomeDef.cpp \ ../../src/BiomeDef.cpp \
../../src/StackWalker.cpp \
../../src/StringCompression.cpp \ ../../src/StringCompression.cpp \
../../src/WorldStorage/FastNBT.cpp \ ../../src/WorldStorage/FastNBT.cpp \
../../lib/zlib/adler32.c \ ../../lib/zlib/adler32.c \
@ -62,7 +64,7 @@ HEADERS += \
../../src/Generating/IntGen.h \ ../../src/Generating/IntGen.h \
../../src/Generating/ProtIntGen.h \ ../../src/Generating/ProtIntGen.h \
../../src/VoronoiMap.h \ ../../src/VoronoiMap.h \
../../src/Noise.h \ ../../src/Noise/Noise.h \
../../src/StringUtils.h \ ../../src/StringUtils.h \
../../src/LoggerListeners.h \ ../../src/LoggerListeners.h \
../../src/Logger.h \ ../../src/Logger.h \
@ -70,7 +72,9 @@ HEADERS += \
../../src/OSSupport/File.h \ ../../src/OSSupport/File.h \
../../src/OSSupport/CriticalSection.h \ ../../src/OSSupport/CriticalSection.h \
../../src/OSSupport/IsThread.h \ ../../src/OSSupport/IsThread.h \
../../src/OSSupport/StackTrace.h \
../../src/BiomeDef.h \ ../../src/BiomeDef.h \
../../src/StackWalker.h \
../../src/StringCompression.h \ ../../src/StringCompression.h \
../../src/WorldStorage/FastNBT.h \ ../../src/WorldStorage/FastNBT.h \
../../lib/zlib/crc32.h \ ../../lib/zlib/crc32.h \
@ -107,4 +111,15 @@ CONFIG += C++11
OTHER_FILES += OTHER_FILES +=
win* {
# Add the advapi32 library for windows compiles; needed for the StackWalker:
LIBS += advapi32.lib
# StackWalker doesn't like how Qt inconsistently defines only UNICODE, but not _UNICODE:
DEFINES -= UNICODE
}

View File

@ -74,6 +74,7 @@ $cfile "../BlockEntities/JukeboxEntity.h"
$cfile "../BlockEntities/NoteEntity.h" $cfile "../BlockEntities/NoteEntity.h"
$cfile "../BlockEntities/SignEntity.h" $cfile "../BlockEntities/SignEntity.h"
$cfile "../BlockEntities/MobHeadEntity.h" $cfile "../BlockEntities/MobHeadEntity.h"
$cfile "../BlockEntities/MobSpawnerEntity.h"
$cfile "../BlockEntities/FlowerPotEntity.h" $cfile "../BlockEntities/FlowerPotEntity.h"
$cfile "../WebAdmin.h" $cfile "../WebAdmin.h"
$cfile "../Root.h" $cfile "../Root.h"

View File

@ -7,6 +7,9 @@
#include "../BlockInfo.h" #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; tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err)) if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
{
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err); tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
}
} }
#endif #endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0); 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; tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err)) if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
{
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err); tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
}
} }
#endif #endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0); 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; tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err)) if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
{
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err); tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
}
} }
#endif #endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0); 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; tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err)) if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
{
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err); tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
}
} }
#endif #endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0); 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; tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err)) if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
{
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err); tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
}
} }
#endif #endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0); 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; tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err)) if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
{
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err); tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
}
} }
#endif #endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0); 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; tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err)) if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
{
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err); tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
}
} }
#endif #endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0); 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) void DeprecatedBindings::Bind(lua_State * tolua_S)
{ {
tolua_beginmodule(tolua_S, nullptr); 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_BlockIsSolid", tolua_get_AllToLua_g_BlockIsSolid, nullptr);
tolua_array(tolua_S, "g_BlockFullyOccupiesVoxel", tolua_get_AllToLua_g_BlockFullyOccupiesVoxel, 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); tolua_endmodule(tolua_S);
} }

View File

@ -1034,11 +1034,11 @@ static int tolua_cWorld_SetSignLines(lua_State * tolua_S)
#ifndef TOLUA_RELEASE #ifndef TOLUA_RELEASE
if (self == nullptr) 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 #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); tolua_pushboolean(tolua_S, res ? 1 : 0);
} }
} }
@ -1046,7 +1046,7 @@ static int tolua_cWorld_SetSignLines(lua_State * tolua_S)
#ifndef TOLUA_RELEASE #ifndef TOLUA_RELEASE
tolua_lerror: 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; return 0;
#endif #endif
} }
@ -3368,6 +3368,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_beginmodule(tolua_S, "cRoot"); tolua_beginmodule(tolua_S, "cRoot");
tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith <cRoot, cPlayer, &cRoot::FindAndDoWithPlayer>); 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, "ForEachPlayer", tolua_ForEach<cRoot, cPlayer, &cRoot::ForEachPlayer>);
tolua_function(tolua_S, "ForEachWorld", tolua_ForEach<cRoot, cWorld, &cRoot::ForEachWorld>); tolua_function(tolua_S, "ForEachWorld", tolua_ForEach<cRoot, cWorld, &cRoot::ForEachWorld>);
tolua_function(tolua_S, "GetFurnaceRecipe", tolua_cRoot_GetFurnaceRecipe); 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, "DoWithFlowerPotAt", tolua_DoWithXYZ<cWorld, cFlowerPotEntity, &cWorld::DoWithFlowerPotAt>);
tolua_function(tolua_S, "DoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>); 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, "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, "ForEachBlockEntityInChunk", tolua_ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>);
tolua_function(tolua_S, "ForEachChestInChunk", tolua_ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>); tolua_function(tolua_S, "ForEachChestInChunk", tolua_ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>);
tolua_function(tolua_S, "ForEachEntity", tolua_ForEach< cWorld, cEntity, &cWorld::ForEachEntity>); tolua_function(tolua_S, "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, "ScheduleTask", tolua_cWorld_ScheduleTask);
tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines); tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines);
tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight); tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight);
tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines);
tolua_endmodule(tolua_S); tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cMapManager"); tolua_beginmodule(tolua_S, "cMapManager");

View File

@ -54,7 +54,7 @@ public:
virtual bool OnChunkUnloaded (cWorld & a_World, int a_ChunkX, int a_ChunkZ) = 0; 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 OnChunkUnloading (cWorld & a_World, int a_ChunkX, int a_ChunkZ) = 0;
virtual bool OnCollectingPickup (cPlayer & a_Player, cPickup & a_Pickup) = 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 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 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; virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0;

View File

@ -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); cCSLock Lock(m_CriticalSection);
bool res = false; bool res = false;

View File

@ -78,7 +78,7 @@ public:
virtual bool OnChunkUnloaded (cWorld & a_World, int a_ChunkX, int a_ChunkZ) override; 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 OnChunkUnloading (cWorld & a_World, int a_ChunkX, int a_ChunkZ) override;
virtual bool OnCollectingPickup (cPlayer & a_Player, cPickup & a_Pickup) 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 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 OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) override;
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override; virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override;

View File

@ -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); FIND_HOOK(HOOK_CRAFTING_NO_RECIPE);
VERIFY_HOOK; 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) 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) if (itr->second->GetName().compare(a_Plugin) == 0)
{ {
return itr->second; return itr->second;

View File

@ -187,7 +187,7 @@ public:
bool CallHookChunkUnloaded (cWorld & a_World, int a_ChunkX, int a_ChunkZ); bool CallHookChunkUnloaded (cWorld & a_World, int a_ChunkX, int a_ChunkZ);
bool CallHookChunkUnloading (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 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 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 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 bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == nullptr, it is a console cmd

View File

@ -247,15 +247,16 @@ void cBeaconEntity::GiveEffects(void)
} }
public: 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) 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_Radius(a_Radius),
, m_PosX(a_PosX) m_PosX(a_PosX),
, m_PosY(a_PosY) m_PosY(a_PosY),
, m_PosZ(a_PosZ) m_PosZ(a_PosZ),
, m_PrimaryEffect(a_PrimaryEffect) m_PrimaryEffect(a_PrimaryEffect),
, m_SecondaryEffect(a_SecondaryEffect) m_SecondaryEffect(a_SecondaryEffect),
, m_EffectLevel(a_EffectLevel) m_EffectLevel(a_EffectLevel)
{}; {
}
} PlayerCallback(Radius, m_PosX, m_PosY, m_PosZ, m_PrimaryEffect, SecondaryEffect, EffectLevel); } PlayerCallback(Radius, m_PosX, m_PosY, m_PosZ, m_PrimaryEffect, SecondaryEffect, EffectLevel);
GetWorld()->ForEachPlayer(PlayerCallback); GetWorld()->ForEachPlayer(PlayerCallback);

View File

@ -1,3 +1,4 @@
// BeaconEntity.h // BeaconEntity.h
// Declares the cBeaconEntity class representing a single beacon in the world // Declares the cBeaconEntity class representing a single beacon in the world
@ -14,15 +15,6 @@
namespace Json
{
class Value;
}
// tolua_begin // tolua_begin
class cBeaconEntity : class cBeaconEntity :
public cBlockEntityWithItems public cBlockEntityWithItems
@ -32,7 +24,7 @@ class cBeaconEntity :
public: public:
// tolua_end // tolua_end
BLOCKENTITY_PROTODEF(cBeaconEntity); BLOCKENTITY_PROTODEF(cBeaconEntity)
cBeaconEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); cBeaconEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);

View File

@ -14,10 +14,11 @@
#include "FlowerPotEntity.h" #include "FlowerPotEntity.h"
#include "FurnaceEntity.h" #include "FurnaceEntity.h"
#include "HopperEntity.h" #include "HopperEntity.h"
#include "MobHeadEntity.h"
#include "MobSpawnerEntity.h"
#include "JukeboxEntity.h" #include "JukeboxEntity.h"
#include "NoteEntity.h" #include "NoteEntity.h"
#include "SignEntity.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_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_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_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_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_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); case E_BLOCK_SIGN_POST: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World);

View File

@ -28,11 +28,6 @@
namespace Json
{
class Value;
};
class cChunk; class cChunk;
class cPlayer; class cPlayer;
class cWorld; class cWorld;
@ -115,7 +110,7 @@ public:
virtual void SendTo(cClientHandle & a_Client) = 0; 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. /// 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); UNUSED(a_Dt);
return false; return false;

View File

@ -29,7 +29,7 @@ class cBlockEntityWithItems :
public: public:
// tolua_end // tolua_end
BLOCKENTITY_PROTODEF(cBlockEntityWithItems); BLOCKENTITY_PROTODEF(cBlockEntityWithItems)
cBlockEntityWithItems( cBlockEntityWithItems(
BLOCKTYPE a_BlockType, // Type of the block that the entity represents BLOCKTYPE a_BlockType, // Type of the block that the entity represents

View File

@ -18,6 +18,7 @@ SET (SRCS
HopperEntity.cpp HopperEntity.cpp
JukeboxEntity.cpp JukeboxEntity.cpp
MobHeadEntity.cpp MobHeadEntity.cpp
MobSpawnerEntity.cpp
NoteEntity.cpp NoteEntity.cpp
SignEntity.cpp) SignEntity.cpp)
@ -36,6 +37,7 @@ SET (HDRS
HopperEntity.h HopperEntity.h
JukeboxEntity.h JukeboxEntity.h
MobHeadEntity.h MobHeadEntity.h
MobSpawnerEntity.h
NoteEntity.h NoteEntity.h
SignEntity.h) SignEntity.h)

View File

@ -7,11 +7,6 @@
namespace Json
{
class Value;
};
class cClientHandle; class cClientHandle;
@ -33,7 +28,7 @@ public:
// tolua_end // tolua_end
BLOCKENTITY_PROTODEF(cChestEntity); BLOCKENTITY_PROTODEF(cChestEntity)
/** Constructor used for normal operation */ /** Constructor used for normal operation */
cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World, BLOCKTYPE a_Type); cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World, BLOCKTYPE a_Type);

View File

@ -15,14 +15,6 @@
namespace Json
{
class Value;
}
// tolua_begin // tolua_begin
@ -36,7 +28,7 @@ public:
// tolua_end // tolua_end
BLOCKENTITY_PROTODEF(cCommandBlockEntity); BLOCKENTITY_PROTODEF(cCommandBlockEntity)
/// Creates a new empty command block entity /// Creates a new empty command block entity
cCommandBlockEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); cCommandBlockEntity(int a_X, int a_Y, int a_Z, cWorld * a_World);

View File

@ -17,7 +17,7 @@ public:
// tolua_end // tolua_end
BLOCKENTITY_PROTODEF(cDispenserEntity); BLOCKENTITY_PROTODEF(cDispenserEntity)
/** Constructor used for normal operation */ /** Constructor used for normal operation */
cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);

View File

@ -16,10 +16,6 @@
namespace Json
{
class Value;
}
class cClientHandle; class cClientHandle;
@ -45,7 +41,7 @@ public:
// tolua_end // tolua_end
BLOCKENTITY_PROTODEF(cDropSpenserEntity); BLOCKENTITY_PROTODEF(cDropSpenserEntity)
cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
virtual ~cDropSpenserEntity(); virtual ~cDropSpenserEntity();

View File

@ -25,7 +25,7 @@ public:
// tolua_end // tolua_end
BLOCKENTITY_PROTODEF(cDropperEntity); BLOCKENTITY_PROTODEF(cDropperEntity)
/// Constructor used for normal operation /// Constructor used for normal operation
cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);

View File

@ -18,7 +18,7 @@ class cEnderChestEntity :
public: public:
// tolua_end // tolua_end
BLOCKENTITY_PROTODEF(cEnderChestEntity); BLOCKENTITY_PROTODEF(cEnderChestEntity)
cEnderChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); cEnderChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
virtual ~cEnderChestEntity(); virtual ~cEnderChestEntity();

View File

@ -15,16 +15,6 @@
namespace Json
{
class Value;
}
// tolua_begin // tolua_begin
class cFlowerPotEntity : class cFlowerPotEntity :
@ -36,7 +26,7 @@ public:
// tolua_end // tolua_end
BLOCKENTITY_PROTODEF(cFlowerPotEntity); BLOCKENTITY_PROTODEF(cFlowerPotEntity)
/** Creates a new flowerpot entity at the specified block coords. a_World may be nullptr */ /** 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); cFlowerPotEntity(int a_BlocX, int a_BlockY, int a_BlockZ, cWorld * a_World);

View File

@ -8,11 +8,6 @@
namespace Json
{
class Value;
}
class cClientHandle; class cClientHandle;
@ -38,7 +33,7 @@ public:
// tolua_end // tolua_end
BLOCKENTITY_PROTODEF(cFurnaceEntity); BLOCKENTITY_PROTODEF(cFurnaceEntity)
/** Constructor used for normal operation */ /** Constructor used for normal operation */
cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cWorld * a_World); cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cWorld * a_World);

View File

@ -31,7 +31,7 @@ public:
// tolua_end // tolua_end
BLOCKENTITY_PROTODEF(cHopperEntity); BLOCKENTITY_PROTODEF(cHopperEntity)
/// Constructor used for normal operation /// Constructor used for normal operation
cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);

View File

@ -7,15 +7,6 @@
namespace Json
{
class Value;
}
// tolua_begin // tolua_begin
class cJukeboxEntity : class cJukeboxEntity :
@ -26,7 +17,7 @@ public:
// tolua_end // tolua_end
BLOCKENTITY_PROTODEF(cJukeboxEntity); BLOCKENTITY_PROTODEF(cJukeboxEntity)
cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
virtual ~cJukeboxEntity(); virtual ~cJukeboxEntity();

View File

@ -14,14 +14,6 @@
namespace Json
{
class Value;
}
// tolua_begin // tolua_begin
@ -34,29 +26,29 @@ public:
// tolua_end // tolua_end
BLOCKENTITY_PROTODEF(cMobHeadEntity); BLOCKENTITY_PROTODEF(cMobHeadEntity)
/** Creates a new mob head entity at the specified block coords. a_World may be nullptr */ /** 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); cMobHeadEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
// tolua_begin // tolua_begin
/** Set the Type */ /** Set the type of the mob head */
void SetType(const eMobHeadType & a_SkullType); void SetType(const eMobHeadType & a_SkullType);
/** Set the Rotation */ /** Set the rotation of the mob head */
void SetRotation(eMobHeadRotation a_Rotation); 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); void SetOwner(const AString & a_Owner);
/** Get the Type */ /** Returns the type of the mob head */
eMobHeadType GetType(void) const { return m_Type; } eMobHeadType GetType(void) const { return m_Type; }
/** Get the Rotation */ /** Returns the rotation of the mob head */
eMobHeadRotation GetRotation(void) const { return m_Rotation; } 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; } AString GetOwner(void) const { return m_Owner; }
// tolua_end // tolua_end

View File

@ -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;
}

View File

@ -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

View File

@ -5,12 +5,6 @@
#include "RedstonePoweredEntity.h" #include "RedstonePoweredEntity.h"
namespace Json
{
class Value;
}
@ -40,7 +34,7 @@ public:
// tolua_end // tolua_end
BLOCKENTITY_PROTODEF(cNoteEntity); BLOCKENTITY_PROTODEF(cNoteEntity)
/// Creates a new note entity. a_World may be nullptr /// Creates a new note entity. a_World may be nullptr
cNoteEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); cNoteEntity(int a_X, int a_Y, int a_Z, cWorld * a_World);

View File

@ -14,15 +14,6 @@
namespace Json
{
class Value;
}
// tolua_begin // tolua_begin
class cSignEntity : class cSignEntity :
@ -34,7 +25,7 @@ public:
// tolua_end // 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 /// 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); cSignEntity(BLOCKTYPE a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World);

View File

@ -200,7 +200,7 @@ public:
BLOCKTYPE BlockStringToType(const AString & a_BlockTypeString) int BlockStringToType(const AString & a_BlockTypeString)
{ {
int res = atoi(a_BlockTypeString.c_str()); int res = atoi(a_BlockTypeString.c_str());
if ((res != 0) || (a_BlockTypeString.compare("0") == 0)) if ((res != 0) || (a_BlockTypeString.compare("0") == 0))

View File

@ -1096,7 +1096,7 @@ class cIniFile;
// tolua_begin // tolua_begin
/// Translates a blocktype string into blocktype. Takes either a number or an items.ini alias as input. Returns -1 on failure. /// 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. /// 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); extern bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item);

View File

@ -52,7 +52,10 @@ public:
static NIBBLETYPE RotationToMetaData(double a_Rotation) static NIBBLETYPE RotationToMetaData(double a_Rotation)
{ {
a_Rotation += 180 + (180 / 4); // So its not aligned with axis 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; a_Rotation = (a_Rotation / 360) * 4;

View File

@ -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, // 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 // 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. // needs extra datamembers.
if (a_Meta & 0x08) return a_Meta; if (a_Meta & 0x08)
{
return a_Meta;
}
// Holds open/closed meta data. 0x0C == 1100. // Holds open/closed meta data. 0x0C == 1100.
NIBBLETYPE OtherMeta = a_Meta & 0x0C; 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 // 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. // needs extra datamembers.
if (a_Meta & 0x08) return a_Meta; if (a_Meta & 0x08)
{
return a_Meta;
}
// Holds open/closed meta data. 0x0C == 1100. // Holds open/closed meta data. 0x0C == 1100.
NIBBLETYPE OtherMeta = a_Meta & 0x0C; NIBBLETYPE OtherMeta = a_Meta & 0x0C;

View File

@ -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 virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{ {
// No pickups // No pickups

View File

@ -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[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)); 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)) 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; 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)) 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; 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)) 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; 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)) 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; Neighbors[3] = true;
}
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
if (Neighbors[i]) if (Neighbors[i])
@ -167,12 +175,30 @@ public:
} }
if (RailsCnt == 1) if (RailsCnt == 1)
{ {
if (Neighbors[7]) return E_META_RAIL_ASCEND_ZP; if (Neighbors[7])
else if (Neighbors[6]) return E_META_RAIL_ASCEND_ZM; {
else if (Neighbors[5]) return E_META_RAIL_ASCEND_XM; return E_META_RAIL_ASCEND_ZP;
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[6])
else if (Neighbors[2] || Neighbors[3]) return E_META_RAIL_ZM_ZP; {
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"); ASSERT(!"Weird neighbor count");
return Meta; return Meta;
} }
@ -185,16 +211,46 @@ public:
} }
if (RailsCnt > 1) if (RailsCnt > 1)
{ {
if (Neighbors[3] && Neighbors[0] && CanThisRailCurve()) return E_META_RAIL_CURVED_ZP_XP; if (Neighbors[3] && Neighbors[0] && CanThisRailCurve())
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; return E_META_RAIL_CURVED_ZP_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[1] && CanThisRailCurve())
else if (Neighbors[3] && Neighbors[6]) return E_META_RAIL_ASCEND_ZM; {
else if (Neighbors[5] && Neighbors[0]) return E_META_RAIL_ASCEND_XM; return E_META_RAIL_CURVED_ZP_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[0] && CanThisRailCurve())
else if (Neighbors[2] && Neighbors[3]) return E_META_RAIL_ZM_ZP; {
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()) if (CanThisRailCurve())
{ {

View File

@ -111,26 +111,36 @@ public:
#ifdef _DEBUG #ifdef _DEBUG
/// Simple RAII class that uses one internal unsigned long for checking if two threads are using an object simultanously /** Simple RAII class that is used for checking that no two threads are using an object simultanously.
class cSingleThreadAccessChecker 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. */
public: class cSingleThreadAccessChecker
cSingleThreadAccessChecker(unsigned long * a_ThreadID) :
m_ThreadID(a_ThreadID)
{ {
ASSERT((*a_ThreadID == 0) || (*a_ThreadID == cIsThread::GetCurrentID())); public:
} cSingleThreadAccessChecker(std::thread::id * a_ThreadID) :
m_ThreadID(a_ThreadID)
~cSingleThreadAccessChecker() {
{ ASSERT(
*m_ThreadID = 0; (*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
);
protected:
unsigned long * m_ThreadID;
} ;
#define CHECK_THREAD cSingleThreadAccessChecker Checker(const_cast<unsigned long *>(&m_ThreadID)) // 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(&m_ThreadID);
#else #else
#define CHECK_THREAD #define CHECK_THREAD
@ -146,9 +156,6 @@ protected:
cByteBuffer::cByteBuffer(size_t a_BufferSize) : cByteBuffer::cByteBuffer(size_t a_BufferSize) :
m_Buffer(new char[a_BufferSize + 1]), m_Buffer(new char[a_BufferSize + 1]),
m_BufferSize(a_BufferSize + 1), m_BufferSize(a_BufferSize + 1),
#ifdef _DEBUG
m_ThreadID(0),
#endif // _DEBUG
m_DataStart(0), m_DataStart(0),
m_WritePos(0), m_WritePos(0),
m_ReadPos(0) m_ReadPos(0)
@ -174,7 +181,7 @@ cByteBuffer::~cByteBuffer()
bool cByteBuffer::Write(const void * a_Bytes, size_t a_Count) bool cByteBuffer::Write(const void * a_Bytes, size_t a_Count)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
// Store the current free space for a check after writing: // 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 size_t cByteBuffer::GetFreeSpace(void) const
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
if (m_WritePos >= m_DataStart) 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() /// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes()
size_t cByteBuffer::GetUsedSpace(void) const size_t cByteBuffer::GetUsedSpace(void) const
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
ASSERT(m_BufferSize >= GetFreeSpace()); ASSERT(m_BufferSize >= GetFreeSpace());
ASSERT((m_BufferSize - GetFreeSpace()) >= 1); 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) /// 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 size_t cByteBuffer::GetReadableSpace(void) const
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
if (m_ReadPos > m_WritePos) if (m_ReadPos > m_WritePos)
{ {
@ -276,7 +283,7 @@ size_t cByteBuffer::GetReadableSpace(void) const
bool cByteBuffer::CanReadBytes(size_t a_Count) const bool cByteBuffer::CanReadBytes(size_t a_Count) const
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
return (a_Count <= GetReadableSpace()); return (a_Count <= GetReadableSpace());
} }
@ -287,7 +294,7 @@ bool cByteBuffer::CanReadBytes(size_t a_Count) const
bool cByteBuffer::CanWriteBytes(size_t a_Count) const bool cByteBuffer::CanWriteBytes(size_t a_Count) const
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
return (a_Count <= GetFreeSpace()); return (a_Count <= GetFreeSpace());
} }
@ -298,7 +305,7 @@ bool cByteBuffer::CanWriteBytes(size_t a_Count) const
bool cByteBuffer::ReadChar(char & a_Value) bool cByteBuffer::ReadChar(char & a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
NEEDBYTES(1); NEEDBYTES(1);
ReadBuf(&a_Value, 1); ReadBuf(&a_Value, 1);
@ -311,7 +318,7 @@ bool cByteBuffer::ReadChar(char & a_Value)
bool cByteBuffer::ReadByte(unsigned char & a_Value) bool cByteBuffer::ReadByte(unsigned char & a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
NEEDBYTES(1); NEEDBYTES(1);
ReadBuf(&a_Value, 1); ReadBuf(&a_Value, 1);
@ -324,7 +331,7 @@ bool cByteBuffer::ReadByte(unsigned char & a_Value)
bool cByteBuffer::ReadBEShort(short & a_Value) bool cByteBuffer::ReadBEShort(short & a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
NEEDBYTES(2); NEEDBYTES(2);
ReadBuf(&a_Value, 2); ReadBuf(&a_Value, 2);
@ -338,7 +345,7 @@ bool cByteBuffer::ReadBEShort(short & a_Value)
bool cByteBuffer::ReadBEInt(int & a_Value) bool cByteBuffer::ReadBEInt(int & a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
NEEDBYTES(4); NEEDBYTES(4);
ReadBuf(&a_Value, 4); ReadBuf(&a_Value, 4);
@ -352,7 +359,7 @@ bool cByteBuffer::ReadBEInt(int & a_Value)
bool cByteBuffer::ReadBEInt64(Int64 & a_Value) bool cByteBuffer::ReadBEInt64(Int64 & a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
NEEDBYTES(8); NEEDBYTES(8);
ReadBuf(&a_Value, 8); ReadBuf(&a_Value, 8);
@ -366,7 +373,7 @@ bool cByteBuffer::ReadBEInt64(Int64 & a_Value)
bool cByteBuffer::ReadBEFloat(float & a_Value) bool cByteBuffer::ReadBEFloat(float & a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
NEEDBYTES(4); NEEDBYTES(4);
ReadBuf(&a_Value, 4); ReadBuf(&a_Value, 4);
@ -380,7 +387,7 @@ bool cByteBuffer::ReadBEFloat(float & a_Value)
bool cByteBuffer::ReadBEDouble(double & a_Value) bool cByteBuffer::ReadBEDouble(double & a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
NEEDBYTES(8); NEEDBYTES(8);
ReadBuf(&a_Value, 8); ReadBuf(&a_Value, 8);
@ -394,7 +401,7 @@ bool cByteBuffer::ReadBEDouble(double & a_Value)
bool cByteBuffer::ReadBool(bool & a_Value) bool cByteBuffer::ReadBool(bool & a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
NEEDBYTES(1); NEEDBYTES(1);
char Value = 0; char Value = 0;
@ -409,7 +416,7 @@ bool cByteBuffer::ReadBool(bool & a_Value)
bool cByteBuffer::ReadBEUTF16String16(AString & a_Value) bool cByteBuffer::ReadBEUTF16String16(AString & a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
short Length; short Length;
if (!ReadBEShort(Length)) if (!ReadBEShort(Length))
@ -430,7 +437,7 @@ bool cByteBuffer::ReadBEUTF16String16(AString & a_Value)
bool cByteBuffer::ReadVarInt(UInt32 & a_Value) bool cByteBuffer::ReadVarInt(UInt32 & a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
UInt32 Value = 0; UInt32 Value = 0;
int Shift = 0; int Shift = 0;
@ -452,7 +459,7 @@ bool cByteBuffer::ReadVarInt(UInt32 & a_Value)
bool cByteBuffer::ReadVarUTF8String(AString & a_Value) bool cByteBuffer::ReadVarUTF8String(AString & a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
UInt32 Size = 0; UInt32 Size = 0;
if (!ReadVarInt(Size)) if (!ReadVarInt(Size))
@ -472,7 +479,7 @@ bool cByteBuffer::ReadVarUTF8String(AString & a_Value)
bool cByteBuffer::ReadLEInt(int & a_Value) bool cByteBuffer::ReadLEInt(int & a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
NEEDBYTES(4); NEEDBYTES(4);
ReadBuf(&a_Value, 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) bool cByteBuffer::ReadPosition(int & a_BlockX, int & a_BlockY, int & a_BlockZ)
{ {
CHECK_THREAD
Int64 Value; Int64 Value;
if (!ReadBEInt64(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) bool cByteBuffer::WriteChar(char a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
PUTBYTES(1); PUTBYTES(1);
return WriteBuf(&a_Value, 1); return WriteBuf(&a_Value, 1);
@ -527,7 +535,7 @@ bool cByteBuffer::WriteChar(char a_Value)
bool cByteBuffer::WriteByte(unsigned char a_Value) bool cByteBuffer::WriteByte(unsigned char a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
PUTBYTES(1); PUTBYTES(1);
return WriteBuf(&a_Value, 1); return WriteBuf(&a_Value, 1);
@ -539,7 +547,7 @@ bool cByteBuffer::WriteByte(unsigned char a_Value)
bool cByteBuffer::WriteBEShort(short a_Value) bool cByteBuffer::WriteBEShort(short a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
PUTBYTES(2); PUTBYTES(2);
u_short Converted = htons((u_short)a_Value); 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) bool cByteBuffer::WriteBEUShort(unsigned short a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
PUTBYTES(2); PUTBYTES(2);
u_short Converted = htons((u_short)a_Value); 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) bool cByteBuffer::WriteBEInt(int a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
PUTBYTES(4); PUTBYTES(4);
UInt32 Converted = HostToNetwork4(&a_Value); UInt32 Converted = HostToNetwork4(&a_Value);
@ -578,7 +586,7 @@ bool cByteBuffer::WriteBEInt(int a_Value)
bool cByteBuffer::WriteBEInt64(Int64 a_Value) bool cByteBuffer::WriteBEInt64(Int64 a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
PUTBYTES(8); PUTBYTES(8);
UInt64 Converted = HostToNetwork8(&a_Value); UInt64 Converted = HostToNetwork8(&a_Value);
@ -591,7 +599,7 @@ bool cByteBuffer::WriteBEInt64(Int64 a_Value)
bool cByteBuffer::WriteBEFloat(float a_Value) bool cByteBuffer::WriteBEFloat(float a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
PUTBYTES(4); PUTBYTES(4);
UInt32 Converted = HostToNetwork4(&a_Value); UInt32 Converted = HostToNetwork4(&a_Value);
@ -604,7 +612,7 @@ bool cByteBuffer::WriteBEFloat(float a_Value)
bool cByteBuffer::WriteBEDouble(double a_Value) bool cByteBuffer::WriteBEDouble(double a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
PUTBYTES(8); PUTBYTES(8);
UInt64 Converted = HostToNetwork8(&a_Value); UInt64 Converted = HostToNetwork8(&a_Value);
@ -618,7 +626,7 @@ bool cByteBuffer::WriteBEDouble(double a_Value)
bool cByteBuffer::WriteBool(bool a_Value) bool cByteBuffer::WriteBool(bool a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
return WriteChar(a_Value ? 1 : 0); return WriteChar(a_Value ? 1 : 0);
} }
@ -629,7 +637,7 @@ bool cByteBuffer::WriteBool(bool a_Value)
bool cByteBuffer::WriteVarInt(UInt32 a_Value) bool cByteBuffer::WriteVarInt(UInt32 a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
// A 32-bit integer can be encoded by at most 5 bytes: // 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) bool cByteBuffer::WriteVarUTF8String(const AString & a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
PUTBYTES(a_Value.size() + 1); // This is a lower-bound on the bytes that will be actually written. Fail early. 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())); bool res = WriteVarInt((UInt32)(a_Value.size()));
@ -667,7 +675,7 @@ bool cByteBuffer::WriteVarUTF8String(const AString & a_Value)
bool cByteBuffer::WriteLEInt(int a_Value) bool cByteBuffer::WriteLEInt(int a_Value)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
#ifdef IS_LITTLE_ENDIAN #ifdef IS_LITTLE_ENDIAN
return WriteBuf((const char *)&a_Value, 4); 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) 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)); 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) bool cByteBuffer::ReadBuf(void * a_Buffer, size_t a_Count)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
NEEDBYTES(a_Count); NEEDBYTES(a_Count);
char * Dst = (char *)a_Buffer; // So that we can do byte math 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) bool cByteBuffer::WriteBuf(const void * a_Buffer, size_t a_Count)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
PUTBYTES(a_Count); PUTBYTES(a_Count);
char * Src = (char *)a_Buffer; // So that we can do byte math 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) bool cByteBuffer::ReadString(AString & a_String, size_t a_Count)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
NEEDBYTES(a_Count); NEEDBYTES(a_Count);
a_String.clear(); 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) 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 // Reads 2 * a_NumChars bytes and interprets it as a UTF16 string, converting it into UTF8 string a_String
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
AString RawData; AString RawData;
if (!ReadString(RawData, a_NumChars * 2)) 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) bool cByteBuffer::SkipRead(size_t a_Count)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
if (!CanReadBytes(a_Count)) if (!CanReadBytes(a_Count))
{ {
@ -823,7 +832,7 @@ bool cByteBuffer::SkipRead(size_t a_Count)
void cByteBuffer::ReadAll(AString & a_Data) void cByteBuffer::ReadAll(AString & a_Data)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
ReadString(a_Data, GetReadableSpace()); ReadString(a_Data, GetReadableSpace());
} }
@ -834,6 +843,7 @@ void cByteBuffer::ReadAll(AString & a_Data)
bool cByteBuffer::ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes) bool cByteBuffer::ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes)
{ {
CHECK_THREAD
if (!a_Dst.CanWriteBytes(a_NumBytes) || !CanReadBytes(a_NumBytes)) if (!a_Dst.CanWriteBytes(a_NumBytes) || !CanReadBytes(a_NumBytes))
{ {
// There's not enough source bytes or space in the dest BB // 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) void cByteBuffer::CommitRead(void)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
m_DataStart = m_ReadPos; m_DataStart = m_ReadPos;
} }
@ -869,7 +879,7 @@ void cByteBuffer::CommitRead(void)
void cByteBuffer::ResetRead(void) void cByteBuffer::ResetRead(void)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
m_ReadPos = m_DataStart; 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) // 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 // Used by ProtoProxy to repeat communication twice, once for parsing and the other time for the remote party
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
size_t DataStart = m_DataStart; size_t DataStart = m_DataStart;
if (m_ReadPos < m_DataStart) if (m_ReadPos < m_DataStart)
@ -902,7 +912,7 @@ void cByteBuffer::ReadAgain(AString & a_Out)
void cByteBuffer::AdvanceReadPos(size_t a_Count) void cByteBuffer::AdvanceReadPos(size_t a_Count)
{ {
CHECK_THREAD; CHECK_THREAD
CheckValid(); CheckValid();
m_ReadPos += a_Count; m_ReadPos += a_Count;
if (m_ReadPos >= m_BufferSize) if (m_ReadPos >= m_BufferSize)

View File

@ -130,13 +130,15 @@ protected:
char * m_Buffer; char * m_Buffer;
size_t m_BufferSize; // Total size of the ringbuffer 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_DataStart; // Where the data starts in the ringbuffer
size_t m_WritePos; // Where the data ends 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 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 */ /** Advances the m_ReadPos by a_Count bytes */
void AdvanceReadPos(size_t a_Count); void AdvanceReadPos(size_t a_Count);

View File

@ -9,6 +9,7 @@ include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/polarssl/include
set(FOLDERS set(FOLDERS
OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++ Bindings OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++ Bindings
WorldStorage Mobs Entities Simulator UI BlockEntities Generating/Prefabs WorldStorage Mobs Entities Simulator UI BlockEntities Generating/Prefabs
Noise
) )
SET (SRCS SET (SRCS
@ -50,7 +51,6 @@ SET (SRCS
MobProximityCounter.cpp MobProximityCounter.cpp
MobSpawner.cpp MobSpawner.cpp
MonsterConfig.cpp MonsterConfig.cpp
Noise.cpp
ProbabDistrib.cpp ProbabDistrib.cpp
RankManager.cpp RankManager.cpp
RCONServer.cpp RCONServer.cpp
@ -65,7 +65,8 @@ SET (SRCS
VoronoiMap.cpp VoronoiMap.cpp
WebAdmin.cpp WebAdmin.cpp
World.cpp World.cpp
main.cpp) main.cpp
)
SET (HDRS SET (HDRS
AllocationPool.h AllocationPool.h
@ -103,7 +104,6 @@ SET (HDRS
Inventory.h Inventory.h
Item.h Item.h
ItemGrid.h ItemGrid.h
LeakFinder.h
LightingThread.h LightingThread.h
LineBlockTracer.h LineBlockTracer.h
LinearInterpolation.h LinearInterpolation.h
@ -113,14 +113,11 @@ SET (HDRS
Map.h Map.h
MapManager.h MapManager.h
Matrix4.h Matrix4.h
MemoryLeak.h
MersenneTwister.h
MobCensus.h MobCensus.h
MobFamilyCollecter.h MobFamilyCollecter.h
MobProximityCounter.h MobProximityCounter.h
MobSpawner.h MobSpawner.h
MonsterConfig.h MonsterConfig.h
Noise.h
ProbabDistrib.h ProbabDistrib.h
RankManager.h RankManager.h
RCONServer.h RCONServer.h
@ -128,7 +125,6 @@ SET (HDRS
Scoreboard.h Scoreboard.h
Server.h Server.h
SetChunkData.h SetChunkData.h
StackWalker.h
Statistics.h Statistics.h
StringCompression.h StringCompression.h
StringUtils.h StringUtils.h
@ -137,7 +133,8 @@ SET (HDRS
VoronoiMap.h VoronoiMap.h
WebAdmin.h WebAdmin.h
World.h World.h
XMLParser.h) XMLParser.h
)
include_directories(".") include_directories(".")
include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/sqlite") include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/sqlite")
@ -175,6 +172,10 @@ if (NOT MSVC)
else () else ()
# MSVC-specific handling: Put all files into one project, separate by the folders: # 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") source_group(Bindings FILES "Bindings/Bindings.cpp" "Bindings/Bindings.h")
# Add all subfolders as solution-folders: # Add all subfolders as solution-folders:
@ -224,7 +225,7 @@ else ()
Bindings/Bindings.cpp PROPERTIES COMPILE_FLAGS "/Yc\"string.h\" /Fp\"$(IntDir)/Bindings.pch\"" Bindings/Bindings.cpp PROPERTIES COMPILE_FLAGS "/Yc\"string.h\" /Fp\"$(IntDir)/Bindings.pch\""
) )
SET_SOURCE_FILES_PROPERTIES( 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") list(APPEND SOURCE "Resources/MCServer.rc")
@ -314,7 +315,7 @@ endif ()
if (NOT MSVC) if (NOT MSVC)
target_link_libraries(${EXECUTABLE} target_link_libraries(${EXECUTABLE}
OSSupport HTTPServer Bindings Items Blocks OSSupport HTTPServer Bindings Items Blocks Noise
Protocol Generating Generating_Prefabs WorldStorage Protocol Generating Generating_Prefabs WorldStorage
Mobs Entities Simulator UI BlockEntities PolarSSL++ Mobs Entities Simulator UI BlockEntities PolarSSL++
) )

View File

@ -92,6 +92,25 @@ end
local g_ViolationPatterns = 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: -- Check against indenting using spaces:
{"^\t* +", "Indenting with a space"}, {"^\t* +", "Indenting with a space"},
@ -116,11 +135,11 @@ local g_ViolationPatterns =
-- Space after keywords: -- Space after keywords:
{"[^_]if%(", "Needs a space after \"if\""}, {"[^_]if%(", "Needs a space after \"if\""},
{"for%(", "Needs a space after \"for\""}, {"%sfor%(", "Needs a space after \"for\""},
{"while%(", "Needs a space after \"while\""}, {"%swhile%(", "Needs a space after \"while\""},
{"switch%(", "Needs a space after \"switch\""}, {"%sswitch%(", "Needs a space after \"switch\""},
{"catch%(", "Needs a space after \"catch\""}, {"%scatch%(", "Needs a space after \"catch\""},
{"template<", "Needs a space after \"template\""}, {"%stemplate<", "Needs a space after \"template\""},
-- No space after keyword's parenthesis: -- No space after keyword's parenthesis:
{"[^%a#]if %( ", "Remove the space after \"(\""}, {"[^%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 -- Ref.: http://stackoverflow.com/questions/10416869/iterate-over-possibly-empty-lines-in-a-way-that-matches-the-expectations-of-exis
local lineCounter = 1 local lineCounter = 1
local lastIndentLevel = 0 local lastIndentLevel = 0
local isLastLineControl = false
all:gsub("\r\n", "\n") -- normalize CRLF into LF-only all:gsub("\r\n", "\n") -- normalize CRLF into LF-only
string.gsub(all .. "\n", "[^\n]*\n", -- Iterate over each line, while preserving empty lines string.gsub(all .. "\n", "[^\n]*\n", -- Iterate over each line, while preserving empty lines
function(a_Line) function(a_Line)
@ -198,6 +218,24 @@ local function ProcessFile(a_FileName)
end end
lastIndentLevel = indentLevel lastIndentLevel = indentLevel
end 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 lineCounter = lineCounter + 1
end 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): -- Process all files in the AllFiles.lst file (generated by cmake):
for fnam in io.lines("AllFiles.lst") do for fnam in io.lines("AllFiles.lst") do
ProcessItem(fnam) ProcessItem(fnam)

View File

@ -16,18 +16,18 @@
#include "BlockEntities/ChestEntity.h" #include "BlockEntities/ChestEntity.h"
#include "BlockEntities/DispenserEntity.h" #include "BlockEntities/DispenserEntity.h"
#include "BlockEntities/DropperEntity.h" #include "BlockEntities/DropperEntity.h"
#include "BlockEntities/FlowerPotEntity.h"
#include "BlockEntities/FurnaceEntity.h" #include "BlockEntities/FurnaceEntity.h"
#include "BlockEntities/HopperEntity.h" #include "BlockEntities/HopperEntity.h"
#include "BlockEntities/JukeboxEntity.h" #include "BlockEntities/JukeboxEntity.h"
#include "BlockEntities/MobHeadEntity.h"
#include "BlockEntities/MobSpawnerEntity.h"
#include "BlockEntities/NoteEntity.h" #include "BlockEntities/NoteEntity.h"
#include "BlockEntities/SignEntity.h" #include "BlockEntities/SignEntity.h"
#include "BlockEntities/MobHeadEntity.h"
#include "BlockEntities/FlowerPotEntity.h"
#include "Entities/Pickup.h" #include "Entities/Pickup.h"
#include "Item.h" #include "Item.h"
#include "Noise.h" #include "Noise/Noise.h"
#include "Root.h" #include "Root.h"
#include "MersenneTwister.h"
#include "Entities/Player.h" #include "Entities/Player.h"
#include "BlockArea.h" #include "BlockArea.h"
#include "Bindings/PluginManager.h" #include "Bindings/PluginManager.h"
@ -1348,6 +1348,7 @@ void cChunk::CreateBlockEntities(void)
case E_BLOCK_NOTE_BLOCK: case E_BLOCK_NOTE_BLOCK:
case E_BLOCK_JUKEBOX: case E_BLOCK_JUKEBOX:
case E_BLOCK_FLOWER_POT: case E_BLOCK_FLOWER_POT:
case E_BLOCK_MOB_SPAWNER:
{ {
if (!HasBlockEntityAt(x + m_PosX * Width, y, z + m_PosZ * Width)) 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_NOTE_BLOCK:
case E_BLOCK_JUKEBOX: case E_BLOCK_JUKEBOX:
case E_BLOCK_FLOWER_POT: 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)); AddBlockEntity(cBlockEntity::CreateByBlockType(a_BlockType, a_BlockMeta, WorldPos.x, WorldPos.y, WorldPos.z, m_World));
break; break;
@ -1869,18 +1871,18 @@ bool cChunk::AddClient(cClientHandle * a_Client)
void cChunk::RemoveClient(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; continue;
} }
m_LoadedByClient.erase(itr); m_LoadedByClient.erase(itrC);
if (!a_Client->IsDestroyed()) 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: // DEBUG:
@ -1889,7 +1891,7 @@ void cChunk::RemoveClient(cClientHandle * a_Client)
(*itr)->GetUniqueID(), a_Client->GetUsername().c_str() (*itr)->GetUniqueID(), a_Client->GetUsername().c_str()
); );
*/ */
a_Client->SendDestroyEntity(*(*itr)); a_Client->SendDestroyEntity(*(*itrE));
} }
} }
return; return;

View File

@ -16,7 +16,6 @@
#include "Mobs/Monster.h" #include "Mobs/Monster.h"
#include "ChatColor.h" #include "ChatColor.h"
#include "OSSupport/Socket.h" #include "OSSupport/Socket.h"
#include "OSSupport/Timer.h"
#include "Items/ItemHandler.h" #include "Items/ItemHandler.h"
#include "Blocks/BlockHandler.h" #include "Blocks/BlockHandler.h"
#include "Blocks/BlockSlab.h" #include "Blocks/BlockSlab.h"
@ -25,8 +24,6 @@
#include "Root.h" #include "Root.h"
#include "Protocol/Authenticator.h" #include "Protocol/Authenticator.h"
#include "MersenneTwister.h"
#include "Protocol/ProtocolRecognizer.h" #include "Protocol/ProtocolRecognizer.h"
#include "CompositeChat.h" #include "CompositeChat.h"
#include "Items/ItemSword.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 */ /** Maximum number of block change interactions a player can perform per tick - exceeding this causes a kick */
#define MAX_BLOCK_CHANGE_INTERACTIONS 20 #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; int cClientHandle::s_ClientCount = 0;
@ -65,7 +57,8 @@ int cClientHandle::s_ClientCount = 0;
// cClientHandle: // cClientHandle:
cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) : 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_IPString(a_Socket->GetIPString()),
m_OutgoingData(64 KiB), m_OutgoingData(64 KiB),
m_Player(nullptr), m_Player(nullptr),
@ -75,8 +68,6 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) :
m_TimeSinceLastPacket(0), m_TimeSinceLastPacket(0),
m_Ping(1000), m_Ping(1000),
m_PingID(1), m_PingID(1),
m_PingStartTime(0),
m_LastPingTime(1000),
m_BlockDigAnimStage(-1), m_BlockDigAnimStage(-1),
m_BlockDigAnimSpeed(0), m_BlockDigAnimSpeed(0),
m_BlockDigAnimX(0), m_BlockDigAnimX(0),
@ -93,15 +84,14 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) :
m_UniqueID(0), m_UniqueID(0),
m_HasSentPlayerChunk(false), m_HasSentPlayerChunk(false),
m_Locale("en_GB"), m_Locale("en_GB"),
m_LastPlacedSign(0, -1, 0),
m_ProtocolVersion(0) m_ProtocolVersion(0)
{ {
m_Protocol = new cProtocolRecognizer(this); m_Protocol = new cProtocolRecognizer(this);
s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread
m_UniqueID = s_ClientCount; m_UniqueID = s_ClientCount;
m_PingStartTime = std::chrono::steady_clock::now();
cTimer t1;
m_LastPingTime = t1.GetNowTime();
LOGD("New ClientHandle created at %p", this); 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) AString cClientHandle::FormatChatPrefix(bool ShouldAppendChatPrefixes, AString a_ChatPrefixS, AString m_Color1, AString m_Color2)
{ {
if (ShouldAppendChatPrefixes) if (ShouldAppendChatPrefixes)
{
return Printf("%s[%s] %s", m_Color1.c_str(), a_ChatPrefixS.c_str(), m_Color2.c_str()); return Printf("%s[%s] %s", m_Color1.c_str(), a_ChatPrefixS.c_str(), m_Color2.c_str());
}
else else
{
return Printf("%s", m_Color1.c_str()); 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" // Delay the first ping until the client "settles down"
// This should fix #889, "BadCast exception, cannot convert bit to fm" error in client // This should fix #889, "BadCast exception, cannot convert bit to fm" error in client
cTimer t1; m_PingStartTime = std::chrono::steady_clock::now() + std::chrono::seconds(3); // Send the first KeepAlive packet in 3 seconds
m_LastPingTime = t1.GetNowTime() + 3000; // Send the first KeepAlive packet in 3 seconds
cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player); cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player);
} }
@ -430,7 +423,7 @@ bool cClientHandle::StreamNextChunk(void)
cCSLock Lock(m_CSChunkLists); cCSLock Lock(m_CSChunkLists);
// High priority: Load the chunks that are in the view-direction of the player (with a radius of 3) // 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; Vector3d Vector = Position + LookVector * cChunkDef::Width * Range;
@ -447,7 +440,7 @@ bool cClientHandle::StreamNextChunk(void)
cChunkCoords Coords(ChunkX, ChunkZ); cChunkCoords Coords(ChunkX, ChunkZ);
// Checks if the chunk is in distance // 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; 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) // 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: // For each distance add chunks in a hollow square centered around current position:
cChunkCoordsList CurcleChunks; cChunkCoordsList CurcleChunks;
@ -528,7 +521,7 @@ void cClientHandle::UnloadOutOfRangeChunks(void)
{ {
int DiffX = Diff((*itr).m_ChunkX, ChunkPosX); int DiffX = Diff((*itr).m_ChunkX, ChunkPosX);
int DiffZ = Diff((*itr).m_ChunkZ, ChunkPosZ); 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); ChunksToRemove.push_back(*itr);
itr = m_LoadedChunks.erase(itr); itr = m_LoadedChunks.erase(itr);
@ -543,7 +536,7 @@ void cClientHandle::UnloadOutOfRangeChunks(void)
{ {
int DiffX = Diff((*itr).m_ChunkX, ChunkPosX); int DiffX = Diff((*itr).m_ChunkX, ChunkPosX);
int DiffZ = Diff((*itr).m_ChunkZ, ChunkPosZ); 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); 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 // TODO: Rewrite this function
LOGD("HandleRightClick: {%d, %d, %d}, face %d, HeldItem: %s", // Distance from the block's center to the player's eye height
a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, ItemToFullString(a_HeldItem).c_str() 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(); cWorld * World = m_Player->GetWorld();
bool AreRealCoords = (Vector3d(a_BlockX, a_BlockY, a_BlockZ) - m_Player->GetPosition()).Length() <= 5;
if ( if (
(a_BlockFace != BLOCK_FACE_NONE) && // The client is interacting with a specific block (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(); m_Player->GetInventory().RemoveOneEquippedItem();
} }
cChunkInterface ChunkInterface(World->GetChunkMap()); 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); 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 const AString & a_Line3, const AString & a_Line4
) )
{ {
cWorld * World = m_Player->GetWorld(); if (m_LastPlacedSign.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
World->UpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, m_Player); {
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) if (a_KeepAliveID == m_PingID)
{ {
cTimer t1; m_Ping = std::chrono::steady_clock::now() - m_PingStartTime;
m_Ping = (short)((t1.GetNowTime() - m_PingStartTime) / 2);
} }
} }
@ -1993,13 +1995,11 @@ void cClientHandle::Tick(float a_Dt)
// Send a ping packet: // Send a ping packet:
if (m_State == csPlaying) if (m_State == csPlaying)
{ {
cTimer t1; if ((m_PingStartTime + PING_TIME_MS <= std::chrono::steady_clock::now()))
if ((m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime()))
{ {
m_PingID++; m_PingID++;
m_PingStartTime = t1.GetNowTime(); m_PingStartTime = std::chrono::steady_clock::now();
m_Protocol->SendKeepAlive(m_PingID); 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) 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); 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) void cClientHandle::SetViewDistance(int a_ViewDistance)
{ {
m_ViewDistance = Clamp(a_ViewDistance, MIN_VIEW_DISTANCE, MAX_VIEW_DISTANCE); m_RequestedViewDistance = a_ViewDistance;
LOGD("Setted %s's view distance to %i", GetUsername().c_str(), m_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());
}
} }

View File

@ -215,11 +215,17 @@ public:
const AString & GetUsername(void) const; const AString & GetUsername(void) const;
void SetUsername( const AString & a_Username); 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); 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; } void SetLocale(AString & a_Locale) { m_Locale = a_Locale; }
AString GetLocale(void) const { return m_Locale; } AString GetLocale(void) const { return m_Locale; }
@ -333,12 +339,12 @@ private:
/** The type used for storing the names of registered plugin channels. */ /** The type used for storing the names of registered plugin channels. */
typedef std::set<AString> cChannels; typedef std::set<AString> cChannels;
/** Number of chunks the player can see in each direction */ /** The actual view distance used, the minimum of client's requested view distance and world's max view distance. */
int m_ViewDistance; int m_CurrentViewDistance;
/** Server generates this many chunks AHEAD of player sight. */ /** The requested view distance from the player. It isn't clamped with 1 and the max view distance of the world. */
static const int GENERATEDISTANCE = 2; int m_RequestedViewDistance;
AString m_IPString; AString m_IPString;
AString m_Username; AString m_Username;
@ -372,12 +378,15 @@ private:
/** Seconds since the last packet data was received (updated in Tick(), reset in DataReceived()) */ /** Seconds since the last packet data was received (updated in Tick(), reset in DataReceived()) */
float m_TimeSinceLastPacket; float m_TimeSinceLastPacket;
short m_Ping; /** Duration of the last completed client ping. */
int m_PingID; std::chrono::steady_clock::duration m_Ping;
long long m_PingStartTime;
long long m_LastPingTime; /** ID of the last ping request sent to the client. */
static const unsigned short PING_TIME_MS = 1000; // Vanilla sends 1 per 20 ticks (1 second or every 1000 ms) 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 // Values required for block dig animation
int m_BlockDigAnimStage; // Current stage of the animation; -1 if not digging int m_BlockDigAnimStage; // Current stage of the animation; -1 if not digging
int m_BlockDigAnimSpeed; // Current speed of the animation (units ???) int m_BlockDigAnimSpeed; // Current speed of the animation (units ???)
@ -432,6 +441,9 @@ private:
/** Client Settings */ /** Client Settings */
AString m_Locale; 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. */ /** The plugin channels that the client has registered. */
cChannels m_PluginChannels; cChannels m_PluginChannels;

View File

@ -289,12 +289,12 @@ void cCraftingRecipes::GetRecipe(cPlayer & a_Player, cCraftingGrid & a_CraftingG
} }
// Built-in recipes: // 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(); a_Recipe.Clear();
if (Recipe.get() == nullptr) if (Recipe.get() == nullptr)
{ {
// Allow plugins to intercept a no-recipe-found situation: // 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; return;
} }
for (cRecipeSlots::const_iterator itr = Recipe->m_Ingredients.begin(); itr != Recipe->m_Ingredients.end(); ++itr) 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; return;
} }
std::auto_ptr<cCraftingRecipes::cRecipe> Recipe(new cCraftingRecipes::cRecipe); std::unique_ptr<cCraftingRecipes::cRecipe> Recipe(new cCraftingRecipes::cRecipe);
// Parse the result: // Parse the result:
AStringVector ResultSplit = StringSplit(Sides[0], ","); AStringVector ResultSplit = StringSplit(Sides[0], ",");
@ -758,7 +758,7 @@ cCraftingRecipes::cRecipe * cCraftingRecipes::MatchRecipe(const cItem * a_Crafti
} // for y, for x } // for y, for x
// The recipe has matched. Create a copy of the recipe and set its coords to match the crafting grid: // 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_Result = a_Recipe->m_Result;
Recipe->m_Width = a_Recipe->m_Width; Recipe->m_Width = a_Recipe->m_Width;
Recipe->m_Height = a_Recipe->m_Height; Recipe->m_Height = a_Recipe->m_Height;

View File

@ -13,7 +13,7 @@
/// Number of milliseconds per cycle /** Number of milliseconds per cycle */
const int CYCLE_MILLISECONDS = 100; const int CYCLE_MILLISECONDS = 100;
@ -87,7 +87,7 @@ void cDeadlockDetect::Execute(void)
} Checker(this); } Checker(this);
cRoot::Get()->ForEachWorld(Checker); cRoot::Get()->ForEachWorld(Checker);
cSleep::MilliSleep(CYCLE_MILLISECONDS); std::this_thread::sleep_for(std::chrono::milliseconds(CYCLE_MILLISECONDS));
} // while (should run) } // while (should run)
} }
@ -119,7 +119,7 @@ void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age)
if (WorldAge.m_Age == a_Age) if (WorldAge.m_Age == a_Age)
{ {
WorldAge.m_NumCyclesSame += 1; WorldAge.m_NumCyclesSame += 1;
if (WorldAge.m_NumCyclesSame > (1000 * m_IntervalSec) / CYCLE_MILLISECONDS) if (WorldAge.m_NumCyclesSame > (m_IntervalSec * 1000) / CYCLE_MILLISECONDS)
{ {
DeadlockDetected(); DeadlockDetected();
} }

View File

@ -28,7 +28,7 @@ class cDeadlockDetect :
public: public:
cDeadlockDetect(void); 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); bool Start(int a_IntervalSec);
protected: protected:

View File

@ -6,7 +6,7 @@
#include "Enchantments.h" #include "Enchantments.h"
#include "WorldStorage/FastNBT.h" #include "WorldStorage/FastNBT.h"
#include "FastRandom.h" #include "FastRandom.h"
#include "Noise.h" #include "Noise/Noise.h"

View File

@ -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 // Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover
// Filter out damage types that are not protected by armor: // 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: // Add up all armor points:
// Ref.: http://www.minecraftwiki.net/wiki/Armor#Defense_points as of 2012_12_20 // 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 // 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. // 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 // 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.x != 0.f)
if (Tracer.HitNormal.y != 0.f) NextSpeed.y = 0.f; {
if (Tracer.HitNormal.z != 0.f) NextSpeed.z = 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 if (Tracer.HitNormal.y == 1.f) // Hit BLOCK_FACE_YP, we are on the ground
{ {
@ -1276,7 +1288,7 @@ bool cEntity::DetectPortal()
return false; 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 // Delay teleportation for four seconds if the entity is a non-creative player
m_PortalCooldownData.m_TicksDelayed++; m_PortalCooldownData.m_TicksDelayed++;

View File

@ -142,8 +142,13 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
if (!IsBlockRail(InsideType)) 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 // When a descending minecart hits a flat rail, it goes through the ground; check for this
if (IsBlockRail(InsideType)) AddPosY(1); // Push cart upwards Chunk->GetBlockTypeMeta(RelPosX, PosY + 1, RelPosZ, InsideType, InsideMeta);
if (IsBlockRail(InsideType))
{
// Push cart upwards
AddPosY(1);
}
} }
bool WasDetectorRail = false; bool WasDetectorRail = false;
@ -218,7 +223,10 @@ void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
// Execute both the entity and block collision checks // Execute both the entity and block collision checks
bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta); 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 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); SetSpeedZ(NO_SPEED);
bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta); bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
if (EntCol || BlckCol) return; if (EntCol || BlckCol)
{
return;
}
if (GetSpeedX() != NO_SPEED) if (GetSpeedX() != NO_SPEED)
{ {
@ -422,7 +433,10 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
SetSpeedX(0); SetSpeedX(0);
bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta); bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
if (EntCol || BlckCol) return; if (EntCol || BlckCol)
{
return;
}
if (GetSpeedZ() != NO_SPEED) if (GetSpeedZ() != NO_SPEED)
{ {
@ -445,7 +459,10 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
SetSpeedZ(NO_SPEED); SetSpeedZ(NO_SPEED);
bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta); bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
if (EntCol || BlckCol) return; if (EntCol || BlckCol)
{
return;
}
if (GetSpeedX() != NO_SPEED) if (GetSpeedX() != NO_SPEED)
{ {

View File

@ -128,7 +128,7 @@ public:
}; };
const cItem & GetSlot(int a_Idx) const { return m_Contents.GetSlot(a_Idx); } 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: protected:
cItemGrid m_Contents; cItemGrid m_Contents;

View File

@ -11,7 +11,6 @@
#include "../BlockEntities/BlockEntity.h" #include "../BlockEntities/BlockEntity.h"
#include "../BlockEntities/EnderChestEntity.h" #include "../BlockEntities/EnderChestEntity.h"
#include "../Root.h" #include "../Root.h"
#include "../OSSupport/Timer.h"
#include "../Chunk.h" #include "../Chunk.h"
#include "../Items/ItemHandler.h" #include "../Items/ItemHandler.h"
#include "../Vector3.h" #include "../Vector3.h"
@ -27,7 +26,7 @@
#define PLAYER_INVENTORY_SAVE_INTERVAL 6000 #define PLAYER_INVENTORY_SAVE_INTERVAL 6000
// 1000 = once per second // 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); SetMaxHealth(MAX_HEALTH);
m_Health = MAX_HEALTH; m_Health = MAX_HEALTH;
cTimer t1; m_LastPlayerListTime = std::chrono::steady_clock::now();
m_LastPlayerListTime = t1.GetNowTime();
m_PlayerName = a_PlayerName; m_PlayerName = a_PlayerName;
cWorld * World = nullptr; cWorld * World = nullptr;
@ -120,6 +117,11 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) :
{ {
m_CanFly = true; 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); cRoot::Get()->GetServer()->PlayerCreated(this);
@ -263,11 +265,10 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
m_Inventory.UpdateItems(); m_Inventory.UpdateItems();
// Send Player List (Once per m_LastPlayerListTime/1000 ms) // Send Player List (Once per m_LastPlayerListTime/1000 ms)
cTimer t1; if (m_LastPlayerListTime + PLAYER_LIST_TIME_MS <= std::chrono::steady_clock::now())
if (m_LastPlayerListTime + PLAYER_LIST_TIME_MS <= t1.GetNowTime())
{ {
m_World->BroadcastPlayerListUpdatePing(*this); m_World->BroadcastPlayerListUpdatePing(*this);
m_LastPlayerListTime = t1.GetNowTime(); m_LastPlayerListTime = std::chrono::steady_clock::now();
} }
if (IsFlying()) if (IsFlying())
@ -358,7 +359,7 @@ float cPlayer::GetXpPercentage()
bool cPlayer::SetCurrentExperience(short int a_CurrentXp) 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); LOGWARNING("Tried to update experiece with an invalid Xp value: %d", a_CurrentXp);
return false; // oops, they gave us a dodgey number 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) 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 // Value was bad, abort and report
LOGWARNING("Attempt was made to increment Xp by %d, which overflowed the short datatype. Ignoring.", LOGWARNING("Attempt was made to increment Xp by %d, which overflowed the short datatype. Ignoring.", a_Xp_delta);
a_Xp_delta);
return -1; // Should we instead just return the current Xp? return -1; // Should we instead just return the current Xp?
} }
m_CurrentXp += a_Xp_delta; m_CurrentXp += a_Xp_delta;
// Make sure they didn't subtract too much // 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 // Update total for score calculation
if (a_Xp_delta > 0) if (a_Xp_delta > 0)
@ -397,8 +397,7 @@ short cPlayer::DeltaExperience(short a_Xp_delta)
m_LifetimeTotalXp += a_Xp_delta; m_LifetimeTotalXp += a_Xp_delta;
} }
LOGD("Player \"%s\" gained/lost %d experience, total is now: %d", LOGD("Player \"%s\" gained/lost %d experience, total is now: %d", GetName().c_str(), a_Xp_delta, m_CurrentXp);
GetName().c_str(), a_Xp_delta, m_CurrentXp);
// Set experience to be updated // Set experience to be updated
m_bDirtyExperience = true; m_bDirtyExperience = true;
@ -1074,7 +1073,7 @@ bool cPlayer::IsGameModeAdventure(void) const
bool cPlayer::IsGameModeSpectator(void) const bool cPlayer::IsGameModeSpectator(void) const
{ {
return (m_GameMode == gmSpectator) || // Either the player is explicitly in Spectator 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); a_World->AddPlayer(this);
SetWorld(a_World); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value 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; return true;
} }
@ -1890,8 +1892,8 @@ void cPlayer::UseEquippedItem(int a_Amount)
void cPlayer::TickBurning(cChunk & a_Chunk) void cPlayer::TickBurning(cChunk & a_Chunk)
{ {
// Don't burn in creative and stop burning in creative if necessary // Don't burn in creative or spectator and stop burning in creative if necessary
if (!IsGameModeCreative()) if (!IsGameModeCreative() && !IsGameModeSpectator())
{ {
super::TickBurning(a_Chunk); super::TickBurning(a_Chunk);
} }
@ -1910,9 +1912,9 @@ void cPlayer::HandleFood(void)
{ {
// Ref.: http://www.minecraftwiki.net/wiki/Hunger // 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; return;
} }
@ -2077,7 +2079,7 @@ void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos)
void cPlayer::ApplyFoodExhaustionFromMovement() void cPlayer::ApplyFoodExhaustionFromMovement()
{ {
if (IsGameModeCreative()) if (IsGameModeCreative() || IsGameModeSpectator())
{ {
return; return;
} }

View File

@ -516,7 +516,7 @@ protected:
/** The item being dragged by the cursor while in a UI window */ /** The item being dragged by the cursor while in a UI window */
cItem m_DraggingItem; cItem m_DraggingItem;
long long m_LastPlayerListTime; std::chrono::steady_clock::time_point m_LastPlayerListTime;
cClientHandle * m_ClientHandle; cClientHandle * m_ClientHandle;

View File

@ -4,13 +4,15 @@
// Implements the cFastRandom class representing a fast random number generator // Implements the cFastRandom class representing a fast random number generator
#include "Globals.h" #include "Globals.h"
#include <time.h>
#include "FastRandom.h" #include "FastRandom.h"
////////////////////////////////////////////////////////////////////////////////
// cFastRandom:
#if 0 && defined(_DEBUG) #if 0 && defined(_DEBUG)
// Self-test // Self-test
// Both ints and floats are quick-tested to see if the random is calculated correctly, checking the range in ASSERTs, // 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) : cFastRandom::cFastRandom(void) :
m_Seed(m_SeedCounter++), m_LinearRand(static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count()))
m_Counter(0)
{ {
} }
@ -102,82 +96,96 @@ cFastRandom::cFastRandom(void) :
int cFastRandom::NextInt(int a_Range) int cFastRandom::NextInt(int a_Range)
{ {
ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges std::uniform_int_distribution<> distribution(0, a_Range - 1);
ASSERT(a_Range > 0); return distribution(m_LinearRand);
// 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);
} }
int cFastRandom::NextInt(int a_Range, int a_Salt) int cFastRandom::NextInt(int a_Range, int a_Salt)
{ {
ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges m_LinearRand.seed(a_Salt);
ASSERT(a_Range > 0); std::uniform_int_distribution<> distribution(0, a_Range - 1);
return distribution(m_LinearRand);
// 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);
} }
float cFastRandom::NextFloat(float a_Range) float cFastRandom::NextFloat(float a_Range)
{ {
// Make the m_Counter operations as minimal as possible, to emulate atomicity std::uniform_real_distribution<float> distribution(0, a_Range);
int Counter = m_Counter++; return distribution(m_LinearRand);
// 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);
} }
float cFastRandom::NextFloat(float a_Range, int a_Salt) float cFastRandom::NextFloat(float a_Range, int a_Salt)
{ {
// Make the m_Counter operations as minimal as possible, to emulate atomicity m_LinearRand.seed(a_Salt);
int Counter = m_Counter++; std::uniform_real_distribution<float> distribution(0, a_Range);
return distribution(m_LinearRand);
// 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);
} }
int cFastRandom::GenerateRandomInteger(int a_Begin, int a_End) int cFastRandom::GenerateRandomInteger(int a_Begin, int a_End)
{ {
cFastRandom Random; std::uniform_int_distribution<> distribution(a_Begin, a_End);
return Random.NextInt(a_End - a_Begin + 1) + a_Begin; 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);
} }

View File

@ -22,6 +22,7 @@ salts, the values they get will be different.
#pragma once #pragma once
#include <random>
@ -30,18 +31,19 @@ salts, the values they get will be different.
class cFastRandom class cFastRandom
{ {
public: public:
cFastRandom(void); 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); 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); 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); 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); float NextFloat(float a_Range, int a_Salt);
/** Returns a random float between 0 and 1. */ /** Returns a random float between 0 and 1. */
@ -49,14 +51,35 @@ public:
/** Returns a random int in the range [a_Begin .. a_End] */ /** Returns a random int in the range [a_Begin .. a_End] */
int GenerateRandomInteger(int a_Begin, int a_End); int GenerateRandomInteger(int a_Begin, int a_End);
protected: private:
int m_Seed;
int m_Counter; std::minstd_rand m_LinearRand;
};
/// Counter that is used to initialize the seed, incremented for each object created
static int m_SeedCounter;
} ;
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;
};

View File

@ -115,7 +115,7 @@ void cFurnaceRecipe::AddFuelFromLine(const AString & a_Line, unsigned int a_Line
Line.erase(Line.begin()); // Remove the beginning "!" Line.erase(Line.begin()); // Remove the beginning "!"
Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end()); 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; int BurnTime;
const AStringVector & Sides = StringSplit(Line, "="); 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()); Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
int CookTime = 200; int CookTime = 200;
std::auto_ptr<cItem> InputItem(new cItem()); std::unique_ptr<cItem> InputItem(new cItem());
std::auto_ptr<cItem> OutputItem(new cItem()); std::unique_ptr<cItem> OutputItem(new cItem());
const AStringVector & Sides = StringSplit(Line, "="); const AStringVector & Sides = StringSplit(Line, "=");
if (Sides.size() != 2) if (Sides.size() != 2)

View File

@ -15,7 +15,7 @@ Interfaces to the various biome generators:
#pragma once #pragma once
#include "ComposableGenerator.h" #include "ComposableGenerator.h"
#include "../Noise.h" #include "../Noise/Noise.h"
#include "../VoronoiMap.h" #include "../VoronoiMap.h"

View File

@ -10,6 +10,7 @@ SET (SRCS
ChunkDesc.cpp ChunkDesc.cpp
ChunkGenerator.cpp ChunkGenerator.cpp
CompoGen.cpp CompoGen.cpp
CompoGenBiomal.cpp
ComposableGenerator.cpp ComposableGenerator.cpp
DistortedHeightmap.cpp DistortedHeightmap.cpp
DungeonRoomsFinisher.cpp DungeonRoomsFinisher.cpp
@ -30,8 +31,10 @@ SET (SRCS
StructGen.cpp StructGen.cpp
TestRailsGen.cpp TestRailsGen.cpp
Trees.cpp Trees.cpp
TwoHeights.cpp
UnderwaterBaseGen.cpp UnderwaterBaseGen.cpp
VillageGen.cpp) VillageGen.cpp
)
SET (HDRS SET (HDRS
BioGen.h BioGen.h
@ -39,7 +42,9 @@ SET (HDRS
ChunkDesc.h ChunkDesc.h
ChunkGenerator.h ChunkGenerator.h
CompoGen.h CompoGen.h
CompoGenBiomal.h
ComposableGenerator.h ComposableGenerator.h
CompositedHeiGen.h
DistortedHeightmap.h DistortedHeightmap.h
DungeonRoomsFinisher.h DungeonRoomsFinisher.h
EndGen.h EndGen.h
@ -58,11 +63,14 @@ SET (HDRS
RainbowRoadsGen.h RainbowRoadsGen.h
Ravines.h Ravines.h
RoughRavines.h RoughRavines.h
ShapeGen.cpp
StructGen.h StructGen.h
TestRailsGen.h TestRailsGen.h
Trees.h Trees.h
TwoHeights.h
UnderwaterBaseGen.h UnderwaterBaseGen.h
VillageGen.h) VillageGen.h
)
if(NOT MSVC) if(NOT MSVC)
add_library(Generating ${SRCS} ${HDRS}) add_library(Generating ${SRCS} ${HDRS})

View File

@ -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; float oct1 = (a_Noise.CubicNoise3D(x * 0.1f, y * 0.1f, z * 0.1f)) * 4;
oct1 = oct1 * oct1 * oct1; oct1 = oct1 * oct1 * oct1;
if (oct1 < 0.f) oct1 = PI_2; if (oct1 < 0.f)
if (oct1 > PI_2) oct1 = PI_2; {
oct1 = PI_2;
}
if (oct1 > PI_2)
{
oct1 = PI_2;
}
return oct1; return oct1;
} }

View File

@ -13,7 +13,6 @@
#pragma once #pragma once
#include "GridStructGen.h" #include "GridStructGen.h"
#include "../Noise.h"

View File

@ -7,7 +7,7 @@
#include "ChunkDesc.h" #include "ChunkDesc.h"
#include "../BlockArea.h" #include "../BlockArea.h"
#include "../Cuboid.h" #include "../Cuboid.h"
#include "../Noise.h" #include "../Noise/Noise.h"
#include "../BlockEntities/BlockEntity.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) void cChunkDesc::SetUseDefaultBiomes(bool a_bUseDefaultBiomes)
{ {
m_bUseDefaultBiomes = 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( void cChunkDesc::FillRelCuboid(
int a_MinX, int a_MaxX, int a_MinX, int a_MaxX,
int a_MinY, int a_MaxY, int a_MinY, int a_MaxY,

View File

@ -29,10 +29,17 @@ class cChunkDesc
{ {
public: public:
// tolua_end // 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 */ /** Uncompressed block metas, 1 meta per byte */
typedef NIBBLETYPE BlockNibbleBytes[cChunkDef::NumBlocks]; typedef NIBBLETYPE BlockNibbleBytes[cChunkDef::NumBlocks];
cChunkDesc(int a_ChunkX, int a_ChunkZ); cChunkDesc(int a_ChunkX, int a_ChunkZ);
~cChunkDesc(); ~cChunkDesc();
@ -57,10 +64,21 @@ public:
EMCSBiome GetBiome(int a_RelX, int a_RelZ); EMCSBiome GetBiome(int a_RelX, int a_RelZ);
// These operate on the heightmap, so they could get out of sync with the data // 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); void SetHeight(int a_RelX, int a_RelZ, int a_Height);
int GetHeight(int a_RelX, int a_RelZ); 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: // Default generation:
void SetUseDefaultBiomes(bool a_bUseDefaultBiomes); void SetUseDefaultBiomes(bool a_bUseDefaultBiomes);
bool IsUsingDefaultBiomes(void) const; bool IsUsingDefaultBiomes(void) const;
@ -77,8 +95,11 @@ public:
/** Reads an area from the chunk into a cBlockArea, blocktypes and blockmetas */ /** 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); 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; 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 */ /** Fills the relative cuboid with specified block; allows cuboid out of range of this chunk */
void FillRelCuboid( void FillRelCuboid(

View File

@ -6,7 +6,7 @@
#include "ChunkDesc.h" #include "ChunkDesc.h"
#include "ComposableGenerator.h" #include "ComposableGenerator.h"
#include "Noise3DGenerator.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) 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); AString BlockType = a_IniFile.GetValueSet(a_SectionName, a_ValueName, a_Default);
BLOCKTYPE Block = BlockStringToType(BlockType); int Block = BlockStringToType(BlockType);
if (Block < 0) 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()); 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);
} }

View File

@ -21,8 +21,9 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// cCompoGenSameBlock: // 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); a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
for (int z = 0; z < cChunkDef::Width; z++) for (int z = 0; z < cChunkDef::Width; z++)
{ {
@ -63,7 +64,7 @@ void cCompoGenSameBlock::InitializeCompoGen(cIniFile & a_IniFile)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// cCompoGenDebugBiomes: // cCompoGenDebugBiomes:
void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc) void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{ {
static BLOCKTYPE Blocks[] = static BLOCKTYPE Blocks[] =
{ {
@ -92,6 +93,7 @@ void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc)
E_BLOCK_BEDROCK, E_BLOCK_BEDROCK,
} ; } ;
a_ChunkDesc.SetHeightFromShape(a_Shape);
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
for (int z = 0; z < cChunkDef::Width; z++) 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: /* The classic composition means:
- 1 layer of grass, 3 of dirt and the rest stone, if the height > sealevel + beachheight - 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.FillBlocks(E_BLOCK_AIR, 0);
a_ChunkDesc.SetHeightFromShape(a_Shape);
// The patterns to use for different situations, must be same length! // The patterns to use for different situations, must be same length!
const BLOCKTYPE PatternGround[] = {m_BlockTop, m_BlockMiddle, m_BlockMiddle, m_BlockMiddle} ; 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) 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_BeachHeight = a_IniFile.GetValueSetI("Generator", "ClassicBeachHeight", m_BeachHeight);
m_BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", m_BeachDepth); m_BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", m_BeachDepth);
m_BlockTop = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockTop", "grass").m_ItemType); m_BlockTop = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockTop", "grass").m_ItemType);
@ -209,323 +212,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:
@ -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(); HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight();
@ -604,17 +290,7 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc)
BLOCKTYPE Block = E_BLOCK_AIR; BLOCKTYPE Block = E_BLOCK_AIR;
if (Val < m_Threshold) // Don't calculate if the block should be Netherrack or Soulsand when it's already decided that it's air. if (Val < 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; Block = E_BLOCK_NETHERRACK;
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;
}
} }
a_ChunkDesc.SetBlockType(x, y + Segment, z, Block); a_ChunkDesc.SetBlockType(x, y + Segment, z, Block);
} }
@ -638,7 +314,7 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc)
CeilingDisguise = -CeilingDisguise; CeilingDisguise = -CeilingDisguise;
} }
int CeilingDisguiseHeight = Height - 2 - (int)CeilingDisguise * 3; int CeilingDisguiseHeight = Height - 2 - FloorC(CeilingDisguise * 3);
for (int y = Height - 1; y > CeilingDisguiseHeight; y--) 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 #ifdef _DEBUG
if (((m_NumHits + m_NumMisses) % 1024) == 10) if (((m_NumHits + m_NumMisses) % 1024) == 10)
@ -731,6 +407,7 @@ void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc)
// Use the cached data: // Use the cached data:
memcpy(a_ChunkDesc.GetBlockTypes(), m_CacheData[Idx].m_BlockTypes, sizeof(a_ChunkDesc.GetBlockTypes())); 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.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_NumHits++;
m_TotalChain += i; m_TotalChain += i;
@ -739,7 +416,7 @@ void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc)
// Not in the cache: // Not in the cache:
m_NumMisses++; m_NumMisses++;
m_Underlying->ComposeTerrain(a_ChunkDesc); m_Underlying->ComposeTerrain(a_ChunkDesc, a_Shape);
// Insert it as the first item in the MRU order: // Insert it as the first item in the MRU order:
int Idx = m_CacheOrder[m_CacheSize - 1]; int Idx = m_CacheOrder[m_CacheSize - 1];
@ -750,6 +427,7 @@ void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc)
m_CacheOrder[0] = Idx; m_CacheOrder[0] = Idx;
memcpy(m_CacheData[Idx].m_BlockTypes, a_ChunkDesc.GetBlockTypes(), sizeof(a_ChunkDesc.GetBlockTypes())); 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_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_ChunkX = ChunkX;
m_CacheData[Idx].m_ChunkZ = ChunkZ; m_CacheData[Idx].m_ChunkZ = ChunkZ;
} }

View File

@ -17,7 +17,7 @@
#pragma once #pragma once
#include "ComposableGenerator.h" #include "ComposableGenerator.h"
#include "../Noise.h" #include "../Noise/Noise.h"
@ -38,7 +38,7 @@ protected:
bool m_IsBedrocked; bool m_IsBedrocked;
// cTerrainCompositionGen overrides: // 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; virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ; } ;
@ -55,7 +55,7 @@ public:
protected: protected:
// cTerrainCompositionGen overrides: // 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; BLOCKTYPE m_BlockSea;
// cTerrainCompositionGen overrides: // 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; 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 : class cCompoGenNether :
public cTerrainCompositionGen public cTerrainCompositionGen
{ {
@ -136,7 +102,7 @@ protected:
int m_Threshold; int m_Threshold;
// cTerrainCompositionGen overrides: // 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; virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ; } ;
@ -153,7 +119,7 @@ public:
~cCompoGenCache(); ~cCompoGenCache();
// cTerrainCompositionGen override: // 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; virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
protected: protected:
@ -166,6 +132,7 @@ protected:
int m_ChunkZ; int m_ChunkZ;
cChunkDef::BlockTypes m_BlockTypes; cChunkDef::BlockTypes m_BlockTypes;
cChunkDesc::BlockNibbleBytes m_BlockMetas; // The metas are uncompressed, 1 meta per byte 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 // To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data

View File

@ -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);
}

View File

@ -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);

View File

@ -17,6 +17,10 @@
#include "StructGen.h" #include "StructGen.h"
#include "FinishGen.h" #include "FinishGen.h"
#include "CompoGenBiomal.h"
#include "CompositedHeiGen.h"
#include "Caves.h" #include "Caves.h"
#include "DistortedHeightmap.h" #include "DistortedHeightmap.h"
#include "DungeonRoomsFinisher.h" #include "DungeonRoomsFinisher.h"
@ -39,7 +43,7 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// cTerrainCompositionGen: // 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", ""); AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", "");
if (CompoGenName.empty()) if (CompoGenName.empty())
@ -48,59 +52,52 @@ cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile
CompoGenName = "Biomal"; CompoGenName = "Biomal";
} }
cTerrainCompositionGen * res = nullptr; // Compositor list is alpha-sorted
if (NoCaseCompare(CompoGenName, "sameblock") == 0) cTerrainCompositionGenPtr res;
if (NoCaseCompare(CompoGenName, "Biomal") == 0)
{ {
res = new cCompoGenSameBlock; res = CreateCompoGenBiomal(a_Seed);
} }
else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0) else if (NoCaseCompare(CompoGenName, "BiomalNoise3D") == 0)
{ {
res = new cCompoGenDebugBiomes; // The composition that used to be provided with BiomalNoise3D is now provided by the Biomal compositor:
res = CreateCompoGenBiomal(a_Seed);
} }
else if (NoCaseCompare(CompoGenName, "classic") == 0) else if (NoCaseCompare(CompoGenName, "Classic") == 0)
{ {
res = new cCompoGenClassic; res = std::make_shared<cCompoGenClassic>();
}
else if (NoCaseCompare(CompoGenName, "DebugBiomes") == 0)
{
res = std::make_shared<cCompoGenDebugBiomes>();
} }
else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0) 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) 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); res = std::make_shared<cCompoGenSameBlock>();
/*
// 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);
//*/
} }
else else
{ {
LOGWARN("Unknown CompositionGen \"%s\", using \"Biomal\" instead.", CompoGenName.c_str()); LOGWARN("Unknown CompositionGen \"%s\", using \"Biomal\" instead.", CompoGenName.c_str());
a_IniFile.SetValue("Generator", "CompositionGen", "Biomal"); 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); ASSERT(res != nullptr);
@ -120,7 +117,7 @@ cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile
cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) : cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) :
super(a_ChunkGenerator), super(a_ChunkGenerator),
m_BiomeGen(), m_BiomeGen(),
m_HeightGen(), m_ShapeGen(),
m_CompositionGen() m_CompositionGen()
{ {
} }
@ -134,7 +131,7 @@ void cComposableGenerator::Initialize(cIniFile & a_IniFile)
super::Initialize(a_IniFile); super::Initialize(a_IniFile);
InitBiomeGen(a_IniFile); InitBiomeGen(a_IniFile);
InitHeightGen(a_IniFile); InitShapeGen(a_IniFile);
InitCompositionGen(a_IniFile); InitCompositionGen(a_IniFile);
InitFinishGens(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()); m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetBiomeMap());
} }
cChunkDesc::Shape shape;
if (a_ChunkDesc.IsUsingDefaultHeight()) 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; bool ShouldUpdateHeightmap = false;
if (a_ChunkDesc.IsUsingDefaultComposition()) if (a_ChunkDesc.IsUsingDefaultComposition())
{ {
m_CompositionGen->ComposeTerrain(a_ChunkDesc); m_CompositionGen->ComposeTerrain(a_ChunkDesc, shape);
ShouldUpdateHeightmap = true;
} }
if (a_ChunkDesc.IsUsingDefaultFinish()) 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; 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: // 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 > 0)
{ {
if (CacheSize < 4) if (CacheSize < 4)
@ -249,6 +254,7 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile)
LOGD("Using a cache for Heightgen of size %d.", CacheSize); LOGD("Using a cache for Heightgen of size %d.", CacheSize);
m_HeightGen = cTerrainHeightGenPtr(new cHeiGenCache(m_HeightGen, 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) 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); 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) for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
{ {
// Finishers, alpha-sorted: // 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 DefaultBottomLavaLevel = (Dimension == dimNether) ? 30 : 10;
int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel); 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 MaxSize = a_IniFile.GetValueSetI("Generator", "DungeonRoomsMaxSize", 7);
int MinSize = a_IniFile.GetValueSetI("Generator", "DungeonRoomsMinSize", 5); 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"); 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) else if (NoCaseCompare(*itr, "Ice") == 0)
{ {
@ -338,7 +354,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
else if (NoCaseCompare(*itr, "LavaLakes") == 0) else if (NoCaseCompare(*itr, "LavaLakes") == 0)
{ {
int Probability = a_IniFile.GetValueSetI("Generator", "LavaLakesProbability", 10); 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) else if (NoCaseCompare(*itr, "LavaSprings") == 0)
{ {
@ -558,6 +574,10 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
GridSize, MaxOffset GridSize, MaxOffset
))); )));
} }
else if (NoCaseCompare(*itr, "SoulsandRims") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSoulsandRims(Seed)));
}
else if (NoCaseCompare(*itr, "Snow") == 0) else if (NoCaseCompare(*itr, "Snow") == 0)
{ {
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSnow)); m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSnow));
@ -576,7 +596,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
} }
else if (NoCaseCompare(*itr, "Trees") == 0) 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) 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 MaxOffset = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxOffset", 128);
int MaxDepth = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxDepth", 7); int MaxDepth = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxDepth", 7);
int MaxSize = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxSize", 128); 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) 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 MaxSize = a_IniFile.GetValueSetI("Generator", "VillageMaxSize", 128);
int MinDensity = a_IniFile.GetValueSetI("Generator", "VillageMinDensity", 50); int MinDensity = a_IniFile.GetValueSetI("Generator", "VillageMinDensity", 50);
int MaxDensity = a_IniFile.GetValueSetI("Generator", "VillageMaxDensity", 80); 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) else if (NoCaseCompare(*itr, "WaterLakes") == 0)
{ {
int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25); 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) else if (NoCaseCompare(*itr, "WaterSprings") == 0)
{ {

View File

@ -26,20 +26,16 @@ See http://forum.mc-server.org/showthread.php?tid=409 for details.
// Forward-declare the shared pointers to subgenerator classes: // Forward-declare the shared pointers to subgenerator classes:
class cBiomeGen; class cBiomeGen;
class cTerrainShapeGen;
class cTerrainHeightGen; class cTerrainHeightGen;
class cTerrainCompositionGen; class cTerrainCompositionGen;
class cFinishGen; class cFinishGen;
typedef SharedPtr<cBiomeGen> cBiomeGenPtr; typedef SharedPtr<cBiomeGen> cBiomeGenPtr;
typedef SharedPtr<cTerrainShapeGen> cTerrainShapeGenPtr;
typedef SharedPtr<cTerrainHeightGen> cTerrainHeightGenPtr; typedef SharedPtr<cTerrainHeightGen> cTerrainHeightGenPtr;
typedef SharedPtr<cTerrainCompositionGen> cTerrainCompositionGenPtr; typedef SharedPtr<cTerrainCompositionGen> cTerrainCompositionGenPtr;
typedef SharedPtr<cFinishGen> cFinishGenPtr; 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 /** The interface that a terrain shape generator must implement
A terrain height generator takes chunk coords on input and outputs an array of terrain heights for that chunk. A terrain shape generator takes chunk coords on input and outputs a 3D array of "shape" for that chunk. The shape here
The output array is sequenced in the same way as the BiomeGen's biome data. 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 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 cTerrainShapeGen
{
public:
virtual ~cTerrainShapeGen() {} // Force a virtual destructor in descendants
/** 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 InitializeShapeGen(cIniFile & a_IniFile) {}
/** 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 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 class cTerrainHeightGen
{ {
public: public:
virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants
/** Generates heightmap for the given chunk */ /** Retrieves the heightmap for the specified chunk. */
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0; virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0;
/** Reads parameters from the ini file, prepares generator for use. */ /** Initializes the generator, reading its parameters from the INI file. */
virtual void InitializeHeightGen(cIniFile & a_IniFile) {} virtual void InitializeHeightGen(cIniFile & a_IniFile) {}
/** Creates the correct TerrainHeightGen descendant based on the ini file settings and the seed provided. /** Creates a cTerrainHeightGen descendant based on the INI file settings. */
a_BiomeGen is the underlying biome generator, some height generators may depend on it to generate more biomes
a_CacheOffByDefault gets set to whether the cache should be disabled by default
Implemented in HeiGen.cpp!
*/
static cTerrainHeightGenPtr CreateHeightGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault); static cTerrainHeightGenPtr CreateHeightGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault);
} ; } ;
@ -109,16 +131,18 @@ class cTerrainCompositionGen
public: public:
virtual ~cTerrainCompositionGen() {} // Force a virtual destructor in descendants 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. */ /** Reads parameters from the ini file, prepares generator for use. */
virtual void InitializeCompoGen(cIniFile & a_IniFile) {} virtual void InitializeCompoGen(cIniFile & a_IniFile) {}
/** Creates the correct TerrainCompositionGen descendant based on the ini file settings and the seed provided. /** 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_BiomeGen is the underlying biome generator, some composition generators may depend on it providing additional biomes around the chunk
a_HeightGen is the underlying height generator, some composition generators may depend on it providing additional values 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 /** The interface that a finisher must implement
Finisher implements changes to the chunk after the rough terrain has been generated. Finisher implements changes to the chunk after the rough terrain has been generated.
Examples of finishers are trees, snow, ore, lilypads and others. 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 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) no longer relevant, all structure generators are considered finishers now (#398)
*/ */
@ -154,23 +178,34 @@ class cComposableGenerator :
public: public:
cComposableGenerator(cChunkGenerator & a_ChunkGenerator); cComposableGenerator(cChunkGenerator & a_ChunkGenerator);
// cChunkGenerator::cGenerator overrides:
virtual void Initialize(cIniFile & a_IniFile) override; virtual void Initialize(cIniFile & a_IniFile) override;
virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) 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; virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
protected: protected:
// The generation composition: // The generator's composition:
cBiomeGenPtr m_BiomeGen; /** The biome generator. */
cTerrainHeightGenPtr m_HeightGen; cBiomeGenPtr m_BiomeGen;
/** The terrain shape generator. */
cTerrainShapeGenPtr m_ShapeGen;
/** The terrain composition generator. */
cTerrainCompositionGenPtr m_CompositionGen; 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); void InitBiomeGen(cIniFile & a_IniFile);
/** Reads the HeightGen settings from the ini and initializes m_HeightGen accordingly */ /** Reads the ShapeGen settings from the ini and initializes m_ShapeGen accordingly */
void InitHeightGen(cIniFile & a_IniFile); void InitShapeGen(cIniFile & a_IniFile);
/** Reads the CompositionGen settings from the ini and initializes m_CompositionGen accordingly */ /** Reads the CompositionGen settings from the ini and initializes m_CompositionGen accordingly */
void InitCompositionGen(cIniFile & a_IniFile); void InitCompositionGen(cIniFile & a_IniFile);

View File

@ -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;
};

View File

@ -14,163 +14,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: // cDistortedHeightmap:
@ -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}, {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 {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 /* biSunflowerPlains */ { 1.0f, 1.0f}, // 129
/* biDesertM */ { 1.0f, 1.0f}, // 130 /* biDesertM */ { 1.0f, 1.0f}, // 130
/* biExtremeHillsM */ {16.0f, 16.0f}, // 131 /* 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) : cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGenPtr a_BiomeGen) :
m_NoiseDistortX(a_Seed + 1000), m_NoiseDistortX(a_Seed + 1000),
m_NoiseDistortZ(a_Seed + 2000), m_NoiseDistortZ(a_Seed + 2000),
m_OceanFloorSelect(a_Seed + 3000), m_CurChunkX(0x7fffffff), // Set impossible coords for the chunk so that it's always considered stale
m_MesaFloor(a_Seed + 4000), m_CurChunkZ(0x7fffffff),
m_BiomeGen(a_BiomeGen), m_BiomeGen(a_BiomeGen),
m_UnderlyingHeiGen(new cHeiGenBiomal(a_Seed, a_BiomeGen)), m_UnderlyingHeiGen(new cHeiGenBiomal(a_Seed, a_BiomeGen)),
m_HeightGen(m_UnderlyingHeiGen, 64), 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)1, (NOISE_DATATYPE)0.5);
m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1); m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1);
m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2); 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: // 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_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyX", 10);
m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyY", 10); m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyY", 10);
m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyZ", 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) void cDistortedHeightmap::PrepareState(int a_ChunkX, int a_ChunkZ)
{ {
if ((m_CurChunkX == a_ChunkX) && (m_CurChunkZ == 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); PrepareState(a_ChunkX, a_ChunkZ);
for (int z = 0; z < cChunkDef::Width; z++) for (int z = 0; z < cChunkDef::Width; z++)
{ {
for (int x = 0; x < cChunkDef::Width; x++) for (int x = 0; x < cChunkDef::Width; x++)
{ {
int NoiseArrayIdx = x + 17 * 257 * z; int idx = x + 17 * 257 * z;
cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel - 1); for (int y = 0; y < cChunkDef::Height; y++)
for (int y = cChunkDef::Height - 1; y > m_SeaLevel - 1; y--)
{ {
int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y]; a_Shape[y + x * 256 + z * 16 * 256] = (y < m_DistortedHeightmap[idx + y * 17]) ? 1 : 0;
if (y < HeightMapHeight)
{
cChunkDef::SetHeight(a_HeightMap, x, z, y);
break;
}
} // for y } // for y
} // for x } // for x
} // for z } // for z
@ -500,36 +252,7 @@ void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::He
void cDistortedHeightmap::InitializeHeightGen(cIniFile & a_IniFile) void cDistortedHeightmap::InitializeShapeGen(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)
{ {
Initialize(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();
}
}

View File

@ -11,7 +11,6 @@
#include "ComposableGenerator.h" #include "ComposableGenerator.h"
#include "HeiGen.h" #include "HeiGen.h"
#include "../Noise.h"
@ -24,17 +23,9 @@
class cDistortedHeightmap : class cDistortedHeightmap :
public cTerrainHeightGen, public cTerrainShapeGen
public cTerrainCompositionGen
{ {
public: public:
/// Structure used for storing block patterns for columns
struct sBlockInfo
{
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
} ;
cDistortedHeightmap(int a_Seed, cBiomeGenPtr a_BiomeGen); cDistortedHeightmap(int a_Seed, cBiomeGenPtr a_BiomeGen);
protected: protected:
@ -52,8 +43,6 @@ protected:
cPerlinNoise m_NoiseDistortX; cPerlinNoise m_NoiseDistortX;
cPerlinNoise m_NoiseDistortZ; 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; int m_SeaLevel;
NOISE_DATATYPE m_FrequencyX; NOISE_DATATYPE m_FrequencyX;
@ -71,9 +60,9 @@ protected:
cTerrainHeightGenPtr m_UnderlyingHeiGen; cTerrainHeightGenPtr m_UnderlyingHeiGen;
/** Cache for 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; cChunkDef::HeightMap m_CurChunkHeights;
// Per-biome terrain generator parameters: // Per-biome terrain generator parameters:
@ -88,54 +77,30 @@ protected:
NOISE_DATATYPE m_DistortAmpX[DIM_X * DIM_Z]; NOISE_DATATYPE m_DistortAmpX[DIM_X * DIM_Z];
NOISE_DATATYPE m_DistortAmpZ[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; 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 /** Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap). */
void InitMesaPattern(int a_Seed);
/// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap)
void PrepareState(int a_ChunkX, int a_ChunkZ); 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); 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); 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); 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); 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); 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, // cTerrainShapeGen overrides:
/// switches to ocean floor pattern if ocean is reached. Always adds bedrock at the very bottom. virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override;
void FillColumnPattern(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ, const sBlockInfo * a_Pattern); virtual void InitializeShapeGen(cIniFile & a_IniFile) override;
/// 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;
} ; } ;

View File

@ -7,6 +7,7 @@
#include "DungeonRoomsFinisher.h" #include "DungeonRoomsFinisher.h"
#include "../FastRandom.h" #include "../FastRandom.h"
#include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/ChestEntity.h"
#include "../BlockEntities/MobSpawnerEntity.h"
@ -57,6 +58,22 @@ public:
int SecondChestPos = (FirstChestPos + 2 + (rnd % (NumPositions - 3))) % NumPositions; int SecondChestPos = (FirstChestPos + 2 + (rnd % (NumPositions - 3))) % NumPositions;
m_Chest1 = DecodeChestCoords(FirstChestPos, SizeX, SizeZ); m_Chest1 = DecodeChestCoords(FirstChestPos, SizeX, SizeZ);
m_Chest2 = DecodeChestCoords(SecondChestPos, 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: protected:
@ -76,9 +93,12 @@ protected:
/** The (absolute) coords of the second chest. The Y coord represents the chest's Meta value (facing). */ /** The (absolute) coords of the second chest. The Y coord represents the chest's Meta value (facing). */
Vector3i m_Chest2; 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) Vector3i DecodeChestCoords(int a_PosIdx, int a_SizeX, int a_SizeZ)
{ {
if (a_PosIdx < a_SizeX) if (a_PosIdx < a_SizeX)
@ -245,7 +265,9 @@ protected:
) )
{ {
a_ChunkDesc.SetBlockTypeMeta(CenterX, b, CenterZ, E_BLOCK_MOB_SPAWNER, 0); 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::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), 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_MaxHalfSize((a_MaxSize + 1) / 2),
m_MinHalfSize((a_MinSize + 1) / 2), m_MinHalfSize((a_MinSize + 1) / 2),
m_HeightProbability(cChunkDef::Height) m_HeightProbability(cChunkDef::Height)
@ -293,13 +315,21 @@ cDungeonRoomsFinisher::cStructurePtr cDungeonRoomsFinisher::CreateStructure(int
int ChunkX, ChunkZ; int ChunkX, ChunkZ;
int RelX = a_OriginX, RelY = 0, RelZ = a_OriginZ; int RelX = a_OriginX, RelY = 0, RelZ = a_OriginZ;
cChunkDef::AbsoluteToRelative(RelX, RelY, RelZ, ChunkX, ChunkZ); cChunkDef::AbsoluteToRelative(RelX, RelY, RelZ, ChunkX, ChunkZ);
cChunkDef::HeightMap HeightMap; cChunkDesc::Shape shape;
m_HeightGen->GenHeightMap(ChunkX, ChunkZ, HeightMap); m_ShapeGen->GenShape(ChunkX, ChunkZ, shape);
int Height = cChunkDef::GetHeight(HeightMap, RelX, RelZ); // Max room height at {a_OriginX, a_OriginZ} int height = 0;
Height = Clamp(m_HeightProbability.MapValue(rnd % m_HeightProbability.GetSum()), 10, Height - 5); 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: // 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));
} }

View File

@ -23,15 +23,15 @@ class cDungeonRoomsFinisher :
public: public:
/** Creates a new dungeon room finisher. /** 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_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). */ 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: protected:
/** The height gen that is used for limiting the rooms' Y coords */ /** The shape gen that is used for limiting the rooms' Y coords */
cTerrainHeightGenPtr m_HeightGen; cTerrainShapeGenPtr m_ShapeGen;
/** Maximum half-size (from center to wall) of the dungeon room's inner (air) area. Default is 3 (vanilla). */ /** Maximum half-size (from center to wall) of the dungeon room's inner (air) area. Default is 3 (vanilla). */
int m_MaxHalfSize; int m_MaxHalfSize;

View File

@ -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)) 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; 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++) for (int x = 0; x < cChunkDef::Width; x++)
{ {
cChunkDef::SetHeight(a_HeightMap, x, z, MaxY); for (int y = 0; y < MaxY; y++)
for (int y = MaxY; y > 0; y--)
{ {
if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0) a_Shape[(x + 16 * z) * 256 + y] = (m_NoiseArray[y * 17 * 17 + z * 17 + z] > 0) ? 1 : 0;
{ }
cChunkDef::SetHeight(a_HeightMap, x, z, y); for (int y = MaxY; y < cChunkDef::Height; y++)
break; {
} a_Shape[(x + 16 * z) * 256 + y] = 0;
} // for y }
} // for x } // for x
} // for z } // 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);
{
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);
for (int z = 0; z < cChunkDef::Width; z++) for (int z = 0; z < cChunkDef::Width; z++)
{ {
for (int x = 0; x < cChunkDef::Width; x++) 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) if (a_Shape[(x + 16 * z) * 256 + y] != 0)
{ {
a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_END_STONE, 0); a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_END_STONE);
}
else
{
a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_AIR, 0);
} }
} // for y } // for y
} // for x } // for x

View File

@ -10,14 +10,14 @@
#pragma once #pragma once
#include "ComposableGenerator.h" #include "ComposableGenerator.h"
#include "../Noise.h" #include "../Noise/Noise.h"
class cEndGen : class cEndGen :
public cTerrainHeightGen, public cTerrainShapeGen,
public cTerrainCompositionGen public cTerrainCompositionGen
{ {
public: public:
@ -59,10 +59,10 @@ protected:
/// Returns true if the chunk is outside of the island's dimensions /// Returns true if the chunk is outside of the island's dimensions
bool IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ); bool IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ);
// cTerrainHeightGen overrides: // cTerrainShapeGen 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;
// cTerrainCompositionGen overrides: // 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; virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ; } ;

View File

@ -10,7 +10,6 @@
#include "Globals.h" #include "Globals.h"
#include "FinishGen.h" #include "FinishGen.h"
#include "../Noise.h"
#include "../BlockID.h" #include "../BlockID.h"
#include "../Simulator/FluidSimulator.h" // for cFluidSimulator::CanWashAway() #include "../Simulator/FluidSimulator.h" // for cFluidSimulator::CanWashAway()
#include "../Simulator/FireSimulator.h" #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_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_WATER_SPRINGS "0, 1; 255, 1"
#define DEF_END_LAVA_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; continue;
} }
// Choose what block to use. // Choose what block to use.
NOISE_DATATYPE BlockType = m_Noise.IntNoise3D((int) ChunkX, y, (int) ChunkZ); NOISE_DATATYPE BlockType = m_Noise.IntNoise3D((int) ChunkX, y, (int) ChunkZ);
if (BlockType < -0.7) if (BlockType < -0.7)
@ -196,10 +197,15 @@ void cFinishGenTallGrass::GenFinish(cChunkDesc & a_ChunkDesc)
{ {
continue; continue;
} }
// Get the top block + 1. This is the place where the grass would finaly be placed: // Get the top block + 1. This is the place where the grass would finaly be placed:
int y = a_ChunkDesc.GetHeight(x, z) + 1; int y = a_ChunkDesc.GetHeight(x, z) + 1;
if (y >= 255)
{
continue;
}
// Check if long grass can be placed: // Check if long grass can be placed:
if ( if (
(a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) || (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) ||
@ -277,7 +283,7 @@ bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_
{ {
return false; return false;
} }
// All conditions met, place a sugarcane here: // All conditions met, place a sugarcane here:
a_ChunkDesc.SetBlockType(a_RelX, a_RelY + 1, a_RelZ, E_BLOCK_SUGARCANE); a_ChunkDesc.SetBlockType(a_RelX, a_RelY + 1, a_RelZ, E_BLOCK_SUGARCANE);
return true; return true;
@ -290,7 +296,7 @@ bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_
void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc) void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
{ {
// Generate small foliage (1-block): // Generate small foliage (1-block):
// TODO: Update heightmap with 1-block-tall foliage // TODO: Update heightmap with 1-block-tall foliage
for (int z = 0; z < cChunkDef::Width; z++) 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 // WEIRD, since we're using heightmap, so there should NOT be anything above it
continue; continue;
} }
const float xx = (float)BlockX; const float xx = (float)BlockX;
float val1 = m_Noise.CubicNoise2D(xx * 0.1f, zz * 0.1f); float val1 = m_Noise.CubicNoise2D(xx * 0.1f, zz * 0.1f);
float val2 = m_Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f); float val2 = m_Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f);
@ -355,7 +361,7 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
} }
break; break;
} // case E_BLOCK_GRASS } // case E_BLOCK_GRASS
case E_BLOCK_SAND: case E_BLOCK_SAND:
{ {
int y = Top + 1; 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 - 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) (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); 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: // cFinishGenSnow:
@ -407,7 +480,7 @@ void cFinishGenSnow::GenFinish(cChunkDesc & a_ChunkDesc)
case biFrozenOcean: case biFrozenOcean:
{ {
int Height = a_ChunkDesc.GetHeight(x, z); 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.SetBlockType(x, Height + 1, z, E_BLOCK_SNOW);
a_ChunkDesc.SetHeight(x, z, Height + 1); a_ChunkDesc.SetHeight(x, z, Height + 1);
@ -512,7 +585,7 @@ void cFinishGenSingleTopBlock::GenFinish(cChunkDesc & a_ChunkDesc)
} }
int Height = a_ChunkDesc.GetHeight(x, z); int Height = a_ChunkDesc.GetHeight(x, z);
if (Height >= cChunkDef::Height) if (Height >= cChunkDef::Height - 1)
{ {
// Too high up // Too high up
continue; continue;
@ -712,7 +785,7 @@ void cFinishGenPreSimulator::StationarizeFluid(
} // for y } // for y
} // for x } // for x
} // for z } // for z
// Turn fluid at the chunk edges into non-stationary fluid: // Turn fluid at the chunk edges into non-stationary fluid:
for (int y = 0; y < cChunkDef::Height; y++) for (int y = 0; y < cChunkDef::Height; y++)
{ {
@ -804,12 +877,12 @@ void cFinishGenFluidSprings::GenFinish(cChunkDesc & a_ChunkDesc)
// Not in this chunk // Not in this chunk
return; return;
} }
// Get the height at which to try: // Get the height at which to try:
int Height = m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 1024, 256 * a_ChunkDesc.GetChunkZ()) / 11; int Height = m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 1024, 256 * a_ChunkDesc.GetChunkZ()) / 11;
Height %= m_HeightDistribution.GetSum(); Height %= m_HeightDistribution.GetSum();
Height = m_HeightDistribution.MapValue(Height); Height = m_HeightDistribution.MapValue(Height);
// Try adding the spring at the height, if unsuccessful, move lower: // Try adding the spring at the height, if unsuccessful, move lower:
for (int y = Height; y > 1; y--) for (int y = Height; y > 1; y--)
{ {
@ -847,7 +920,7 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int
{ {
return false; return false;
} }
static const struct static const struct
{ {
int x, y, z; int x, y, z;
@ -878,7 +951,7 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int
{ {
return false; return false;
} }
// Has exactly one air neighbor, place a spring: // Has exactly one air neighbor, place a spring:
a_ChunkDesc.SetBlockTypeMeta(x, y, z, m_Fluid, 0); a_ChunkDesc.SetBlockTypeMeta(x, y, z, m_Fluid, 0);
return true; 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;
}

View File

@ -16,8 +16,9 @@
#include "ComposableGenerator.h" #include "ComposableGenerator.h"
#include "../Noise.h" #include "../Noise/Noise.h"
#include "../ProbabDistrib.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 : class cFinishGenSprinkleFoliage :
public cFinishGen public cFinishGen
{ {
public: public:
cFinishGenSprinkleFoliage(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {} cFinishGenSprinkleFoliage(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {}
protected: protected:
cNoise m_Noise; cNoise m_Noise;
int m_Seed; int m_Seed;
/// Tries to place sugarcane at the coords specified, returns true if successful /// 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); 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: // cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ; } ;
@ -167,31 +190,31 @@ public:
{ {
m_IsAllowedBelow[idx] = false; m_IsAllowedBelow[idx] = false;
} }
// Load the allowed blocks into m_IsAllowedBelow // Load the allowed blocks into m_IsAllowedBelow
for (BlockList::iterator itr = a_AllowedBelow.begin(); itr != a_AllowedBelow.end(); ++itr) for (BlockList::iterator itr = a_AllowedBelow.begin(); itr != a_AllowedBelow.end(); ++itr)
{ {
m_IsAllowedBelow[*itr] = true; m_IsAllowedBelow[*itr] = true;
} }
// Initialize all the biome types. // Initialize all the biome types.
for (size_t idx = 0; idx < ARRAYCOUNT(m_IsBiomeAllowed); ++idx) for (size_t idx = 0; idx < ARRAYCOUNT(m_IsBiomeAllowed); ++idx)
{ {
m_IsBiomeAllowed[idx] = false; m_IsBiomeAllowed[idx] = false;
} }
// Load the allowed biomes into m_IsBiomeAllowed // Load the allowed biomes into m_IsBiomeAllowed
for (BiomeList::iterator itr = a_Biomes.begin(); itr != a_Biomes.end(); ++itr) for (BiomeList::iterator itr = a_Biomes.begin(); itr != a_Biomes.end(); ++itr)
{ {
m_IsBiomeAllowed[*itr] = true; m_IsBiomeAllowed[*itr] = true;
} }
} }
protected: protected:
cNoise m_Noise; cNoise m_Noise;
BLOCKTYPE m_BlockType; BLOCKTYPE m_BlockType;
int m_Amount; ///< Relative amount of blocks to try adding. 1 = one block per 256 biome columns. int m_Amount; ///< Relative amount of blocks to try adding. 1 = one block per 256 biome columns.
int GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap); int GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap);
// Returns true if the given biome is a biome that is allowed. // Returns true if the given biome is a biome that is allowed.
@ -206,7 +229,7 @@ protected:
return m_IsAllowedBelow[a_BlockBelow]; return m_IsAllowedBelow[a_BlockBelow];
} }
// cFinishGen override: // cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ; } ;
@ -223,11 +246,11 @@ public:
m_Level(a_Level) m_Level(a_Level)
{ {
} }
int GetLevel(void) const { return m_Level; } int GetLevel(void) const { return m_Level; }
protected: protected:
int m_Level; int m_Level;
// cFinishGen override: // cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ; } ;
@ -241,7 +264,7 @@ class cFinishGenPreSimulator :
{ {
public: public:
cFinishGenPreSimulator(bool a_PreSimulateFallingBlocks, bool a_PreSimulateWater, bool a_PreSimulateLava); cFinishGenPreSimulator(bool a_PreSimulateFallingBlocks, bool a_PreSimulateWater, bool a_PreSimulateLava);
protected: protected:
bool m_PreSimulateFallingBlocks; bool m_PreSimulateFallingBlocks;
@ -253,7 +276,7 @@ protected:
cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
cChunkDef::HeightMap & a_HeightMap // Height map to update by the current data cChunkDef::HeightMap & a_HeightMap // Height map to update by the current data
); );
/** For each fluid block: /** For each fluid block:
- if all surroundings are of the same fluid, makes it stationary; otherwise makes it flowing (excl. top) - 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 - all fluid on the chunk's edge is made flowing
@ -278,7 +301,7 @@ class cFinishGenFluidSprings :
{ {
public: public:
cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, eDimension a_Dimension); cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, eDimension a_Dimension);
protected: protected:
cNoise m_Noise; cNoise m_Noise;
@ -289,10 +312,43 @@ protected:
// cFinishGen override: // cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) 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); 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);
} ;

View File

@ -10,7 +10,7 @@
#pragma once #pragma once
#include "ComposableGenerator.h" #include "ComposableGenerator.h"
#include "../Noise.h" #include "../Noise/Noise.h"

View File

@ -15,7 +15,6 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// cHeiGenFlat: // 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) 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++) for (int i = 0; i < m_CacheSize; i++)
@ -159,6 +149,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:
@ -750,39 +785,51 @@ cTerrainHeightGenPtr cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cB
} }
a_CacheOffByDefault = false; a_CacheOffByDefault = false;
cTerrainHeightGen * res = nullptr; cTerrainHeightGenPtr res;
if (NoCaseCompare(HeightGenName, "flat") == 0) 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 a_CacheOffByDefault = true; // We're generating faster than a cache would retrieve data
} }
else if (NoCaseCompare(HeightGenName, "classic") == 0) else if (NoCaseCompare(HeightGenName, "classic") == 0)
{ {
res = new cHeiGenClassic(a_Seed); res = std::make_shared<cHeiGenClassic>(a_Seed);
} }
else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0) 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) 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) 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) 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) 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: // Performance-testing:
@ -801,15 +848,14 @@ cTerrainHeightGenPtr cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cB
{ {
// No match found, force-set the default and retry // No match found, force-set the default and retry
LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str()); LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str());
a_IniFile.DeleteValue("Generator", "HeightGen");
a_IniFile.SetValue("Generator", "HeightGen", "Biomal"); a_IniFile.SetValue("Generator", "HeightGen", "Biomal");
return CreateHeightGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault); return CreateHeightGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault);
} }
// Read the settings: // Read the settings:
res->InitializeHeightGen(a_IniFile); res->InitializeHeightGen(a_IniFile);
return cTerrainHeightGenPtr(res); return res;
} }

View File

@ -2,10 +2,12 @@
// HeiGen.h // HeiGen.h
/* /*
Interfaces to the various height generators: Interfaces to the various height-based terrain shape generators:
- cHeiGenFlat - cHeiGenFlat
- cHeiGenClassic - cHeiGenClassic
- cHeiGenBiomal - cHeiGenBiomal
Also implements the heightmap cache
*/ */
@ -15,7 +17,79 @@ Interfaces to the various height generators:
#pragma once #pragma once
#include "ComposableGenerator.h" #include "ComposableGenerator.h"
#include "../Noise.h" #include "../Noise/Noise.h"
/** A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation */
class cHeiGenCache :
public cTerrainHeightGen
{
public:
cHeiGenCache(cTerrainHeightGenPtr a_HeiGenToCache, int a_CacheSize);
~cHeiGenCache();
// 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:
struct sCacheData
{
int m_ChunkX;
int m_ChunkZ;
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
sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used
// Cache statistics
int m_NumHits;
int m_NumMisses;
int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits)
} ;
/** 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;
};
@ -40,47 +114,6 @@ protected:
/// A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation
class cHeiGenCache :
public cTerrainHeightGen
{
public:
cHeiGenCache(cTerrainHeightGenPtr a_HeiGenToCache, int a_CacheSize);
~cHeiGenCache();
// 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
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;
int m_ChunkZ;
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
int m_CacheSize;
int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array
sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used
// Cache statistics
int m_NumHits;
int m_NumMisses;
int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits)
} ;
class cHeiGenClassic : class cHeiGenClassic :
public cTerrainHeightGen public cTerrainHeightGen
{ {
@ -137,7 +170,11 @@ public:
m_BiomeGen(a_BiomeGen) 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: protected:
typedef cChunkDef::BiomeMap BiomeNeighbors[3][3]; typedef cChunkDef::BiomeMap BiomeNeighbors[3][3];
@ -154,11 +191,8 @@ protected:
float m_BaseHeight; float m_BaseHeight;
} ; } ;
static const sGenParam m_GenParam[256]; 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); NOISE_DATATYPE GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const BiomeNeighbors & a_BiomeNeighbors);
} ; } ;

Some files were not shown because too many files have changed in this diff Show More