diff options
73 files changed, 1672 insertions, 990 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index dd9b1e67c..4c47eb509 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.2) +cmake_minimum_required (VERSION 2.8.7) # Without this, the MSVC variable isn't defined for MSVC builds ( http://www.cmake.org/pipermail/cmake/2011-November/047130.html ) enable_language(CXX C) diff --git a/Install/Lua-LICENSE.txt b/Install/ThirdPartyLicenses/Lua-LICENSE.txt index 3c6d06fcf..3c6d06fcf 100644 --- a/Install/Lua-LICENSE.txt +++ b/Install/ThirdPartyLicenses/Lua-LICENSE.txt diff --git a/Install/LuaExpat-license.html b/Install/ThirdPartyLicenses/LuaExpat-license.html index bd4a54f9a..bd4a54f9a 100644 --- a/Install/LuaExpat-license.html +++ b/Install/ThirdPartyLicenses/LuaExpat-license.html diff --git a/Install/LuaSQLite3-LICENSE.txt b/Install/ThirdPartyLicenses/LuaSQLite3-LICENSE.txt index cf1014378..cf1014378 100644 --- a/Install/LuaSQLite3-LICENSE.txt +++ b/Install/ThirdPartyLicenses/LuaSQLite3-LICENSE.txt diff --git a/Install/MersenneTwister-LICENSE.txt b/Install/ThirdPartyLicenses/MersenneTwister-LICENSE.txt index 5c7a6ef04..5c7a6ef04 100644 --- a/Install/MersenneTwister-LICENSE.txt +++ b/Install/ThirdPartyLicenses/MersenneTwister-LICENSE.txt diff --git a/Install/SQLiteCpp-LICENSE.txt b/Install/ThirdPartyLicenses/SQLiteCpp-LICENSE.txt index ec952abba..ec952abba 100644 --- a/Install/SQLiteCpp-LICENSE.txt +++ b/Install/ThirdPartyLicenses/SQLiteCpp-LICENSE.txt diff --git a/Install/Zip2008.list b/Install/Zip2008.list index 5736867d8..dba435f60 100644 --- a/Install/Zip2008.list +++ b/Install/Zip2008.list @@ -8,8 +8,4 @@ ..\MCServer\monsters.ini MCServer*debug.cmd *.example.ini -Lua-LICENSE.txt -LuaExpat-license.html -LuaSQLite3-LICENSE.txt -SQLiteCpp-LICENSE.txt -MersenneTwister-LICENSE.txt +ThirdPartyLicenses diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index e7f9e9b18..718cb4e98 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -1188,7 +1188,7 @@ These ItemGrids are available in the API and can be manipulated by the plugins, constructor = { { Params = "", Return = "cItem", Notes = "Creates a new empty cItem object" }, - { Params = "ItemType, Count, Damage, EnchantmentString", Return = "cItem", Notes = "Creates a new cItem object of the specified type, count (1 by default), damage (0 by default) and enchantments (non-enchanted by default)" }, + { Params = "ItemType, Count, Damage, EnchantmentString, CustomName, Lore", Return = "cItem", Notes = "Creates a new cItem object of the specified type, count (1 by default), damage (0 by default), enchantments (non-enchanted by default), CustomName (empty by default) and Lore (string, empty by default)" }, { Params = "cItem", Return = "cItem", Notes = "Creates an exact copy of the cItem object in the parameter" }, } , AddCount = { Params = "AmountToAdd", Return = "cItem", Notes = "Adds the specified amount to the item count. Returns self (useful for chaining)." }, @@ -1201,12 +1201,14 @@ These ItemGrids are available in the API and can be manipulated by the plugins, IsDamageable = { Params = "", Return = "bool", Notes = "Returns true if this item does account for its damage" }, IsEmpty = { Params = "", Return = "bool", Notes = "Returns true if this object represents an empty item (zero count or invalid ID)" }, IsEqual = { Params = "cItem", Return = "bool", Notes = "Returns true if the item in the parameter is the same as the one stored in the object (type, damage, lore, name and enchantments)" }, - IsEnchantable = { Params = "", Return = "bool", Notes = "Returns true if the item is enchantable" }, IsFullStack = { Params = "", Return = "bool", Notes = "Returns true if the item is stacked up to its maximum stacking" }, IsSameType = { Params = "cItem", Return = "bool", Notes = "Returns true if the item in the parameter is of the same ItemType as the one stored in the object. This is true even if the two items have different enchantments" }, IsBothNameAndLoreEmpty = { Params = "", Return = "bool", Notes = "Returns if both the custom name and lore are not set." }, IsCustomNameEmpty = { Params = "", Return = "bool", Notes = "Returns if the custom name of the cItem is empty." }, IsLoreEmpty = { Params = "", Return = "", Notes = "Returns if the lore of the cItem is empty." }, + GetEnchantability = { Params = "", Return = "number", Notes = "Returns the enchantability of the item. When the item hasn't a enchantability, it will returns 0" }, + EnchantByXPLevels = { Params = "NumXPLevels", Return = "bool", Notes = "Enchants the item using the specified number of XP levels. Returns true if item enchanted, false if not." }, + IsEnchantable = { Params = "ItemType, WithBook", Return = "bool", Notes = "(STATIC) Returns true if the specified item type is enchantable. If WithBook is true, the function is used in the anvil inventory with book enchantments. So it checks the \"only book enchantments\" too. Example: You can only enchant a hoe with a book." }, }, Variables = { @@ -1214,8 +1216,10 @@ These ItemGrids are available in the API and can be manipulated by the plugins, m_ItemCount = { Type = "number", Notes = "Number of items in this stack" }, m_ItemDamage = { Type = "number", Notes = "The damage of the item. Zero means no damage. Maximum damage can be queried with GetMaxDamage()" }, m_ItemType = { Type = "number", Notes = "The item type. One of E_ITEM_ or E_BLOCK_ constants" }, - m_CustomName = { Type = "string", Notes = "The custom name for an item." }, - m_Lore = { Type = "string", Notes = "The lore for an item. Line breaks are represented by the ` character." }, + m_CustomName = { Type = "string", Notes = "The custom name for an item." }, + m_Lore = { Type = "string", Notes = "The lore for an item. Line breaks are represented by the ` character." }, + m_RepairCost = { Type = "number", Notes = "The repair cost of the item. The anvil need this value" }, + m_Enchantments = { Type = "{{cEnchantments|cEnchantments}}}", Notes = "The enchantments of the item." }, }, AdditionalInfo = { @@ -2340,6 +2344,7 @@ end ForEachBlockEntityInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each block entity in the chunk. Returns true if all block entities in the chunk have been processed (including when there are zero block entities), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next block entity, or true to abort the enumeration. Use {{tolua}}.cast() to cast the Callback's BlockEntity parameter to the correct {{cBlockEntity}} descendant." }, ForEachChestInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each chest in the chunk. Returns true if all chests in the chunk have been processed (including when there are zero chests), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cChestEntity|ChestEntity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next chest, or true to abort the enumeration." }, ForEachEntity = { Params = "CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each entity in the loaded world. Returns true if all the entities have been processed (including when there are zero entities), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next entity, or true to abort the enumeration." }, + ForEachEntityInBox = { Params = "{{cBoundingBox|Box}}, CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each entity in the specified bounding box. Returns true if all the entities have been processed (including when there are zero entities), or false if the callback function has aborted the enumeration by returning true. If any chunk within the bounding box is not valid, it is silently skipped without any notification. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}})</pre> The callback should return false or no value to continue with the next entity, or true to abort the enumeration." }, ForEachEntityInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each entity in the specified chunk. Returns true if all the entities have been processed (including when there are zero entities), or false if the chunk is not loaded or the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next entity, or true to abort the enumeration." }, ForEachFurnaceInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each furnace in the chunk. Returns true if all furnaces in the chunk have been processed (including when there are zero furnaces), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cFurnaceEntity|FurnaceEntity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next furnace, or true to abort the enumeration." }, ForEachPlayer = { Params = "CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each player in the loaded world. Returns true if all the players have been processed (including when there are zero players), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The callback should return false or no value to continue with the next player, or true to abort the enumeration." }, diff --git a/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html b/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html index 35c880b00..dd124e119 100644 --- a/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html +++ b/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html @@ -202,7 +202,7 @@ function Explode(Split, Player) if (#Split ~= 2) then -- There was more or less than one argument (excluding the "/explode" bit) -- Send the proper usage to the player and exit - SendMessage(Player, "Usage: /explode [playername]") + Player:SendMessage("Usage: /explode [playername]") return true end @@ -213,7 +213,7 @@ function Explode(Split, Player) if (Explodee:GetName() == Split[2]) then -- Create an explosion at the same position as they are; see <a href="cWorld.html">API docs</a> for further details of this function Player:GetWorld():DoExplosionAt(Explodee:GetPosX(), Explodee:GetPosY(), Explodee:GetPosZ(), false, esPlugin) - SendMessageSuccess(Player, Split[2] .. " was successfully exploded") + Player:SendMessageSuccess(Split[2] .. " was successfully exploded") HasExploded = true; return true -- Signalize to MCS that we do not need to call this callback for any more players end @@ -224,7 +224,7 @@ function Explode(Split, Player) if not(HasExploded) then -- We have not broken out so far, therefore, the player must not exist, send failure - SendMessageFailure(Player, Split[2] .. " was not found") + Player:SendMessageFailure(Split[2] .. " was not found") end return true diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index 179935c08..0e7e647d5 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -65,6 +65,8 @@ function Initialize(Plugin) PM:BindCommand("/sb", "debuggers", HandleSetBiome, "- Sets the biome around you to the specified one"); PM:BindCommand("/wesel", "debuggers", HandleWESel, "- Expands the current WE selection by 1 block in X/Z"); PM:BindCommand("/rmitem", "debuggers", HandleRMItem, "- Remove the specified item from the inventory."); + PM:BindCommand("/pickups", "debuggers", HandlePickups, "- Spawns random pickups around you"); + PM:BindCommand("/poof", "debuggers", HandlePoof, "- Nudges pickups close to you away from you"); Plugin:AddWebTab("Debuggers", HandleRequest_Debuggers) Plugin:AddWebTab("StressTest", HandleRequest_StressTest) @@ -1558,3 +1560,57 @@ end + +local PossibleItems = +{ + cItem(E_ITEM_DIAMOND), + cItem(E_ITEM_GOLD), + cItem(E_ITEM_IRON), + cItem(E_ITEM_DYE, 1, E_META_DYE_BLUE), -- Lapis lazuli + cItem(E_ITEM_COAL), +} + + + + + +function HandlePickups(a_Split, a_Player) + local PlayerX = a_Player:GetPosX() + local PlayerY = a_Player:GetPosY() + local PlayerZ = a_Player:GetPosZ() + local World = a_Player:GetWorld() + local Range = 12 + for x = 0, Range do for z = 0, Range do + local px = PlayerX + x - Range / 2 + local pz = PlayerZ + z - Range / 2 + local Items = cItems() + Items:Add(PossibleItems[math.random(#PossibleItems)]) + World:SpawnItemPickups(Items, px, PlayerY, pz, 0) + end end -- for z, for x + return true +end + + + + +function HandlePoof(a_Split, a_Player) + local PlayerPos = Vector3d(a_Player:GetPosition()) -- Create a copy of the position + PlayerPos.y = PlayerPos.y - 1 + local Box = cBoundingBox(PlayerPos, 4, 2) + local NumEntities = 0 + a_Player:GetWorld():ForEachEntityInBox(Box, + function (a_Entity) + if not(a_Entity:IsPlayer()) then + local AddSpeed = a_Entity:GetPosition() - PlayerPos -- Speed away from the player + a_Entity:AddSpeed(AddSpeed * 32 / (AddSpeed:SqrLength() + 1)) -- The further away, the less speed to add + NumEntities = NumEntities + 1 + end + end + ) + a_Player:SendMessage("Poof! (" .. NumEntities .. " entities)") + return true +end + + + + diff --git a/MCServer/webadmin/files/background.gif b/MCServer/webadmin/files/background.gif Binary files differnew file mode 100644 index 000000000..cab9bed56 --- /dev/null +++ b/MCServer/webadmin/files/background.gif diff --git a/MCServer/webadmin/files/favicon.ico b/MCServer/webadmin/files/favicon.ico Binary files differnew file mode 100644 index 000000000..ea4bde926 --- /dev/null +++ b/MCServer/webadmin/files/favicon.ico diff --git a/MCServer/webadmin/files/logo.png b/MCServer/webadmin/files/logo.png Binary files differnew file mode 100644 index 000000000..50733e824 --- /dev/null +++ b/MCServer/webadmin/files/logo.png diff --git a/MCServer/webadmin/files/mc-logo.png b/MCServer/webadmin/files/mc-logo.png Binary files differnew file mode 100644 index 000000000..9a77a490f --- /dev/null +++ b/MCServer/webadmin/files/mc-logo.png diff --git a/MCServer/webadmin/files/style.css b/MCServer/webadmin/files/style.css new file mode 100644 index 000000000..e7ffe3999 --- /dev/null +++ b/MCServer/webadmin/files/style.css @@ -0,0 +1,326 @@ +/* reset CSS */ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-size: 100%; + vertical-align: baseline; + background: transparent; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} + +/* remember to define focus styles! */ +:focus { + outline: 0; +} + +/* remove textarea resize at Safari */ +textarea { + resize: none; +} + +/* remember to highlight inserts somehow! */ +ins { + text-decoration: none; +} +del { + text-decoration: line-through; +} + +/* tables still need 'cellspacing="0"' in the markup */ +table { + border-collapse: collapse; + border-spacing: 0; +} + + +/* + Origional from http://www.perspectived.com/ + Modified by Ben Phelps + Made for FakeTruth - MCServer +*/ + +/* Basic ---------------------------------------- */ + +.clear { clear: both; } + +body { + background: white; + font-family: Arial, Helvetica, sans-serif; + font-size: 12px; + color: #646464; + text-align: center; +} + +#wrapper { + text-align: left; + width: 930px; + margin: 0 auto; +} + +/* Logo ---------------------------------------- */ + +h1 { + margin: 15px 0 10px 5px; + width: 180px; + height: 36px; + background: url(logo.png) no-repeat left top; +} + +h1 a { + display: block; + width: 225px; + height: 28px; +} + +h1 span { display: none; } + +a { + color: #646464; +} + +/* Container ---------------------------------------- */ + +#containerHolder { + background: #eee; + padding: 5px; +} + + +#container { + background: #fff url(background.gif) repeat-y left top; + border: 1px solid #ddd; + width: 918px; + +} + +#connectHolder { + background: #eee; + padding: 5px; + margin-bottom:8px; +} + + +#connect { + border: 1px solid #ddd; + background-color: #fff; + padding:5px; + width: 908px; +} + +.pics { + height: 375px; + width: 600px; +} + +.pics img { + padding: 5px; + border: 1px solid #ddd; + background-color: #eee; + width: 600px; + height: 375px; + margin-left: 15px; +} + +/* Login -------------------------------------- */ + +#loginLogo { + margin: 0 auto; + margin-top:100px; + width: 180px; + height: 36px; + background-image: url(logo.png); +} + +#loginHolder { + background: #eee; + padding: 5px; + width: 310px; + margin: 0 auto; + height: 90px; + margin-top:20px; +} + +#login { + padding:10px; + width: 288px; + height: 68px; + border: 1px solid #ddd; + background:#fff; + text-align: left; +} + + +/* Sidebar ---------------------------------------- */ + +#sidebar { + width: 179px; + float: left; +} + +#sidebar .sideNav { width: 179px; } + +#sidebar .sideNav li { border-bottom: 1px solid #ddd; width: 179px; } + +#sidebar .sideNav li a { + display: block; + color: #646464; + background: #f6f6f6; + text-decoration: none; + height: 29px; + line-height: 29px; + padding: 0 19px; + width: 141px; +} + +#sidebar .sideNav li a:hover { background: #fdfcf6; } + +#sidebar .sideNav li a.active, #sidebar .sideNav li a.active:hover { + background: #f0f7fa; + color: #c66653; +} + +/* Breadcrumb ---------------------------------------- */ + +h2 { + width: 718px; + float: right; + color: #646464; + font-size: 16px; + line-height: 16px; + font-weight: bold; + margin: 20px 0 0 0; + padding: 0 0 10px 0; + border-bottom: 1px solid #ddd; +} + +h2 a { + color: #646464; + text-decoration: none; +} + +h2 a.active { color: #c66653; } + +h2 a:hover { text-decoration: underline; } + +/* Content ---------------------------------------- */ + +#main { + width: 700px; + float: right; + padding: 0 19px 0 0; +} + +#main p { + + padding: 10px; + +} + +h3 { + font-size: 14px; + line-height: 14px; + font-weight: bold; + color: #5494af; + padding: 0 0 0 10px; + margin: 20px 0 10px; +} + +h4 { + padding: 0 0 0 10px; + margin: 20px 0 10px; +} + +#main ul { + padding: 0 0 0 10px; + list-style-type: circle; + list-style-position: inside; +} + +#main table { + border-top: 1px solid #ddd; + width: 700px; +} + +#main table tr th { + text-align: left; + background: #f6f6f6; + padding: 0px 20px; + height: 20px; + line-height: 20px; + border-bottom: 1px solid #ddd; +} + +#main table tr td { + background: #f6f6f6; + padding: 0px 20px; + height: 29px; + line-height: 29px; + border-bottom: 1px solid #ddd; +} + +#main table tr.odd td { + background: #fbfbfb; +} + +#main table tr:hover td { background: #fdfcf6; } + +#main table .action { + text-align: right; + padding: 0 20px 0 10px; +} + +#main table tr .action a { margin: 0 0 0 10px; text-decoration: none; color: #9b9b9b; } +#main table tr:hover .action .edit { color: #c5a059; } +#main table tr:hover .action .delete { color: #a02b2b; } +#main table tr:hover .action .view { color: #55a34a; } + +#main table tr:hover .action a:hover { text-decoration: underline; } + +fieldset { + border: 1px solid #ddd; + padding: 19px; + margin: 0 0 20px 0; + background: #fbfbfb; +} + +form p { margin: 0 0 14px 0; float: left; width: 100%; } + +label { + display: block; + width: 100%; + margin: 0 0 7px 0; + line-height: 12px; +} + +/* Footer ---------------------------------------- */ + +#footer { + margin: 10px 0 30px 0; + font-size: 11px; + line-height: 11px; + color: #9B9B9B; + padding: 0 0 0 5px; +} + +#footer a { color: #9B9B9B; } + +#footer a:hover { text-decoration: none; } diff --git a/MCServer/webadmin/login_template.html b/MCServer/webadmin/login_template.html new file mode 100644 index 000000000..913a85db0 --- /dev/null +++ b/MCServer/webadmin/login_template.html @@ -0,0 +1,25 @@ +<html> +<head> + <title>MCServer WebAdmin - Login</title> + <meta charset="UTF-8"> + <link rel="icon" href="favicon.ico"> + <style type="text/css"> + header { + margin: 0 auto; + text-align: center; + vertical-align: middle; + } + </style> +</head> + +<body> + <header> + <img src="mc-logo.png" alt="MCServer Logo" class="logo"> + <h1>MCServer - WebAdmin</h1> + <form method="get" action="webadmin/"> + <input type="submit" value="Log in"> + </form> + </header> +</body> + +</html> diff --git a/MCServer/webadmin/template.lua b/MCServer/webadmin/template.lua index a066d8b33..a7480f83e 100644 --- a/MCServer/webadmin/template.lua +++ b/MCServer/webadmin/template.lua @@ -57,7 +57,7 @@ end function ShowPage(WebAdmin, TemplateRequest) SiteContent = {} local BaseURL = WebAdmin:GetBaseURL(TemplateRequest.Request.Path) - local Title = "MCServer" + local Title = "MCServer WebAdmin" local MemoryUsageKiB = cRoot:GetPhysicalRAMUsage() local NumChunks = cRoot:Get():GetTotalChunkCount() local PluginPage = WebAdmin:GetPage(TemplateRequest.Request) @@ -74,339 +74,9 @@ function ShowPage(WebAdmin, TemplateRequest) <!DOCTYPE html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> -<link rel="icon" href="data:application/octet-stream;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQQAAAAAAgIDBRghJ5o5TlumCg0QCQAAAAABAgIEAAEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAgAAAAMAAAAACQwPMxsnL88jMz3/S2d6/0xoetcaIig6AAAAAAEBAQMBAQEDAAAAAAAAAAAAAAAAAQEBAwAAAAAEAwMEFhwhgRomMPwfLTj/IC86/DJHWPxKaH3/TGN0/jk+QYgEBgcFAAAAAAEBAgMAAAAAAAAAAgAAAAAKDRAuHSMpzB8rNP8dKTP8FiIp/QkXGv8sSEr/QV1u/UhnefxWdIb/P1dm0BIYHDEAAAAAAAEBAgAAAAARGB1oIC44/R0rNf8PGiL7DxUa/gwdHv8JKSP/HUdC/x9HQf81W17+Qllv+0lkef9ObYH+Ii42bAAAAAAAAAAAFyIqyBopMf8THSX6BRkY/wIbGP8HHhv/FTs1/yJhVP8lZ1b/H05I/xcuNf8jPET6UWp+/0xqfdAAAAAAAQECBxEcI9oOHh//BRgV/QwsJv8NKyb/EDEr/xU3Mv8zeW7/MHpr/ydqXP8oalz/HVtO/i9KUf9AW2zgBwkLDQEEBBgKGhfuCCMd/w4uKf4RNC7/FTcy/w8yLv8PMi7/LXFn/y55av86gW//OoV7/y11av4YTkj/GkFB8gUICh4BCActCSUf+xAxKv8TNjD/EzYx/w8xLP8PMCr/Fjgy/zp+c/8yfXP/OoN5/zN9cf86hHb/NHlt/y1xZP4LGhc0BhEORQ8zLP8SNC7+EDIt/xEzLf8PMCv/DTAs/w4xKv8vdWT/PYh4/y93bf8sdWj/N4R3/zWBdv43hHn/EysoTQgXFWEQMy//DzEs/Q8xK/8SNC3/FjUv/xEuK/8WPjf/OIBw/0OEdP83e27/N31w/zN8bP8vdWj8Mn5z/xg3MmgLHRp8FDkz/xExLPwNKyT/EjIs/xpEPP8kX1T/OY2C/0KVhv8zgG//NH9z/zuBdf8xeGX/PIF1/DSAdf8cRDyEDCMenBEvJ/8VODT4IVZM/C11af06inv/QZaG/z2Rgf84iXz/O5F+/z2Nff85iX3+OYJ2/DuBdPg5g3X/IVBIohIzLaUydGb/RJiJ/TyYiv88k4P/O4t6/j+Rg/w+j3/9PYt5/TyOgfwuhHf+Nox+/zyViP9Aloj9Q5WC/yxiVa0ECgkHEyciLh1BOWwsZV2sN39y4juNfv5Cmon/O5OF/z2Shf86kYT/NoyA/ziGeeUqZlywHEI8chAjHzQDBwUKAAAAAAAAAAAAAAAAAAAAAAQIBwsSKCQ9JU9GgDN2a8owdGjLH0xFghMpJUAFDAsNAAAAAAAAAAAAAAAAAAAAAP5/AAD8PwAA8A8AAOAHAADAAwAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAAAAAAAAAAAAAAA4AcAAPw/AAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBBAAAAAEAAAAAGB4leTRGUpAICQsDAAAAAAECAgQAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAgABAQQAAAAADA4RHRsjK7UaJi7/U3SH/1Z1isgbJCosAAAAAAEBAgMBAQECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQMAAAABAAAAABgeJGEkMz3wHSw1/yExOvxLaHn8TWuA/2SJovkzRVB1AAAAAAAAAAACAgIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQIBAQEEAAAAAAoLDhkcJS2xHy03/xkmL/0fLjb8IC85/0FabP9IZHX8O1Jj/GCFnP9KZHTBEhccIwAAAAABAQIEAQEBAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEDAAAAAgAAAAAXHSJZICw27BQeJv8aJzD7JTZC/iQ1P/8nOUX/JzpJ/0hjdf9FX3H+V3iO+01tgv9Wanj0R0dHZwAAAAAAAAABAQICAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBBAAAAAAMDAwUJCouqSAwOv8XIir9Exwj/CM0P/8eLDf/IzM+/xclLv8oPlD/NUla/0Vhc/9EXnH/OU5f/DxPX/xudHn/Ulxjtg8WGxsAAAAAAQICBAEBAQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAwAAAAIAAAAAFhofUTI0NugdJSv/Gicw+yQ0P/0YJC7/GCQt/xEbJP8QGSD/CxUb/yhMTf9AWmz/PVds/z5Xaf8+VWf/Q15x/jtTZPtJX3D/VneK7iUxOlsAAAAAAAAAAgECAgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAACAkLER4oMKIdKTL/GCEn/RQfJ/weLDb/Gigy/x0lLP8aJS7/DiAj/wgOE/8HFRn/Gks8/0BNS/8xUFv/VXyS/0ZseP9Ye5D/XH+U/0Zhc/xFX3L9SGN2/ztPXqkPFBYUAAAAAAEBAQIAAAAAAAAAAAAAAAAAAAABAAAAABUbIUoqOkbjHy43/xIcJPseLTb9Gicv/xQgKP8NEhn/ISIk/xYkKv8MKyP/Bh0b/wYiHf8RQzz/JTE2/yNYT/8tTVX/KVRM/1N5if9dgJj/RmBy/0pmev1KZXn7Qltt/1NxhOcgKjJOAAAAAAAAAAEAAAAAAQEBBAAAAAATGiBhIjE8/xwrNPwXIyv8His1/yMzPf8OFh7/BgwS/wQSEv8IDxT/CxQa/wUfG/8HJyL/ES0m/ylrYv8YPD//E0I2/xg8Mv8UQTP/LklT/zJHWf80Slv/Ql1x/1yAmP8/VWf8Smd7/E5rgP8fKTFuAAAAAAEBAQQAAQEEAAAAABYhJ5QaJy//FSAo+RglLv8iMz3/Ex8o/wUOEv8GIR3/BB8a/wQYFv8EEBL/Ax4a/wsqJv8VODL/JGhY/xhXTP8kWUv/KWNT/ypuWf8aMDX/HCU0/yEwPv8iMD7/M0dY/0FdcP9egpr6W36T/zlQXacAAAAAAQEBBAAAAAMAAAAAFR8mrR0rNP8YJCz8Fykt/xYiKv8FCRD/BRkY/wglIf8FHhr/AxsY/wYhHf8PKyb/FTo2/xU7N/8ralj/IGRY/ydpV/8wdWr/JW1g/xw9P/8dSkn/FjQ0/xQ/O/8aMDf/TVhi/0ljdvxYe5D/R2NyvgAAAAAAAAACAAAAAQAAAAANFRzGEh0m/x0uN/wKHRn/Bg4T/wMUEv8JJyH/Dy0p/wYjH/8IJB//Gjsz/xQyK/8TNDD/DjMu/z96a/8veG3/J3Fg/zd8b/8oa1//FExD/yhjVv8aWEb/ImRa/xhXTv86SUz/QlNl/Uhmff9bfpPWAAAAAAAAAAAAAAAAAQIDAxEaI9wSIyj/CBYW/QYWE/8HGRr/CSsi/wosI/8RMSz/ES4o/wwtKP8QMy7/Cisn/w8uKv8YNzD/Pod9/zB/df82e3L/Mnho/yduWP8ueW3/N3Zq/ypyZf8nbV3/IFxT/xE4Of8zT1z+Mldc/ztRY+kHCwwNAAAAAAAAAAABBQUSER0d7wwcGP8IIxv+CSId/wsgHP8QMiz/EjYx/w4vKv8XODP/EjIu/w4yLv8KLir/Cywn/w8wKv8qcWj/MH50/ylzYf8ve2j/JnFb/zh+bf8+hHr/LXhu/zJ+bf8wcGf/E09G/x4/Qv4jVEz/IS8++QcKDR0AAAAAAAAAAAEIBiUGFBP+BxgU/wsrJP4HJSD/ES8q/w4xLv8OMi7/FjUu/xM3Mv8XOzf/FDg0/xEzL/8TODT/Cy4q/yRlW/83e3H/KG9g/zSEeP9AhXj/P35u/0GMgf8wf3b/NoR8/yxzaP8hXVT/ED88/xdQSP4gVFL/Bg4QMgAAAAAAAAAAAwsKOgUdGv8NJyH9Dy8p/w8xKf8WOjb/DDEs/xI2MP8UNzD/DTAo/xM2MP8MLib/FTUx/xU1MP8OMy7/PXdo/0KLgP8udW3/JnJo/zyFef9Ai4H/PYd+/y54a/8mcmn/O3xv/zR1aP8obmD/I2JY/CFmWf8LIBpJAAAAAAAAAAAGFBFSCSsk/wssJPwMLib/EjEo/xU3Mv8OMij/HEA4/w4zMP8OMSz/EDAq/wktJ/8NMCv/FDEp/xU3Mf89gnj/PYyA/zyDef8udWv/N4Z8/zB8cv8zd27/LnZj/0GOev9FjYD/OoR7/zuEdv8+gHD7O4Z6/xo2L2IAAAAAAAAAAAcYE20MMCn/EDEr+w4vJv8WODP/EzMv/w8yLP8TODT/DS8q/xAwKv8IKyf/DC8s/w8wK/8MLST/FTQt/zaBdv8pd2P/P4t2/zaEef85e27/Nnpt/y11bP8xeW//QJCD/zyMgf8we2n/N4N6/yduZPtBkYj/Ik5JfAAAAAAAAAAADyMfiBA0Lv8PMiz7GDk1/xAxLf8RMS3/DzIt/xAyLP8PMCf/FzQu/w4vKf8OMC3/EzQv/wgsJ/8JLCf/ImlT/zV3Zf9IkHv/OYh7/yNuaP80e2//KXJp/yNtXf82gnb/MH10/yp3av9AjoX/MH10+zB7cP8eT0mWAAAAAAAAAAARLCiiDjMu/wotJvsTODT/DC8r/wkqJf8PMCj/EDIr/w0yKv8ZOzb/FjUv/xQ2M/8TOjb/DS8r/xk8NP8ocGL/QIV0/0WBcf9Cf3D/Mnhu/zd6bv84e27/KXRk/zh7bP8tdmX/M3lt/zR6bP8veWj8MHxx/y1dUq8AAAAAAAAAAAsnJLsRMi7/DC4q/BU0Lv8OMSz/DS4p/xg4Mv8SNi//ETAq/xs/N/8aPTX/ETEs/xQvKv8YMyv/H05F/yZya/9Km4j/SI+A/zp6a/9Cf23/RYh7/zuGe/86e2v/RYuA/y57af86e2r/J29l/ypyZ/w+jIL/KWVbxwAAAAAAAAAADysn0Rc6Nf8PMS39DS8q/xUzLf8QMiz/DS8l/xEyKv8UNC//EzEs/xAtI/8QMyv/H1FN/zeBeP9CnI//Q5SF/0aRff87h3f/LXVl/yJsWv8xe3T/Q4l+/zh5Zf8reWT/Lnlm/zqHef84fHD/Im9p/Th/dP8pa2HcAAYGBwQIBw4TMy3kFjsy/xI1L/4PMSz/GTo1/xIyKv8KKyL/EzAq/xA0MP8ZQjr/JF1T/zp/cP8xiHb/LIh6/0egkf9Gn5D/NYZ2/ziGcv8whXr/NYuA/zN9dP9Ahnz/Mn91/yNrWP85emb/QYJ3/0SLe/8vd2/+NXpq/zF2ZuwHEREXBxAPIRI3MPQVNSv/GTcw/hY0Lv8UNDH/Cysn/xQ2MP8oX1f/KnFn/0ONf/8yinz/MIh9/z6JeP85in//No+H/yyBdv82iX7/QZOE/yt/aP87jnn/R417/zaFef89kIP/M3dq/0OHev84gXf/R5KA/0KJfv4yf3P/NXhn+hEeGysDDww3EjQr/hcyLP8JJx//Ciwp/yJQSv8ybmD/MYJv/zmUiP8ug3X/QIp8/zyOfP9ElYX/T5yN/0KWhf88i3f/PYh4/ziHfP9El4X/P5aC/zaMf/9Ek4b/M4R2/z2ThP85iX7/Qop9/y+BeP83emz/NHlm/zeCef4mcmn/CyAcQgQTEVQVOTH/GT85/CRYTfstdWr8QJWL/j+Thv8thHT/N4l7/zaIdP85hnL/PI58/zmOfv8+lIb/RZF+/0COe/9AkH7/Qot2/0CTgv8+mI3/MIh2/zOFcv88joL/OY+F/zeFev83in//N4x+/jyFcPw3f237OHpv+0iQf/8fRj1fCh4cTyRkWOxFnIj+TqSR/0idiP9Qo5P/M4N5/Sp9cPs+k4X8RJeL/jqLev8/j37/RpaH/z2SiP84hnn/PZKI/zCHdv86jXz/P46E/zSIfv8xiHv/J31x/y6Eef4rgG/8OYx9+zyPhv05jIH/SZ6Q/1Wyof8/lH//Oohx9yJHPV4AAAAABhAOChw2LzkmTEV3MGxcuDuHdu1Akob/QpuP/0uZiP8yh3r/MIB0/DuKffs4in38PpaD/j6ZjP89kYT/PpCC/0CNgP82g3b+MIV6/S6EevspfHP8Po5//0ihkP8+mon/N4+B/zKCdPM1c2jDH01FhBoyK0YEDQoRAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAQsKDxUuKkIsXliDMnRpwjeEcfRCloH/RJuK/zyRgP9AlIP/PYx+/TKEdvw8kIH8Q5eM/TSLf/8+lYX/SJyM/zuYjf8+lIj3O4F1ySVZU4wWMy1LBhAMFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAgIDAQICBAEBAQIAAAAAAAAAAAAAAAAAAAAACxQRFhk2MkomWFGLOXhryEiZhvRElYf/N46G/zCGef9Dk4P/O5CF9SVpYMsrWlCQFzg0UAwZFxoAAAAAAAAAAAAAAAAAAAAAAAAAAgECAgQBAgIDAAEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAgIDAQMCBAEBAQIAAAAAAAAAAAAAAAAAAAAACBcTHx4+OVw5dWmnK2peqBo5Ml0MHBkiAAICAQAAAAAAAAAAAAAAAAABAQEBAgIEAQICAwABAQIAAAAAAAAAAAAAAAAAAAAAAAAAAP//f////D////gf///gB///wAP//wAA//4AAH/4AAAf8AAAD+AAAAfAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAOAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAHwAAAH/gAAf//gB////n//"> +<link rel="icon" href="/favicon.ico"> <title>]] .. Title .. [[</title> - -<style type="text/css" media="screen"> - - /* reset CSS */ - - html, body, div, span, applet, object, iframe, - h1, h2, h3, h4, h5, h6, p, blockquote, pre, - a, abbr, acronym, address, big, cite, code, - del, dfn, em, font, img, ins, kbd, q, s, samp, - small, strike, strong, sub, sup, tt, var, - b, u, i, center, - dl, dt, dd, ol, ul, li, - fieldset, form, label, legend, - table, caption, tbody, tfoot, thead, tr, th, td { - margin: 0; - padding: 0; - border: 0; - outline: 0; - font-size: 100%; - vertical-align: baseline; - background: transparent; - } - body { - line-height: 1; - } - ol, ul { - list-style: none; - } - blockquote, q { - quotes: none; - } - - /* remember to define focus styles! */ - :focus { - outline: 0; - } - - /* remove textarea resize at Safari */ - textarea { - resize: none; - } - - /* remember to highlight inserts somehow! */ - ins { - text-decoration: none; - } - del { - text-decoration: line-through; - } - - /* tables still need 'cellspacing="0"' in the markup */ - table { - border-collapse: collapse; - border-spacing: 0; - } - - - /* - Origional from http://www.perspectived.com/ - Modified by Ben Phelps - Made for FakeTruth - MCServer - */ - - /* Basic ---------------------------------------- */ - - .clear { clear: both; } - - body { - background: white; - font-family: Arial, Helvetica, sans-serif; - font-size: 12px; - color: #646464; - text-align: center; - } - - #wrapper { - text-align: left; - width: 930px; - margin: 0 auto; - } - - /* Logo ---------------------------------------- */ - - h1 { - margin: 15px 0 10px 5px; - width: 180px; - height: 36px; - background: url(data:image/png;base64,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) no-repeat left top; - } - - h1 a { - display: block; - width: 225px; - height: 28px; - } - - h1 span { display: none; } - - a { - color: #646464; - } - - /* Container ---------------------------------------- */ - - #containerHolder { - background: #eee; - padding: 5px; - } - - - #container { - background: #fff url(data:image/gif;base64,R0lGODlhtAABAIAAAN3d3f///yH5BAAAAAAALAAAAAC0AAEAAAIMjI+py+0Po5y0WgYKADs%3D) repeat-y left top; - border: 1px solid #ddd; - width: 918px; - - } - - #connectHolder { - background: #eee; - padding: 5px; - margin-bottom:8px; - } - - - #connect { - border: 1px solid #ddd; - background-color: #fff; - padding:5px; - width: 908px; - } - - .pics { - height: 375px; - width: 600px; - } - - .pics img { - padding: 5px; - border: 1px solid #ddd; - background-color: #eee; - width: 600px; - height: 375px; - margin-left: 15px; - } - - /* Login -------------------------------------- */ - - #loginLogo { - margin: 0 auto; - margin-top:100px; - width: 180px; - height: 36px; - background-image: url(data:image/png;base64,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); - } - - #loginHolder { - background: #eee; - padding: 5px; - width: 310px; - margin: 0 auto; - height: 90px; - margin-top:20px; - } - - #login { - padding:10px; - width: 288px; - height: 68px; - border: 1px solid #ddd; - background:#fff; - text-align: left; - } - - - /* Sidebar ---------------------------------------- */ - - #sidebar { - width: 179px; - float: left; - } - - #sidebar .sideNav { width: 179px; } - - #sidebar .sideNav li { border-bottom: 1px solid #ddd; width: 179px; } - - #sidebar .sideNav li a { - display: block; - color: #646464; - background: #f6f6f6; - text-decoration: none; - height: 29px; - line-height: 29px; - padding: 0 19px; - width: 141px; - } - - #sidebar .sideNav li a:hover { background: #fdfcf6; } - - #sidebar .sideNav li a.active, #sidebar .sideNav li a.active:hover { - background: #f0f7fa; - color: #c66653; - } - - /* Breadcrumb ---------------------------------------- */ - - h2 { - width: 718px; - float: right; - color: #646464; - font-size: 16px; - line-height: 16px; - font-weight: bold; - margin: 20px 0 0 0; - padding: 0 0 10px 0; - border-bottom: 1px solid #ddd; - } - - h2 a { - color: #646464; - text-decoration: none; - } - - h2 a.active { color: #c66653; } - - h2 a:hover { text-decoration: underline; } - - /* Content ---------------------------------------- */ - - #main { - width: 700px; - float: right; - padding: 0 19px 0 0; - } - - #main p { - - padding: 10px; - - } - - h3 { - font-size: 14px; - line-height: 14px; - font-weight: bold; - color: #5494af; - padding: 0 0 0 10px; - margin: 20px 0 10px; - } - - h4 { - padding: 0 0 0 10px; - margin: 20px 0 10px; - } - - #main ul { - padding: 0 0 0 10px; - list-style-type: circle; - list-style-position: inside; - } - - #main table { - border-top: 1px solid #ddd; - width: 700px; - } - - #main table tr th { - text-align: left; - background: #f6f6f6; - padding: 0px 20px; - height: 20px; - line-height: 20px; - border-bottom: 1px solid #ddd; - } - - #main table tr td { - background: #f6f6f6; - padding: 0px 20px; - height: 29px; - line-height: 29px; - border-bottom: 1px solid #ddd; - } - - #main table tr.odd td { - background: #fbfbfb; - } - - #main table tr:hover td { background: #fdfcf6; } - - #main table .action { - text-align: right; - padding: 0 20px 0 10px; - } - - #main table tr .action a { margin: 0 0 0 10px; text-decoration: none; color: #9b9b9b; } - #main table tr:hover .action .edit { color: #c5a059; } - #main table tr:hover .action .delete { color: #a02b2b; } - #main table tr:hover .action .view { color: #55a34a; } - - #main table tr:hover .action a:hover { text-decoration: underline; } - - fieldset { - border: 1px solid #ddd; - padding: 19px; - margin: 0 0 20px 0; - background: #fbfbfb; - } - - form p { margin: 0 0 14px 0; float: left; width: 100%; } - - label { - display: block; - width: 100%; - margin: 0 0 7px 0; - line-height: 12px; - } - - /* Footer ---------------------------------------- */ - - #footer { - margin: 10px 0 30px 0; - font-size: 11px; - line-height: 11px; - color: #9B9B9B; - padding: 0 0 0 5px; - } - - #footer a { color: #9B9B9B; } - - #footer a:hover { text-decoration: none; } -</style> - +<link rel="stylesheet" type="text/css" media="screen" href="/style.css"> </head> <body> @@ -421,16 +91,16 @@ function ShowPage(WebAdmin, TemplateRequest) <ul class="sideNav"> ]]) - + local AllPlugins = WebAdmin:GetPlugins() for key,value in pairs(AllPlugins) do local PluginWebTitle = value:GetWebTitle() local TabNames = value:GetTabNames() if (GetTableSize(TabNames) > 0) then - Output("<li>"..PluginWebTitle.."</li>"); + Output("<li>"..PluginWebTitle.."</li>\n"); for webname,prettyname in pairs(TabNames) do - Output("<li><a href='" .. BaseURL .. PluginWebTitle .. "/" .. webname .. "'>" .. prettyname .. "</a></li>") + Output("<li><a href='" .. BaseURL .. PluginWebTitle .. "/" .. webname .. "'>" .. prettyname .. "</a></li>\n") end end end @@ -464,4 +134,4 @@ function ShowPage(WebAdmin, TemplateRequest) ]]) return table.concat(SiteContent) -end
\ No newline at end of file +end diff --git a/SetFlags.cmake b/SetFlags.cmake index 0e2e0c277..0835189ad 100644 --- a/SetFlags.cmake +++ b/SetFlags.cmake @@ -205,6 +205,15 @@ macro(enable_profile) endif() endmacro() +#this is a hack because we can't use cmake 2.8.10 because of travis +macro(get_clang_version) + execute_process( + COMMAND "${CMAKE_CXX_COMPILER}" "--version" + OUTPUT_VARIABLE CLANG_VERSION_OUTPUT) + string(REGEX MATCH "version ([0-9]\\.[0-9])" x ${CLANG_VERSION_OUTPUT}) + set(CLANG_VERSION ${CMAKE_MATCH_1}) +endmacro() + macro(set_exe_flags) # Remove disabling the maximum warning level: # clang does not like a command line that reads -Wall -Wextra -w -Wall -Wextra and does not output any warnings @@ -223,17 +232,29 @@ macro(set_exe_flags) add_flags_cxx("-ffast-math") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + get_clang_version() + if ("${CLANG_VERSION}" VERSION_LESS 3.0) + message(FATAL_ERROR "MCServer requires clang version 3.0 or higher, version is ${CLANG_VERSION}") + endif() # clang does not provide the __extern_always_inline macro and a part of libm depends on this when using fast-math add_flags_cxx("-D__extern_always_inline=inline") add_flags_cxx("-Werror -Weverything -Wno-c++98-compat-pedantic -Wno-string-conversion") - add_flags_cxx("-Wno-error=switch-enum -Wno-documentation -Wno-exit-time-destructors") + add_flags_cxx("-Wno-error=switch-enum -Wno-exit-time-destructors") add_flags_cxx("-Wno-error=sign-conversion -Wno-error=conversion -Wno-padded") add_flags_cxx("-Wno-error=deprecated -Wno-error=weak-vtables -Wno-error=float-equal") add_flags_cxx("-Wno-error=missing-prototypes -Wno-error=non-virtual-dtor") - add_flags_cxx("-Wno-error=covered-switch-default -Wno-error=shadow -Wno-error=old-style-cast") - add_flags_cxx("-Wno-error=exit-time-destructors -Wno-error=missing-variable-declarations") - add_flags_cxx("-Wno-error=global-constructors -Wno-implicit-fallthrough") - add_flags_cxx("-Wno-error=extra-semi -Wno-weak-vtables -Wno-switch-enum") + add_flags_cxx("-Wno-error=shadow -Wno-error=old-style-cast -Wno-error=global-constructors") + add_flags_cxx("-Wno-error=exit-time-destructors") + add_flags_cxx("-Wno-weak-vtables -Wno-switch-enum") + if ("${CLANG_VERSION}" VERSION_GREATER 3.0) + # flags that are not present in 3.0 + add_flags_cxx("-Wno-error=covered-switch-default -Wno-error=missing-variable-declarations") + add_flags_cxx("-Wno-implicit-fallthrough -Wno-error=extra-semi") + endif() + if ("${CLANG_VERSION}" VERSION_GREATER 3.1) + # flags introduced in 3.2 + add_flags_cxx("-Wno-documentation") + endif() endif() endif() diff --git a/lib/inifile/iniFile.cpp b/lib/inifile/iniFile.cpp index 2bf6c91ed..7cfe7661f 100644 --- a/lib/inifile/iniFile.cpp +++ b/lib/inifile/iniFile.cpp @@ -668,6 +668,24 @@ void cIniFile::Clear(void) +bool cIniFile::HasValue(const AString & a_KeyName, const AString & a_ValueName) +{ + // Find the key: + int keyID = FindKey(a_KeyName); + if (keyID == noID) + { + return false; + } + + // Find the value: + int valueID = FindValue(keyID, a_ValueName); + return (valueID != noID); +} + + + + + void cIniFile::AddHeaderComment(const AString & comment) { comments.push_back(comment); diff --git a/lib/inifile/iniFile.h b/lib/inifile/iniFile.h index 58fecd0cf..33229bff0 100644 --- a/lib/inifile/iniFile.h +++ b/lib/inifile/iniFile.h @@ -53,7 +53,9 @@ private: /// Removes the UTF-8 BOMs (Byte order makers), if present. void RemoveBom(AString & a_line) const; + public: + enum errors { noID = -1, @@ -79,6 +81,9 @@ public: /// Deletes all stored ini data (but doesn't touch the file) void Clear(void); + + /** Returns true iff the specified value exists. */ + bool HasValue(const AString & a_KeyName, const AString & a_ValueName); /// Returns index of specified key, or noID if not found int FindKey(const AString & keyname) const; diff --git a/lib/tolua++/include/tolua++.h b/lib/tolua++/include/tolua++.h index 8da427fe3..c8b654ae6 100644 --- a/lib/tolua++/include/tolua++.h +++ b/lib/tolua++/include/tolua++.h @@ -36,7 +36,9 @@ extern "C" { #define TEMPLATE_BIND(p) #endif -#define TOLUA_TEMPLATE_BIND(p) +#ifndef TOLUA_TEMPLATE_BIND + #define TOLUA_TEMPLATE_BIND(p) +#endif #define TOLUA_PROTECTED_DESTRUCTOR #define TOLUA_PROPERTY_TYPE(p) diff --git a/src/Bindings/LuaChunkStay.cpp b/src/Bindings/LuaChunkStay.cpp index 59b02d8f7..154bcb200 100644 --- a/src/Bindings/LuaChunkStay.cpp +++ b/src/Bindings/LuaChunkStay.cpp @@ -107,7 +107,7 @@ void cLuaChunkStay::AddChunkCoord(cLuaState & L, int a_Index) } } // for itr - m_Chunks[] - m_Chunks.push_back(cChunkCoords(ChunkX, ZERO_CHUNK_Y, ChunkZ)); + m_Chunks.push_back(cChunkCoords(ChunkX, ChunkZ)); } diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 9fe93ccc2..ba2f3c5e0 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -859,6 +859,32 @@ void cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal) +void cLuaState::GetStackValue(int a_StackPos, pBoundingBox & a_ReturnedVal) +{ + tolua_Error err; + if (tolua_isusertype(m_LuaState, a_StackPos, "cBoundingBox", false, &err)) + { + a_ReturnedVal = *((cBoundingBox **)lua_touserdata(m_LuaState, a_StackPos)); + } +} + + + + + +void cLuaState::GetStackValue(int a_StackPos, pWorld & a_ReturnedVal) +{ + tolua_Error err; + if (tolua_isusertype(m_LuaState, a_StackPos, "cWorld", false, &err)) + { + a_ReturnedVal = *((cWorld **)lua_touserdata(m_LuaState, a_StackPos)); + } +} + + + + + bool cLuaState::CallFunction(int a_NumResults) { ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index eeb93fd4d..44f187701 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -59,6 +59,10 @@ class cTNTEntity; class cCreeper; class cHopperEntity; class cBlockEntity; +class cBoundingBox; + +typedef cBoundingBox * pBoundingBox; +typedef cWorld * pWorld; @@ -230,6 +234,12 @@ public: /** Retrieve value at a_StackPos, if it is a valid number, converting and clamping it to eWeather. If not, a_Value is unchanged. */ void GetStackValue(int a_StackPos, eWeather & a_Value); + + /** Retrieve value at a_StackPos, if it is a valid cBoundingBox class. If not, a_Value is unchanged */ + void GetStackValue(int a_StackPos, pBoundingBox & a_Value); + + /** Retrieve value at a_StackPos, if it is a valid cWorld class. If not, a_Value is unchanged */ + void GetStackValue(int a_StackPos, pWorld & a_Value); // Include the cLuaState::Call() overload implementation that is generated by the gen_LuaState_Call.lua script: @@ -328,6 +338,14 @@ protected: */ bool PushFunction(int a_FnRef); + /** Pushes a function that has been saved as a reference. + Returns true if successful. Logs a warning on failure + */ + bool PushFunction(const cRef & a_FnRef) + { + return PushFunction((int)a_FnRef); + } + /** Pushes a function that is stored in a referenced table by name Returns true if successful. Logs a warning on failure */ diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index adf10a72f..b7ea65759 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -679,6 +679,75 @@ static int tolua_ForEachInChunk(lua_State * tolua_S) template < class Ty1, class Ty2, + bool (Ty1::*Func1)(const cBoundingBox &, cItemCallback<Ty2> &) +> +static int tolua_ForEachInBox(lua_State * tolua_S) +{ + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cWorld") || + !L.CheckParamUserType(2, "cBoundingBox") || + !L.CheckParamFunction(3) || + !L.CheckParamEnd(4) + ) + { + return 0; + } + + // Get the params: + Ty1 * Self = NULL; + cBoundingBox * Box = NULL; + L.GetStackValues(1, Self, Box); + ASSERT(Self != NULL); // We have verified the type at the top, so we should get valid objects here + ASSERT(Box != NULL); + + // Create a reference for the function: + cLuaState::cRef FnRef(L, 3); + + // Callback wrapper for the Lua function: + class cLuaCallback : public cItemCallback<Ty2> + { + public: + cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FuncRef) : + m_LuaState(a_LuaState), + m_FnRef(a_FuncRef) + {} + + private: + // cItemCallback<Ty2> overrides: + virtual bool Item(Ty2 * a_Item) override + { + bool res = false; + if (!m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, res)) + { + LOGWARNING("Failed to call Lua callback"); + m_LuaState.LogStackTrace(); + return true; // Abort enumeration + } + + return res; + } + cLuaState & m_LuaState; + cLuaState::cRef & m_FnRef; + } Callback(L, FnRef); + + bool bRetVal = (Self->*Func1)(*Box, Callback); + + FnRef.UnRef(); + + /* Push return value on stack */ + tolua_pushboolean(tolua_S, bRetVal); + return 1; +} + + + + + +template < + class Ty1, + class Ty2, bool (Ty1::*Func1)(cItemCallback<Ty2> &) > static int tolua_ForEach(lua_State * tolua_S) @@ -3327,6 +3396,7 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "ForEachBlockEntityInChunk", tolua_ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>); tolua_function(tolua_S, "ForEachChestInChunk", tolua_ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>); tolua_function(tolua_S, "ForEachEntity", tolua_ForEach< cWorld, cEntity, &cWorld::ForEachEntity>); + tolua_function(tolua_S, "ForEachEntityInBox", tolua_ForEachInBox< cWorld, cEntity, &cWorld::ForEachEntityInBox>); tolua_function(tolua_S, "ForEachEntityInChunk", tolua_ForEachInChunk<cWorld, cEntity, &cWorld::ForEachEntityInChunk>); tolua_function(tolua_S, "ForEachFurnaceInChunk", tolua_ForEachInChunk<cWorld, cFurnaceEntity, &cWorld::ForEachFurnaceInChunk>); tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach< cWorld, cPlayer, &cWorld::ForEachPlayer>); diff --git a/src/Bindings/gen_LuaState_Call.lua b/src/Bindings/gen_LuaState_Call.lua index 2d8630d12..7f62573c7 100644 --- a/src/Bindings/gen_LuaState_Call.lua +++ b/src/Bindings/gen_LuaState_Call.lua @@ -109,7 +109,7 @@ local function WriteOverload(f, a_NumParams, a_NumReturns) -- Write the function signature: f:write("bool Call(") - f:write("FnT a_Function") + f:write("const FnT & a_Function") for i = 1, a_NumParams do f:write(", ParamT", i, " a_Param", i) end diff --git a/src/BlockEntities/SignEntity.cpp b/src/BlockEntities/SignEntity.cpp index 97fed0f04..23d9ef926 100644 --- a/src/BlockEntities/SignEntity.cpp +++ b/src/BlockEntities/SignEntity.cpp @@ -15,6 +15,7 @@ cSignEntity::cSignEntity(BLOCKTYPE a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World) : super(a_BlockType, a_X, a_Y, a_Z, a_World) { + ASSERT((a_Y >= 0) && (a_Y < cChunkDef::Height)); } diff --git a/src/BlockID.h b/src/BlockID.h index 08c576886..d835f9518 100644 --- a/src/BlockID.h +++ b/src/BlockID.h @@ -362,6 +362,8 @@ enum ENUM_ITEM_ID E_ITEM_LEAD = 420, E_ITEM_NAME_TAG = 421, E_ITEM_MINECART_WITH_COMMAND_BLOCK = 422, + E_ITEM_RAW_MUTTON = 423, + E_ITEM_MUTTON = 424, // Keep these two as the last values of the consecutive list, without a number - they will get their correct number assigned automagically by C++ // IsValidItem() depends on this! diff --git a/src/BoundingBox.h b/src/BoundingBox.h index 793466302..928e62afa 100644 --- a/src/BoundingBox.h +++ b/src/BoundingBox.h @@ -80,6 +80,17 @@ public: /// Calculates the intersection of the two bounding boxes; returns true if nonempty bool Intersect(const cBoundingBox & a_Other, cBoundingBox & a_Intersection); + double GetMinX(void) const { return m_Min.x; } + double GetMinY(void) const { return m_Min.y; } + double GetMinZ(void) const { return m_Min.z; } + + double GetMaxX(void) const { return m_Max.x; } + double GetMaxY(void) const { return m_Max.y; } + double GetMaxZ(void) const { return m_Max.z; } + + const Vector3d & GetMin(void) const { return m_Min; } + const Vector3d & GetMax(void) const { return m_Max; } + protected: Vector3d m_Min; Vector3d m_Max; diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 40ffff834..99e48df95 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -37,6 +37,7 @@ #include "MobSpawner.h" #include "BlockInServerPluginInterface.h" #include "SetChunkData.h" +#include "BoundingBox.h" #include "json/json.h" @@ -65,19 +66,18 @@ sSetBlock::sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_Bloc // cChunk: cChunk::cChunk( - int a_ChunkX, int a_ChunkY, int a_ChunkZ, + int a_ChunkX, int a_ChunkZ, cChunkMap * a_ChunkMap, cWorld * a_World, cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP, cAllocationPool<cChunkData::sChunkSection> & a_Pool ) : - m_IsValid(false), + m_Presence(cpInvalid), m_IsLightValid(false), m_IsDirty(false), m_IsSaving(false), m_HasLoadFailed(false), m_StayCount(0), m_PosX(a_ChunkX), - m_PosY(a_ChunkY), m_PosZ(a_ChunkZ), m_World(a_World), m_ChunkMap(a_ChunkMap), @@ -165,11 +165,22 @@ cChunk::~cChunk() -void cChunk::SetValid(void) +void cChunk::SetPresence(cChunk::ePresence a_Presence) { - m_IsValid = true; - - m_World->GetChunkMap()->ChunkValidated(); + m_Presence = a_Presence; + if (a_Presence == cpPresent) + { + m_World->GetChunkMap()->ChunkValidated(); + } +} + + + + + +void cChunk::SetShouldGenerateIfLoadFailed(bool a_ShouldGenerateIfLoadFailed) +{ + m_ShouldGenerateIfLoadFailed = a_ShouldGenerateIfLoadFailed; } @@ -178,6 +189,9 @@ void cChunk::SetValid(void) void cChunk::MarkRegenerating(void) { + // Set as queued again: + SetPresence(cpQueued); + // Tell all clients attached to this chunk that they want this chunk: for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) { @@ -191,7 +205,11 @@ void cChunk::MarkRegenerating(void) bool cChunk::CanUnload(void) { - return m_LoadedByClient.empty() && !m_IsDirty && (m_StayCount == 0); + return + m_LoadedByClient.empty() && // The chunk is not used by any client + !m_IsDirty && // The chunk has been saved properly or hasn't been touched since the load / gen + (m_StayCount == 0) && // The chunk is not in a ChunkStay + (m_Presence != cpQueued) ; // The chunk is not queued for loading / generating (otherwise multi-load / multi-gen could occur) } @@ -223,7 +241,7 @@ void cChunk::MarkSaved(void) void cChunk::MarkLoaded(void) { m_IsDirty = false; - SetValid(); + SetPresence(cpPresent); } @@ -232,12 +250,17 @@ void cChunk::MarkLoaded(void) void cChunk::MarkLoadFailed(void) { - if (m_IsValid) + ASSERT(m_Presence == cpQueued); + + // If the chunk is marked as needed, generate it: + if (m_ShouldGenerateIfLoadFailed) { - return; + m_World->GetGenerator().QueueGenerateChunk(m_PosX, m_PosZ, false); + } + else + { + m_Presence = cpInvalid; } - - m_HasLoadFailed = true; } @@ -246,6 +269,8 @@ void cChunk::MarkLoadFailed(void) void cChunk::GetAllData(cChunkDataCallback & a_Callback) { + ASSERT(m_Presence == cpPresent); + a_Callback.HeightMap(&m_HeightMap); a_Callback.BiomeData(&m_BiomeMap); @@ -272,6 +297,7 @@ void cChunk::SetAllData(cSetChunkData & a_SetChunkData) { ASSERT(a_SetChunkData.IsHeightMapValid()); ASSERT(a_SetChunkData.AreBiomesValid()); + ASSERT(IsQueued()); memcpy(m_BiomeMap, a_SetChunkData.GetBiomes(), sizeof(m_BiomeMap)); memcpy(m_HeightMap, a_SetChunkData.GetHeightMap(), sizeof(m_HeightMap)); @@ -317,7 +343,7 @@ void cChunk::SetAllData(cSetChunkData & a_SetChunkData) CreateBlockEntities(); // Set the chunk data as valid. This may be needed for some simulators that perform actions upon block adding (Vaporize) - SetValid(); + SetPresence(cpPresent); // Wake up all simulators for their respective blocks: WakeUpSimulators(); @@ -653,7 +679,7 @@ void cChunk::MoveEntityToNewChunk(cEntity * a_Entity) cChunk * Neighbor = GetNeighborChunk(a_Entity->GetChunkX() * cChunkDef::Width, a_Entity->GetChunkZ() * cChunkDef::Width); if (Neighbor == NULL) { - Neighbor = m_ChunkMap->GetChunkNoLoad(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ()); + Neighbor = m_ChunkMap->GetChunkNoLoad(a_Entity->GetChunkX(), a_Entity->GetChunkZ()); if (Neighbor == NULL) { // TODO: What to do with this? @@ -1319,11 +1345,11 @@ void cChunk::CreateBlockEntities(void) case E_BLOCK_JUKEBOX: case E_BLOCK_FLOWER_POT: { - if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) + if (!HasBlockEntityAt(x + m_PosX * Width, y, z + m_PosZ * Width)) { m_BlockEntities.push_back(cBlockEntity::CreateByBlockType( BlockType, GetMeta(x, y, z), - x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World + x + m_PosX * Width, y, z + m_PosZ * Width, m_World )); } break; @@ -1960,6 +1986,30 @@ bool cChunk::ForEachEntity(cEntityCallback & a_Callback) +bool cChunk::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback) +{ + // The entity list is locked by the parent chunkmap's CS + for (cEntityList::iterator itr = m_Entities.begin(), itr2 = itr; itr != m_Entities.end(); itr = itr2) + { + ++itr2; + cBoundingBox EntBox((*itr)->GetPosition(), (*itr)->GetWidth() / 2, (*itr)->GetHeight()); + if (!EntBox.DoesIntersect(a_Box)) + { + // The entity is not in the specified box + continue; + } + if (a_Callback.Item(*itr)) + { + return false; + } + } // for itr - m_Entitites[] + return true; +} + + + + + bool cChunk::DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult) { // The entity list is locked by the parent chunkmap's CS @@ -2603,7 +2653,7 @@ cChunk * cChunk::GetRelNeighborChunk(int a_RelX, int a_RelZ) int BlockZ = m_PosZ * cChunkDef::Width + a_RelZ; int ChunkX, ChunkZ; BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ); - return m_ChunkMap->GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + return m_ChunkMap->GetChunkNoLoad(ChunkX, ChunkZ); } // Walk the neighbors: @@ -3128,7 +3178,7 @@ void cChunk::PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ, int & a Vector3i cChunk::PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ) { - return Vector3i(m_PosX * Width + a_RelX, m_PosY * Height + a_RelY, m_PosZ * Width + a_RelZ); + return Vector3i(m_PosX * Width + a_RelX, a_RelY, m_PosZ * Width + a_RelZ); } diff --git a/src/Chunk.h b/src/Chunk.h index e5de22e3b..f282694c2 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -66,8 +66,16 @@ class cChunk : public cChunkDef // The inheritance is "misused" here only to inherit the functions and constants defined in cChunkDef { public: + /** Represents the presence state of the chunk */ + enum ePresence + { + cpInvalid, /**< The chunk is not present at all and is not queued in the loader / generator */ + cpQueued, /**< The chunk is not present, but is queued for loading / generation */ + cpPresent, /**< The chunk is present */ + }; + cChunk( - int a_ChunkX, int a_ChunkY, int a_ChunkZ, // Chunk coords + int a_ChunkX, int a_ChunkZ, // Chunk coords cChunkMap * a_ChunkMap, cWorld * a_World, // Parent objects cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP, // Neighbor chunks cAllocationPool<cChunkData::sChunkSection> & a_Pool @@ -75,11 +83,25 @@ public: cChunk(cChunk & other); ~cChunk(); - bool IsValid(void) const {return m_IsValid; } // Returns true if the chunk block data is valid (loaded / generated) - void SetValid(void); // Also wakes up any calls to cChunkMap::GetHeight() - void MarkRegenerating(void); // Marks all clients attached to this chunk as wanting this chunk - bool IsDirty(void) const {return m_IsDirty; } // Returns true if the chunk has changed since it was last saved - bool HasLoadFailed(void) const {return m_HasLoadFailed; } // Returns true if the chunk failed to load and hasn't been generated since then + /** Returns true iff the chunk block data is valid (loaded / generated) */ + bool IsValid(void) const {return (m_Presence == cpPresent); } + + /** Returns true iff the chunk is in the queue for loading / generating */ + bool IsQueued(void) const {return (m_Presence == cpQueued); } + + /** Sets the chunk's presence. + Wakes up any calls to cChunkMap::GetHeight() when setting to cpPresent. */ + void SetPresence(ePresence a_Presence); + + /** Called to indicate whether the chunk should be queued in the generator if it fails to load. Set by cChunkMap::GetChunk(). */ + void SetShouldGenerateIfLoadFailed(bool a_ShouldGenerateIfLoadFailed); + + /** Marks all clients attached to this chunk as wanting this chunk. Also sets presence to cpQueued. */ + void MarkRegenerating(void); + + /** Returns true iff the chunk has changed since it was last saved. */ + bool IsDirty(void) const {return m_IsDirty; } + bool CanUnload(void); bool IsLightValid(void) const {return m_IsLightValid; } @@ -94,7 +116,10 @@ public: void MarkSaving(void); // Marks the chunk as being saved. void MarkSaved(void); // Marks the chunk as saved, if it didn't change from the last call to MarkSaving() void MarkLoaded(void); // Marks the chunk as freshly loaded. Fails if the chunk is already valid - void MarkLoadFailed(void); // Marks the chunk as failed to load. Ignored is the chunk is already valid + + /** Marks the chunk as failed to load. + If m_ShouldGenerateIfLoadFailed is set, queues the chunk for generating. */ + void MarkLoadFailed(void); /** Gets all chunk data, calls the a_Callback's methods for each data type */ void GetAllData(cChunkDataCallback & a_Callback); @@ -135,7 +160,6 @@ public: void TickBlock(int a_RelX, int a_RelY, int a_RelZ); int GetPosX(void) const { return m_PosX; } - int GetPosY(void) const { return m_PosY; } int GetPosZ(void) const { return m_PosZ; } cWorld * GetWorld(void) const { return m_World; } @@ -216,6 +240,10 @@ public: /** Calls the callback for each entity; returns true if all entities processed, false if the callback aborted by returning true */ bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible + /** Calls the callback for each entity that has a nonempty intersection with the specified boundingbox. + Returns true if all entities processed, false if the callback aborted by returning true. */ + bool ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback); // Lua-accessible + /** Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found. */ bool DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult); // Lua-accessible @@ -430,7 +458,12 @@ private: typedef std::vector<sSetBlockQueueItem> sSetBlockQueueVector; - bool m_IsValid; // True if the chunk is loaded / generated + /** Holds the presence status of the chunk - if it is present, or in the loader / generator queue, or unloaded */ + ePresence m_Presence; + + /** If the chunk fails to load, should it be queued in the generator or reset back to invalid? */ + bool m_ShouldGenerateIfLoadFailed; + bool m_IsLightValid; // True if the blocklight and skylight are calculated bool m_IsDirty; // True if the chunk has changed since it was last saved bool m_IsSaving; // True if the chunk is being saved @@ -449,7 +482,7 @@ private: /** Number of times the chunk has been requested to stay (by various cChunkStay objects); if zero, the chunk can be unloaded */ int m_StayCount; - int m_PosX, m_PosY, m_PosZ; + int m_PosX, m_PosZ; cWorld * m_World; cChunkMap * m_ChunkMap; diff --git a/src/ChunkDef.h b/src/ChunkDef.h index 51075ab4a..f4ed66c4b 100644 --- a/src/ChunkDef.h +++ b/src/ChunkDef.h @@ -16,11 +16,6 @@ -/** This is really only a placeholder to be used in places where we need to "make up" a chunk's Y coord. -It will help us when the new chunk format comes out and we need to patch everything up for compatibility. -*/ -#define ZERO_CHUNK_Y 0 - // Used to smoothly convert to new axis ordering. One will be removed when deemed stable. #define AXIS_ORDER_YZX 1 // Original (1.1-) #define AXIS_ORDER_XZY 2 // New (1.2+) @@ -197,32 +192,32 @@ public: inline static int GetHeight(const HeightMap & a_HeightMap, int a_X, int a_Z) { - ASSERT((a_X >= 0) && (a_X <= Width)); - ASSERT((a_Z >= 0) && (a_Z <= Width)); + ASSERT((a_X >= 0) && (a_X < Width)); + ASSERT((a_Z >= 0) && (a_Z < Width)); return a_HeightMap[a_X + Width * a_Z]; } inline static void SetHeight(HeightMap & a_HeightMap, int a_X, int a_Z, unsigned char a_Height) { - ASSERT((a_X >= 0) && (a_X <= Width)); - ASSERT((a_Z >= 0) && (a_Z <= Width)); + ASSERT((a_X >= 0) && (a_X < Width)); + ASSERT((a_Z >= 0) && (a_Z < Width)); a_HeightMap[a_X + Width * a_Z] = a_Height; } inline static EMCSBiome GetBiome(const BiomeMap & a_BiomeMap, int a_X, int a_Z) { - ASSERT((a_X >= 0) && (a_X <= Width)); - ASSERT((a_Z >= 0) && (a_Z <= Width)); + ASSERT((a_X >= 0) && (a_X < Width)); + ASSERT((a_Z >= 0) && (a_Z < Width)); return a_BiomeMap[a_X + Width * a_Z]; } inline static void SetBiome(BiomeMap & a_BiomeMap, int a_X, int a_Z, EMCSBiome a_Biome) { - ASSERT((a_X >= 0) && (a_X <= Width)); - ASSERT((a_Z >= 0) && (a_Z <= Width)); + ASSERT((a_X >= 0) && (a_X < Width)); + ASSERT((a_Z >= 0) && (a_Z < Width)); a_BiomeMap[a_X + Width * a_Z] = a_Biome; } @@ -377,14 +372,13 @@ class cChunkCoords { public: int m_ChunkX; - int m_ChunkY; int m_ChunkZ; - cChunkCoords(int a_ChunkX, int a_ChunkY, int a_ChunkZ) : m_ChunkX(a_ChunkX), m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ) {} + cChunkCoords(int a_ChunkX, int a_ChunkZ) : m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ) {} bool operator == (const cChunkCoords & a_Other) const { - return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkY == a_Other.m_ChunkY) && (m_ChunkZ == a_Other.m_ChunkZ)); + return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkZ == a_Other.m_ChunkZ)); } } ; @@ -395,6 +389,27 @@ typedef std::vector<cChunkCoords> cChunkCoordsVector; +class cChunkCoordsWithBool +{ +public: + int m_ChunkX; + int m_ChunkZ; + bool m_ForceGenerate; + + cChunkCoordsWithBool(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate) : m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ), m_ForceGenerate(a_ForceGenerate){} + + bool operator == (const cChunkCoordsWithBool & a_Other) const + { + return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkZ == a_Other.m_ChunkZ) && (m_ForceGenerate == a_Other.m_ForceGenerate)); + } +}; + +typedef std::list<cChunkCoordsWithBool> cChunkCoordsWithBoolList; + + + + + /// Interface class used as a callback for operations that involve chunk coords class cChunkCoordCallback { diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index a3692ef11..9c105c5af 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -143,26 +143,27 @@ cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk(int a_ChunkX, int a_ChunkZ) -cChunkPtr cChunkMap::GetChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +cChunkPtr cChunkMap::GetChunk(int a_ChunkX, int a_ChunkZ) { - // No need to lock m_CSLayers, since it's already locked by the operation that called us - ASSERT(m_CSLayers.IsLockedByCurrentThread()); + ASSERT(m_CSLayers.IsLockedByCurrentThread()); // m_CSLayers should already be locked by the operation that called us - cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ); + cChunkLayer * Layer = GetLayerForChunk(a_ChunkX, a_ChunkZ); if (Layer == NULL) { // An error must have occurred, since layers are automatically created if they don't exist return NULL; } - cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); + cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkZ); if (Chunk == NULL) { return NULL; } - if (!(Chunk->IsValid())) + if (!Chunk->IsValid() && !Chunk->IsQueued()) { - m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ, true); + Chunk->SetPresence(cChunk::cpQueued); + Chunk->SetShouldGenerateIfLoadFailed(true); + m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkZ); } return Chunk; } @@ -171,24 +172,26 @@ cChunkPtr cChunkMap::GetChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkY, int a_ChunkZ) +cChunkPtr cChunkMap::GetChunkNoGen(int a_ChunkX, int a_ChunkZ) { - // No need to lock m_CSLayers, since it's already locked by the operation that called us - cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ); + ASSERT(m_CSLayers.IsLockedByCurrentThread()); // m_CSLayers should already be locked by the operation that called us + + cChunkLayer * Layer = GetLayerForChunk(a_ChunkX, a_ChunkZ); if (Layer == NULL) { // An error must have occurred, since layers are automatically created if they don't exist return NULL; } - cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); + cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkZ); if (Chunk == NULL) { return NULL; } - if (!(Chunk->IsValid())) + if (!Chunk->IsValid() && !Chunk->IsQueued()) { - m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ, false); + Chunk->SetPresence(cChunk::cpQueued); + m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkZ); } return Chunk; @@ -198,9 +201,10 @@ cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkY, int a_ChunkZ) -cChunkPtr cChunkMap::GetChunkNoLoad( int a_ChunkX, int a_ChunkY, int a_ChunkZ) +cChunkPtr cChunkMap::GetChunkNoLoad( int a_ChunkX, int a_ChunkZ) { - // No need to lock m_CSLayers, since it's already locked by the operation that called us + ASSERT(m_CSLayers.IsLockedByCurrentThread()); // m_CSLayers should already be locked by the operation that called us + cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ); if (Layer == NULL) { @@ -208,7 +212,7 @@ cChunkPtr cChunkMap::GetChunkNoLoad( int a_ChunkX, int a_ChunkY, int a_ChunkZ) return NULL; } - return Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); + return Layer->GetChunk(a_ChunkX, a_ChunkZ); } @@ -222,7 +226,7 @@ bool cChunkMap::LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTY int ChunkX, ChunkZ; cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); if (Chunk == NULL) { return false; @@ -244,7 +248,7 @@ bool cChunkMap::LockedGetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLO int ChunkX, ChunkZ; cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); if (Chunk == NULL) { return false; @@ -265,7 +269,7 @@ bool cChunkMap::LockedGetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIB int ChunkX, ChunkZ; cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); if (Chunk == NULL) { return false; @@ -284,7 +288,7 @@ bool cChunkMap::LockedSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTY // We already have m_CSLayers locked since this can be called only from within the tick thread int ChunkX, ChunkZ; cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); if (Chunk == NULL) { return false; @@ -303,7 +307,7 @@ bool cChunkMap::LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLO // We already have m_CSLayers locked since this can be called only from within the tick thread int ChunkX, ChunkZ; cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); if (Chunk == NULL) { return false; @@ -336,7 +340,7 @@ cChunk * cChunkMap::FindChunk(int a_ChunkX, int a_ChunkZ) void cChunkMap::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ()); if (Chunk == NULL) { return; @@ -356,7 +360,7 @@ void cChunkMap::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, c x = a_BlockX; z = a_BlockZ; cChunkDef::BlockToChunk(x, z, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if (Chunk == NULL) { return; @@ -375,7 +379,7 @@ void cChunkMap::BroadcastBlockBreakAnimation(int a_entityID, int a_blockX, int a int ChunkX, ChunkZ; cChunkDef::BlockToChunk(a_blockX, a_blockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if (Chunk == NULL) { return; @@ -393,7 +397,7 @@ void cChunkMap::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, c cCSLock Lock(m_CSLayers); int ChunkX, ChunkZ; cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return; @@ -408,7 +412,7 @@ void cChunkMap::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, c void cChunkMap::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, 0, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ); if (Chunk == NULL) { return; @@ -424,7 +428,7 @@ void cChunkMap::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSeriali void cChunkMap::BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ()); if (Chunk == NULL) { return; @@ -440,7 +444,7 @@ void cChunkMap::BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer & void cChunkMap::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ()); if (Chunk == NULL) { return; @@ -456,7 +460,7 @@ void cChunkMap::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHa void cChunkMap::BroadcastEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ()); if (Chunk == NULL) { return; @@ -472,7 +476,7 @@ void cChunkMap::BroadcastEntityEffect(const cEntity & a_Entity, int a_EffectID, void cChunkMap::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ()); if (Chunk == NULL) { return; @@ -488,7 +492,7 @@ void cChunkMap::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotN void cChunkMap::BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ()); if (Chunk == NULL) { return; @@ -504,7 +508,7 @@ void cChunkMap::BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientH void cChunkMap::BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ()); if (Chunk == NULL) { return; @@ -520,7 +524,7 @@ void cChunkMap::BroadcastEntityLook(const cEntity & a_Entity, const cClientHandl void cChunkMap::BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ()); if (Chunk == NULL) { return; @@ -536,7 +540,7 @@ void cChunkMap::BroadcastEntityMetadata(const cEntity & a_Entity, const cClientH void cChunkMap::BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ()); if (Chunk == NULL) { return; @@ -552,7 +556,7 @@ void cChunkMap::BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, ch void cChunkMap::BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ()); if (Chunk == NULL) { return; @@ -568,7 +572,7 @@ void cChunkMap::BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX void cChunkMap::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ()); if (Chunk == NULL) { return; @@ -584,7 +588,7 @@ void cChunkMap::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, c void cChunkMap::BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ()); if (Chunk == NULL) { return; @@ -600,7 +604,7 @@ void cChunkMap::BroadcastEntityVelocity(const cEntity & a_Entity, const cClientH void cChunkMap::BroadcastEntityAnimation(const cEntity & a_Entity, char a_Animation, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ()); if (Chunk == NULL) { return; @@ -619,7 +623,7 @@ void cChunkMap::BroadcastParticleEffect(const AString & a_ParticleName, float a_ int ChunkX, ChunkZ; cChunkDef::BlockToChunk((int) a_SrcX, (int) a_SrcZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if (Chunk == NULL) { return; @@ -636,7 +640,7 @@ void cChunkMap::BroadcastRemoveEntityEffect(const cEntity & a_Entity, int a_Effe { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ()); if (Chunk == NULL) { return; @@ -655,7 +659,7 @@ void cChunkMap::BroadcastSoundEffect(const AString & a_SoundName, double a_X, do int ChunkX, ChunkZ; cChunkDef::BlockToChunk((int)std::floor(a_X), (int)std::floor(a_Z), ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if (Chunk == NULL) { return; @@ -674,7 +678,7 @@ void cChunkMap::BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_S int ChunkX, ChunkZ; cChunkDef::BlockToChunk(a_SrcX, a_SrcZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if (Chunk == NULL) { return; @@ -690,7 +694,7 @@ void cChunkMap::BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_S void cChunkMap::BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ()); if (Chunk == NULL) { return; @@ -708,7 +712,7 @@ void cChunkMap::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, c cCSLock Lock(m_CSLayers); int ChunkX, ChunkZ; cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if (Chunk == NULL) { return; @@ -727,7 +731,7 @@ void cChunkMap::BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_Bl int ChunkX, ChunkZ; cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if (Chunk == NULL) { return; @@ -745,7 +749,7 @@ void cChunkMap::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClien cCSLock Lock(m_CSLayers); int ChunkX, ChunkZ; cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return; @@ -763,7 +767,7 @@ void cChunkMap::UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, i cCSLock Lock(m_CSLayers); int ChunkX, ChunkZ; cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return; @@ -778,7 +782,7 @@ void cChunkMap::UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, i bool cChunkMap::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ); if (Chunk == NULL) { return false; @@ -795,7 +799,7 @@ void cChunkMap::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ) cCSLock Lock(m_CSLayers); int ChunkX, ChunkZ; cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return; @@ -824,7 +828,7 @@ void cChunkMap::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_M int MaxZ = std::min(a_MaxBlockZ, z * cChunkDef::Width + cChunkDef::Width - 1); for (int x = MinChunkX; x <= MaxChunkX; x++) { - cChunkPtr Chunk = GetChunkNoGen(x, 0, z); + cChunkPtr Chunk = GetChunkNoGen(x, z); if ((Chunk == NULL) || !Chunk->IsValid()) { continue; @@ -852,7 +856,7 @@ void cChunkMap::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_M void cChunkMap::MarkRedstoneDirty(int a_ChunkX, int a_ChunkZ) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return; @@ -867,7 +871,7 @@ void cChunkMap::MarkRedstoneDirty(int a_ChunkX, int a_ChunkZ) void cChunkMap::MarkChunkDirty(int a_ChunkX, int a_ChunkZ, bool a_MarkRedstoneDirty) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return; @@ -886,7 +890,7 @@ void cChunkMap::MarkChunkDirty(int a_ChunkX, int a_ChunkZ, bool a_MarkRedstoneDi void cChunkMap::MarkChunkSaving(int a_ChunkX, int a_ChunkZ) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return; @@ -901,7 +905,7 @@ void cChunkMap::MarkChunkSaving(int a_ChunkX, int a_ChunkZ) void cChunkMap::MarkChunkSaved (int a_ChunkX, int a_ChunkZ) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return; @@ -919,7 +923,7 @@ void cChunkMap::SetChunkData(cSetChunkData & a_SetChunkData) int ChunkZ = a_SetChunkData.GetChunkZ(); { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); if (Chunk == NULL) { return; @@ -964,7 +968,7 @@ void cChunkMap::ChunkLighted( ) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ); if (Chunk == NULL) { return; @@ -980,7 +984,7 @@ void cChunkMap::ChunkLighted( bool cChunkMap::GetChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -996,7 +1000,7 @@ bool cChunkMap::GetChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_ bool cChunkMap::GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -1009,10 +1013,21 @@ bool cChunkMap::GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_Blo +bool cChunkMap::IsChunkQueued(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ); + return (Chunk != NULL) && Chunk->IsQueued(); +} + + + + + bool cChunkMap::IsChunkValid(int a_ChunkX, int a_ChunkZ) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ); return (Chunk != NULL) && Chunk->IsValid(); } @@ -1023,7 +1038,7 @@ bool cChunkMap::IsChunkValid(int a_ChunkX, int a_ChunkZ) bool cChunkMap::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ); return (Chunk != NULL) && Chunk->HasAnyClients(); } @@ -1038,7 +1053,7 @@ int cChunkMap::GetHeight(int a_BlockX, int a_BlockZ) cCSLock Lock(m_CSLayers); int ChunkX, ChunkZ, BlockY = 0; cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ); if (Chunk == NULL) { return 0; @@ -1065,7 +1080,7 @@ bool cChunkMap::TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height) cCSLock Lock(m_CSLayers); int ChunkX, ChunkZ, BlockY = 0; cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -1088,7 +1103,7 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList) int ChunkX = a_BlockList.front().ChunkX; int ChunkZ = a_BlockList.front().ChunkZ; cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk != NULL) && Chunk->IsValid()) { for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();) @@ -1135,7 +1150,7 @@ void cChunkMap::CollectPickupsByPlayer(cPlayer * a_Player) int BlockX = (int)(a_Player->GetPosX()); // Truncating doesn't matter much; we're scanning entire chunks anyway int BlockY = (int)(a_Player->GetPosY()); int BlockZ = (int)(a_Player->GetPosZ()); - int ChunkX, ChunkZ, ChunkY = ZERO_CHUNK_Y; + int ChunkX = 0, ChunkZ = 0; cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); int OtherChunkX = ChunkX + ((BlockX > 8) ? 1 : -1); int OtherChunkZ = ChunkZ + ((BlockZ > 8) ? 1 : -1); @@ -1144,13 +1159,13 @@ void cChunkMap::CollectPickupsByPlayer(cPlayer * a_Player) // The only time the chunks are not valid is when the player is downloading the initial world and they should not call this at that moment cCSLock Lock(m_CSLayers); - GetChunkNoLoad(ChunkX, ChunkY, ChunkZ)->CollectPickupsByPlayer(a_Player); + GetChunkNoLoad(ChunkX, ChunkZ)->CollectPickupsByPlayer(a_Player); // Check the neighboring chunks as well: - GetChunkNoLoad(OtherChunkX, ChunkY, ChunkZ)->CollectPickupsByPlayer (a_Player); - GetChunkNoLoad(OtherChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player); - GetChunkNoLoad(ChunkX, ChunkY, ChunkZ)->CollectPickupsByPlayer (a_Player); - GetChunkNoLoad(ChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player); + GetChunkNoLoad(OtherChunkX, ChunkZ)->CollectPickupsByPlayer (a_Player); + GetChunkNoLoad(OtherChunkX, OtherChunkZ)->CollectPickupsByPlayer(a_Player); + GetChunkNoLoad(ChunkX, ChunkZ)->CollectPickupsByPlayer (a_Player); + GetChunkNoLoad(ChunkX, OtherChunkZ)->CollectPickupsByPlayer(a_Player); } @@ -1177,7 +1192,7 @@ BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ) cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ); if ((Chunk != NULL) && Chunk->IsValid()) { return Chunk->GetBlock(a_BlockX, a_BlockY, a_BlockZ); @@ -1206,7 +1221,7 @@ NIBBLETYPE cChunkMap::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ); if ((Chunk != NULL) && Chunk->IsValid()) { return Chunk->GetMeta(a_BlockX, a_BlockY, a_BlockZ); @@ -1224,7 +1239,7 @@ NIBBLETYPE cChunkMap::GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ) cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ); if ((Chunk != NULL) && Chunk->IsValid()) { return Chunk->GetSkyLight(a_BlockX, a_BlockY, a_BlockZ); @@ -1242,7 +1257,7 @@ NIBBLETYPE cChunkMap::GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_Block cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ); if ((Chunk != NULL) && Chunk->IsValid()) { return Chunk->GetBlockLight(a_BlockX, a_BlockY, a_BlockZ); @@ -1261,7 +1276,7 @@ void cChunkMap::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYP // a_BlockXYZ now contains relative coords! cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ); if ((Chunk != NULL) && Chunk->IsValid()) { Chunk->SetMeta(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta); @@ -1284,7 +1299,7 @@ void cChunkMap::SetBlock(cWorldInterface & a_WorldInterface, int a_BlockX, int a cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ); if ((Chunk != NULL) && Chunk->IsValid()) { Chunk->SetBlock(X, Y, Z, a_BlockType, a_BlockMeta, a_SendToClients); @@ -1303,7 +1318,7 @@ void cChunkMap::QueueSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYP cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ); if ((Chunk != NULL) && Chunk->IsValid()) { Chunk->QueueSetBlock(X, Y, Z, a_BlockType, a_BlockMeta, a_Tick, a_PreviousBlockType); @@ -1320,7 +1335,7 @@ bool cChunkMap::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCK cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ); if ((Chunk != NULL) && Chunk->IsValid()) { Chunk->GetBlockTypeMeta(X, Y, Z, a_BlockType, a_BlockMeta); @@ -1339,7 +1354,7 @@ bool cChunkMap::GetBlockInfo(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ); if ((Chunk != NULL) && Chunk->IsValid()) { Chunk->GetBlockInfo(X, Y, Z, a_BlockType, a_Meta, a_SkyLight, a_BlockLight); @@ -1357,7 +1372,7 @@ void cChunkMap::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_Filt cCSLock Lock(m_CSLayers); for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) { - cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ); + cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { continue; @@ -1378,7 +1393,7 @@ void cChunkMap::ReplaceTreeBlocks(const sSetBlockVector & a_Blocks) cCSLock Lock(m_CSLayers); for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) { - cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ); + cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { continue; @@ -1413,7 +1428,7 @@ EMCSBiome cChunkMap::GetBiomeAt (int a_BlockX, int a_BlockZ) cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ); if ((Chunk != NULL) && Chunk->IsValid()) { return Chunk->GetBiomeAt(X, Z); @@ -1434,7 +1449,7 @@ bool cChunkMap::SetBiomeAt(int a_BlockX, int a_BlockZ, EMCSBiome a_Biome) cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ); if ((Chunk != NULL) && Chunk->IsValid()) { Chunk->SetBiomeAt(X, Z, a_Biome); @@ -1467,7 +1482,7 @@ bool cChunkMap::SetAreaBiome(int a_MinX, int a_MaxX, int a_MinZ, int a_MaxZ, EMC { int MinRelZ = (z == MinChunkZ) ? MinZ : 0; int MaxRelZ = (z == MaxChunkZ) ? MaxZ : cChunkDef::Width - 1; - cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z); + cChunkPtr Chunk = GetChunkNoLoad(x, z); if ((Chunk != NULL) && Chunk->IsValid()) { Chunk->SetAreaBiome(MinRelX, MaxRelX, MinRelZ, MaxRelZ, a_Biome); @@ -1491,7 +1506,7 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) cCSLock Lock(m_CSLayers); for (sSetBlockVector::iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) { - cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ); + cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { if (!a_ContinueOnFailure) @@ -1519,7 +1534,7 @@ bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z) { cCSLock Lock(m_CSLayers); - cChunkPtr DestChunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr DestChunk = GetChunk( ChunkX, ChunkZ); if ((DestChunk == NULL) || !DestChunk->IsValid()) { return false; @@ -1542,7 +1557,7 @@ void cChunkMap::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player) cChunkDef::AbsoluteToRelative(a_X, a_Y, a_Z, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ); if ((Chunk != NULL) && (Chunk->IsValid())) { Chunk->SendBlockTo(a_X, a_Y, a_Z, a_Player->GetClientHandle()); @@ -1556,12 +1571,12 @@ void cChunkMap::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player) void cChunkMap::CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk1 = GetChunkNoGen(a_ChunkX1, ZERO_CHUNK_Y, a_ChunkZ1); + cChunkPtr Chunk1 = GetChunkNoGen(a_ChunkX1, a_ChunkZ1); if (Chunk1 == NULL) { return; } - cChunkPtr Chunk2 = GetChunkNoGen(a_ChunkX2, ZERO_CHUNK_Y, a_ChunkZ2); + cChunkPtr Chunk2 = GetChunkNoGen(a_ChunkX2, a_ChunkZ2); if (Chunk2 == NULL) { return; @@ -1623,7 +1638,7 @@ void cChunkMap::CompareChunkClients(cChunk * a_Chunk1, cChunk * a_Chunk2, cClien bool cChunkMap::AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunk(a_ChunkX, a_ChunkZ); if (Chunk == NULL) { return false; @@ -1638,7 +1653,7 @@ bool cChunkMap::AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Cli void cChunkMap::RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ); if (Chunk == NULL) { return; @@ -1667,7 +1682,7 @@ void cChunkMap::RemoveClientFromChunks(cClientHandle * a_Client) void cChunkMap::AddEntity(cEntity * a_Entity) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), a_Entity->GetChunkZ()); if ( (Chunk == NULL) || // Chunk not present at all (!Chunk->IsValid() && !a_Entity->IsPlayer()) // Chunk present, but no valid data; players need to spawn in such chunks (#953) @@ -1688,7 +1703,7 @@ void cChunkMap::AddEntity(cEntity * a_Entity) void cChunkMap::AddEntityIfNotPresent(cEntity * a_Entity) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), a_Entity->GetChunkZ()); if ( (Chunk == NULL) || // Chunk not present at all (!Chunk->IsValid() && !a_Entity->IsPlayer()) // Chunk present, but no valid data; players need to spawn in such chunks (#953) @@ -1729,7 +1744,7 @@ bool cChunkMap::HasEntity(int a_UniqueID) void cChunkMap::RemoveEntity(cEntity * a_Entity) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), a_Entity->GetChunkZ()); // Even if a chunk is not valid, it may still contain entities such as players; make sure to remove them (#1190) if (Chunk == NULL) @@ -1763,7 +1778,7 @@ bool cChunkMap::ForEachEntity(cEntityCallback & a_Callback) bool cChunkMap::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -1775,6 +1790,38 @@ bool cChunkMap::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback +bool cChunkMap::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback) +{ + // Calculate the chunk range for the box: + int MinChunkX = (int)floor(a_Box.GetMinX() / cChunkDef::Width); + int MinChunkZ = (int)floor(a_Box.GetMinZ() / cChunkDef::Width); + int MaxChunkX = (int)floor((a_Box.GetMaxX() + cChunkDef::Width) / cChunkDef::Width); + int MaxChunkZ = (int)floor((a_Box.GetMaxZ() + cChunkDef::Width) / cChunkDef::Width); + + // Iterate over each chunk in the range: + cCSLock Lock(m_CSLayers); + for (int z = MinChunkZ; z <= MaxChunkZ; z++) + { + for (int x = MinChunkX; x <= MaxChunkX; x++) + { + cChunkPtr Chunk = GetChunkNoGen(x, z); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + continue; + } + if (!Chunk->ForEachEntityInBox(a_Box, a_Callback)) + { + return false; + } + } // for x + } // for z + return true; +} + + + + + void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlocksAffected) { // Don't explode if outside of Y range (prevents the following test running into unallocated memory): @@ -1989,7 +2036,7 @@ bool cChunkMap::DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback) bool cChunkMap::ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEntityCallback & a_Callback) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2004,7 +2051,7 @@ bool cChunkMap::ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEnti bool cChunkMap::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2019,7 +2066,7 @@ bool cChunkMap::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & bool cChunkMap::ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2034,7 +2081,7 @@ bool cChunkMap::ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCa bool cChunkMap::ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2049,7 +2096,7 @@ bool cChunkMap::ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallba bool cChunkMap::ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2064,7 +2111,7 @@ bool cChunkMap::ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpens bool cChunkMap::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2082,7 +2129,7 @@ bool cChunkMap::DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cB int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2100,7 +2147,7 @@ bool cChunkMap::DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeacon int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2118,7 +2165,7 @@ bool cChunkMap::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCa int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2136,7 +2183,7 @@ bool cChunkMap::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDis int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2154,7 +2201,7 @@ bool cChunkMap::DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropp int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2172,7 +2219,7 @@ bool cChunkMap::DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cD int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2190,7 +2237,7 @@ bool cChunkMap::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurna int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2207,7 +2254,7 @@ bool cChunkMap::DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNot int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2224,7 +2271,7 @@ bool cChunkMap::DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, c int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2242,7 +2289,7 @@ bool cChunkMap::DoWithMobHeadAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHe int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2260,7 +2307,7 @@ bool cChunkMap::DoWithFlowerPotAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFlo int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2278,7 +2325,7 @@ bool cChunkMap::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2290,62 +2337,20 @@ bool cChunkMap::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & -void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkZ) { cCSLock Lock(m_CSLayers); - GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -/// Loads the chunk synchronously, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) -bool cChunkMap::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - { - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); - if (Chunk == NULL) - { - // Internal error - return false; - } - if (Chunk->IsValid()) - { - // Already loaded - return true; - } - if (Chunk->HasLoadFailed()) - { - // Already tried loading and it failed - return false; - } - } - return m_World->GetStorage().LoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -/// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() -void cChunkMap::LoadChunks(const cChunkCoordsList & a_Chunks) -{ - for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr) - { - LoadChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); - } // for itr - a_Chunks[] + GetChunk(a_ChunkX, a_ChunkZ); } -void cChunkMap::ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +void cChunkMap::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkY, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ); if (Chunk == NULL) { return; @@ -2362,7 +2367,7 @@ bool cChunkMap::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const ASt cCSLock Lock(m_CSLayers); int ChunkX, ChunkZ; cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; @@ -2377,7 +2382,7 @@ bool cChunkMap::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const ASt void cChunkMap::MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ); if (Chunk == NULL) { // Not present @@ -2393,7 +2398,7 @@ void cChunkMap::MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ) bool cChunkMap::IsChunkLighted(int a_ChunkX, int a_ChunkZ) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ); if (Chunk == NULL) { // Not present @@ -2414,7 +2419,7 @@ bool cChunkMap::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinCh { for (int x = a_MinChunkX; x <= a_MaxChunkX; x++) { - cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z); + cChunkPtr Chunk = GetChunkNoLoad(x, z); if ((Chunk == NULL) || (!Chunk->IsValid())) { // Not present / not valid @@ -2456,7 +2461,7 @@ bool cChunkMap::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBl { for (int x = MinChunkX; x <= MaxChunkX; x++) { - cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z); + cChunkPtr Chunk = GetChunkNoLoad(x, z); if ((Chunk == NULL) || (!Chunk->IsValid())) { // Not present / not valid @@ -2497,7 +2502,7 @@ void cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCK cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); if (Chunk != NULL) { Chunk->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Rand); @@ -2514,7 +2519,7 @@ void cChunkMap::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Nu cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); if (Chunk != NULL) { Chunk->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); @@ -2531,7 +2536,7 @@ void cChunkMap::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBl cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); if (Chunk != NULL) { Chunk->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); @@ -2548,7 +2553,7 @@ void cChunkMap::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ) cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); if (Chunk != NULL) { Chunk->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ); @@ -2603,7 +2608,7 @@ void cChunkMap::TickBlock(int a_BlockX, int a_BlockY, int a_BlockZ) cCSLock Lock(m_CSLayers); int ChunkX, ChunkZ; cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return; @@ -2672,7 +2677,7 @@ void cChunkMap::QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ) // a_BlockXYZ now contains relative coords! cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); if (Chunk != NULL) { Chunk->QueueTickBlock(a_BlockX, a_BlockY, a_BlockZ); @@ -2686,7 +2691,7 @@ void cChunkMap::QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ) void cChunkMap::SetChunkAlwaysTicked(int a_ChunkX, int a_ChunkZ, bool a_AlwaysTicked) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ); if (Chunk != NULL) { Chunk->SetAlwaysTicked(a_AlwaysTicked); @@ -2731,7 +2736,7 @@ cChunkMap::cChunkLayer::~cChunkLayer() -cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ) +cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkZ) { // Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check @@ -2751,7 +2756,7 @@ cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkY, int a_Ch cChunk * neixp = (LocalX < LAYER_SIZE - 1) ? m_Chunks[Index + 1] : m_Parent->FindChunk(a_ChunkX + 1, a_ChunkZ); cChunk * neizm = (LocalZ > 0) ? m_Chunks[Index - LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX, a_ChunkZ - 1); cChunk * neizp = (LocalZ < LAYER_SIZE - 1) ? m_Chunks[Index + LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX, a_ChunkZ + 1); - m_Chunks[Index] = new cChunk(a_ChunkX, 0, a_ChunkZ, m_Parent, m_Parent->GetWorld(), neixm, neixp, neizm, neizp, m_Pool); + m_Chunks[Index] = new cChunk(a_ChunkX, a_ChunkZ, m_Parent, m_Parent->GetWorld(), neixm, neixp, neizm, neizp, m_Pool); } return m_Chunks[Index]; } @@ -2951,7 +2956,7 @@ void cChunkMap::cChunkLayer::Save(void) { if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->IsDirty()) { - World->GetStorage().QueueSaveChunk(m_Chunks[i]->GetPosX(), m_Chunks[i]->GetPosY(), m_Chunks[i]->GetPosZ()); + World->GetStorage().QueueSaveChunk(m_Chunks[i]->GetPosX(), m_Chunks[i]->GetPosZ()); } } // for i - m_Chunks[] } @@ -3024,7 +3029,7 @@ void cChunkMap::AddChunkStay(cChunkStay & a_ChunkStay) const cChunkCoordsVector & WantedChunks = a_ChunkStay.GetChunks(); for (cChunkCoordsVector::const_iterator itr = WantedChunks.begin(); itr != WantedChunks.end(); ++itr) { - cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); + cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkZ); if (Chunk == NULL) { continue; @@ -3073,7 +3078,7 @@ void cChunkMap::DelChunkStay(cChunkStay & a_ChunkStay) const cChunkCoordsVector & Chunks = a_ChunkStay.GetChunks(); for (cChunkCoordsVector::const_iterator itr = Chunks.begin(), end = Chunks.end(); itr != end; ++itr) { - cChunkPtr Chunk = GetChunkNoLoad(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(itr->m_ChunkX, itr->m_ChunkZ); if (Chunk == NULL) { continue; diff --git a/src/ChunkMap.h b/src/ChunkMap.h index 1e9a0f982..7354536d4 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -36,6 +36,7 @@ class cBlockArea; class cMobCensus; class cMobSpawner; class cSetChunkData; +class cBoundingBox; typedef std::list<cClientHandle *> cClientHandleList; typedef cChunk * cChunkPtr; @@ -132,6 +133,9 @@ public: /** Copies the chunk's blocktypes into a_Blocks; returns true if successful */ bool GetChunkBlockTypes (int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_Blocks); + /** Returns true iff the chunk is in the loader / generator queue. */ + bool IsChunkQueued(int a_ChunkX, int a_ChunkZ); + bool IsChunkValid (int a_ChunkX, int a_ChunkZ); bool HasChunkAnyClients (int a_ChunkX, int a_ChunkZ); int GetHeight (int a_BlockX, int a_BlockZ); // Waits for the chunk to get loaded / generated @@ -209,6 +213,11 @@ public: /** Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true */ bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Lua-accessible + /** Calls the callback for each entity that has a nonempty intersection with the specified boundingbox. + Returns true if all entities processed, false if the callback aborted by returning true. + If any chunk in the box is missing, ignores the entities in that chunk silently. */ + bool ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback); // Lua-accessible + /** Destroys and returns a list of blocks destroyed in the explosion at the specified coordinates */ void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlockAffected); @@ -270,16 +279,10 @@ public: bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible /** Touches the chunk, causing it to be loaded or generated */ - void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + void TouchChunk(int a_ChunkX, int a_ChunkZ); - /** Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) */ - bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /** Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() */ - void LoadChunks(const cChunkCoordsList & a_Chunks); - /** Marks the chunk as failed-to-load */ - void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ); /** Sets the sign text. Returns true if sign text changed. */ bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); @@ -363,7 +366,7 @@ private: ~cChunkLayer(); /** Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check */ - cChunkPtr GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ); + cChunkPtr GetChunk( int a_ChunkX, int a_ChunkZ); /** Returns the specified chunk, or NULL if not created yet */ cChunk * FindChunk(int a_ChunkX, int a_ChunkZ); @@ -456,9 +459,9 @@ private: std::auto_ptr<cAllocationPool<cChunkData::sChunkSection> > m_Pool; - cChunkPtr GetChunk (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading / generating if not valid - cChunkPtr GetChunkNoGen (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading if not valid; doesn't generate - cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Doesn't load, doesn't generate + cChunkPtr GetChunk (int a_ChunkX, int a_ChunkZ); // Also queues the chunk for loading / generating if not valid + cChunkPtr GetChunkNoGen (int a_ChunkX, int a_ChunkZ); // Also queues the chunk for loading if not valid; doesn't generate + cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkZ); // Doesn't load, doesn't generate /** Gets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) */ bool LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); diff --git a/src/ChunkSender.cpp b/src/ChunkSender.cpp index ebcf0e272..95c4d03d2 100644 --- a/src/ChunkSender.cpp +++ b/src/ChunkSender.cpp @@ -81,7 +81,7 @@ void cChunkSender::ChunkReady(int a_ChunkX, int a_ChunkZ) // This is probably never gonna be called twice for the same chunk, and if it is, we don't mind, so we don't check { cCSLock Lock(m_CS); - m_ChunksReady.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); + m_ChunksReady.push_back(cChunkCoords(a_ChunkX, a_ChunkZ)); } m_evtQueue.Set(); } @@ -95,12 +95,12 @@ void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * ASSERT(a_Client != NULL); { cCSLock Lock(m_CS); - if (std::find(m_SendChunks.begin(), m_SendChunks.end(), sSendChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ, a_Client)) != m_SendChunks.end()) + if (std::find(m_SendChunks.begin(), m_SendChunks.end(), sSendChunk(a_ChunkX, a_ChunkZ, a_Client)) != m_SendChunks.end()) { // Already queued, bail out return; } - m_SendChunks.push_back(sSendChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ, a_Client)); + m_SendChunks.push_back(sSendChunk(a_ChunkX, a_ChunkZ, a_Client)); } m_evtQueue.Set(); } @@ -160,7 +160,7 @@ void cChunkSender::Execute(void) m_ChunksReady.pop_front(); Lock.Unlock(); - SendChunk(Coords.m_ChunkX, Coords.m_ChunkY, Coords.m_ChunkZ, NULL); + SendChunk(Coords.m_ChunkX, Coords.m_ChunkZ, NULL); } else { @@ -169,7 +169,7 @@ void cChunkSender::Execute(void) m_SendChunks.pop_front(); Lock.Unlock(); - SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkY, Chunk.m_ChunkZ, Chunk.m_Client); + SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, Chunk.m_Client); } Lock.Lock(); int RemoveCount = m_RemoveCount; @@ -186,14 +186,14 @@ void cChunkSender::Execute(void) -void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) +void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) { ASSERT(m_World != NULL); // Ask the client if it still wants the chunk: if (a_Client != NULL) { - if (!a_Client->WantsSendChunk(a_ChunkX, a_ChunkY, a_ChunkZ)) + if (!a_Client->WantsSendChunk(a_ChunkX, a_ChunkZ)) { return; } diff --git a/src/ChunkSender.h b/src/ChunkSender.h index 624a3a0bd..a0e9087a9 100644 --- a/src/ChunkSender.h +++ b/src/ChunkSender.h @@ -95,13 +95,11 @@ protected: struct sSendChunk { int m_ChunkX; - int m_ChunkY; int m_ChunkZ; cClientHandle * m_Client; - sSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) : + sSendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) : m_ChunkX(a_ChunkX), - m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ), m_Client(a_Client) { @@ -111,7 +109,6 @@ protected: { return ( (a_Other.m_ChunkX == m_ChunkX) && - (a_Other.m_ChunkY == m_ChunkY) && (a_Other.m_ChunkZ == m_ChunkZ) && (a_Other.m_Client == m_Client) ); @@ -162,7 +159,7 @@ protected: virtual void BlockEntity (cBlockEntity * a_Entity) override; /// Sends the specified chunk to a_Client, or to all chunk clients if a_Client == NULL - void SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client); + void SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); } ; diff --git a/src/ChunkStay.cpp b/src/ChunkStay.cpp index b5002a63d..38aa89a37 100644 --- a/src/ChunkStay.cpp +++ b/src/ChunkStay.cpp @@ -51,7 +51,7 @@ void cChunkStay::Add(int a_ChunkX, int a_ChunkZ) return; } } // for itr - Chunks[] - m_Chunks.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); + m_Chunks.push_back(cChunkCoords(a_ChunkX, a_ChunkZ)); } diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index f9c6a664c..6b31bfa64 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -472,13 +472,13 @@ void cClientHandle::StreamChunks(void) // For each distance touch chunks in a hollow square centered around current position: for (int i = -d; i <= d; ++i) { - World->TouchChunk(ChunkPosX + d, ZERO_CHUNK_Y, ChunkPosZ + i); - World->TouchChunk(ChunkPosX - d, ZERO_CHUNK_Y, ChunkPosZ + i); + World->TouchChunk(ChunkPosX + d, ChunkPosZ + i); + World->TouchChunk(ChunkPosX - d, ChunkPosZ + i); } // for i for (int i = -d + 1; i < d; ++i) { - World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ + d); - World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ - d); + World->TouchChunk(ChunkPosX + i, ChunkPosZ + d); + World->TouchChunk(ChunkPosX + i, ChunkPosZ - d); } // for i } // for d } @@ -501,8 +501,8 @@ void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkZ) { { cCSLock Lock(m_CSChunkLists); - m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); - m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); + m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, a_ChunkZ)); + m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, a_ChunkZ)); } World->SendChunkTo(a_ChunkX, a_ChunkZ, this); } @@ -2734,7 +2734,7 @@ bool cClientHandle::HasPluginChannel(const AString & a_PluginChannel) -bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkZ) { if (m_State >= csDestroying) { @@ -2742,7 +2742,7 @@ bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) } cCSLock Lock(m_CSChunkLists); - return (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)) != m_ChunksToSend.end()); + return (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, a_ChunkZ)) != m_ChunksToSend.end()); } @@ -2758,9 +2758,9 @@ void cClientHandle::AddWantedChunk(int a_ChunkX, int a_ChunkZ) LOGD("Adding chunk [%d, %d] to wanted chunks for client %p", a_ChunkX, a_ChunkZ, this); cCSLock Lock(m_CSChunkLists); - if (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)) == m_ChunksToSend.end()) + if (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, a_ChunkZ)) == m_ChunksToSend.end()) { - m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); + m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, a_ChunkZ)); } } @@ -2858,11 +2858,27 @@ void cClientHandle::SocketClosed(void) -void cClientHandle::HandleEnchantItem(Byte & WindowID, Byte & Enchantment) +void cClientHandle::HandleEnchantItem(Byte & a_WindowID, Byte & a_Enchantment) { - cEnchantingWindow * Window = (cEnchantingWindow*)m_Player->GetWindow(); + if (a_Enchantment > 2) + { + LOGWARNING("%s attempt to crash the server with invalid enchanting selection!", GetUsername().c_str()); + Kick("Invalid enchanting!"); + return; + } + + if ( + (m_Player->GetWindow() == NULL) || + (m_Player->GetWindow()->GetWindowID() != a_WindowID) || + (m_Player->GetWindow()->GetWindowType() != cWindow::wtEnchantment) + ) + { + return; + } + + cEnchantingWindow * Window = (cEnchantingWindow*) m_Player->GetWindow(); cItem Item = *Window->m_SlotArea->GetSlot(0, *m_Player); - int BaseEnchantmentLevel = Window->GetPropertyValue(Enchantment); + int BaseEnchantmentLevel = Window->GetPropertyValue(a_Enchantment); if (Item.EnchantByXPLevels(BaseEnchantmentLevel)) { diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 7ae70a07f..593d0780d 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -209,7 +209,7 @@ public: // tolua_end /** Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend) */ - bool WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + bool WantsSendChunk(int a_ChunkX, int a_ChunkZ); /** Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend) */ void AddWantedChunk(int a_ChunkX, int a_ChunkZ); @@ -269,7 +269,7 @@ public: void RemoveFromWorld(void); /** Called when the player will enchant a Item */ - void HandleEnchantItem(Byte & WindowID, Byte & Enchantment); + void HandleEnchantItem(Byte & a_WindowID, Byte & a_Enchantment); private: diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 89d1cffa1..9bcdcffeb 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -387,7 +387,7 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) case cMonster::mtGhast: case cMonster::mtZombiePigman: case cMonster::mtMagmaCube: - { + { break; }; default: StartBurning(BurnTicks * 20); @@ -417,7 +417,7 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) } if (!Player->IsOnGround()) - { + { if ((a_TDI.DamageType == dtAttack) || (a_TDI.DamageType == dtArrowAttack)) { a_TDI.FinalDamage += 2; diff --git a/src/Entities/EntityEffect.cpp b/src/Entities/EntityEffect.cpp index 3e28392f4..f08755674 100644 --- a/src/Entities/EntityEffect.cpp +++ b/src/Entities/EntityEffect.cpp @@ -34,14 +34,14 @@ cEntityEffect::eType cEntityEffect::GetPotionEffectType(short a_ItemDamage) case 0x08: return cEntityEffect::effWeakness; case 0x09: return cEntityEffect::effStrength; case 0x0a: return cEntityEffect::effSlowness; + case 0x0b: return cEntityEffect::effJumpBoost; case 0x0c: return cEntityEffect::effInstantDamage; case 0x0d: return cEntityEffect::effWaterBreathing; case 0x0e: return cEntityEffect::effInvisibility; - + // No effect potions case 0x00: case 0x07: - case 0x0b: // Will be potion of leaping in 1.8 case 0x0f: { break; diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp index aab534f41..87b5bed07 100644 --- a/src/Entities/Pickup.cpp +++ b/src/Entities/Pickup.cpp @@ -150,10 +150,14 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk) } } - if (!IsDestroyed() && (m_Item.m_ItemCount < m_Item.GetMaxStackSize())) // Don't combine into an already full pickup + // Try to combine the pickup with adjacent same-item pickups: + if (!IsDestroyed() && (m_Item.m_ItemCount < m_Item.GetMaxStackSize())) // Don't combine if already full { + // By using a_Chunk's ForEachEntity() instead of cWorld's, pickups don't combine across chunk boundaries. + // That is a small price to pay for not having to traverse the entire world for each entity. + // The speedup in the tick thread is quite considerable. cPickupCombiningCallback PickupCombiningCallback(GetPosition(), this); - m_World->ForEachEntity(PickupCombiningCallback); // Not ForEachEntityInChunk, otherwise pickups don't combine across chunk boundaries + a_Chunk.ForEachEntity(PickupCombiningCallback); if (PickupCombiningCallback.FoundMatchingPickup()) { m_World->BroadcastEntityMetadata(*this); diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 756410989..b0da6965a 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -1670,7 +1670,11 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) cEnderChestEntity::LoadFromJson(root["enderchestinventory"], m_EnderChestContents); m_LoadedWorldName = root.get("world", "world").asString(); - a_World = cRoot::Get()->GetWorld(GetLoadedWorldName(), true); + a_World = cRoot::Get()->GetWorld(GetLoadedWorldName(), false); + if (a_World == NULL) + { + a_World = cRoot::Get()->GetDefaultWorld(); + } m_LastBedPos.x = root.get("SpawnX", a_World->GetSpawnX()).asInt(); m_LastBedPos.y = root.get("SpawnY", a_World->GetSpawnY()).asInt(); diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp index 8fad9f5c9..60ad4e3eb 100644 --- a/src/Generating/BioGen.cpp +++ b/src/Generating/BioGen.cpp @@ -151,7 +151,7 @@ void cBioGenCache::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a LOGD("BioGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses)); LOGD("BioGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits); } - + for (int i = 0; i < m_CacheSize; i++) { if ( @@ -208,6 +208,59 @@ void cBioGenCache::InitializeBiomeGen(cIniFile & a_IniFile) +//////////////////////////////////////////////////////////////////////////////// +// cBioGenMulticache: + +cBioGenMulticache::cBioGenMulticache(cBiomeGen * a_BioGenToCache, size_t a_CacheSize, size_t a_CachesLength) : + m_CachesLength(a_CachesLength) +{ + m_Caches.reserve(a_CachesLength); + for (size_t i = 0; i < a_CachesLength; i++) + { + m_Caches.push_back(new cBioGenCache(a_BioGenToCache, a_CacheSize)); + } +} + + + + + +cBioGenMulticache::~cBioGenMulticache() +{ + for (cBiomeGens::iterator it = m_Caches.begin(); it != m_Caches.end(); it++) + { + delete *it; + } +} + + + + + +void cBioGenMulticache::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + const size_t coefficient = 3; + const size_t cacheIdx = ((size_t)a_ChunkX + coefficient * (size_t)a_ChunkZ) % m_CachesLength; + + m_Caches[cacheIdx]->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap); +} + + + + + +void cBioGenMulticache::InitializeBiomeGen(cIniFile & a_IniFile) +{ + for (cBiomeGens::iterator it = m_Caches.begin(); it != m_Caches.end(); it++) + { + cBiomeGen * tmp = *it; + tmp->InitializeBiomeGen(a_IniFile); + } +} + + + + //////////////////////////////////////////////////////////////////////////////// // cBiomeGenList: diff --git a/src/Generating/BioGen.h b/src/Generating/BioGen.h index 227ec97d7..20d199611 100644 --- a/src/Generating/BioGen.h +++ b/src/Generating/BioGen.h @@ -80,6 +80,36 @@ protected: +class cBioGenMulticache : + public cBiomeGen +{ + + typedef cBiomeGen super; + +public: + /* + a_CacheSize defines the size of each singular cache + a_CachesLength defines how many caches are used for the multicache + */ + cBioGenMulticache(cBiomeGen * a_BioGenToCache, size_t a_CacheSize, size_t a_CachesLength); // Doesn't take ownership of a_BioGenToCache + ~cBioGenMulticache(); + +protected: + typedef std::vector<cBiomeGen *> cBiomeGens; + + + size_t m_CachesLength; + cBiomeGens m_Caches; + + + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; +}; + + + + + /// Base class for generators that use a list of available biomes. This class takes care of the list. class cBiomeGenList : public cBiomeGen diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp index a1188f984..d615456c1 100644 --- a/src/Generating/ChunkGenerator.cpp +++ b/src/Generating/ChunkGenerator.cpp @@ -52,10 +52,21 @@ bool cChunkGenerator::Start(cPluginInterface & a_PluginInterface, cChunkSink & a m_PluginInterface = &a_PluginInterface; m_ChunkSink = &a_ChunkSink; - MTRand rnd; - m_Seed = a_IniFile.GetValueSetI("Seed", "Seed", (int)rnd.randInt()); + // Get the seed; create a new one and log it if not found in the INI file: + if (a_IniFile.HasValue("Seed", "Seed")) + { + m_Seed = a_IniFile.GetValueI("Seed", "Seed"); + } + else + { + MTRand rnd; + m_Seed = rnd.randInt(); + LOGINFO("Chosen a new random seed for world: %d", m_Seed); + a_IniFile.SetValueI("Seed", "Seed", m_Seed); + } + + // Get the generator engine based on the INI file settings: AString GeneratorName = a_IniFile.GetValueSet("Generator", "Generator", "Composable"); - if (NoCaseCompare(GeneratorName, "Noise3D") == 0) { m_Generator = new cNoise3DGenerator(*this); @@ -99,15 +110,17 @@ void cChunkGenerator::Stop(void) -void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate) { + ASSERT(m_ChunkSink->IsChunkQueued(a_ChunkX, a_ChunkZ)); + { cCSLock Lock(m_CS); // Check if it is already in the queue: - for (cChunkCoordsList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr) + for (cChunkCoordsWithBoolList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr) { - if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ)) + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ)) { // Already in the queue, bail out return; @@ -119,7 +132,7 @@ void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_Chunk { LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (" SIZE_T_FMT ")", a_ChunkX, a_ChunkZ, m_Queue.size()); } - m_Queue.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); + m_Queue.push_back(cChunkCoordsWithBool(a_ChunkX, a_ChunkZ, a_ForceGenerate)); } m_Event.Set(); @@ -229,9 +242,9 @@ void cChunkGenerator::Execute(void) continue; } - cChunkCoords coords = m_Queue.front(); // Get next coord from queue - m_Queue.erase( m_Queue.begin()); // Remove coordinate from queue + cChunkCoordsWithBool coords = m_Queue.front(); // Get next coord from queue bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT); + m_Queue.erase(m_Queue.begin()); // Remove coordinate from queue Lock.Unlock(); // Unlock ASAP m_evtRemoved.Set(); @@ -245,8 +258,7 @@ void cChunkGenerator::Execute(void) LastReportTick = clock(); } - // Hack for regenerating chunks: if Y != 0, the chunk is considered invalid, even if it has its data set - if ((coords.m_ChunkY == 0) && m_ChunkSink->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ)) + if (!coords.m_ForceGenerate && m_ChunkSink->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ)) { LOGD("Chunk [%d, %d] already generated, skipping generation", coords.m_ChunkX, coords.m_ChunkZ); // Already generated, ignore request @@ -259,8 +271,8 @@ void cChunkGenerator::Execute(void) continue; } - LOGD("Generating chunk [%d, %d, %d]", coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ); - DoGenerate(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ); + LOGD("Generating chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ); + DoGenerate(coords.m_ChunkX, coords.m_ChunkZ); NumChunksGenerated++; } // while (!bStop) @@ -269,11 +281,12 @@ void cChunkGenerator::Execute(void) -void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ) { ASSERT(m_PluginInterface != NULL); ASSERT(m_ChunkSink != NULL); - + ASSERT(m_ChunkSink->IsChunkQueued(a_ChunkX, a_ChunkZ)); + cChunkDesc ChunkDesc(a_ChunkX, a_ChunkZ); m_PluginInterface->CallHookChunkGenerating(ChunkDesc); m_Generator->DoGenerate(a_ChunkX, a_ChunkZ, ChunkDesc); diff --git a/src/Generating/ChunkGenerator.h b/src/Generating/ChunkGenerator.h index 88d71f3f9..190d9e616 100644 --- a/src/Generating/ChunkGenerator.h +++ b/src/Generating/ChunkGenerator.h @@ -106,6 +106,10 @@ public: If this callback returns false, the chunk is not generated. */ virtual bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) = 0; + + /** Called to check whether the specified chunk is in the queued state. + Currently used only in Debug-mode asserts. */ + virtual bool IsChunkQueued(int a_ChunkX, int a_ChunkZ) = 0; } ; @@ -116,7 +120,7 @@ public: void Stop(void); /// Queues the chunk for generation; removes duplicate requests - void QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + void QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate); /// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading. void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap); @@ -137,10 +141,10 @@ private: int m_Seed; - cCriticalSection m_CS; - cChunkCoordsList m_Queue; - cEvent m_Event; ///< Set when an item is added to the queue or the thread should terminate - cEvent m_evtRemoved; ///< Set when an item is removed from the queue + cCriticalSection m_CS; + cChunkCoordsWithBoolList m_Queue; + cEvent m_Event; ///< Set when an item is added to the queue or the thread should terminate + cEvent m_evtRemoved; ///< Set when an item is removed from the queue cGenerator * m_Generator; ///< The actual generator engine used to generate chunks @@ -154,7 +158,7 @@ private: // cIsThread override: virtual void Execute(void) override; - void DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + void DoGenerate(int a_ChunkX, int a_ChunkZ); }; diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp index 6f4007d24..69068d231 100644 --- a/src/Generating/ComposableGenerator.cpp +++ b/src/Generating/ComposableGenerator.cpp @@ -230,17 +230,29 @@ void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile) // Add a cache, if requested: int CacheSize = a_IniFile.GetValueSetI("Generator", "BiomeGenCacheSize", CacheOffByDefault ? 0 : 64); - if (CacheSize > 0) + + if (CacheSize <= 0) + { + return; + } + + int MultiCacheLength = a_IniFile.GetValueSetI("Generator", "BiomeGenMultiCacheLength", 4); + if (CacheSize < 4) + { + LOGWARNING("Biomegen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d", + CacheSize, 4 + ); + CacheSize = 4; + } + LOGD("Using a cache for biomegen of size %d.", CacheSize); + m_UnderlyingBiomeGen = m_BiomeGen; + if (MultiCacheLength > 0) + { + LOGD("Enabling multicache for biomegen of length %d.", MultiCacheLength); + m_BiomeGen = new cBioGenMulticache(m_UnderlyingBiomeGen, CacheSize, MultiCacheLength); + } + else { - if (CacheSize < 4) - { - LOGWARNING("Biomegen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d", - CacheSize, 4 - ); - CacheSize = 4; - } - LOGD("Using a cache for biomegen of size %d.", CacheSize); - m_UnderlyingBiomeGen = m_BiomeGen; m_BiomeGen = new cBioGenCache(m_UnderlyingBiomeGen, CacheSize); } } diff --git a/src/Item.cpp b/src/Item.cpp index 2c5deaddf..ebdf99ca5 100644 --- a/src/Item.cpp +++ b/src/Item.cpp @@ -190,31 +190,35 @@ void cItem::FromJson(const Json::Value & a_Value) -bool cItem::IsEnchantable(short item) +bool cItem::IsEnchantable(short a_ItemType, bool a_WithBook) { - if ((item >= 256) && (item <= 259)) + if ( + ItemCategory::IsAxe(a_ItemType) || + ItemCategory::IsSword(a_ItemType) || + ItemCategory::IsShovel(a_ItemType) || + ItemCategory::IsPickaxe(a_ItemType) || + (a_WithBook && ItemCategory::IsHoe(a_ItemType)) || + ItemCategory::IsArmor(a_ItemType) + ) { return true; } - if ((item >= 267) && (item <= 279)) - { - return true; - } - if ((item >= 283) && (item <= 286)) - { - return true; - } - if ((item >= 290) && (item <= 294)) - { - return true; - } - if ((item >= 298) && (item <= 317)) - { - return true; - } - if ((item == 346) || (item == 359) || (item == 261)) + + switch (a_ItemType) { - return true; + case E_ITEM_BOOK: + case E_ITEM_BOW: + case E_ITEM_FISHING_ROD: + { + return true; + } + + case E_ITEM_CARROT_ON_STICK: + case E_ITEM_SHEARS: + case E_ITEM_FLINT_AND_STEEL: + { + return a_WithBook; + } } return false; @@ -299,73 +303,77 @@ int cItem::GetEnchantability() bool cItem::EnchantByXPLevels(int a_NumXPLevels) { - if (!cItem::IsEnchantable(m_ItemType) && (m_ItemType != E_ITEM_BOOK)) + if (!cItem::IsEnchantable(m_ItemType)) { return false; } int Enchantability = GetEnchantability(); + if (Enchantability == 0) + { + return false; + } cFastRandom Random; int ModifiedEnchantmentLevel = a_NumXPLevels + (int)Random.NextFloat((float)Enchantability / 4) + (int)Random.NextFloat((float)Enchantability / 4) + 1; float RandomBonus = 1.0F + (Random.NextFloat(1) + Random.NextFloat(1) - 1.0F) * 0.15F; int FinalEnchantmentLevel = (int)(ModifiedEnchantmentLevel * RandomBonus + 0.5F); - cWeightedEnchantments enchantments; - cEnchantments::AddItemEnchantmentWeights(enchantments, m_ItemType, FinalEnchantmentLevel); + cWeightedEnchantments Enchantments; + cEnchantments::AddItemEnchantmentWeights(Enchantments, m_ItemType, FinalEnchantmentLevel); if (m_ItemType == E_ITEM_BOOK) { m_ItemType = E_ITEM_ENCHANTED_BOOK; } - cEnchantments Enchantment1 = cEnchantments::GetRandomEnchantmentFromVector(enchantments); + cEnchantments Enchantment1 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments); m_Enchantments.AddFromString(Enchantment1.ToString()); - cEnchantments::RemoveEnchantmentWeightFromVector(enchantments, Enchantment1); + cEnchantments::RemoveEnchantmentWeightFromVector(Enchantments, Enchantment1); // Checking for conflicting enchantments - cEnchantments::CheckEnchantmentConflictsFromVector(enchantments, Enchantment1); + cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment1); float NewEnchantmentLevel = (float)a_NumXPLevels; // Next Enchantment (Second) NewEnchantmentLevel = NewEnchantmentLevel / 2; float SecondEnchantmentChance = (NewEnchantmentLevel + 1) / 50 * 100; - if (enchantments.empty() || (Random.NextFloat(100) > SecondEnchantmentChance)) + if (Enchantments.empty() || (Random.NextFloat(100) > SecondEnchantmentChance)) { return true; } - cEnchantments Enchantment2 = cEnchantments::GetRandomEnchantmentFromVector(enchantments); + cEnchantments Enchantment2 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments); m_Enchantments.AddFromString(Enchantment2.ToString()); - cEnchantments::RemoveEnchantmentWeightFromVector(enchantments, Enchantment2); + cEnchantments::RemoveEnchantmentWeightFromVector(Enchantments, Enchantment2); // Checking for conflicting enchantments - cEnchantments::CheckEnchantmentConflictsFromVector(enchantments, Enchantment2); + cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment2); // Next Enchantment (Third) NewEnchantmentLevel = NewEnchantmentLevel / 2; float ThirdEnchantmentChance = (NewEnchantmentLevel + 1) / 50 * 100; - if (enchantments.empty() || (Random.NextFloat(100) > ThirdEnchantmentChance)) + if (Enchantments.empty() || (Random.NextFloat(100) > ThirdEnchantmentChance)) { return true; } - cEnchantments Enchantment3 = cEnchantments::GetRandomEnchantmentFromVector(enchantments); + cEnchantments Enchantment3 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments); m_Enchantments.AddFromString(Enchantment3.ToString()); - cEnchantments::RemoveEnchantmentWeightFromVector(enchantments, Enchantment3); + cEnchantments::RemoveEnchantmentWeightFromVector(Enchantments, Enchantment3); // Checking for conflicting enchantments - cEnchantments::CheckEnchantmentConflictsFromVector(enchantments, Enchantment3); + cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment3); // Next Enchantment (Fourth) NewEnchantmentLevel = NewEnchantmentLevel / 2; float FourthEnchantmentChance = (NewEnchantmentLevel + 1) / 50 * 100; - if (enchantments.empty() || (Random.NextFloat(100) > FourthEnchantmentChance)) + if (Enchantments.empty() || (Random.NextFloat(100) > FourthEnchantmentChance)) { return true; } - cEnchantments Enchantment4 = cEnchantments::GetRandomEnchantmentFromVector(enchantments); + cEnchantments Enchantment4 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments); m_Enchantments.AddFromString(Enchantment4.ToString()); return true; diff --git a/src/Item.h b/src/Item.h index d8b9e78a0..056b5eb8a 100644 --- a/src/Item.h +++ b/src/Item.h @@ -183,8 +183,10 @@ public: /** Loads the item data from JSON representation */ void FromJson(const Json::Value & a_Value); - /** Returns true if the specified item type is enchantable (as per 1.2.5 protocol requirements) */ - static bool IsEnchantable(short a_ItemType); // tolua_export + /** Returns true if the specified item type is enchantable. + If WithBook is true, the function is used in the anvil inventory with book enchantments. + So it checks the "only book enchantments" too. Example: You can only enchant a hoe with a book. */ + static bool IsEnchantable(short a_ItemType, bool a_WithBook = false); // tolua_export /** Returns the enchantability of the item. When the item hasn't a enchantability, it will returns 0 */ int GetEnchantability(); // tolua_export diff --git a/src/Items/ItemFood.h b/src/Items/ItemFood.h index 98050cad8..9035344df 100644 --- a/src/Items/ItemFood.h +++ b/src/Items/ItemFood.h @@ -29,7 +29,7 @@ public: switch (m_ItemType) { // Please keep alpha-sorted. - case E_ITEM_BAKED_POTATO: return FoodInfo(6, 7.2); + case E_ITEM_BAKED_POTATO: return FoodInfo(5, 7.2); case E_ITEM_BREAD: return FoodInfo(5, 6); // Carrots handled in ItemSeeds case E_ITEM_COOKED_CHICKEN: return FoodInfo(6, 7.2); @@ -43,14 +43,16 @@ public: case E_ITEM_POISONOUS_POTATO: return FoodInfo(2, 1.2); // Potatoes handled in ItemSeeds case E_ITEM_PUMPKIN_PIE: return FoodInfo(8, 4.8); + case E_ITEM_RED_APPLE: return FoodInfo(4, 2.4); case E_ITEM_RAW_BEEF: return FoodInfo(3, 1.8); case E_ITEM_RAW_CHICKEN: return FoodInfo(2, 1.2); case E_ITEM_RAW_FISH: return FoodInfo(2, 1.2); + case E_ITEM_RAW_MUTTON: return FoodInfo(2, 1.2); case E_ITEM_RAW_PORKCHOP: return FoodInfo(3, 1.8); - case E_ITEM_RED_APPLE: return FoodInfo(4, 2.4); case E_ITEM_ROTTEN_FLESH: return FoodInfo(4, 0.8); case E_ITEM_SPIDER_EYE: return FoodInfo(2, 3.2); case E_ITEM_STEAK: return FoodInfo(8, 12.8); + case E_ITEM_MUTTON: return FoodInfo(6, 9.6); } LOGWARNING("%s: Unknown food item (%d), returning zero nutrition", __FUNCTION__, m_ItemType); return FoodInfo(0, 0.f); diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp index bceedaf69..67c945ce4 100644 --- a/src/Items/ItemHandler.cpp +++ b/src/Items/ItemHandler.cpp @@ -207,7 +207,7 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType) } // Food (please keep alpha-sorted): - // (carrots and potatoes handled in SeedHandler as both seed and food + // (carrots and potatoes handled separately in SeedHandler as they're both seed and food) case E_ITEM_BAKED_POTATO: case E_ITEM_BREAD: case E_ITEM_COOKED_CHICKEN: @@ -217,13 +217,15 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType) case E_ITEM_GOLDEN_CARROT: case E_ITEM_MELON_SLICE: case E_ITEM_MUSHROOM_SOUP: + case E_ITEM_MUTTON: case E_ITEM_POISONOUS_POTATO: case E_ITEM_PUMPKIN_PIE: + case E_ITEM_RED_APPLE: case E_ITEM_RAW_BEEF: case E_ITEM_RAW_CHICKEN: case E_ITEM_RAW_FISH: + case E_ITEM_RAW_MUTTON: case E_ITEM_RAW_PORKCHOP: - case E_ITEM_RED_APPLE: case E_ITEM_ROTTEN_FLESH: case E_ITEM_SPIDER_EYE: case E_ITEM_STEAK: diff --git a/src/Items/ItemSeeds.h b/src/Items/ItemSeeds.h index 54a1183d7..e1db7c5f4 100644 --- a/src/Items/ItemSeeds.h +++ b/src/Items/ItemSeeds.h @@ -37,7 +37,7 @@ public: { switch (m_ItemType) { - case E_ITEM_CARROT: return FoodInfo(4, 4.8); + case E_ITEM_CARROT: return FoodInfo(3, 4.8); case E_ITEM_POTATO: return FoodInfo(1, 0.6); default: return FoodInfo(0, 0); } diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp index 9fb47201d..1a82115d2 100644 --- a/src/Mobs/Sheep.cpp +++ b/src/Mobs/Sheep.cpp @@ -39,6 +39,13 @@ void cSheep::GetDrops(cItems & a_Drops, cEntity * a_Killer) { a_Drops.push_back(cItem(E_BLOCK_WOOL, 1, m_WoolColor)); } + + int LootingLevel = 0; + if (a_Killer != NULL) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 1, 3 + LootingLevel, IsOnFire() ? E_ITEM_MUTTON : E_ITEM_RAW_MUTTON); } diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 1091b877f..f24ef320d 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -1355,6 +1355,7 @@ void cProtocol172::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity) void cProtocol172::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) { ASSERT(m_State == 3); // In game mode? + ASSERT((a_BlockY >= 0) && (a_BlockY < cChunkDef::Height)); cPacketizer Pkt(*this, 0x33); Pkt.WriteInt(a_BlockX); diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index ccfa19eb6..d3100d0fb 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -71,7 +71,9 @@ public: virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) override; virtual void SendDestroyEntity (const cEntity & a_Entity) override; virtual void SendDisconnect (const AString & a_Reason) override; + virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override; virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+) + virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override; virtual void SendEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) override; virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; virtual void SendEntityHeadLook (const cEntity & a_Entity) override; @@ -82,6 +84,8 @@ public: virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override; virtual void SendEntityVelocity (const cEntity & a_Entity) override; + virtual void SendExperience (void) override; + virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; virtual void SendGameMode (eGameMode a_GameMode) override; virtual void SendHealth (void) override; @@ -93,10 +97,9 @@ public: virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override; virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override; virtual void SendPaintingSpawn (const cPainting & a_Painting) override; + virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override; virtual void SendPickupSpawn (const cPickup & a_Pickup) override; virtual void SendPlayerAbilities (void) override; - virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override; - virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override; virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override; virtual void SendPlayerMaxSpeed (void) override; virtual void SendPlayerMoveLook (void) override; @@ -105,12 +108,9 @@ public: virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override; virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override; - virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override; - virtual void SendExperience (void) override; - virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; - virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override; virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override; - virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override; + virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override; + virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override; virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; virtual void SendSpawnMob (const cMonster & a_Mob) override; diff --git a/src/SetChunkData.cpp b/src/SetChunkData.cpp index bfe59fbcb..707dfb9e8 100644 --- a/src/SetChunkData.cpp +++ b/src/SetChunkData.cpp @@ -44,7 +44,6 @@ cSetChunkData::cSetChunkData( // Check the params' validity: ASSERT(a_BlockTypes != NULL); ASSERT(a_BlockMetas != NULL); - ASSERT(a_Biomes != NULL); // Copy block types and metas: memcpy(m_BlockTypes, a_BlockTypes, sizeof(cChunkDef::BlockTypes)); diff --git a/src/StringCompression.cpp b/src/StringCompression.cpp index 71d64e71e..af9f1687f 100644 --- a/src/StringCompression.cpp +++ b/src/StringCompression.cpp @@ -180,3 +180,65 @@ extern int UncompressStringGZIP(const char * a_Data, size_t a_Length, AString & + +extern int InflateString(const char * a_Data, size_t a_Length, AString & a_Uncompressed) +{ + a_Uncompressed.reserve(a_Length); + + char Buffer[64 KiB]; + z_stream strm; + memset(&strm, 0, sizeof(strm)); + strm.next_in = (Bytef *)a_Data; + strm.avail_in = (uInt)a_Length; + strm.next_out = (Bytef *)Buffer; + strm.avail_out = sizeof(Buffer); + + int res = inflateInit(&strm); // Force GZIP decoding + if (res != Z_OK) + { + LOG("%s: inflation initialization failed: %d (\"%s\").", __FUNCTION__, res, strm.msg); + return res; + } + + for (;;) + { + res = inflate(&strm, Z_NO_FLUSH); + switch (res) + { + case Z_OK: + { + // Some data has been uncompressed. Consume the buffer and continue uncompressing + a_Uncompressed.append(Buffer, sizeof(Buffer) - strm.avail_out); + strm.next_out = (Bytef *)Buffer; + strm.avail_out = sizeof(Buffer); + if (strm.avail_in == 0) + { + // All data has been uncompressed + inflateEnd(&strm); + return Z_OK; + } + break; + } + + case Z_STREAM_END: + { + // Finished uncompressing. Consume the rest of the buffer and return + a_Uncompressed.append(Buffer, sizeof(Buffer) - strm.avail_out); + inflateEnd(&strm); + return Z_OK; + } + + default: + { + // An error has occurred, log it and return the error value + LOG("%s: inflation failed: %d (\"%s\").", __FUNCTION__, res, strm.msg); + inflateEnd(&strm); + return res; + } + } // switch (res) + } // while (true) +} + + + + diff --git a/src/StringCompression.h b/src/StringCompression.h index 038240797..dde17ce30 100644 --- a/src/StringCompression.h +++ b/src/StringCompression.h @@ -21,5 +21,7 @@ extern int CompressStringGZIP(const char * a_Data, size_t a_Length, AString & a_ /// Uncompresses a_Data into a_Uncompressed using GZIP; returns Z_OK for success or Z_XXX error constants same as zlib extern int UncompressStringGZIP(const char * a_Data, size_t a_Length, AString & a_Uncompressed); +/** Uncompresses a_Data into a_Uncompressed using Inflate; returns Z_OK for success or Z_XXX error constants same as zlib */ +extern int InflateString(const char * a_Data, size_t a_Length, AString & a_Uncompressed); diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp index 4199bbf56..b4facb2d3 100644 --- a/src/UI/SlotArea.cpp +++ b/src/UI/SlotArea.cpp @@ -207,7 +207,7 @@ void cSlotArea::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ m_ParentWindow.DistributeStack(Slot, a_Player, this, true); if (Slot.IsEmpty()) { - // Empty the slot completely, the cilent doesn't like left-over ItemType with zero count + // Empty the slot completely, the client doesn't like left-over ItemType with zero count Slot.Empty(); } SetSlot(a_SlotNum, a_Player, Slot); @@ -1389,8 +1389,11 @@ void cSlotAreaBeacon::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) //////////////////////////////////////////////////////////////////////////////// // cSlotAreaEnchanting: -cSlotAreaEnchanting::cSlotAreaEnchanting(cEnchantingWindow & a_ParentWindow) : - cSlotAreaTemporary(1, a_ParentWindow) +cSlotAreaEnchanting::cSlotAreaEnchanting(cEnchantingWindow & a_ParentWindow, int a_BlockX, int a_BlockY, int a_BlockZ) : + cSlotAreaTemporary(1, a_ParentWindow), + m_BlockX(a_BlockX), + m_BlockY(a_BlockY), + m_BlockZ(a_BlockZ) { a_ParentWindow.m_SlotArea = this; } @@ -1409,7 +1412,7 @@ void cSlotAreaEnchanting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickActio LOGWARNING("GetSlot(%d) returned NULL! Ignoring click", a_SlotNum); return; } - + switch (a_ClickAction) { case caShiftLeftClick: @@ -1428,6 +1431,25 @@ void cSlotAreaEnchanting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickActio MiddleClicked(a_Player, a_SlotNum); return; } + case caDropKey: + case caCtrlDropKey: + { + DropClicked(a_Player, a_SlotNum, false); + return; + } + case caNumber1: + case caNumber2: + case caNumber3: + case caNumber4: + case caNumber5: + case caNumber6: + case caNumber7: + case caNumber8: + case caNumber9: + { + NumberClicked(a_Player, a_SlotNum, a_ClickAction); + return; + } default: { break; @@ -1443,106 +1465,36 @@ void cSlotAreaEnchanting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickActio bAsync = true; } cItem & DraggingItem = a_Player.GetDraggingItem(); - switch (a_ClickAction) + + if (DraggingItem.IsEmpty()) { - case caRightClick: + // DraggingItem is empty -> Switch draggingitem and slot + if (!Slot.IsEmpty()) { - // Right-clicked - if (DraggingItem.IsEmpty()) - { - DraggingItem = Slot.CopyOne(); - Slot.Empty(); - break; - } - - if (Slot.IsEmpty()) - { - Slot = DraggingItem.CopyOne(); - DraggingItem.m_ItemCount -= 1; - if (DraggingItem.m_ItemCount <= 0) - { - DraggingItem.Empty(); - } - } - else if ((!DraggingItem.IsEqual(Slot)) && (DraggingItem.m_ItemCount == 1)) - { - // Swap contents - cItem tmp(DraggingItem); - DraggingItem = Slot; - Slot = tmp; - } - break; + std::swap(DraggingItem, Slot); } - - case caLeftClick: - { - // Left-clicked - if (DraggingItem.IsEmpty()) - { - DraggingItem = Slot.CopyOne(); - Slot.Empty(); - break; - } - - if (DraggingItem.IsEqual(Slot)) - { - // Do nothing - break; - } - - if (!Slot.IsEmpty()) - { - if (DraggingItem.m_ItemCount == 1) - { - // Swap contents - cItem tmp(DraggingItem); - DraggingItem = Slot; - Slot = tmp; - } - } - else - { - Slot = DraggingItem.CopyOne(); - DraggingItem.m_ItemCount -= 1; - if (DraggingItem.m_ItemCount <= 0) - { - DraggingItem.Empty(); - } - } - break; - } - default: + } + else if (Slot.IsEmpty()) + { + // DraggingItem isn't empty and slot is empty -> Set one dragging item in the slot + Slot = DraggingItem.CopyOne(); + DraggingItem.m_ItemCount -= 1; + + if (DraggingItem.m_ItemCount <= 0) { - LOGWARNING("SlotArea: Unhandled click action: %d (%s)", a_ClickAction, ClickActionToString(a_ClickAction)); - m_ParentWindow.BroadcastWholeWindow(); - return; + DraggingItem.Empty(); } - } // switch (a_ClickAction - - SetSlot(a_SlotNum, a_Player, Slot); - if (bAsync) - { - m_ParentWindow.BroadcastWholeWindow(); } - UpdateResult(a_Player); -} - - - - - -void cSlotAreaEnchanting::DblClicked(cPlayer & a_Player, int a_SlotNum) -{ - cItem & Dragging = a_Player.GetDraggingItem(); - if ((!Dragging.IsEmpty()) || (a_SlotNum != 0)) + else if ((DraggingItem.m_ItemCount == 1) && !DraggingItem.IsEqual(Slot)) { - return; + // DraggingItem and slot aren't empty -> Switch items + std::swap(DraggingItem, Slot); } - - cItem Item = *GetSlot(0, a_Player); - if (!m_ParentWindow.CollectItemsToHand(Item, *this, a_Player, false)) + + SetSlot(a_SlotNum, a_Player, Slot); + if (bAsync) { - m_ParentWindow.CollectItemsToHand(Item, *this, a_Player, true); + m_ParentWindow.BroadcastWholeWindow(); } } @@ -1567,7 +1519,15 @@ void cSlotAreaEnchanting::DistributeStack(cItem & a_ItemStack, cPlayer & a_Playe { a_ItemStack.Empty(); } +} + + + + +void cSlotAreaEnchanting::OnPlayerAdded(cPlayer & a_Player) +{ + super::OnPlayerAdded(a_Player); UpdateResult(a_Player); } @@ -1587,29 +1547,33 @@ void cSlotAreaEnchanting::OnPlayerRemoved(cPlayer & a_Player) +void cSlotAreaEnchanting::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + super::SetSlot(a_SlotNum, a_Player, a_Item); + UpdateResult(a_Player); +} + + + + + void cSlotAreaEnchanting::UpdateResult(cPlayer & a_Player) { cItem Item = *GetSlot(0, a_Player); - if (Item.IsEmpty() || !Item.m_Enchantments.IsEmpty()) - { - m_ParentWindow.SetProperty(0, 0, a_Player); - m_ParentWindow.SetProperty(1, 0, a_Player); - m_ParentWindow.SetProperty(2, 0, a_Player); - } - else if (cItem::IsEnchantable(Item.m_ItemType) || Item.m_ItemType == E_ITEM_BOOK) + if (cItem::IsEnchantable(Item.m_ItemType) && Item.m_Enchantments.IsEmpty()) { int Bookshelves = std::min(GetBookshelvesCount(a_Player.GetWorld()), 15); cFastRandom Random; - int base = (Random.GenerateRandomInteger(1, 8) + (int)floor((float)Bookshelves / 2) + Random.GenerateRandomInteger(0, Bookshelves)); - int topSlot = std::max(base / 3, 1); - int middleSlot = (base * 2) / 3 + 1; - int bottomSlot = std::max(base, Bookshelves * 2); + int Base = (Random.GenerateRandomInteger(1, 8) + (int)floor((float)Bookshelves / 2) + Random.GenerateRandomInteger(0, Bookshelves)); + int TopSlot = std::max(Base / 3, 1); + int MiddleSlot = (Base * 2) / 3 + 1; + int BottomSlot = std::max(Base, Bookshelves * 2); - m_ParentWindow.SetProperty(0, topSlot, a_Player); - m_ParentWindow.SetProperty(1, middleSlot, a_Player); - m_ParentWindow.SetProperty(2, bottomSlot, a_Player); + m_ParentWindow.SetProperty(0, TopSlot, a_Player); + m_ParentWindow.SetProperty(1, MiddleSlot, a_Player); + m_ParentWindow.SetProperty(2, BottomSlot, a_Player); } else { @@ -1625,12 +1589,9 @@ void cSlotAreaEnchanting::UpdateResult(cPlayer & a_Player) int cSlotAreaEnchanting::GetBookshelvesCount(cWorld * a_World) { - int PosX, PosY, PosZ; - ((cEnchantingWindow*)&m_ParentWindow)->GetBlockPos(PosX, PosY, PosZ); - int Bookshelves = 0; cBlockArea Area; - Area.Read(a_World, PosX - 2, PosX + 2, PosY, PosY + 1, PosZ - 2, PosZ + 2); + Area.Read(a_World, m_BlockX - 2, m_BlockX + 2, m_BlockY, m_BlockY + 1, m_BlockZ - 2, m_BlockZ + 2); static const struct { @@ -1678,7 +1639,7 @@ int cSlotAreaEnchanting::GetBookshelvesCount(cWorld * a_World) if ( (Area.GetRelBlockType(CheckCoords[i].m_AirX, CheckCoords[i].m_AirY, CheckCoords[i].m_AirZ) == E_BLOCK_AIR) && // There's air in the checkspot (Area.GetRelBlockType(CheckCoords[i].m_BookX, CheckCoords[i].m_BookY, CheckCoords[i].m_BookZ) == E_BLOCK_BOOKCASE) // There's bookcase in the wanted place - ) + ) { Bookshelves++; } diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h index 9a96f2f3c..6bbc87b76 100644 --- a/src/UI/SlotArea.h +++ b/src/UI/SlotArea.h @@ -349,14 +349,15 @@ class cSlotAreaEnchanting : typedef cSlotAreaTemporary super; public: - cSlotAreaEnchanting(cEnchantingWindow & a_ParentWindow); + cSlotAreaEnchanting(cEnchantingWindow & a_ParentWindow, int a_BlockX, int a_BlockY, int a_BlockZ); // cSlotArea overrides: virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; - virtual void DblClicked(cPlayer & a_Player, int a_SlotNum) override; virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override; + virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; // cSlotAreaTemporary overrides: + virtual void OnPlayerAdded (cPlayer & a_Player) override; virtual void OnPlayerRemoved(cPlayer & a_Player) override; /* Get the count of bookshelves who stand in the near of the enchanting table */ @@ -365,6 +366,8 @@ public: protected: /** Handles a click in the item slot. */ void UpdateResult(cPlayer & a_Player); + + int m_BlockX, m_BlockY, m_BlockZ; }; diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp index 8f4913030..66900269f 100644 --- a/src/UI/Window.cpp +++ b/src/UI/Window.cpp @@ -881,7 +881,7 @@ cEnchantingWindow::cEnchantingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) : m_BlockY(a_BlockY), m_BlockZ(a_BlockZ) { - m_SlotAreas.push_back(new cSlotAreaEnchanting(*this)); + m_SlotAreas.push_back(new cSlotAreaEnchanting(*this, m_BlockX, m_BlockY, m_BlockZ)); m_SlotAreas.push_back(new cSlotAreaInventory(*this)); m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); } @@ -892,8 +892,13 @@ cEnchantingWindow::cEnchantingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) : void cEnchantingWindow::SetProperty(int a_Property, int a_Value) { - m_PropertyValue[a_Property] = a_Value; + if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue))) + { + ASSERT(!"a_Property is invalid"); + return; + } + m_PropertyValue[a_Property] = a_Value; super::SetProperty(a_Property, a_Value); } @@ -903,8 +908,13 @@ void cEnchantingWindow::SetProperty(int a_Property, int a_Value) void cEnchantingWindow::SetProperty(int a_Property, int a_Value, cPlayer & a_Player) { - m_PropertyValue[a_Property] = a_Value; + if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue))) + { + ASSERT(!"a_Property is invalid"); + return; + } + m_PropertyValue[a_Property] = a_Value; super::SetProperty(a_Property, a_Value, a_Player); } @@ -914,18 +924,13 @@ void cEnchantingWindow::SetProperty(int a_Property, int a_Value, cPlayer & a_Pla int cEnchantingWindow::GetPropertyValue(int a_Property) { - return m_PropertyValue[a_Property]; -} - - - - + if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue))) + { + ASSERT(!"a_Property is invalid"); + return 0; + } -void cEnchantingWindow::GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ) -{ - a_PosX = m_BlockX; - a_PosY = m_BlockY; - a_PosZ = m_BlockZ; + return m_PropertyValue[a_Property]; } diff --git a/src/UI/Window.h b/src/UI/Window.h index 9fb0e3b38..3d860407f 100644 --- a/src/UI/Window.h +++ b/src/UI/Window.h @@ -291,9 +291,6 @@ public: /** Return the Value of a Property */ int GetPropertyValue(int a_Property); - /** Get the Position from the Enchantment Table */ - void GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ); - cSlotArea * m_SlotArea; protected: diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp index 23eedbd14..341c64236 100644 --- a/src/WebAdmin.cpp +++ b/src/WebAdmin.cpp @@ -131,8 +131,24 @@ bool cWebAdmin::Start(void) m_TemplateScript.RegisterAPILibs(); if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua")) { - LOGWARN("Could not load WebAdmin template \"%s\", using default template.", FILE_IO_PREFIX "webadmin/template.lua"); + LOGWARN("Could not load WebAdmin template \"%s\". WebAdmin disabled!", FILE_IO_PREFIX "webadmin/template.lua"); m_TemplateScript.Close(); + m_HTTPServer.Stop(); + return false; + } + + if (!LoadLoginTemplate()) + { + LOGWARN("Could not load WebAdmin login template \"%s\", using fallback template.", FILE_IO_PREFIX "webadmin/login_template.html"); + + // Sets the fallback template: + m_LoginTemplate = \ + "<h1>MCServer WebAdmin</h1>" \ + "<center>" \ + "<form method='get' action='webadmin/'>" \ + "<input type='submit' value='Log in'>" \ + "</form>" \ + "</center>"; } m_IsRunning = m_HTTPServer.Start(*this); @@ -159,6 +175,28 @@ void cWebAdmin::Stop(void) +bool cWebAdmin::LoadLoginTemplate(void) +{ + cFile File(FILE_IO_PREFIX "webadmin/login_template.html", cFile::fmRead); + if (!File.IsOpen()) + { + return false; + } + + AString TemplateContent; + if (File.ReadRestOfFile(TemplateContent) == -1) + { + return false; + } + + m_LoginTemplate = TemplateContent; + return true; +} + + + + + void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) { if (!a_Request.HasAuth()) @@ -298,17 +336,64 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque void cWebAdmin::HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) { UNUSED(a_Request); - static const char LoginForm[] = \ - "<h1>MCServer WebAdmin</h1>" \ - "<center>" \ - "<form method='get' action='webadmin/'>" \ - "<input type='submit' value='Log in'>" \ - "</form>" \ - "</center>"; + cHTTPResponse Resp; Resp.SetContentType("text/html"); a_Connection.Send(Resp); - a_Connection.Send(LoginForm, sizeof(LoginForm) - 1); + a_Connection.Send(m_LoginTemplate); + a_Connection.FinishResponse(); +} + + + + + +void cWebAdmin::HandleFileRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +{ + AString FileURL = a_Request.GetURL(); + std::replace(FileURL.begin(), FileURL.end(), '\\', '/'); + + // Remove all leading backslashes: + if (FileURL[0] == '/') + { + size_t FirstCharToRead = FileURL.find_first_not_of('/'); + if (FirstCharToRead != AString::npos) + { + FileURL = FileURL.substr(FirstCharToRead); + } + } + + // Remove all "../" strings: + ReplaceString(FileURL, "../", ""); + + bool LoadedSuccessfull = false; + AString Content = "<h2>404 Not Found</h2>"; + AString Path = Printf(FILE_IO_PREFIX "webadmin/files/%s", FileURL.c_str()); + if (cFile::IsFile(Path)) + { + cFile File(Path, cFile::fmRead); + AString FileContent; + if (File.IsOpen() && (File.ReadRestOfFile(FileContent) != -1)) + { + LoadedSuccessfull = true; + Content = FileContent; + } + } + + // Find content type (The currently method is very bad. We should change it later) + AString ContentType = "text/html"; + size_t LastPointPosition = Path.find_last_of('.'); + if (LoadedSuccessfull && (LastPointPosition != AString::npos) && (LastPointPosition < Path.length())) + { + AString FileExtension = Path.substr(LastPointPosition + 1); + ContentType = GetContentTypeFromFileExt(FileExtension); + } + + // Send the response: + cHTTPResponse Resp; + Resp.SetContentType(ContentType); + a_Connection.Send(Resp); + a_Connection.Send(Content); a_Connection.FinishResponse(); } @@ -316,6 +401,41 @@ void cWebAdmin::HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & +AString cWebAdmin::GetContentTypeFromFileExt(const AString & a_FileExtension) +{ + static bool IsInitialized = false; + static std::map<AString, AString> ContentTypeMap; + if (!IsInitialized) + { + // Initialize the ContentTypeMap: + ContentTypeMap["png"] = "image/png"; + ContentTypeMap["fif"] = "image/fif"; + ContentTypeMap["gif"] = "image/gif"; + ContentTypeMap["jpeg"] = "image/jpeg"; + ContentTypeMap["jpg"] = "image/jpeg"; + ContentTypeMap["jpe"] = "image/jpeg"; + ContentTypeMap["tiff"] = "image/tiff"; + ContentTypeMap["ico"] = "image/ico"; + ContentTypeMap["csv"] = "image/comma-separated-values"; + ContentTypeMap["css"] = "text/css"; + ContentTypeMap["js"] = "text/javascript"; + ContentTypeMap["txt"] = "text/plain"; + ContentTypeMap["rtx"] = "text/richtext"; + ContentTypeMap["xml"] = "text/xml"; + } + + AString FileExtension = StrToLower(a_FileExtension); + if (ContentTypeMap.find(a_FileExtension) == ContentTypeMap.end()) + { + return "text/html"; + } + return ContentTypeMap[FileExtension]; +} + + + + + sWebAdminPage cWebAdmin::GetPage(const HTTPRequest & a_Request) { sWebAdminPage Page; @@ -382,6 +502,7 @@ AString cWebAdmin::GetDefaultPage(void) + AString cWebAdmin::GetBaseURL( const AString& a_URL) { return GetBaseURL(StringSplit(a_URL, "/")); @@ -528,7 +649,7 @@ void cWebAdmin::OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & } else { - // TODO: Handle other requests + HandleFileRequest(a_Connection, a_Request); } // Delete any request data assigned to the request: @@ -551,4 +672,3 @@ void cWebAdmin::cWebadminRequestData::OnBody(const char * a_Data, size_t a_Size) - diff --git a/src/WebAdmin.h b/src/WebAdmin.h index aefc1d145..94b95dbcf 100644 --- a/src/WebAdmin.h +++ b/src/WebAdmin.h @@ -116,6 +116,9 @@ public: /** Stops the HTTP server, if it was started. */ void Stop(void); + /** Loads the login template. Returns true if the loading succeeds, false if not. */ + bool LoadLoginTemplate(void); + void AddPlugin(cWebPlugin * a_Plugin); void RemovePlugin(cWebPlugin * a_Plugin); @@ -146,6 +149,9 @@ public: /** Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) */ static AString GetBaseURL(const AStringVector & a_URLSplit); + /** Returns the content type from the file extension. If the extension isn't in the list, the function returns "text/html" */ + static AString GetContentTypeFromFileExt(const AString & a_FileExtension); + protected: /** Common base class for request body data handlers */ class cRequestData @@ -205,6 +211,9 @@ protected: /** The Lua template script to provide templates: */ cLuaState m_TemplateScript; + /** The template that provides the login site: */ + AString m_LoginTemplate; + /** The HTTP server which provides the underlying HTTP parsing, serialization and events */ cHTTPServer m_HTTPServer; @@ -214,6 +223,9 @@ protected: /** Handles requests for the root page */ void HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); + /** Handles requests for a file */ + void HandleFileRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); + // cHTTPServer::cCallbacks overrides: virtual void OnRequestBegun (cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override; virtual void OnRequestBody (cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size) override; diff --git a/src/World.cpp b/src/World.cpp index 99e09c658..e669f6fa0 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -442,7 +442,7 @@ void cWorld::InitializeSpawn(void) { for (int z = 0; z < ViewDist; z++) { - m_ChunkMap->TouchChunk(x + ChunkX-(ViewDist - 1) / 2, ZERO_CHUNK_Y, z + ChunkZ-(ViewDist - 1) / 2); // Queue the chunk in the generator / loader + m_ChunkMap->TouchChunk(x + ChunkX-(ViewDist - 1) / 2, z + ChunkZ-(ViewDist - 1) / 2); // Queue the chunk in the generator / loader } } @@ -2374,6 +2374,8 @@ void cWorld::MarkChunkSaved (int a_ChunkX, int a_ChunkZ) void cWorld::QueueSetChunkData(const cSetChunkDataPtr & a_SetChunkData) { + ASSERT(IsChunkQueued(a_SetChunkData->GetChunkX(), a_SetChunkData->GetChunkZ())); + // Validate biomes, if needed: if (!a_SetChunkData->AreBiomesValid()) { @@ -2424,7 +2426,7 @@ void cWorld::SetChunkData(cSetChunkData & a_SetChunkData) // Save the chunk right after generating, so that we don't have to generate it again on next run if (a_SetChunkData.ShouldMarkDirty()) { - m_Storage.QueueSaveChunk(ChunkX, 0, ChunkZ); + m_Storage.QueueSaveChunk(ChunkX, ChunkZ); } } @@ -2463,6 +2465,15 @@ bool cWorld::GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockT +bool cWorld::IsChunkQueued(int a_ChunkX, int a_ChunkZ) const +{ + return m_ChunkMap->IsChunkQueued(a_ChunkX, a_ChunkZ); +} + + + + + bool cWorld::IsChunkValid(int a_ChunkX, int a_ChunkZ) const { return m_ChunkMap->IsChunkValid(a_ChunkX, a_ChunkZ); @@ -2696,6 +2707,15 @@ bool cWorld::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & +bool cWorld::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback) +{ + return m_ChunkMap->ForEachEntityInBox(a_Box, a_Callback); +} + + + + + bool cWorld::DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback) { return m_ChunkMap->DoWithEntityByID(a_UniqueID, a_Callback); @@ -2769,36 +2789,18 @@ void cWorld::RemoveClientFromChunkSender(cClientHandle * a_Client) -void cWorld::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - m_ChunkMap->TouchChunk(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -bool cWorld::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - return m_ChunkMap->LoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -void cWorld::LoadChunks(const cChunkCoordsList & a_Chunks) +void cWorld::TouchChunk(int a_ChunkX, int a_ChunkZ) { - m_ChunkMap->LoadChunks(a_Chunks); + m_ChunkMap->TouchChunk(a_ChunkX, a_ChunkZ); } -void cWorld::ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +void cWorld::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ) { - m_ChunkMap->ChunkLoadFailed(a_ChunkX, a_ChunkY, a_ChunkZ); + m_ChunkMap->ChunkLoadFailed(a_ChunkX, a_ChunkZ); } @@ -2903,8 +2905,7 @@ void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ) { m_ChunkMap->MarkChunkRegenerating(a_ChunkX, a_ChunkZ); - // Trick: use Y=1 to force the chunk generation even though the chunk data is already present - m_Generator.QueueGenerateChunk(a_ChunkX, 1, a_ChunkZ); + m_Generator.QueueGenerateChunk(a_ChunkX, a_ChunkZ, true); } @@ -2913,7 +2914,7 @@ void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ) void cWorld::GenerateChunk(int a_ChunkX, int a_ChunkZ) { - m_Generator.QueueGenerateChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + m_Generator.QueueGenerateChunk(a_ChunkX, a_ChunkZ, false); } @@ -3512,6 +3513,15 @@ bool cWorld::cChunkGeneratorCallbacks::IsChunkValid(int a_ChunkX, int a_ChunkZ) +bool cWorld::cChunkGeneratorCallbacks::IsChunkQueued(int a_ChunkX, int a_ChunkZ) +{ + return m_World->IsChunkQueued(a_ChunkX, a_ChunkZ); +} + + + + + bool cWorld::cChunkGeneratorCallbacks::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) { return m_World->HasChunkAnyClients(a_ChunkX, a_ChunkZ); diff --git a/src/World.h b/src/World.h index 578c9682b..6316c6811 100644 --- a/src/World.h +++ b/src/World.h @@ -279,7 +279,12 @@ public: /** Gets the chunk's blocks, only the block types */ bool GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes); - bool IsChunkValid (int a_ChunkX, int a_ChunkZ) const; + /** Returns true iff the chunk is in the loader / generator queue. */ + bool IsChunkQueued(int a_ChunkX, int a_ChunkZ) const; + + /** Returns true iff the chunk is present and valid. */ + bool IsChunkValid(int a_ChunkX, int a_ChunkZ) const; + bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const; /** Queues a task to unload unused chunks onto the tick thread. The prefferred way of unloading*/ @@ -324,6 +329,11 @@ public: /** Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true */ bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp + /** Calls the callback for each entity that has a nonempty intersection with the specified boundingbox. + Returns true if all entities processed, false if the callback aborted by returning true. + If any chunk in the box is missing, ignores the entities in that chunk silently. */ + bool ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp + /** Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found and callback returned false. */ bool DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp @@ -351,16 +361,10 @@ public: void RemoveClientFromChunkSender(cClientHandle * a_Client); /** Touches the chunk, causing it to be loaded or generated */ - void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /** Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) */ - bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /** Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() */ - void LoadChunks(const cChunkCoordsList & a_Chunks); + void TouchChunk(int a_ChunkX, int a_ChunkZ); /** Marks the chunk as failed-to-load: */ - void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ); /** Sets the sign text, asking plugins for permission first. a_Player is the player who this change belongs to, may be NULL. Returns true if sign text changed. Same as UpdateSign() */ bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player = NULL); // Exported in ManualBindings.cpp @@ -380,7 +384,7 @@ public: /** Regenerate the given chunk: */ void RegenerateChunk(int a_ChunkX, int a_ChunkZ); // tolua_export - /** Generates the given chunk, if not already generated */ + /** Generates the given chunk */ void GenerateChunk(int a_ChunkX, int a_ChunkZ); // tolua_export /** Queues a chunk for lighting; a_Callback is called after the chunk is lighted */ @@ -817,6 +821,7 @@ private: virtual void OnChunkGenerated (cChunkDesc & a_ChunkDesc) override; virtual bool IsChunkValid (int a_ChunkX, int a_ChunkZ) override; virtual bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) override; + virtual bool IsChunkQueued (int a_ChunkX, int a_ChunkZ) override; // cPluginInterface overrides: virtual void CallHookChunkGenerating(cChunkDesc & a_ChunkDesc) override; diff --git a/src/WorldStorage/ScoreboardSerializer.cpp b/src/WorldStorage/ScoreboardSerializer.cpp index da8236e0d..e30eecf67 100644 --- a/src/WorldStorage/ScoreboardSerializer.cpp +++ b/src/WorldStorage/ScoreboardSerializer.cpp @@ -283,37 +283,37 @@ bool cScoreboardSerializer::LoadScoreboardFromNBT(const cParsedNBT & a_NBT) bool AllowsFriendlyFire = true, CanSeeFriendlyInvisible = false; int CurrLine = a_NBT.FindChildByName(Child, "Name"); - if (CurrLine >= 0) + if ((CurrLine >= 0) && (a_NBT.GetType(CurrLine) == TAG_String)) { - Name = a_NBT.GetInt(CurrLine); + Name = a_NBT.GetString(CurrLine); } CurrLine = a_NBT.FindChildByName(Child, "DisplayName"); - if (CurrLine >= 0) + if ((CurrLine >= 0) && (a_NBT.GetType(CurrLine) == TAG_String)) { - DisplayName = a_NBT.GetInt(CurrLine); + DisplayName = a_NBT.GetString(CurrLine); } CurrLine = a_NBT.FindChildByName(Child, "Prefix"); - if (CurrLine >= 0) + if ((CurrLine >= 0) && (a_NBT.GetType(CurrLine) == TAG_String)) { - Prefix = a_NBT.GetInt(CurrLine); + Prefix = a_NBT.GetString(CurrLine); } CurrLine = a_NBT.FindChildByName(Child, "Suffix"); - if (CurrLine >= 0) + if ((CurrLine >= 0) && (a_NBT.GetType(CurrLine) == TAG_String)) { - Suffix = a_NBT.GetInt(CurrLine); + Suffix = a_NBT.GetString(CurrLine); } CurrLine = a_NBT.FindChildByName(Child, "AllowFriendlyFire"); - if (CurrLine >= 0) + if ((CurrLine >= 0) && (a_NBT.GetType(CurrLine) == TAG_Int)) { AllowsFriendlyFire = (a_NBT.GetInt(CurrLine) != 0); } CurrLine = a_NBT.FindChildByName(Child, "SeeFriendlyInvisibles"); - if (CurrLine >= 0) + if ((CurrLine >= 0) && (a_NBT.GetType(CurrLine) == TAG_Int)) { CanSeeFriendlyInvisible = (a_NBT.GetInt(CurrLine) != 0); } diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index e79cc291d..fe309ce4e 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -66,8 +66,17 @@ Since only the header is actually in the memory, this number can be high, but st */ #define MAX_MCA_FILES 32 -/// The maximum size of an inflated chunk; raw chunk data is 192 KiB, allow 64 KiB more of entities -#define CHUNK_INFLATE_MAX 256 KiB +#define LOAD_FAILED(CHX, CHZ) \ + { \ + const int RegionX = FAST_FLOOR_DIV(CHX, 32); \ + const int RegionZ = FAST_FLOOR_DIV(CHZ, 32); \ + LOGERROR("%s (%d): Loading chunk [%d, %d] from file r.%d.%d.mca failed. " \ + "The server will now abort in order to avoid further data loss. " \ + "Please add the reported file and this message to the issue report.", \ + __FUNCTION__, __LINE__, CHX, CHZ, RegionX, RegionZ \ + ); \ + *((volatile int *)0) = 0; /* Crash intentionally */ \ + } @@ -248,29 +257,22 @@ cWSSAnvil::cMCAFile * cWSSAnvil::LoadMCAFile(const cChunkCoords & a_Chunk) bool cWSSAnvil::LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & a_Data) { - // Decompress the data: - char Uncompressed[CHUNK_INFLATE_MAX]; - z_stream strm; - strm.zalloc = (alloc_func)NULL; - strm.zfree = (free_func)NULL; - strm.opaque = NULL; - inflateInit(&strm); - strm.next_out = (Bytef *)Uncompressed; - strm.avail_out = sizeof(Uncompressed); - strm.next_in = (Bytef *)a_Data.data(); - strm.avail_in = (uInt)a_Data.size(); - int res = inflate(&strm, Z_FINISH); - inflateEnd(&strm); - if (res != Z_STREAM_END) + // Uncompress the data: + AString Uncompressed; + int res = InflateString(a_Data.data(), a_Data.size(), Uncompressed); + if (res != Z_OK) { + LOGWARNING("Uncompressing chunk [%d, %d] failed: %d", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, res); + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } // Parse the NBT data: - cParsedNBT NBT(Uncompressed, strm.total_out); + cParsedNBT NBT(Uncompressed.data(), Uncompressed.size()); if (!NBT.IsValid()) { // NBT Parsing failed + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } @@ -317,11 +319,19 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT int Level = a_NBT.FindChildByName(0, "Level"); if (Level < 0) { + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } int Sections = a_NBT.FindChildByName(Level, "Sections"); - if ((Sections < 0) || (a_NBT.GetType(Sections) != TAG_List) || (a_NBT.GetChildrenType(Sections) != TAG_Compound)) + if ((Sections < 0) || (a_NBT.GetType(Sections) != TAG_List)) + { + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); + return false; + } + eTagType SectionsType = a_NBT.GetChildrenType(Sections); + if ((SectionsType != TAG_Compound) && (SectionsType != TAG_End)) { + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } for (int Child = a_NBT.GetFirstChild(Sections); Child >= 0; Child = a_NBT.GetNextSibling(Child)) @@ -585,7 +595,7 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con // Get the BlockEntity's position int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, Child, x, y, z)) + if (!GetBlockEntityNBTPos(a_NBT, Child, x, y, z) || (y < 0) || (y >= cChunkDef::Height)) { LOGWARNING("Bad block entity, missing the coords. Will be ignored."); continue; @@ -613,6 +623,8 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con cBlockEntity * cWSSAnvil::LoadBlockEntityFromNBT(const cParsedNBT & a_NBT, int a_Tag, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { + ASSERT((a_BlockY >= 0) && (a_BlockY < cChunkDef::Height)); + // Load the specific BlockEntity type: switch (a_BlockType) { @@ -2811,30 +2823,42 @@ bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, AString & a } unsigned ChunkLocation = ntohl(m_Header[LocalX + 32 * LocalZ]); unsigned ChunkOffset = ChunkLocation >> 8; + if (ChunkOffset < 2) + { + return false; + } m_File.Seek((int)ChunkOffset * 4096); int ChunkSize = 0; if (m_File.Read(&ChunkSize, 4) != 4) { + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } ChunkSize = ntohl((u_long)ChunkSize); char CompressionType = 0; if (m_File.Read(&CompressionType, 1) != 1) { + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } if (CompressionType != 2) { // Chunk is in an unknown compression + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } ChunkSize--; // HACK: This depends on the internal knowledge that AString's data() function returns the internal buffer directly a_Data.assign(ChunkSize, '\0'); - return (m_File.Read((void *)a_Data.data(), ChunkSize) == ChunkSize); + if (m_File.Read((void *)a_Data.data(), ChunkSize) == ChunkSize) + { + return true; + } + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); + return false; } @@ -2889,7 +2913,13 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri // Store the header: ChunkSize = ((u_long)a_Data.size() + MCA_CHUNK_HEADER_LENGTH + 4095) / 4096; // Round data size *up* to nearest 4KB sector, make it a sector number - ASSERT(ChunkSize < 256); + if (ChunkSize > 255) + { + LOGWARNING("Cannot save chunk [%d, %d], the data is too large (%u KiB, maximum is 1024 KiB). Remove some entities and retry.", + a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, (unsigned)(ChunkSize * 4) + ); + return false; + } m_Header[LocalX + 32 * LocalZ] = htonl((ChunkSector << 8) | ChunkSize); if (m_File.Seek(0) < 0) { diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp index 58f9e3cab..6760186b2 100644 --- a/src/WorldStorage/WSSCompact.cpp +++ b/src/WorldStorage/WSSCompact.cpp @@ -980,7 +980,7 @@ bool cWSSCompact::cPAKFile::SaveChunkToData(const cChunkCoords & a_Chunk, cWorld if (!a_World->GetChunkData(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, Serializer)) { // Chunk not valid - LOG("cWSSCompact: Trying to save chunk [%d, %d, %d] that has no data, ignoring request.", a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ); + LOG("cWSSCompact: Trying to save chunk [%d, %d] that has no data, ignoring request.", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } @@ -999,7 +999,7 @@ bool cWSSCompact::cPAKFile::SaveChunkToData(const cChunkCoords & a_Chunk, cWorld int errorcode = CompressString(Data.data(), Data.size(), CompressedData, m_CompressionFactor); if (errorcode != Z_OK) { - LOGERROR("Error %i compressing data for chunk [%d, %d, %d]", errorcode, a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ); + LOGERROR("Error %i compressing data for chunk [%d, %d]", errorcode, a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } @@ -1010,7 +1010,7 @@ bool cWSSCompact::cPAKFile::SaveChunkToData(const cChunkCoords & a_Chunk, cWorld sChunkHeader * Header = new sChunkHeader; if (Header == NULL) { - LOGWARNING("Cannot create a new chunk header to save chunk [%d, %d, %d]", a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ); + LOGWARNING("Cannot create a new chunk header to save chunk [%d, %d]", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } Header->m_CompressedSize = (int)CompressedData.size(); diff --git a/src/WorldStorage/WorldStorage.cpp b/src/WorldStorage/WorldStorage.cpp index 707e8f929..179cf9393 100644 --- a/src/WorldStorage/WorldStorage.cpp +++ b/src/WorldStorage/WorldStorage.cpp @@ -141,9 +141,11 @@ size_t cWorldStorage::GetSaveQueueLength(void) -void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate) +void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkZ) { - m_LoadQueue.EnqueueItem(sChunkLoad(a_ChunkX, a_ChunkY, a_ChunkZ, a_Generate)); + ASSERT(m_World->IsChunkQueued(a_ChunkX, a_ChunkZ)); + + m_LoadQueue.EnqueueItem(cChunkCoords(a_ChunkX, a_ChunkZ)); m_Event.Set(); } @@ -151,9 +153,11 @@ void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, boo -void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkZ) { - m_SaveQueue.EnqueueItemIfNotPresent(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); + ASSERT(m_World->IsChunkValid(a_ChunkX, a_ChunkZ)); + + m_SaveQueue.EnqueueItemIfNotPresent(cChunkCoords(a_ChunkX, a_ChunkZ)); m_Event.Set(); } @@ -161,9 +165,9 @@ void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkZ) { - m_LoadQueue.Remove(sChunkLoad(a_ChunkX, a_ChunkY, a_ChunkZ, true)); + m_LoadQueue.Remove(cChunkCoords(a_ChunkX, a_ChunkZ)); } @@ -242,22 +246,14 @@ void cWorldStorage::Execute(void) bool cWorldStorage::LoadOneChunk(void) { - sChunkLoad ToLoad(0, 0, 0, false); + cChunkCoords ToLoad(0, 0); bool ShouldLoad = m_LoadQueue.TryDequeueItem(ToLoad); - if (ShouldLoad && !LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ)) + + if (ShouldLoad) { - if (ToLoad.m_Generate) - { - // The chunk couldn't be loaded, generate it: - m_World->GetGenerator().QueueGenerateChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ); - } - else - { - // TODO: Notify the world that the load has failed: - // m_World->ChunkLoadFailed(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ); - } + return LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkZ); } - return ShouldLoad; + return false; } @@ -266,7 +262,7 @@ bool cWorldStorage::LoadOneChunk(void) bool cWorldStorage::SaveOneChunk(void) { - cChunkCoords ToSave(0, 0, 0); + cChunkCoords ToSave(0, 0); bool ShouldSave = m_SaveQueue.TryDequeueItem(ToSave); if (ShouldSave && m_World->IsChunkValid(ToSave.m_ChunkX, ToSave.m_ChunkZ)) { @@ -283,15 +279,11 @@ bool cWorldStorage::SaveOneChunk(void) -bool cWorldStorage::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +bool cWorldStorage::LoadChunk(int a_ChunkX, int a_ChunkZ) { - if (m_World->IsChunkValid(a_ChunkX, a_ChunkZ)) - { - // Already loaded (can happen, since the queue is async) - return true; - } + ASSERT(m_World->IsChunkQueued(a_ChunkX, a_ChunkZ)); - cChunkCoords Coords(a_ChunkX, a_ChunkY, a_ChunkZ); + cChunkCoords Coords(a_ChunkX, a_ChunkZ); // First try the schema that is used for saving if (m_SaveSchema->LoadChunk(Coords)) @@ -309,7 +301,7 @@ bool cWorldStorage::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) } // Notify the chunk owner that the chunk failed to load (sets cChunk::m_HasLoadFailed to true): - m_World->ChunkLoadFailed(a_ChunkX, a_ChunkY, a_ChunkZ); + m_World->ChunkLoadFailed(a_ChunkX, a_ChunkZ); return false; } diff --git a/src/WorldStorage/WorldStorage.h b/src/WorldStorage/WorldStorage.h index dd07ecb64..fc7e9f84c 100644 --- a/src/WorldStorage/WorldStorage.h +++ b/src/WorldStorage/WorldStorage.h @@ -64,13 +64,10 @@ public: cWorldStorage(void); ~cWorldStorage(); - void QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate); // Queues the chunk for loading; if not loaded, the chunk will be generated if a_Generate is true - void QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + void QueueLoadChunk(int a_ChunkX, int a_ChunkZ); + void QueueSaveChunk(int a_ChunkX, int a_ChunkZ); - /// Loads the chunk specified; returns true on success, false on failure - bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - void UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + void UnqueueLoad(int a_ChunkX, int a_ChunkZ); void UnqueueSave(const cChunkCoords & a_Chunk); bool Start(cWorld * a_World, const AString & a_StorageSchemaName, int a_StorageCompressionFactor); // Hide the cIsThread's Start() method, we need to provide args @@ -84,40 +81,10 @@ public: protected: - struct sChunkLoad - { - int m_ChunkX; - int m_ChunkY; - int m_ChunkZ; - bool m_Generate; // If true, the chunk will be generated if it cannot be loaded - - sChunkLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate) : m_ChunkX(a_ChunkX), m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ), m_Generate(a_Generate) {} - - bool operator ==(const sChunkLoad other) const - { - return ( - (this->m_ChunkX == other.m_ChunkX) && - (this->m_ChunkY == other.m_ChunkY) && - (this->m_ChunkZ == other.m_ChunkZ) - ); - } - } ; - - struct FuncTable - { - static void Delete(sChunkLoad) {} - static void Combine(sChunkLoad & a_orig, const sChunkLoad a_new) - { - a_orig.m_Generate |= a_new.m_Generate; - } - }; - - typedef cQueue<sChunkLoad, FuncTable> sChunkLoadQueue; - cWorld * m_World; AString m_StorageSchemaName; - sChunkLoadQueue m_LoadQueue; + cChunkCoordsQueue m_LoadQueue; cChunkCoordsQueue m_SaveQueue; /// All the storage schemas (all used for loading) @@ -125,7 +92,11 @@ protected: /// The one storage schema used for saving cWSSchema * m_SaveSchema; + + /// Loads the chunk specified; returns true on success, false on failure + bool LoadChunk(int a_ChunkX, int a_ChunkZ); + void InitSchemas(int a_StorageCompressionFactor); virtual void Execute(void) override; diff --git a/tests/ChunkData/CopyBlocks.cpp b/tests/ChunkData/CopyBlocks.cpp index ec9451099..99f416e94 100644 --- a/tests/ChunkData/CopyBlocks.cpp +++ b/tests/ChunkData/CopyBlocks.cpp @@ -45,7 +45,7 @@ int main(int argc, char ** argv) BLOCKTYPE * WritePosition = &TestBuffer[WritePosIdx]; memset(TestBuffer, 0x03, sizeof(TestBuffer)); size_t LastReportedStep = 1; - for (size_t idx = 0; idx < 5000; idx += 7) + for (size_t idx = 0; idx < 5000; idx += 73) { if (idx / 500 != LastReportedStep) { @@ -53,7 +53,7 @@ int main(int argc, char ** argv) LastReportedStep = idx / 500; } - for (size_t len = 3; len < 1000; len += 13) + for (size_t len = 3; len < 700; len += 13) { Data.CopyBlockTypes(WritePosition, idx, len); |