1
0

Merge branch 'master' into crafting

This commit is contained in:
Masy98 2014-08-30 21:31:51 +02:00
commit 2d5a38bd61
88 changed files with 4888 additions and 1585 deletions

View File

@ -942,24 +942,6 @@ cFile:Delete("/usr/bin/virus.exe");
Inherits = "cEntity",
},
cGroup =
{
Desc = [[
This class represents a group {{cPlayer|players}} can be in. Groups define the permissions players
have, and optionally the color of their name in the chat.
]],
Functions =
{
SetName = { Return = "" },
GetName = { Return = "string" },
SetColor = { Return = "" },
GetColor = { Return = "string" },
AddCommand = { Return = "" },
AddPermission = { Return = "" },
InheritFrom = { Return = "" },
},
}, -- cGroup
cIniFile =
{
Desc = [[
@ -1760,7 +1742,6 @@ a_Player:OpenWindow(Window);
Functions =
{
AddFoodExhaustion = { Params = "Exhaustion", Return = "", Notes = "Adds the specified number to the food exhaustion. Only positive numbers expected." },
AddToGroup = { Params = "GroupName", Return = "", Notes = "Temporarily adds the player to the specified group. The assignment is lost when the player disconnects." },
CalcLevelFromXp = { Params = "XPAmount", Return = "number", Notes = "(STATIC) Returns the level which is reached with the specified amount of XP. Inverse of XpForLevel()." },
CanFly = { Return = "bool", Notes = "Returns if the player is able to fly." },
CloseWindow = { Params = "[CanRefuse]", Return = "", Notes = "Closes the currently open UI window. If CanRefuse is true (default), the window may refuse the closing." },
@ -1770,7 +1751,7 @@ a_Player:OpenWindow(Window);
FoodPoison = { Params = "NumTicks", Return = "", Notes = "Starts the food poisoning for the specified amount of ticks; if already foodpoisoned, sets FoodPoisonedTicksRemaining to the larger of the two" },
ForceSetSpeed = { Params = "{{Vector3d|Direction}}", Notes = "Forces the player to move to the given direction." },
GetClientHandle = { Params = "", Return = "{{cClientHandle}}", Notes = "Returns the client handle representing the player's connection. May be nil (AI players)." },
GetColor = { Return = "string", Notes = "Returns the full color code to be used for this player (based on the first group). Prefix player messages with this code." },
GetColor = { Return = "string", Notes = "Returns the full color code to be used for this player's messages (based on their rank). Prefix player messages with this code." },
GetCurrentXp = { Params = "", Return = "number", Notes = "Returns the current amount of XP" },
GetEffectiveGameMode = { Params = "", Return = "{{Globals#GameMode|GameMode}}", Notes = "(OBSOLETE) Returns the current resolved game mode of the player. If the player is set to inherit the world's gamemode, returns that instead. See also GetGameMode() and IsGameModeXXX() functions. Note that this function is the same as GetGameMode(), use that function instead." },
GetEquippedItem = { Params = "", Return = "{{cItem}}", Notes = "Returns the item that the player is currently holding; empty item if holding nothing." },
@ -1784,7 +1765,6 @@ a_Player:OpenWindow(Window);
GetFoodSaturationLevel = { Params = "", Return = "number", Notes = "Returns the food saturation (overcharge of the food level, is depleted before food level)" },
GetFoodTickTimer = { Params = "", Return = "", Notes = "Returns the number of ticks past the last food-based heal or damage action; when this timer reaches 80, a new heal / damage is applied." },
GetGameMode = { Return = "{{Globals#GameMode|GameMode}}", Notes = "Returns the player's gamemode. The player may have their gamemode unassigned, in which case they inherit the gamemode from the current {{cWorld|world}}.<br /> <b>NOTE:</b> Instead of comparing the value returned by this function to the gmXXX constants, use the IsGameModeXXX() functions. These functions handle the gamemode inheritance automatically."},
GetGroups = { Return = "array-table of {{cGroup}}", Notes = "Returns all the groups that this player is member of, as a table. The groups are stored in the array part of the table, beginning with index 1."},
GetIP = { Return = "string", Notes = "Returns the IP address of the player, if available. Returns an empty string if there's no IP to report."},
GetInventory = { Return = "{{cInventory|Inventory}}", Notes = "Returns the player's inventory"},
GetMaxSpeed = { Params = "", Return = "number", Notes = "Returns the player's current maximum speed, relative to the game default speed. Takes into account the sprinting / flying status." },
@ -1807,15 +1787,13 @@ a_Player:OpenWindow(Window);
IsGameModeAdventure = { Params = "", Return = "bool", Notes = "Returns true if the player is in the gmAdventure gamemode, or has their gamemode unset and the world is a gmAdventure world." },
IsGameModeCreative = { Params = "", Return = "bool", Notes = "Returns true if the player is in the gmCreative gamemode, or has their gamemode unset and the world is a gmCreative world." },
IsGameModeSurvival = { Params = "", Return = "bool", Notes = "Returns true if the player is in the gmSurvival gamemode, or has their gamemode unset and the world is a gmSurvival world." },
IsInGroup = { Params = "GroupNameString", Return = "bool", Notes = "Returns true if the player is a member of the specified group." },
IsOnGround = { Params = "", Return = "bool", Notes = "Returns true if the player is on ground (not falling, not jumping, not flying)" },
IsSatiated = { Params = "", Return = "bool", Notes = "Returns true if the player is satiated (cannot eat)." },
IsVisible = { Params = "", Return = "bool", Notes = "Returns true if the player is visible to other players" },
LoadPermissionsFromDisk = { Params = "", Return = "", Notes = "Reloads the player's permissions from the disk. This loses any temporary changes made to the player's groups." },
LoadRank = { Params = "", Return = "", Notes = "Reloads the player's rank, message visuals and permissions from the {{cRankManager}}, based on the player's current rank." },
MoveTo = { Params = "{{Vector3d|NewPosition}}", Return = "Tries to move the player into the specified position." },
MoveToWorld = { Params = "WorldName", Return = "bool", Return = "Moves the player to the specified world. Returns true if successful." },
OpenWindow = { Params = "{{cWindow|Window}}", Return = "", Notes = "Opens the specified UI window for the player." },
RemoveFromGroup = { Params = "GroupName", Return = "", Notes = "Temporarily removes the player from the specified group. This change is lost when the player disconnects." },
Respawn = { Params = "", Return = "", Notes = "Restores the health, extinguishes fire, makes visible and sends the Respawn packet." },
SendMessage = { Params = "Message", Return = "", Notes = "Sends the specified message to the player." },
SendMessageFailure = { Params = "Message", Return = "", Notes = "Prepends Rose [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and sends message to player. For a command that failed to run because of insufficient permissions, etc." },
@ -2004,6 +1982,71 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
},
}, -- cPluginManager
cRankManager =
{
Desc = [[
Manages the players' permissions. The players are assigned a single rank, which contains groups of
permissions. The functions in this class query or modify these.</p>
<p>
All the functions are static, call them using the <code>cRankManager:Function()</code> convention.</p>
<p>
The players are identified by their UUID, to support player renaming.</p>
<p>
The rank also contains specific "mesage visuals" - bits that are used for formatting messages from the
players. There's a message prefix, which is put in front of every message the player sends, and the
message suffix that is appended to each message. There's also a PlayerNameColorCode, which holds the
color that is used for the player's name in the messages.</p>
<p>
Each rank can contain any number of permission groups. These groups allow for an easier setup of the
permissions - you can share groups among ranks, so the usual approach is to group similar permissions
together and add that group to any rank that should use those permissions.</p>
<p>
Permissions are added to individual groups. Each group can support unlimited permissions. Note that
adding a permission to a group will make the permission available to all the ranks that contain that
permission group.</p>
<p>
One rank is reserved as the Default rank. All players that don't have an explicit rank assigned to them
will behave as if assigned to this rank. The default rank can be changed to any other rank at any time.
Note that the default rank cannot be removed from the RankManager - RemoveRank() will change the default
rank to the replacement rank, if specified, and fail if no replacement rank is specified. Renaming the
default rank using RenameRank() will change the default rank to the new name.
]],
Functions =
{
AddGroup = { Params = "GroupName", Return = "", Notes = "Adds the group of the specified name. Logs a warning and does nothing if the group already exists." },
AddGroupToRank = { Params = "GroupName, RankName", Return = "bool", Notes = "Adds the specified group to the specified rank. Returns true on success, false on failure - if the group name or the rank name is not found." },
AddPermissionToGroup = { Params = "Permission, GroupName", Return = "bool", Notes = "Adds the specified permission to the specified group. Returns true on success, false on failure - if the group name is not found." },
AddRank = { Params = "RankName, MsgPrefix, MsgSuffix, MsgNameColorCode", Return = "", Notes = "Adds a new rank of the specified name and with the specified message visuals. Logs an info message and does nothing if the rank already exists." },
GetAllGroups = { Params = "", Return = "array-table of groups' names", Notes = "Returns an array-table containing the names of all the groups that are known to the manager." },
GetAllPermissions = { Params = "", Return = "array-table of permissions", Notes = "Returns an array-table containing all the permissions that are known to the manager." },
GetAllRanks = { Params = "", Return = "array-table of ranks' names", Notes = "Returns an array-table containing the names of all the ranks that are known to the manager." },
GetDefaultRank = { Params = "", Return = "string", Notes = "Returns the name of the default rank. " },
GetGroupPermissions = { Params = "GroupName", Return = "array-table of permissions", Notes = "Returns an array-table containing the permissions that the specified group contains." },
GetPlayerGroups = { Params = "PlayerUUID", Return = "array-table of groups' names", Notes = "Returns an array-table of the names of the groups that are assigned to the specified player through their rank. Returns an empty table if the player is not known or has no rank or groups assigned to them." },
GetPlayerMsgVisuals = { Params = "PlayerUUID", Return = "MsgPrefix, MsgSuffix, MsgNameColorCode", Notes = "Returns the message visuals assigned to the player. If the player is not explicitly assigned a rank, the default rank's visuals are returned. If there is an error, no value is returned at all." },
GetPlayerPermissions = { Params = "PlayerUUID", Return = "array-table of permissions", Notes = "Returns the permissions that the specified player is assigned through their rank. Returns the default rank's permissions if the player has no explicit rank assigned to them. Returns an empty array on error." },
GetPlayerRankName = { Params = "PlayerUUID", Return = "RankName", Notes = "Returns the name of the rank that is assigned to the specified player. An empty string (NOT the default rank) is returned if the player has no rank assigned to them." },
GetRankGroups = { Params = "RankName", Return = "array-table of groups' names", Notes = "Returns an array-table of the names of all the groups that are assigned to the specified rank. Returns an empty table if there is no such rank." },
GetRankPermissions = { Params = "RankName", Return = "array-table of permissions", Notes = "Returns an array-table of all the permissions that are assigned to the specified rank through its groups. Returns an empty table if there is no such rank." },
GetRankVisuals = { Params = "RankName", Return = "MsgPrefix, MsgSuffix, MsgNameColorCode", Notes = "Returns the message visuals for the specified rank. Returns no value if the specified rank does not exist." },
GroupExists = { Params = "GroupName", Return = "bool", Notes = "Returns true iff the specified group exists." },
IsGroupInRank = { Params = "GroupName, RankName", Return = "bool", Notes = "Returns true iff the specified group is assigned to the specified rank." },
IsPermissionInGroup = { Params = "Permission, GroupName", Return = "bool", Notes = "Returns true iff the specified permission is assigned to the specified group." },
IsPlayerRankSet = { Params = "PlayerUUID", Return = "bool", Notes = "Returns true iff the specified player has a rank assigned to them." },
RankExists = { Params = "RankName", Return = "bool", Notes = "Returns true iff the specified rank exists." },
RemoveGroup = { Params = "GroupName", Return = "", Notes = "Removes the specified group completely. The group will be removed from all the ranks using it and then erased from the manager. Logs an info message and does nothing if the group doesn't exist." },
RemoveGroupFromRank = { Params = "GroupName, RankName", Return = "", Notes = "Removes the specified group from the specified rank. The group will still exist, even if it isn't assigned to any rank. Logs an info message and does nothing if the group or rank doesn't exist." },
RemovePermissionFromGroup = { Params = "Permission, GroupName", Return = "", Notes = "Removes the specified permission from the specified group. Logs an info message and does nothing if the group doesn't exist." },
RemovePlayerRank = { Params = "PlayerUUID", Return = "", Notes = "Removes the player's rank; the player's left without a rank. Note that this doesn't change the {{cPlayer}} instances for the already connected players, you need to update all the instances manually. No action if the player has no rank assigned to them already." },
RemoveRank = { Params = "RankName, [ReplacementRankName]", Return = "", Notes = "Removes the specified rank. If ReplacementRankName is given, the players that have RankName will get their rank set to ReplacementRankName. If it isn't given, or is an invalid rank, the players will be removed from the manager, their ranks will be unset completely. Logs an info message and does nothing if the rank is not found." },
RenameGroup = { Params = "OldName, NewName", Return = "", Notes = "Renames the specified group. Logs an info message and does nothing if the group is not found or the new name is already used." },
RenameRank = { Params = "OldName, NewName", Return = "", Notes = "Renames the specified rank. Logs an info message and does nothing if the rank is not found or the new name is already used." },
SetDefaultRank = { Params = "RankName", Return = "bool", Notes = "Sets the specified rank as the default rank. Returns true on success, false on failure (rank doesn't exist)." },
SetPlayerRank = { Params = "PlayerUUID, PlayerName, RankName", Return = "", Notes = "Updates the rank for the specified player. The player name is provided for reference, the UUID is used for identification. Logs a warning and does nothing if the rank is not found." },
SetRankVisuals = { Params = "RankName, MsgPrefix, MsgSuffix, MsgNameColorCode", Return = "", Notes = "Updates the rank's message visuals. Logs an info message and does nothing if rank not found." },
},
}, -- cRankManager
cRoot =
{
Desc = [[
@ -2032,7 +2075,6 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
GetDefaultWorld = { Params = "", Return = "{{cWorld|cWorld}}", Notes = "Returns the world object from the default world." },
GetFurnaceFuelBurnTime = { Params = "{{cItem|Fuel}}", Return = "number", Notes = "(STATIC) Returns the number of ticks for how long the item would fuel a furnace. Returns zero if not a fuel." },
GetFurnaceRecipe = { Params = "{{cItem|InItem}}", Return = "{{cItem|OutItem}}, NumTicks, {{cItem|InItem}}", Notes = "(STATIC) Returns the furnace recipe for smelting the specified input. If a recipe is found, returns the smelted result, the number of ticks required for the smelting operation, and the input consumed (note that MCServer supports smelting M items into N items and different smelting rates). If no recipe is found, returns no value." },
GetGroupManager = { Params = "", Return = "{{cGroupManager|cGroupManager}}", Notes = "Returns the cGroupManager object." },
GetPhysicalRAMUsage = { Params = "", Return = "number", Notes = "Returns the amount of physical RAM that the entire MCServer process is using, in KiB. Negative if the OS doesn't support this query." },
GetPluginManager = { Params = "", Return = "{{cPluginManager|cPluginManager}}", Notes = "Returns the cPluginManager object." },
GetPrimaryServerVersion = { Params = "", Return = "number", Notes = "Returns the servers primary server version." },
@ -2116,10 +2158,11 @@ end
{
GetDescription = { Return = "string", Notes = "Returns the server description set in the settings.ini." },
GetMaxPlayers = { Return = "number", Notes = "Returns the max amount of players who can join the server." },
SetMaxPlayers = { Params = "number", Notes = "Sets the max amount of players who can join." },
GetNumPlayers = { Return = "number", Notes = "Returns the amount of players online." },
GetServerID = { Return = "string", Notes = "Returns the ID of the server?" },
IsHardcore = { Params = "", Return = "bool", Notes = "Returns true if the server is hardcore (players get banned on death)." },
SetMaxPlayers = { Params = "number", Notes = "Sets the max amount of players who can join." },
ShouldAuthenticate = { Params = "", Return = "bool", Notes = "Returns true iff the server is set to authenticate players (\"online mode\")." },
},
}, -- cServer
@ -2527,8 +2570,8 @@ World:ForEachEntity(
The following code snippet checks if the player holds a shovel.
<pre class="prettyprint lang-lua">
-- a_Player is a {{cPlayer}} object, possibly received as a hook param
local HeldItem = a_Player:GetEquippedItem();
if (cItemCategory:IsShovel(HeldItem.m_ItemType)) then
local HeldItem = a_Player:GetEquippedItem()
if (ItemCategory.IsShovel(HeldItem.m_ItemType)) then
-- It's a shovel
end
</pre>

@ -1 +1 @@
Subproject commit 1b16c23c216d359e9fe0334c63deeecc347e69bd
Subproject commit 7cc99285ae5117418f657c3b295ca71f2b75b4f4

View File

@ -33,6 +33,7 @@ function Initialize(Plugin)
PM:AddHook(cPluginManager.HOOK_PROJECTILE_HIT_BLOCK, OnProjectileHitBlock);
PM:AddHook(cPluginManager.HOOK_CHUNK_UNLOADING, OnChunkUnloading);
PM:AddHook(cPluginManager.HOOK_WORLD_STARTED, OnWorldStarted);
PM:AddHook(cPluginManager.HOOK_PROJECTILE_HIT_BLOCK, OnProjectileHitBlock);
-- _X: Disabled so that the normal operation doesn't interfere with anything
-- PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated);
@ -80,7 +81,8 @@ function Initialize(Plugin)
TestBlockAreasString()
TestStringBase64()
TestUUIDFromName()
-- TestUUIDFromName()
-- TestRankMgr()
--[[
-- Test cCompositeChat usage in console-logging:
@ -352,6 +354,18 @@ end
function TestRankMgr()
LOG("Testing the rank manager")
cRankManager:AddRank("LuaRank")
cRankManager:AddGroup("LuaTestGroup")
cRankManager:AddGroupToRank("LuaTestGroup", "LuaRank")
cRankManager:AddPermissionToGroup("luaperm", "LuaTestGroup")
end
function TestSQLiteBindings()
LOG("Testing SQLite bindings...");
@ -1532,3 +1546,15 @@ end
function OnProjectileHitBlock(a_ProjectileEntity, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockHitPos)
-- This simple test is for testing issue #1326 - simply declaring this hook would crash the server upon call
LOG("Projectile hit block")
LOG(" Projectile EntityID: " .. a_ProjectileEntity:GetUniqueID())
LOG(" Block: {" .. a_BlockX .. ", " .. a_BlockY .. ", " .. a_BlockZ .. "}, face " .. a_BlockFace)
LOG(" HitPos: {" .. a_BlockHitPos.x .. ", " .. a_BlockHitPos.y .. ", " .. a_BlockHitPos.z .. "}")
end

View File

@ -100,7 +100,7 @@ BrickSlab, 6 = BrickBlock, 1:1, 2:1, 3:1
StonebrickSlab, 6 = StoneBrick, 1:1, 2:1, 3:1
NetherbrickSlab, 6 = NetherBrick, 1:1, 2:1, 3:1
Quartzslab, 6 = QuartzBlock, 1:1, 2:1, 3:1
snow, 6 = SnowBlock, 1:1, 2:1, 3:1
snow, 6 = SnowBlock, 1:1, 2:1, 3:1
# Stairs:

View File

@ -12,7 +12,8 @@
:: It expects the MS Performance tools installed in C:\Program Files\Microsoft Visual Studio 9.0\Team Tools\Performance Tools
:: You can override this path by setting the pt environment variable prior to launching this script
::
:: By default it will launch the release version of MCServer; set the app environment variable to another executable to run that instead.
:: By default it will launch the 32-bit release version of MCServer; set the app environment variable to another executable to run that instead.
:: Set the IsExecutablex64 env variable to \x64 to profile a 64-bit executable instead (available as the profile_run_x64.cmd script)
:: Note that the app needs to be compiled with the "/PROFILE" flag in order for the profiling to work
@ -45,7 +46,7 @@ if %outputdir%n == n (
::Create the output directory, if it didn't exist
:: Create the output directory, if it didn't exist
mkdir %outputdir%
@ -55,15 +56,15 @@ mkdir %outputdir%
:: Start the profiler
set outputname=profile.vsp
set output=%outputdir%\%outputname%
%pt%\vsperfcmd /start:sample /output:%output%
%pt%%IsExecutablex64%\vsperfcmd /start:sample /output:%output%
if errorlevel 1 goto haderror
:: Launch the application via the profiler
%pt%\vsperfcmd /launch:%app%
if errorlevel 1 goto haderror
%pt%%IsExecutablex64%\vsperfcmd /launch:%app%
if errorlevel 1 goto haderrorshutdown
:: Shut down the profiler (this command waits, until the application is terminated)
%pt%\vsperfcmd /shutdown
%pt%%IsExecutablex64%\vsperfcmd /shutdown
if errorlevel 1 goto haderror
@ -86,6 +87,10 @@ goto finished
:haderrorshutdown
echo An error was encountered, shutting down the profiler
%pt%%IsExecutablex64%\vsperfcmd /shutdown
:haderror
echo An error was encountered
pause

View File

@ -2,4 +2,4 @@
:: This script uses the profile_run.cmd script to run profiling on the DebugProfile executable
set app=MCServer_debug_profile.exe
call profile_run.cmd
call profile_run.cmd

View File

@ -0,0 +1,5 @@
@echo off
:: This script uses the profile_run.cmd script to run profiling on a x64 release executable
set IsExecutablex64=\x64
call profile_run.cmd

View File

@ -1,3 +1,7 @@
*.vcproj
*.vcxproj
*.sln
*.user
.xls
Statistics.txt
*.bmp
@ -7,3 +11,4 @@ Profiling
*.png
world/
*.html
*.xls

View File

@ -1,55 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Express 2013 for Windows Desktop
VisualStudioVersion = 12.0.21005.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AnvilStats", "AnvilStats.vcxproj", "{CF996A5E-0A86-4004-9710-682B06B5AEBA}"
ProjectSection(ProjectDependencies) = postProject
{B61007AC-B557-4B67-A765-E468C0C3A821} = {B61007AC-B557-4B67-A765-E468C0C3A821}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\..\lib\zlib\zlib.vcxproj", "{B61007AC-B557-4B67-A765-E468C0C3A821}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
DebugProfile|Win32 = DebugProfile|Win32
MinSizeRel|Win32 = MinSizeRel|Win32
Release profiled|Win32 = Release profiled|Win32
Release|Win32 = Release|Win32
ReleaseProfile|Win32 = ReleaseProfile|Win32
RelWithDebInfo|Win32 = RelWithDebInfo|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Debug|Win32.ActiveCfg = Debug|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Debug|Win32.Build.0 = Debug|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.DebugProfile|Win32.ActiveCfg = Debug|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.DebugProfile|Win32.Build.0 = Debug|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.MinSizeRel|Win32.ActiveCfg = Release|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.MinSizeRel|Win32.Build.0 = Release|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Release profiled|Win32.ActiveCfg = Release profiled|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Release profiled|Win32.Build.0 = Release profiled|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Release|Win32.ActiveCfg = Release|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Release|Win32.Build.0 = Release|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.ReleaseProfile|Win32.ActiveCfg = Release|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.ReleaseProfile|Win32.Build.0 = Release|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.RelWithDebInfo|Win32.ActiveCfg = Release|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.RelWithDebInfo|Win32.Build.0 = Release|Win32
{B61007AC-B557-4B67-A765-E468C0C3A821}.Debug|Win32.ActiveCfg = Debug|Win32
{B61007AC-B557-4B67-A765-E468C0C3A821}.Debug|Win32.Build.0 = Debug|Win32
{B61007AC-B557-4B67-A765-E468C0C3A821}.DebugProfile|Win32.ActiveCfg = DebugProfile|Win32
{B61007AC-B557-4B67-A765-E468C0C3A821}.DebugProfile|Win32.Build.0 = DebugProfile|Win32
{B61007AC-B557-4B67-A765-E468C0C3A821}.MinSizeRel|Win32.ActiveCfg = MinSizeRel|Win32
{B61007AC-B557-4B67-A765-E468C0C3A821}.MinSizeRel|Win32.Build.0 = MinSizeRel|Win32
{B61007AC-B557-4B67-A765-E468C0C3A821}.Release profiled|Win32.ActiveCfg = Release|Win32
{B61007AC-B557-4B67-A765-E468C0C3A821}.Release profiled|Win32.Build.0 = Release|Win32
{B61007AC-B557-4B67-A765-E468C0C3A821}.Release|Win32.ActiveCfg = Release|Win32
{B61007AC-B557-4B67-A765-E468C0C3A821}.Release|Win32.Build.0 = Release|Win32
{B61007AC-B557-4B67-A765-E468C0C3A821}.ReleaseProfile|Win32.ActiveCfg = ReleaseProfile|Win32
{B61007AC-B557-4B67-A765-E468C0C3A821}.ReleaseProfile|Win32.Build.0 = ReleaseProfile|Win32
{B61007AC-B557-4B67-A765-E468C0C3A821}.RelWithDebInfo|Win32.ActiveCfg = RelWithDebInfo|Win32
{B61007AC-B557-4B67-A765-E468C0C3A821}.RelWithDebInfo|Win32.Build.0 = RelWithDebInfo|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -1,468 +0,0 @@
<?xml version="1.0" encoding="windows-1250"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="AnvilStats"
ProjectGUID="{CF996A5E-0A86-4004-9710-682B06B5AEBA}"
RootNamespace="AnvilStats"
Keyword="Win32Proj"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="&quot;..\..\lib&quot;"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="Globals.h"
WarningLevel="3"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
StackReserveSize="16777216"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories="&quot;..\..\lib&quot;"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="Globals.h"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
StackReserveSize="16777216"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release profiled|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories="&quot;..\..\lib&quot;"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="Globals.h"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
StackReserveSize="16777216"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
Profile="true"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\AnvilStats.cpp"
>
</File>
<File
RelativePath=".\BiomeMap.cpp"
>
</File>
<File
RelativePath=".\BiomeMap.h"
>
</File>
<File
RelativePath=".\Callback.h"
>
</File>
<File
RelativePath=".\ChunkExtract.cpp"
>
</File>
<File
RelativePath=".\ChunkExtract.h"
>
</File>
<File
RelativePath=".\Globals.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release profiled|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\Globals.h"
>
</File>
<File
RelativePath=".\HeightBiomeMap.cpp"
>
</File>
<File
RelativePath=".\HeightBiomeMap.h"
>
</File>
<File
RelativePath=".\HeightMap.cpp"
>
</File>
<File
RelativePath=".\HeightMap.h"
>
</File>
<File
RelativePath=".\ImageComposingCallback.cpp"
>
</File>
<File
RelativePath=".\ImageComposingCallback.h"
>
</File>
<File
RelativePath=".\Processor.cpp"
>
</File>
<File
RelativePath=".\Processor.h"
>
</File>
<File
RelativePath=".\SpringStats.cpp"
>
</File>
<File
RelativePath=".\SpringStats.h"
>
</File>
<File
RelativePath=".\Statistics.cpp"
>
</File>
<File
RelativePath=".\Statistics.h"
>
</File>
<File
RelativePath=".\Utils.cpp"
>
</File>
<File
RelativePath=".\Utils.h"
>
</File>
</Filter>
<Filter
Name="shared"
>
<File
RelativePath="..\..\src\OSSupport\CriticalSection.cpp"
>
</File>
<File
RelativePath="..\..\src\OSSupport\CriticalSection.h"
>
</File>
<File
RelativePath="..\..\src\Endianness.h"
>
</File>
<File
RelativePath="..\..\src\OSSupport\Event.cpp"
>
</File>
<File
RelativePath="..\..\src\OSSupport\Event.h"
>
</File>
<File
RelativePath="..\..\src\WorldStorage\FastNBT.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="NBT_RESERVE_SIZE=10000"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="NBT_RESERVE_SIZE=10000"
/>
</FileConfiguration>
<FileConfiguration
Name="Release profiled|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="NBT_RESERVE_SIZE=10000"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\..\src\WorldStorage\FastNBT.h"
>
</File>
<File
RelativePath="..\..\src\OSSupport\File.cpp"
>
</File>
<File
RelativePath="..\..\src\OSSupport\File.h"
>
</File>
<File
RelativePath="..\..\src\OSSupport\GZipFile.cpp"
>
</File>
<File
RelativePath="..\..\src\OSSupport\GZipFile.h"
>
</File>
<File
RelativePath="..\..\src\OSSupport\IsThread.cpp"
>
</File>
<File
RelativePath="..\..\src\OSSupport\IsThread.h"
>
</File>
<File
RelativePath="..\..\src\StringUtils.cpp"
>
</File>
<File
RelativePath="..\..\src\StringUtils.h"
>
</File>
</Filter>
<File
RelativePath=".\AnvilStats.txt"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -0,0 +1,144 @@
cmake_minimum_required (VERSION 2.8.3)
project (AnvilStats)
include(../../SetFlags.cmake)
set_flags()
set_lib_flags()
# Set include paths to the used libraries:
include_directories("../../lib")
include_directories("../../src")
function(flatten_files arg1)
set(res "")
foreach(f ${${arg1}})
get_filename_component(f ${f} ABSOLUTE)
list(APPEND res ${f})
endforeach()
set(${arg1} "${res}" PARENT_SCOPE)
endfunction()
add_subdirectory(../../lib/zlib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/lib/zlib)
set_exe_flags()
# Include the shared files:
set(SHARED_SRC
../../src/ByteBuffer.cpp
../../src/StringUtils.cpp
../../src/LoggerListeners.cpp
../../src/Logger.cpp
../../src/WorldStorage/FastNBT.cpp
../BiomeVisualiser/BiomeColors.cpp
)
set(SHARED_HDR
../../src/ByteBuffer.h
../../src/StringUtils.h
../../src/LoggerListeners.h
../../src/Logger.h
../../src/WorldStorage/FastNBT.h
../BiomeVisualiser/BiomeColors.h
)
set(SHARED_OSS_SRC
../../src/OSSupport/CriticalSection.cpp
../../src/OSSupport/Event.cpp
../../src/OSSupport/File.cpp
../../src/OSSupport/GZipFile.cpp
../../src/OSSupport/IsThread.cpp
../../src/OSSupport/Timer.cpp
)
set(SHARED_OSS_HDR
../../src/OSSupport/CriticalSection.h
../../src/OSSupport/Event.h
../../src/OSSupport/File.h
../../src/OSSupport/GZipFile.h
../../src/OSSupport/IsThread.h
../../src/OSSupport/Timer.h
)
flatten_files(SHARED_SRC)
flatten_files(SHARED_HDR)
flatten_files(SHARED_OSS_SRC)
flatten_files(SHARED_OSS_HDR)
source_group("Shared" FILES ${SHARED_SRC} ${SHARED_HDR})
source_group("Shared\\OSSupport" FILES ${SHARED_OSS_SRC} ${SHARED_OSS_HDR})
# Include the main source files:
set(SOURCES
AnvilStats.cpp
BiomeMap.cpp
ChunkExtract.cpp
Globals.cpp
HeightBiomeMap.cpp
HeightMap.cpp
ImageComposingCallback.cpp
Processor.cpp
SpringStats.cpp
Statistics.cpp
Utils.cpp
)
set(HEADERS
BiomeMap.h
Callback.h
ChunkExtract.h
Globals.h
HeightBiomeMap.h
HeightMap.h
ImageComposingCallback.h
Processor.h
SpringStats.h
Statistics.h
Utils.h
AnvilStats.txt
)
source_group("" FILES ${SOURCES} ${HEADERS})
add_definitions(-DNBT_RESERVE_SIZE=10000)
add_executable(AnvilStats
${SOURCES}
${HEADERS}
${SHARED_SRC}
${SHARED_HDR}
${SHARED_OSS_SRC}
${SHARED_OSS_HDR}
)
target_link_libraries(AnvilStats zlib)
# Under MSVC we need to enlarge the default stack size for the executable:
if (MSVC)
get_target_property(TEMP AnvilStats LINK_FLAGS)
if (TEMP STREQUAL "TEMP-NOTFOUND")
SET(TEMP "") # set to empty string
message("LINKER_FLAGS not found")
else ()
SET(TEMP "${TEMP} ") # a space to cleanly separate from existing content
message("LINKER_FLAGS: ${LINKER_FLAGS}")
endif ()
# append our values
SET(TEMP "${TEMP}/STACK:16777216")
set_target_properties(AnvilStats PROPERTIES LINK_FLAGS ${TEMP})
endif ()

View File

@ -241,6 +241,17 @@ public:
/** Clamp value to the specified range. */
template <typename T>
T Clamp(T a_Value, T a_Min, T a_Max)
{
return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value);
}
// Common headers (part 2, with macros):
#include "../../src/ChunkDef.h"
#include "../../src/BlockID.h"

View File

@ -28,6 +28,7 @@ cProcessor::cThread::cThread(cCallback & a_Callback, cProcessor & a_ParentProces
m_Callback(a_Callback),
m_ParentProcessor(a_ParentProcessor)
{
LOG("Created a new thread: %p", this);
super::Start();
}
@ -35,11 +36,20 @@ cProcessor::cThread::cThread(cCallback & a_Callback, cProcessor & a_ParentProces
void cProcessor::cThread::WaitForStart(void)
{
m_HasStarted.Wait();
}
void cProcessor::cThread::Execute(void)
{
LOG("Started a new thread: %d", cIsThread::GetCurrentID());
LOG("Started a new thread: %p, ID %d", this, cIsThread::GetCurrentID());
m_ParentProcessor.m_ThreadsHaveStarted.Set();
m_HasStarted.Set();
for (;;)
{
@ -52,7 +62,7 @@ void cProcessor::cThread::Execute(void)
ProcessFile(FileName);
} // for-ever
LOG("Thread %d terminated", cIsThread::GetCurrentID());
LOG("Thread %p (ID %d) terminated", this, cIsThread::GetCurrentID());
}
@ -522,20 +532,18 @@ void cProcessor::ProcessWorld(const AString & a_WorldFolder, cCallbackFactory &
#endif // _DEBUG
//*/
// Start all the threads:
for (int i = 0; i < NumThreads; i++)
{
cCallback * Callback = a_CallbackFactory.GetNewCallback();
m_Threads.push_back(new cThread(*Callback, *this));
}
// Wait for the first thread to start processing:
m_ThreadsHaveStarted.Wait();
// Wait for all threads to finish
// simply by calling each thread's destructor sequentially
// Wait for all threads to finish:
LOG("Waiting for threads to finish");
for (cThreads::iterator itr = m_Threads.begin(), end = m_Threads.end(); itr != end; ++itr)
{
(*itr)->WaitForStart();
delete *itr;
} // for itr - m_Threads[]
LOG("Processor finished");

View File

@ -30,6 +30,7 @@ class cProcessor
cCallback & m_Callback;
cProcessor & m_ParentProcessor;
cEvent m_HasStarted;
// cIsThread override:
virtual void Execute(void) override;
@ -48,6 +49,9 @@ class cProcessor
public:
cThread(cCallback & a_Callback, cProcessor & a_ParentProcessor);
/** Waits until the thread starts processing the callback code. */
void WaitForStart(void);
} ;
typedef std::vector<cThread *> cThreads;
@ -65,10 +69,12 @@ protected:
AStringList m_FileQueue;
cThreads m_Threads;
cEvent m_ThreadsHaveStarted; // This is signalled by each thread to notify the parent thread that it can start waiting for those threads
/** Populates m_FileQueue with Anvil files from the specified folder. */
void PopulateFileQueue(const AString & a_WorldFolder);
/** Returns one filename from m_FileQueue, and removes the name from the queue. */
AString GetOneFileName(void);
} ;

View File

@ -26,9 +26,11 @@ cStatistics::cStats::cStats(void) :
m_MinChunkZ(0x7fffffff),
m_MaxChunkZ(0x80000000)
{
memset(m_BiomeCounts, 0, sizeof(m_BiomeCounts));
memset(m_BlockCounts, 0, sizeof(m_BlockCounts));
memset(m_SpawnerEntity, 0, sizeof(m_SpawnerEntity));
memset(m_BiomeCounts, 0, sizeof(m_BiomeCounts));
memset(m_BlockCounts, 0, sizeof(m_BlockCounts));
memset(m_PerHeightBlockCounts, 0, sizeof(m_PerHeightBlockCounts));
memset(m_PerHeightSpawners, 0, sizeof(m_PerHeightSpawners));
memset(m_SpawnerEntity, 0, sizeof(m_SpawnerEntity));
}
@ -46,6 +48,11 @@ void cStatistics::cStats::Add(const cStatistics::cStats & a_Stats)
for (int j = 0; j <= 255; j++)
{
m_BlockCounts[i][j] += a_Stats.m_BlockCounts[i][j];
m_PerHeightBlockCounts[i][j] += a_Stats.m_PerHeightBlockCounts[i][j];
}
for (int j = 0; j < ARRAYCOUNT(m_PerHeightSpawners[0]); j++)
{
m_PerHeightSpawners[i][j] += a_Stats.m_PerHeightSpawners[i][j];
}
}
for (int i = 0; i < ARRAYCOUNT(m_SpawnerEntity); i++)
@ -149,6 +156,7 @@ bool cStatistics::OnSection
for (int y = 0; y < 16; y++)
{
int Height = (int)a_Y * 16 + y;
for (int z = 0; z < 16; z++)
{
for (int x = 0; x < 16; x++)
@ -156,6 +164,7 @@ bool cStatistics::OnSection
unsigned char Biome = m_BiomeData[x + 16 * z]; // Cannot use cChunkDef, different datatype
unsigned char BlockType = cChunkDef::GetBlock(a_BlockTypes, x, y, z);
m_Stats.m_BlockCounts[Biome][BlockType] += 1;
m_Stats.m_PerHeightBlockCounts[Height][BlockType] += 1;
}
}
}
@ -259,16 +268,27 @@ bool cStatistics::OnTileTick(
void cStatistics::OnSpawner(cParsedNBT & a_NBT, int a_TileEntityTag)
{
// Get the spawned entity type:
int EntityIDTag = a_NBT.FindChildByName(a_TileEntityTag, "EntityId");
if ((EntityIDTag < 0) || (a_NBT.GetType(EntityIDTag) != TAG_String))
{
return;
}
eEntityType Ent = GetEntityType(a_NBT.GetString(EntityIDTag));
if (Ent < ARRAYCOUNT(m_Stats.m_SpawnerEntity))
if (Ent >= ARRAYCOUNT(m_Stats.m_SpawnerEntity))
{
m_Stats.m_SpawnerEntity[Ent] += 1;
return;
}
m_Stats.m_SpawnerEntity[Ent] += 1;
// Get the spawner pos:
int PosYTag = a_NBT.FindChildByName(a_TileEntityTag, "y");
if ((PosYTag < 0) || (a_NBT.GetType(PosYTag) != TAG_Int))
{
return;
}
int BlockY = Clamp(a_NBT.GetInt(PosYTag), 0, 255);
m_Stats.m_PerHeightSpawners[BlockY][Ent] += 1;
}
@ -316,10 +336,14 @@ cStatisticsFactory::~cStatisticsFactory()
SaveBiomes();
LOG(" BlockTypes.xls");
SaveBlockTypes();
LOG(" PerHeightBlockTypes.xls");
SavePerHeightBlockTypes();
LOG(" BiomeBlockTypes.xls");
SaveBiomeBlockTypes();
LOG(" Spawners.xls");
SaveSpawners();
LOG(" PerHeightSpawners.xls");
SavePerHeightSpawners();
}
@ -395,6 +419,61 @@ void cStatisticsFactory::SaveBlockTypes(void)
void cStatisticsFactory::SavePerHeightBlockTypes(void)
{
// Export as two tables: biomes 0-127 and 128-255, because OpenOffice doesn't support more than 256 columns
cFile f;
if (!f.Open("PerHeightBlockTypes.xls", cFile::fmWrite))
{
LOG("Cannot write to file PerHeightBlockTypes.xls. Statistics not written.");
return;
}
// Write header:
f.Printf("Blocks 0 - 127:\nHeight");
for (int i = 0; i < 128; i++)
{
f.Printf("\t%s(%d)", GetBlockTypeString(i), i);
}
f.Printf("\n");
// Write first half:
for (int y = 0; y < 256; y++)
{
f.Printf("%d", y);
for (int BlockType = 0; BlockType < 128; BlockType++)
{
f.Printf("\t%llu", m_CombinedStats.m_PerHeightBlockCounts[y][BlockType]);
} // for BlockType
f.Printf("\n");
} // for y - height (0 - 127)
f.Printf("\n");
// Write second header:
f.Printf("Blocks 128 - 255:\nHeight");
for (int i = 128; i < 256; i++)
{
f.Printf("\t%s(%d)", GetBlockTypeString(i), i);
}
f.Printf("\n");
// Write second half:
for (int y = 0; y < 256; y++)
{
f.Printf("%d", y);
for (int BlockType = 128; BlockType < 256; BlockType++)
{
f.Printf("\t%llu", m_CombinedStats.m_PerHeightBlockCounts[y][BlockType]);
} // for BlockType
f.Printf("\n");
} // for y - height (0 - 127)
}
void cStatisticsFactory::SaveBiomeBlockTypes(void)
{
// Export as two tables: biomes 0-127 and 128-255, because OpenOffice doesn't support more than 256 columns
@ -521,3 +600,41 @@ void cStatisticsFactory::SaveSpawners(void)
void cStatisticsFactory::SavePerHeightSpawners(void)
{
cFile f;
if (!f.Open("PerHeightSpawners.xls", cFile::fmWrite))
{
LOG("Cannot write to file PerHeightSpawners.xls. Statistics not written.");
return;
}
// Write header:
f.Printf("Height\tTotal");
for (int i = 0; i < entMax; i++)
{
f.Printf("\t%s", GetEntityTypeString((eEntityType)i));
}
f.Printf("\n");
// Write individual lines:
for (int y = 0; y < 256; y++)
{
UInt64 Total = 0;
for (int i = 0; i < entMax; i++)
{
Total += m_CombinedStats.m_PerHeightSpawners[y][i];
}
f.Printf("%d\t%llu", y, Total);
for (int i = 0; i < entMax; i++)
{
f.Printf("\t%llu", m_CombinedStats.m_PerHeightSpawners[y][i]);
}
f.Printf("\n");
}
}

View File

@ -31,6 +31,8 @@ public:
UInt64 m_NumEntities;
UInt64 m_NumTileEntities;
UInt64 m_NumTileTicks;
UInt64 m_PerHeightBlockCounts[256][256]; // First dimension is the height, second dimension is BlockType
UInt64 m_PerHeightSpawners[256][entMax + 1]; // First dimension is the height, second dimension is spawned entity type
int m_MinChunkX, m_MaxChunkX; // X coords range
int m_MinChunkZ, m_MaxChunkZ; // Z coords range
@ -74,6 +76,8 @@ protected:
virtual bool OnEmptySection(unsigned char a_Y) override;
virtual bool OnSectionsFinished(void) override { return false; } // continue processing
virtual bool OnEntity(
const AString & a_EntityType,
double a_PosX, double a_PosY, double a_PosZ,
@ -128,9 +132,11 @@ protected:
void JoinResults(void);
void SaveBiomes(void);
void SaveBlockTypes(void);
void SavePerHeightBlockTypes(void);
void SaveBiomeBlockTypes(void);
void SaveStatistics(void);
void SaveSpawners(void);
void SavePerHeightSpawners(void);
} ;

View File

@ -272,7 +272,7 @@ extern const char * GetEntityTypeString(eEntityType a_EntityType)
int GetNumCores(void)
{
// Get number of cores by querying the system process affinity mask (Windows-specific)
DWORD Affinity, ProcAffinity;
DWORD_PTR Affinity, ProcAffinity;
GetProcessAffinityMask(GetCurrentProcess(), &ProcAffinity, &Affinity);
int NumCores = 0;
while (Affinity > 0)

View File

@ -16,6 +16,7 @@
#include "../../../lua/src/lauxlib.h"
#include <stdlib.h>
#include <assert.h>
TOLUA_API void tolua_pushvalue (lua_State* L, int lo)
{
@ -55,12 +56,14 @@ TOLUA_API void tolua_pushusertype (lua_State* L, void* value, const char* type)
else
{
luaL_getmetatable(L, type);
assert(!lua_isnil(L, -1)); /* Failure here means that the usertype is unknown to ToLua. Check what type you're pushing. */
lua_pushstring(L,"tolua_ubox");
lua_rawget(L,-2); /* stack: mt ubox */
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
lua_pushstring(L, "tolua_ubox");
lua_rawget(L, LUA_REGISTRYINDEX);
if (lua_isnil(L, -1))
{
lua_pop(L, 1);
lua_pushstring(L, "tolua_ubox");
lua_rawget(L, LUA_REGISTRYINDEX);
};
lua_pushlightuserdata(L,value);
lua_rawget(L,-2); /* stack: mt ubox ubox[u] */

View File

@ -3,7 +3,7 @@
#include <memory>
template<class T>
template <class T>
class cAllocationPool
{
public:
@ -34,7 +34,7 @@ public:
/** Allocates memory storing unused elements in a linked list. Keeps at least NumElementsInReserve
elements in the list unless malloc fails so that the program has a reserve to handle OOM.**/
template<class T, size_t NumElementsInReserve>
template <class T, size_t NumElementsInReserve>
class cListAllocationPool : public cAllocationPool<T>
{
public:

View File

@ -67,7 +67,6 @@ $cfile "../Root.h"
$cfile "../Cuboid.h"
$cfile "../BoundingBox.h"
$cfile "../Tracer.h"
$cfile "../Group.h"
$cfile "../BlockArea.h"
$cfile "../Generating/ChunkDesc.h"
$cfile "../CraftingRecipes.h"

View File

@ -11,6 +11,7 @@ SET (SRCS
LuaState.cpp
LuaWindow.cpp
ManualBindings.cpp
ManualBindings_RankManager.cpp
Plugin.cpp
PluginLua.cpp
PluginManager.cpp
@ -96,7 +97,6 @@ set(BINDING_DEPENDENCIES
../Entities/HangingEntity.h
../Entities/ItemFrame.h
../Generating/ChunkDesc.h
../Group.h
../Inventory.h
../Item.h
../ItemGrid.h

View File

@ -460,7 +460,43 @@ void cLuaState::Push(const Vector3d & a_Vector)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, (void *)&a_Vector, "Vector3d");
tolua_pushusertype(m_LuaState, (void *)&a_Vector, "Vector3<double>");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::Push(const Vector3d * a_Vector)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, (void *)a_Vector, "Vector3<double>");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::Push(const Vector3i & a_Vector)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, (void *)&a_Vector, "Vector3<int>");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::Push(const Vector3i * a_Vector)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, (void *)a_Vector, "Vector3<int>");
m_NumCurrentFunctionArgs += 1;
}
@ -708,11 +744,11 @@ void cLuaState::Push(TakeDamageInfo * a_TDI)
void cLuaState::Push(Vector3i * a_Vector)
void cLuaState::Push(Vector3d * a_Vector)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_Vector, "Vector3i");
tolua_pushusertype(m_LuaState, a_Vector, "Vector3<double>");
m_NumCurrentFunctionArgs += 1;
}
@ -720,11 +756,11 @@ void cLuaState::Push(Vector3i * a_Vector)
void cLuaState::Push(Vector3d * a_Vector)
void cLuaState::Push(Vector3i * a_Vector)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_Vector, "Vector3d");
tolua_pushusertype(m_LuaState, a_Vector, "Vector3<int>");
m_NumCurrentFunctionArgs += 1;
}

View File

@ -186,6 +186,9 @@ public:
void Push(const HTTPRequest * a_Request);
void Push(const HTTPTemplateRequest * a_Request);
void Push(const Vector3d & a_Vector);
void Push(const Vector3d * a_Vector);
void Push(const Vector3i & a_Vector);
void Push(const Vector3i * a_Vector);
// Push a value onto the stack (keep alpha-sorted):
void Push(bool a_Value);

View File

@ -301,11 +301,11 @@ static int tolua_cFile_GetFolderContents(lua_State * tolua_S)
template<
template <
class Ty1,
class Ty2,
bool (Ty1::*Func1)(const AString &, cItemCallback<Ty2> &)
>
>
static int tolua_DoWith(lua_State* tolua_S)
{
int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
@ -395,7 +395,7 @@ static int tolua_DoWith(lua_State* tolua_S)
template<
template <
class Ty1,
class Ty2,
bool (Ty1::*Func1)(int, cItemCallback<Ty2> &)
@ -485,7 +485,7 @@ static int tolua_DoWithID(lua_State* tolua_S)
template<
template <
class Ty1,
class Ty2,
bool (Ty1::*Func1)(int, int, int, cItemCallback<Ty2> &)
@ -580,7 +580,7 @@ static int tolua_DoWithXYZ(lua_State* tolua_S)
template<
template <
class Ty1,
class Ty2,
bool (Ty1::*Func1)(int, int, cItemCallback<Ty2> &)
@ -676,7 +676,7 @@ static int tolua_ForEachInChunk(lua_State * tolua_S)
template<
template <
class Ty1,
class Ty2,
bool (Ty1::*Func1)(cItemCallback<Ty2> &)
@ -1803,49 +1803,30 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S)
static int tolua_cPlayer_GetGroups(lua_State * tolua_S)
static int tolua_cPlayer_GetPermissions(lua_State * tolua_S)
{
// Function signature: cPlayer:GetPermissions() -> {permissions-array}
// Check the params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cPlayer") ||
!L.CheckParamEnd (2)
)
{
return 0;
}
// Get the params:
cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, NULL);
const cPlayer::GroupList & AllGroups = self->GetGroups();
lua_createtable(tolua_S, (int)AllGroups.size(), 0);
int newTable = lua_gettop(tolua_S);
int index = 1;
cPlayer::GroupList::const_iterator iter = AllGroups.begin();
while (iter != AllGroups.end())
if (self == NULL)
{
const cGroup * Group = *iter;
tolua_pushusertype(tolua_S, (void *)Group, "const cGroup");
lua_rawseti(tolua_S, newTable, index);
++iter;
++index;
}
return 1;
}
static int tolua_cPlayer_GetResolvedPermissions(lua_State * tolua_S)
{
cPlayer * self = (cPlayer*) tolua_tousertype(tolua_S, 1, NULL);
cPlayer::StringList AllPermissions = self->GetResolvedPermissions();
lua_createtable(tolua_S, (int)AllPermissions.size(), 0);
int newTable = lua_gettop(tolua_S);
int index = 1;
cPlayer::StringList::iterator iter = AllPermissions.begin();
while (iter != AllPermissions.end())
{
std::string & Permission = *iter;
lua_pushlstring(tolua_S, Permission.c_str(), Permission.length());
lua_rawseti(tolua_S, newTable, index);
++iter;
++index;
LOGWARNING("%s: invalid self (%p)", __FUNCTION__, self);
return 0;
}
// Push the permissions:
L.Push(self->GetPermissions());
return 1;
}
@ -1902,6 +1883,40 @@ static int tolua_cPlayer_OpenWindow(lua_State * tolua_S)
static int tolua_cPlayer_PermissionMatches(lua_State * tolua_S)
{
// Function signature: cPlayer:PermissionMatches(PermissionStr, TemplateStr) -> bool
// Check the params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cPlayer") ||
!L.CheckParamString (2, 3) ||
!L.CheckParamEnd (4)
)
{
return 0;
}
// Get the params:
cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, NULL);
if (self == NULL)
{
LOGWARNING("%s: invalid self (%p)", __FUNCTION__, self);
return 0;
}
AString Permission, Template;
L.GetStackValues(2, Permission, Template);
// Push the result of the match:
L.Push(self->PermissionMatches(StringSplit(Permission, "."), StringSplit(Template, ".")));
return 1;
}
template <
class OBJTYPE,
void (OBJTYPE::*SetCallback)(cPluginLua * a_Plugin, int a_FnRef)
@ -2399,6 +2414,62 @@ static int tolua_cMojangAPI_GetUUIDsFromPlayerNames(lua_State * L)
static int tolua_cMojangAPI_MakeUUIDDashed(lua_State * L)
{
// Function signature: cMojangAPI:MakeUUIDDashed(UUID) -> string
// Check params:
cLuaState S(L);
if (
!S.CheckParamUserTable(1, "cMojangAPI") ||
!S.CheckParamString(2) ||
!S.CheckParamEnd(3)
)
{
return 0;
}
// Get the params:
AString UUID;
S.GetStackValue(2, UUID);
// Push the result:
S.Push(cRoot::Get()->GetMojangAPI().MakeUUIDDashed(UUID));
return 1;
}
static int tolua_cMojangAPI_MakeUUIDShort(lua_State * L)
{
// Function signature: cMojangAPI:MakeUUIDShort(UUID) -> string
// Check params:
cLuaState S(L);
if (
!S.CheckParamUserTable(1, "cMojangAPI") ||
!S.CheckParamString(2) ||
!S.CheckParamEnd(3)
)
{
return 0;
}
// Get the params:
AString UUID;
S.GetStackValue(2, UUID);
// Push the result:
S.Push(cRoot::Get()->GetMojangAPI().MakeUUIDShort(UUID));
return 1;
}
static int Lua_ItemGrid_GetSlotCoords(lua_State * L)
{
tolua_Error tolua_err;
@ -3295,9 +3366,9 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cPlayer");
tolua_function(tolua_S, "GetGroups", tolua_cPlayer_GetGroups);
tolua_function(tolua_S, "GetResolvedPermissions", tolua_cPlayer_GetResolvedPermissions);
tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow);
tolua_function(tolua_S, "GetPermissions", tolua_cPlayer_GetPermissions);
tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow);
tolua_function(tolua_S, "PermissionMatches", tolua_cPlayer_PermissionMatches);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cLuaWindow");
@ -3340,6 +3411,8 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "GetPlayerNameFromUUID", tolua_cMojangAPI_GetPlayerNameFromUUID);
tolua_function(tolua_S, "GetUUIDFromPlayerName", tolua_cMojangAPI_GetUUIDFromPlayerName);
tolua_function(tolua_S, "GetUUIDsFromPlayerNames", tolua_cMojangAPI_GetUUIDsFromPlayerNames);
tolua_function(tolua_S, "MakeUUIDDashed", tolua_cMojangAPI_MakeUUIDDashed);
tolua_function(tolua_S, "MakeUUIDShort", tolua_cMojangAPI_MakeUUIDShort);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cItemGrid");
@ -3347,6 +3420,8 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_endmodule(tolua_S);
tolua_function(tolua_S, "md5", tolua_md5);
BindRankManager(tolua_S);
tolua_endmodule(tolua_S);
}

View File

@ -1,8 +1,24 @@
#pragma once
struct lua_State;
/** Provides namespace for the bindings. */
class ManualBindings
{
public:
static void Bind( lua_State* tolua_S);
/** Binds all the manually implemented functions to tolua_S. */
static void Bind(lua_State * tolua_S);
protected:
/** Binds the manually implemented cRankManager glue code to tolua_S.
Implemented in ManualBindings_RankManager.cpp. */
static void BindRankManager(lua_State * tolua_S);
};

File diff suppressed because it is too large Load Diff

View File

@ -73,7 +73,7 @@ public:
virtual bool OnPlayerFoodLevelChange (cPlayer & a_Player, int a_NewFoodLevel) = 0;
virtual bool OnPlayerJoined (cPlayer & a_Player) = 0;
virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) = 0;
virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition) = 0;
virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0;
virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0;

View File

@ -835,14 +835,14 @@ bool cPluginLua::OnPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_Block
bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition)
bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition)
{
cCSLock Lock(m_CriticalSection);
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_MOVING];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
{
m_LuaState.Call((int)(**itr), &a_Player, &a_OldPosition, &a_NewPosition, cLuaState::Return, res);
m_LuaState.Call((int)(**itr), &a_Player, a_OldPosition, a_NewPosition, cLuaState::Return, res);
if (res)
{
return true;

View File

@ -98,8 +98,8 @@ public:
virtual bool OnPlayerFishing (cPlayer & a_Player, cItems & a_Reward) override;
virtual bool OnPlayerFoodLevelChange (cPlayer & a_Player, int a_NewFoodLevel) override;
virtual bool OnPlayerJoined (cPlayer & a_Player) override;
virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition) override;
virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) override;
virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) override;
virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override;

View File

@ -54,9 +54,9 @@ local Combinations =
{9, 2},
-- Special combinations:
{5, 5},
{7, 3},
{8, 3},
{5, 5},
{9, 5},
}
@ -183,6 +183,33 @@ for _, combination in ipairs(Combinations) do
WriteOverload(f, combination[1], combination[2])
end
-- Generate the cLuaState::GetStackValues() multi-param templates:
for i = 2, 6 do
f:write("/** Reads ", i, " consecutive values off the stack */\ntemplate <\n")
-- Write the template function header:
local txt = {}
for idx = 1, i do
table.insert(txt, "\ttypename ArgT" .. idx)
end
f:write(table.concat(txt, ",\n"))
-- Write the argument declarations:
txt = {}
f:write("\n>\nvoid GetStackValues(\n\tint a_BeginPos,\n")
for idx = 1, i do
table.insert(txt, "\tArgT" .. idx .. " & Arg" .. idx)
end
f:write(table.concat(txt, ",\n"))
-- Write the function body:
f:write("\n)\n{\n")
for idx = 1, i do
f:write("\tGetStackValue(a_BeginPos + ", idx - 1, ", Arg", idx, ");\n")
end
f:write("}\n\n\n\n\n\n")
end
-- Close the generated file
f:close()

View File

@ -28,7 +28,7 @@ typedef void (CombinatorFunc)(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLE
// This wild construct allows us to pass a function argument and still have it inlined by the compiler :)
/// Merges two blocktypes and blockmetas of the specified sizes and offsets using the specified combinator function
template<bool MetasValid, CombinatorFunc Combinator>
template <bool MetasValid, CombinatorFunc Combinator>
void InternalMergeBlocks(
BLOCKTYPE * a_DstTypes, const BLOCKTYPE * a_SrcTypes,
NIBBLETYPE * a_DstMetas, const NIBBLETYPE * a_SrcMetas,
@ -74,7 +74,7 @@ void InternalMergeBlocks(
/// Combinator used for cBlockArea::msOverwrite merging
template<bool MetaValid>
template <bool MetaValid>
void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
a_DstType = a_SrcType;
@ -89,7 +89,7 @@ void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLE
/// Combinator used for cBlockArea::msFillAir merging
template<bool MetaValid>
template <bool MetaValid>
void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
if (a_DstType == E_BLOCK_AIR)
@ -108,7 +108,7 @@ void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETY
/// Combinator used for cBlockArea::msImprint merging
template<bool MetaValid>
template <bool MetaValid>
void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
if (a_SrcType != E_BLOCK_AIR)
@ -127,7 +127,7 @@ void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETY
/// Combinator used for cBlockArea::msLake merging
template<bool MetaValid>
template <bool MetaValid>
void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
// Sponge is the NOP block
@ -201,7 +201,7 @@ void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE
/** Combinator used for cBlockArea::msSpongePrint merging */
template<bool MetaValid>
template <bool MetaValid>
void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
// Sponge overwrites nothing, everything else overwrites anything
@ -220,7 +220,7 @@ void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBB
/** Combinator used for cBlockArea::msDifference merging */
template<bool MetaValid>
template <bool MetaValid>
void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
if ((a_DstType == a_SrcType) && (!MetaValid || (a_DstMeta == a_SrcMeta)))
@ -246,7 +246,7 @@ void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBL
/** Combinator used for cBlockArea::msMask merging */
template<bool MetaValid>
template <bool MetaValid>
void MergeCombinatorMask(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
// If the blocks are the same, keep the dest; otherwise replace with air
@ -1764,7 +1764,9 @@ NIBBLETYPE cBlockArea::GetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBL
cBlockArea::cChunkReader::cChunkReader(cBlockArea & a_Area) :
m_Area(a_Area),
m_Origin(a_Area.m_Origin.x, a_Area.m_Origin.y, a_Area.m_Origin.z)
m_Origin(a_Area.m_Origin.x, a_Area.m_Origin.y, a_Area.m_Origin.z),
m_CurrentChunkX(0),
m_CurrentChunkZ(0)
{
}
@ -2119,7 +2121,7 @@ void cBlockArea::RelSetData(
template<bool MetasValid>
template <bool MetasValid>
void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas)
{
// Block types are compulsory, block metas are voluntary

View File

@ -362,7 +362,7 @@ protected:
NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
);
template<bool MetasValid>
template <bool MetasValid>
void MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas);
// tolua_begin
} ;

View File

@ -40,14 +40,15 @@ public:
) override
{
a_BlockType = m_BlockType;
NIBBLETYPE HighBits = a_BlockMeta & 0x0c; // Only highest two bits are preserved
NIBBLETYPE Meta = (NIBBLETYPE)a_Player->GetEquippedItem().m_ItemDamage;
int Direction = (int)floor(a_Player->GetYaw() * 4.0 / 360.0 + 1.5) & 0x3;
switch (Direction)
{
case 0: a_BlockMeta = 0x2 | HighBits; break;
case 1: a_BlockMeta = 0x3 | HighBits; break;
case 2: a_BlockMeta = 0x0 | HighBits; break;
case 3: a_BlockMeta = 0x1 | HighBits; break;
case 0: a_BlockMeta = 0x2 | Meta << 2; break;
case 1: a_BlockMeta = 0x3 | Meta << 2; break;
case 2: a_BlockMeta = 0x0 | Meta << 2; break;
case 3: a_BlockMeta = 0x1 | Meta << 2; break;
default:
{
return false;

View File

@ -7,7 +7,7 @@
// For example to use in class Foo which should inherit Bar use
// class Foo : public cClearMetaOnDrop<Bar>;
template<class Base>
template <class Base>
class cClearMetaOnDrop : public Base
{
public:

View File

@ -20,7 +20,7 @@ Usage:
Inherit from this class providing your base class as Base, the BitMask for the direction bits in bitmask and the masked value for the directions in North, East, South, West. There is also an aptional parameter AssertIfNotMatched. Set this if it is invalid for a block to exist in any other state.
*/
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched = false>
template <class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched = false>
class cMetaRotator : public Base
{
public:
@ -41,7 +41,7 @@ public:
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
template <class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaRotateCW(NIBBLETYPE a_Meta)
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
@ -63,7 +63,7 @@ NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatc
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
template <class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaRotateCCW(NIBBLETYPE a_Meta)
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
@ -85,7 +85,7 @@ NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatc
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
template <class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaMirrorXY(NIBBLETYPE a_Meta)
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
@ -102,7 +102,7 @@ NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatc
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
template <class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaMirrorYZ(NIBBLETYPE a_Meta)
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);

View File

@ -27,7 +27,7 @@
)
#define IS_LITTLE_ENDIAN
#elif ( \
defined (__ARMEB__) || defined(__sparc) \
defined (__ARMEB__) || defined(__sparc) || defined(__powerpc__) || defined(__POWERPC__) \
)
#define IS_BIG_ENDIAN
#else

View File

@ -34,8 +34,6 @@ SET (SRCS
FastRandom.cpp
FurnaceRecipe.cpp
Globals.cpp
Group.cpp
GroupManager.cpp
Inventory.cpp
Item.cpp
ItemGrid.cpp
@ -53,6 +51,7 @@ SET (SRCS
MonsterConfig.cpp
Noise.cpp
ProbabDistrib.cpp
RankManager.cpp
RCONServer.cpp
Root.cpp
Scoreboard.cpp
@ -98,8 +97,6 @@ SET (HDRS
ForEachChunkProvider.h
FurnaceRecipe.h
Globals.h
Group.h
GroupManager.h
Inventory.h
Item.h
ItemGrid.h
@ -122,6 +119,7 @@ SET (HDRS
MonsterConfig.h
Noise.h
ProbabDistrib.h
RankManager.h
RCONServer.h
Root.h
Scoreboard.h

View File

@ -108,7 +108,7 @@ local g_ViolationPatterns =
-- Check that all commas have spaces after them and not in front of them:
{" ,", "Extra space before a \",\""},
{",[^%s\"%%]", "Needs a space after a \",\""}, -- Report all except >> "," << needed for splitting and >>,%s<< needed for formatting
{",[^%s\"%%\']", "Needs a space after a \",\""}, -- Report all except >> "," << needed for splitting and >>,%s<< needed for formatting
-- Check that opening braces are not at the end of a code line:
{"[^%s].-{\n?$", "Brace should be on a separate line"},
@ -119,6 +119,7 @@ local g_ViolationPatterns =
{"while%(", "Needs a space after \"while\""},
{"switch%(", "Needs a space after \"switch\""},
{"catch%(", "Needs a space after \"catch\""},
{"template<", "Needs a space after \"template\""},
-- No space after keyword's parenthesis:
{"[^%a#]if %( ", "Remove the space after \"(\""},

View File

@ -296,6 +296,16 @@ void cChunk::SetAllData(cSetChunkData & a_SetChunkData)
}
m_BlockEntities.clear();
std::swap(a_SetChunkData.GetBlockEntities(), m_BlockEntities);
// Check that all block entities have a valid blocktype at their respective coords (DEBUG-mode only):
#ifdef _DEBUG
for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
{
BLOCKTYPE EntityBlockType = (*itr)->GetBlockType();
BLOCKTYPE WorldBlockType = GetBlock((*itr)->GetRelX(), (*itr)->GetPosY(), (*itr)->GetRelZ());
ASSERT(EntityBlockType == WorldBlockType);
} // for itr - m_BlockEntities
#endif // _DEBUG
// Set all block entities' World variable:
for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)

View File

@ -155,7 +155,7 @@ public:
void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, bool a_SendToClients = true); // Doesn't force block updates on neighbors, use for simple changes such as grass growing etc.
BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const;
BLOCKTYPE GetBlock(Vector3i a_cords) const { return GetBlock(a_cords.x, a_cords.y, a_cords.z);}
BLOCKTYPE GetBlock(const Vector3i & a_RelCoords) const { return GetBlock(a_RelCoords.x, a_RelCoords.y, a_RelCoords.z); }
void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
void GetBlockInfo (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight);

View File

@ -419,7 +419,7 @@ public:
X Data;
cCoordWithData(int a_X, int a_Y, int a_Z) :
x(a_X), y(a_Y), z(a_Z)
x(a_X), y(a_Y), z(a_Z), Data()
{
}

View File

@ -75,11 +75,21 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) :
m_TimeSinceLastPacket(0),
m_Ping(1000),
m_PingID(1),
m_PingStartTime(0),
m_LastPingTime(1000),
m_BlockDigAnimStage(-1),
m_BlockDigAnimSpeed(0),
m_BlockDigAnimX(0),
m_BlockDigAnimY(256), // Invalid Y, so that the coords don't get picked up
m_BlockDigAnimZ(0),
m_HasStartedDigging(false),
m_LastDigBlockX(0),
m_LastDigBlockY(256), // Invalid Y, so that the coords don't get picked up
m_LastDigBlockZ(0),
m_State(csConnected),
m_ShouldCheckDownloaded(false),
m_NumExplosionsThisTick(0),
m_NumBlockChangeInteractionsThisTick(0),
m_UniqueID(0),
m_HasSentPlayerChunk(false),
m_Locale("en_GB")
@ -912,11 +922,16 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
return;
}
// Check for clickthrough-blocks:
/* When the user breaks a fire block, the client send the wrong block location.
We must find the right block with the face direction. */
if (a_BlockFace != BLOCK_FACE_NONE)
if ((a_Status == DIG_STATUS_STARTED) || (a_Status == DIG_STATUS_FINISHED))
{
if (a_BlockFace == BLOCK_FACE_NONE)
{
return;
}
/* Check for clickthrough-blocks:
When the user breaks a fire block, the client send the wrong block location.
We must find the right block with the face direction. */
int BlockX = a_BlockX;
int BlockY = a_BlockY;
int BlockZ = a_BlockZ;
@ -927,17 +942,16 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
a_BlockY = BlockY;
a_BlockZ = BlockZ;
}
}
if (
((a_Status == DIG_STATUS_STARTED) || (a_Status == DIG_STATUS_FINISHED)) && // Only do a radius check for block destruction - things like pickup tossing send coordinates that are to be ignored
((Diff(m_Player->GetPosX(), (double)a_BlockX) > 6) ||
(Diff(m_Player->GetPosY(), (double)a_BlockY) > 6) ||
(Diff(m_Player->GetPosZ(), (double)a_BlockZ) > 6))
)
{
m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
return;
if (
((Diff(m_Player->GetPosX(), (double)a_BlockX) > 6) ||
(Diff(m_Player->GetPosY(), (double)a_BlockY) > 6) ||
(Diff(m_Player->GetPosZ(), (double)a_BlockZ) > 6))
)
{
m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
return;
}
}
cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
@ -1045,7 +1059,8 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc
if (
m_Player->IsGameModeCreative() &&
ItemCategory::IsSword(m_Player->GetInventory().GetEquippedItem().m_ItemType)
ItemCategory::IsSword(m_Player->GetInventory().GetEquippedItem().m_ItemType) &&
(m_Player->GetWorld()->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_FIRE)
)
{
// Players can't destroy blocks with a Sword in the hand.

View File

@ -1,4 +1,4 @@

// CraftingRecipes.cpp
// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes
@ -83,7 +83,7 @@ cItem & cCraftingGrid::GetItem(int x, int y) const
void cCraftingGrid::SetItem(int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth)
void cCraftingGrid::SetItem(int x, int y, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth)
{
// Accessible through scripting, must verify parameters:
if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height))
@ -228,7 +228,7 @@ void cCraftingRecipe::Clear(void)
void cCraftingRecipe::SetResult(ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth)
void cCraftingRecipe::SetResult(ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth)
{
m_Result = cItem(a_ItemType, a_ItemCount, a_ItemHealth);
}
@ -324,7 +324,11 @@ void cCraftingRecipes::LoadRecipes(void)
return;
}
AString Everything;
f.ReadRestOfFile(Everything);
if (!f.ReadRestOfFile(Everything))
{
LOGWARNING("Cannot read file \"crafting.txt\", no crafting recipes will be available!");
return;
}
f.Close();
// Split it into lines, then process each line as a single recipe:
@ -362,7 +366,10 @@ void cCraftingRecipes::ClearRecipes(void)
void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine)
{
AStringVector Sides = StringSplit(a_RecipeLine, "=");
AString RecipeLine(a_RecipeLine);
RecipeLine.erase(std::remove(RecipeLine.begin(), RecipeLine.end(), ' '), RecipeLine.end());
AStringVector Sides = StringSplit(RecipeLine, "=");
if (Sides.size() != 2)
{
LOGWARNING("crafting.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1);
@ -388,8 +395,7 @@ void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine
}
if (ResultSplit.size() > 1)
{
Recipe->m_Result.m_ItemCount = atoi(ResultSplit[1].c_str());
if (Recipe->m_Result.m_ItemCount == 0)
if (!StringToInteger<char>(ResultSplit[1].c_str(), Recipe->m_Result.m_ItemCount))
{
LOGWARNING("crafting.txt: line %d: Cannot parse result count, ignoring the recipe.", a_LineNum);
LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str());
@ -441,8 +447,7 @@ bool cCraftingRecipes::ParseItem(const AString & a_String, cItem & a_Item)
if (Split.size() > 1)
{
AString Damage = TrimString(Split[1]);
a_Item.m_ItemDamage = atoi(Damage.c_str());
if ((a_Item.m_ItemDamage == 0) && (Damage.compare("0") != 0))
if (!StringToInteger<short>(Damage.c_str(), a_Item.m_ItemDamage))
{
// Parsing the number failed
return false;

View File

@ -33,7 +33,7 @@ public:
int GetWidth (void) const {return m_Width; }
int GetHeight(void) const {return m_Height; }
cItem & GetItem (int x, int y) const;
void SetItem (int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth);
void SetItem (int x, int y, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth);
void SetItem (int x, int y, const cItem & a_Item);
void Clear (void);
@ -72,13 +72,13 @@ public:
int GetIngredientsHeight(void) const {return m_Ingredients.GetHeight(); }
cItem & GetIngredient (int x, int y) const {return m_Ingredients.GetItem(x, y); }
const cItem & GetResult (void) const {return m_Result; }
void SetResult (ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth);
void SetResult (ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth);
void SetResult (const cItem & a_Item)
{
m_Result = a_Item;
}
void SetIngredient (int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth)
void SetIngredient (int x, int y, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth)
{
m_Ingredients.SetItem(x, y, a_ItemType, a_ItemCount, a_ItemHealth);
}

View File

@ -21,7 +21,8 @@ const int CYCLE_MILLISECONDS = 100;
cDeadlockDetect::cDeadlockDetect(void) :
super("DeadlockDetect")
super("DeadlockDetect"),
m_IntervalSec(1000)
{
}
@ -136,6 +137,7 @@ void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age)
void cDeadlockDetect::DeadlockDetected(void)
{
LOGERROR("Deadlock detected, aborting the server");
ASSERT(!"Deadlock detected");
abort();
}

View File

@ -528,7 +528,7 @@ inline float GetSpecialSignf( float a_Val)
template<class T> inline T Diff(T a_Val1, T a_Val2)
template <class T> inline T Diff(T a_Val1, T a_Val2)
{
return std::abs(a_Val1 - a_Val2);
}

View File

@ -871,11 +871,101 @@ bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
return true;
}
case E_META_RAIL_CURVED_ZM_XM:
case E_META_RAIL_CURVED_ZM_XP:
case E_META_RAIL_CURVED_ZP_XM:
case E_META_RAIL_CURVED_ZP_XP:
{
// TODO - simply can't be bothered right now
Vector3d Distance = MinecartCollisionCallback.GetCollidedEntityPosition() - Vector3d(GetPosX(), 0, GetPosZ());
// Prevent division by small numbers
if (abs(Distance.z) < 0.001)
{
Distance.z = 0.001;
}
/* Check to which side the minecart is to be pushed.
Let's consider a z-x-coordinate system where the minecart is the center (0/0).
The minecart moves along the line x = -z, the perpendicular line to this is x = z.
In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */
if (
((Distance.z > 0) && ((Distance.x / Distance.z) >= 1)) ||
((Distance.z < 0) && ((Distance.x / Distance.z) <= 1))
)
{
// Moving -X +Z
if ((-GetSpeedX() * 0.4 / sqrt(2)) < 0.01)
{
// ~ SpeedX >= 0 Immobile or not moving in the "right" direction. Give it a bump!
AddSpeedX(-4 / sqrt(2));
AddSpeedZ(4 / sqrt(2));
}
else
{
// ~ SpeedX < 0 Moving in the "right" direction. Only accelerate it a bit.
SetSpeedX(GetSpeedX() * 0.4 / sqrt(2));
SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2));
}
}
else if ((GetSpeedX() * 0.4 / sqrt(2)) < 0.01)
{
// Moving +X -Z
// ~ SpeedX <= 0 Immobile or not moving in the "right" direction
AddSpeedX(4 / sqrt(2));
AddSpeedZ(-4 / sqrt(2));
}
else
{
// ~ SpeedX > 0 Moving in the "right" direction
SetSpeedX(GetSpeedX() * 0.4 / sqrt(2));
SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2));
}
break;
}
case E_META_RAIL_CURVED_ZM_XP:
case E_META_RAIL_CURVED_ZP_XM:
{
Vector3d Distance = MinecartCollisionCallback.GetCollidedEntityPosition() - Vector3d(GetPosX(), 0, GetPosZ());
// Prevent division by small numbers
if (abs(Distance.z) < 0.001)
{
Distance.z = 0.001;
}
/* Check to which side the minecart is to be pushed.
Let's consider a z-x-coordinate system where the minecart is the center (0/0).
The minecart moves along the line x = z, the perpendicular line to this is x = -z.
In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */
if (
((Distance.z > 0) && ((Distance.x / Distance.z) <= -1)) ||
((Distance.z < 0) && ((Distance.x / Distance.z) >= -1))
)
{
// Moving +X +Z
if ((GetSpeedX() * 0.4) < 0.01)
{
// ~ SpeedX <= 0 Immobile or not moving in the "right" direction
AddSpeedX(4 / sqrt(2));
AddSpeedZ(4 / sqrt(2));
}
else
{
// ~ SpeedX > 0 Moving in the "right" direction
SetSpeedX(GetSpeedX() * 0.4 / sqrt(2));
SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2));
}
}
else if ((-GetSpeedX() * 0.4) < 0.01)
{
// Moving -X -Z
// ~ SpeedX >= 0 Immobile or not moving in the "right" direction
AddSpeedX(-4 / sqrt(2));
AddSpeedZ(-4 / sqrt(2));
}
else
{
// ~ SpeedX < 0 Moving in the "right" direction
SetSpeedX(GetSpeedX() * 0.4 / sqrt(2));
SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2));
}
break;
}
default: break;

View File

@ -10,8 +10,6 @@
#include "../Bindings/PluginManager.h"
#include "../BlockEntities/BlockEntity.h"
#include "../BlockEntities/EnderChestEntity.h"
#include "../GroupManager.h"
#include "../Group.h"
#include "../Root.h"
#include "../OSSupport/Timer.h"
#include "../Chunk.h"
@ -59,7 +57,6 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) :
m_EnderChestContents(9, 3),
m_CurrentWindow(NULL),
m_InventoryWindow(NULL),
m_Color('-'),
m_GameMode(eGameMode_NotSet),
m_IP(""),
m_ClientHandle(a_Client),
@ -1367,48 +1364,6 @@ void cPlayer::SetVisible(bool a_bVisible)
void cPlayer::AddToGroup( const AString & a_GroupName)
{
cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName);
m_Groups.push_back( Group);
LOGD("Added %s to group %s", GetName().c_str(), a_GroupName.c_str());
ResolveGroups();
ResolvePermissions();
}
void cPlayer::RemoveFromGroup( const AString & a_GroupName)
{
bool bRemoved = false;
for (GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr)
{
if ((*itr)->GetName().compare(a_GroupName) == 0)
{
m_Groups.erase( itr);
bRemoved = true;
break;
}
}
if (bRemoved)
{
LOGD("Removed %s from group %s", GetName().c_str(), a_GroupName.c_str());
ResolveGroups();
ResolvePermissions();
}
else
{
LOGWARN("Tried to remove %s from group %s but was not in that group", GetName().c_str(), a_GroupName.c_str());
}
}
bool cPlayer::HasPermission(const AString & a_Permission)
{
if (a_Permission.empty())
@ -1417,47 +1372,18 @@ bool cPlayer::HasPermission(const AString & a_Permission)
return true;
}
AStringVector Split = StringSplit( a_Permission, ".");
PermissionMap Possibilities = m_ResolvedPermissions;
// Now search the namespaces
while (Possibilities.begin() != Possibilities.end())
AStringVector Split = StringSplit(a_Permission, ".");
// Iterate over all granted permissions; if any matches, then return success:
for (AStringVectorVector::const_iterator itr = m_SplitPermissions.begin(), end = m_SplitPermissions.end(); itr != end; ++itr)
{
PermissionMap::iterator itr = Possibilities.begin();
if (itr->second)
if (PermissionMatches(Split, *itr))
{
AStringVector OtherSplit = StringSplit( itr->first, ".");
if (OtherSplit.size() <= Split.size())
{
unsigned int i;
for (i = 0; i < OtherSplit.size(); ++i)
{
if (OtherSplit[i].compare( Split[i]) != 0)
{
if (OtherSplit[i].compare("*") == 0) return true; // WildCard man!! WildCard!
break;
}
}
if (i == Split.size()) return true;
}
}
Possibilities.erase( itr);
}
// Nothing that matched :(
return false;
}
bool cPlayer::IsInGroup( const AString & a_Group)
{
for (GroupList::iterator itr = m_ResolvedGroups.begin(); itr != m_ResolvedGroups.end(); ++itr)
{
if (a_Group.compare( (*itr)->GetName().c_str()) == 0)
return true;
}
}
} // for itr - m_SplitPermissions[]
// No granted permission matches
return false;
}
@ -1465,68 +1391,35 @@ bool cPlayer::IsInGroup( const AString & a_Group)
void cPlayer::ResolvePermissions()
bool cPlayer::PermissionMatches(const AStringVector & a_Permission, const AStringVector & a_Template)
{
m_ResolvedPermissions.clear(); // Start with an empty map
// Copy all player specific permissions into the resolved permissions map
for (PermissionMap::iterator itr = m_Permissions.begin(); itr != m_Permissions.end(); ++itr)
// Check the sub-items if they are the same or there's a wildcard:
size_t lenP = a_Permission.size();
size_t lenT = a_Template.size();
size_t minLen = std::min(lenP, lenT);
for (size_t i = 0; i < minLen; i++)
{
m_ResolvedPermissions[ itr->first ] = itr->second;
}
for (GroupList::iterator GroupItr = m_ResolvedGroups.begin(); GroupItr != m_ResolvedGroups.end(); ++GroupItr)
{
const cGroup::PermissionMap & Permissions = (*GroupItr)->GetPermissions();
for (cGroup::PermissionMap::const_iterator itr = Permissions.begin(); itr != Permissions.end(); ++itr)
if (a_Template[i] == "*")
{
m_ResolvedPermissions[ itr->first ] = itr->second;
// Has matched so far and now there's a wildcard in the template, so the permission matches:
return true;
}
if (a_Permission[i] != a_Template[i])
{
// Found a mismatch
return false;
}
}
}
void cPlayer::ResolveGroups()
{
// Clear resolved groups first
m_ResolvedGroups.clear();
// Get a complete resolved list of all groups the player is in
std::map< cGroup*, bool > AllGroups; // Use a map, because it's faster than iterating through a list to find duplicates
GroupList ToIterate;
for (GroupList::iterator GroupItr = m_Groups.begin(); GroupItr != m_Groups.end(); ++GroupItr)
// So far all the sub-items have matched
// If the sub-item count is the same, then the permission matches:
if (lenP == lenT)
{
ToIterate.push_back( *GroupItr);
}
while (ToIterate.begin() != ToIterate.end())
{
cGroup* CurrentGroup = *ToIterate.begin();
if (AllGroups.find( CurrentGroup) != AllGroups.end())
{
LOGWARNING("ERROR: Player \"%s\" is in the group multiple times (\"%s\"). Please fix your settings in users.ini!",
GetName().c_str(), CurrentGroup->GetName().c_str()
);
}
else
{
AllGroups[ CurrentGroup ] = true;
m_ResolvedGroups.push_back( CurrentGroup); // Add group to resolved list
const cGroup::GroupList & Inherits = CurrentGroup->GetInherits();
for (cGroup::GroupList::const_iterator itr = Inherits.begin(); itr != Inherits.end(); ++itr)
{
if (AllGroups.find( *itr) != AllGroups.end())
{
LOGERROR("ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!", GetName().c_str(), (*itr)->GetName().c_str());
continue;
}
ToIterate.push_back( *itr);
}
}
ToIterate.erase( ToIterate.begin());
return true;
}
// There are more sub-items in either the permission or the template, not a match:
return false;
}
@ -1535,17 +1428,14 @@ void cPlayer::ResolveGroups()
AString cPlayer::GetColor(void) const
{
if (m_Color != '-')
if (m_MsgNameColorCode.empty() || (m_MsgNameColorCode == "-"))
{
return cChatColor::Delimiter + m_Color;
// Color has not been assigned, return an empty string:
return AString();
}
if (m_Groups.size() < 1)
{
return cChatColor::White;
}
return (*m_Groups.begin())->GetColor();
// Return the color, including the delimiter:
return cChatColor::Delimiter + m_MsgNameColorCode;
}
@ -1661,48 +1551,9 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
void cPlayer::LoadPermissionsFromDisk()
{
m_Groups.clear();
m_Permissions.clear();
cIniFile IniFile;
if (IniFile.ReadFile("users.ini"))
{
AString Groups = IniFile.GetValueSet(GetName(), "Groups", "Default");
AStringVector Split = StringSplitAndTrim(Groups, ",");
for (AStringVector::const_iterator itr = Split.begin(), end = Split.end(); itr != end; ++itr)
{
if (!cRoot::Get()->GetGroupManager()->ExistsGroup(*itr))
{
LOGWARNING("The group %s for player %s was not found!", itr->c_str(), GetName().c_str());
}
AddToGroup(*itr);
}
AString Color = IniFile.GetValue(GetName(), "Color", "-");
if (!Color.empty())
{
m_Color = Color[0];
}
}
else
{
cGroupManager::GenerateDefaultUsersIni(IniFile);
IniFile.AddValue("Groups", GetName(), "Default");
AddToGroup("Default");
}
IniFile.WriteFile("users.ini");
ResolvePermissions();
}
bool cPlayer::LoadFromDisk(cWorldPtr & a_World)
{
LoadPermissionsFromDisk();
LoadRank();
// Load from the UUID file:
if (LoadFromFile(GetUUIDFileName(m_UUID), a_World))
@ -1937,26 +1788,6 @@ bool cPlayer::SaveToDisk()
cPlayer::StringList cPlayer::GetResolvedPermissions()
{
StringList Permissions;
const PermissionMap& ResolvedPermissions = m_ResolvedPermissions;
for (PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr)
{
if (itr->second)
{
Permissions.push_back( itr->first);
}
}
return Permissions;
}
void cPlayer::UseEquippedItem(int a_Amount)
{
if (IsGameModeCreative()) // No damage in creative
@ -2215,6 +2046,31 @@ void cPlayer::ApplyFoodExhaustionFromMovement()
void cPlayer::LoadRank(void)
{
// Load the values from cRankManager:
cRankManager & RankMgr = cRoot::Get()->GetRankManager();
m_Rank = RankMgr.GetPlayerRankName(m_UUID);
if (m_Rank.empty())
{
m_Rank = RankMgr.GetDefaultRank();
}
m_Permissions = RankMgr.GetPlayerPermissions(m_UUID);
RankMgr.GetRankVisuals(m_Rank, m_MsgPrefix, m_MsgSuffix, m_MsgNameColorCode);
// Break up the individual permissions on each dot, into m_SplitPermissions:
m_SplitPermissions.clear();
m_SplitPermissions.reserve(m_Permissions.size());
for (AStringVector::const_iterator itr = m_Permissions.begin(), end = m_Permissions.end(); itr != end; ++itr)
{
m_SplitPermissions.push_back(StringSplit(*itr, "."));
} // for itr - m_Permissions[]
}
void cPlayer::Detach()
{
super::Detach();

View File

@ -13,7 +13,6 @@
class cGroup;
class cWindow;
class cClientHandle;
class cTeam;
@ -236,24 +235,20 @@ public:
// tolua_end
typedef std::list< cGroup* > GroupList;
typedef std::list< std::string > StringList;
bool HasPermission(const AString & a_Permission); // tolua_export
/** Adds a player to existing group or creates a new group when it doesn't exist */
void AddToGroup( const AString & a_GroupName); // tolua_export
/** Removes a player from the group, resolves permissions and group inheritance (case sensitive) */
void RemoveFromGroup( const AString & a_GroupName); // tolua_export
bool HasPermission( const AString & a_Permission); // tolua_export
const GroupList & GetGroups() { return m_Groups; } // >> EXPORTED IN MANUALBINDINGS <<
StringList GetResolvedPermissions(); // >> EXPORTED IN MANUALBINDINGS <<
bool IsInGroup( const AString & a_Group); // tolua_export
/** Returns true iff a_Permission matches the a_Template.
A match is defined by either being exactly the same, or each sub-item matches until there's a wildcard in a_Template.
Ie. {"a", "b", "c"} matches {"a", "b", "*"} but doesn't match {"a", "b"} */
static bool PermissionMatches(const AStringVector & a_Permission, const AStringVector & a_Template); // Exported in ManualBindings with AString params
/** Returns all the permissions that the player has assigned to them. */
const AStringVector & GetPermissions(void) { return m_Permissions; } // Exported in ManualBindings.cpp
// tolua_begin
/** Returns the full color code to use for this player, based on their primary group or set in m_Color.
The returned value includes the cChatColor::Delimiter. */
/** Returns the full color code to use for this player, based on their rank.
The returned value either is empty, or includes the cChatColor::Delimiter. */
AString GetColor(void) const;
/** tosses the item in the selected hotbar slot */
@ -347,8 +342,6 @@ public:
*/
bool LoadFromFile(const AString & a_FileName, cWorldPtr & a_World);
void LoadPermissionsFromDisk(void); // tolua_export
const AString & GetLoadedWorldName() { return m_LoadedWorldName; }
void UseEquippedItem(int a_Amount = 1);
@ -422,6 +415,11 @@ public:
/** Returns the UUID (short format) that has been read from the client, or empty string if not available. */
const AString & GetUUID(void) const { return m_UUID; }
/** (Re)loads the rank and permissions from the cRankManager.
Expects the m_UUID member to be valid.
Loads the m_Rank, m_Permissions, m_MsgPrefix, m_MsgSuffix and m_MsgNameColorCode members. */
void LoadRank(void);
// tolua_end
// cEntity overrides:
@ -432,12 +430,22 @@ public:
virtual void Detach(void);
protected:
typedef std::map< std::string, bool > PermissionMap;
PermissionMap m_ResolvedPermissions;
PermissionMap m_Permissions;
GroupList m_ResolvedGroups;
GroupList m_Groups;
typedef std::vector<std::vector<AString> > AStringVectorVector;
/** The name of the rank assigned to this player. */
AString m_Rank;
/** All the permissions that this player has, based on their rank. */
AStringVector m_Permissions;
/** All the permissions that this player has, based on their rank, split into individual dot-delimited parts.
This is used mainly by the HasPermission() function to optimize the lookup. */
AStringVectorVector m_SplitPermissions;
// Message visuals:
AString m_MsgPrefix, m_MsgSuffix;
AString m_MsgNameColorCode;
AString m_PlayerName;
AString m_LoadedWorldName;
@ -482,8 +490,6 @@ protected:
/** The player's last saved bed position */
Vector3i m_LastBedPos;
char m_Color;
eGameMode m_GameMode;
AString m_IP;

View File

@ -45,7 +45,7 @@ public:
float NextFloat(float a_Range, int a_Salt);
/** Returns a random float between 0 and 1. */
float NextFloat(void) { return NextFloat(1); };
float NextFloat(void) { return NextFloat(1); }
/** Returns a random int in the range [a_Begin .. a_End] */
int GenerateRandomInteger(int a_Begin, int a_End);

View File

@ -12,6 +12,7 @@ SET (SRCS
CompoGen.cpp
ComposableGenerator.cpp
DistortedHeightmap.cpp
DungeonRoomsFinisher.cpp
EndGen.cpp
FinishGen.cpp
GridStructGen.cpp
@ -40,6 +41,7 @@ SET (HDRS
CompoGen.h
ComposableGenerator.h
DistortedHeightmap.h
DungeonRoomsFinisher.h
EndGen.h
FinishGen.h
GridStructGen.h

View File

@ -166,6 +166,9 @@ cCaveTunnel::cCaveTunnel(
if ((a_BlockStartY <= 0) && (a_BlockEndY <= 0))
{
// Don't bother detailing this cave, it's under the world anyway
m_MinBlockX = m_MaxBlockX = 0;
m_MinBlockY = m_MaxBlockY = -1;
m_MinBlockZ = m_MaxBlockZ = 0;
return;
}

View File

@ -27,6 +27,7 @@ const unsigned int QUEUE_SKIP_LIMIT = 500;
cChunkGenerator::cChunkGenerator(void) :
super("cChunkGenerator"),
m_Seed(0), // Will be overwritten by the actual generator
m_Generator(NULL),
m_PluginInterface(NULL),
m_ChunkSink(NULL)

View File

@ -19,6 +19,7 @@
#include "Caves.h"
#include "DistortedHeightmap.h"
#include "DungeonRoomsFinisher.h"
#include "EndGen.h"
#include "MineShafts.h"
#include "NetherFortGen.h"
@ -343,6 +344,14 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
float Threshold = (float)a_IniFile.GetValueSetF("Generator", "DualRidgeCavesThreshold", 0.3);
m_FinishGens.push_back(new cStructGenDualRidgeCaves(Seed, Threshold));
}
else if (NoCaseCompare(*itr, "DungeonRooms") == 0)
{
int GridSize = a_IniFile.GetValueSetI("Generator", "DungeonRoomsGridSize", 48);
int MaxSize = a_IniFile.GetValueSetI("Generator", "DungeonRoomsMaxSize", 7);
int MinSize = a_IniFile.GetValueSetI("Generator", "DungeonRoomsMinSize", 5);
AString HeightDistrib = a_IniFile.GetValueSet ("Generator", "DungeonRoomsHeightDistrib", "0, 0; 10, 10; 11, 500; 40, 500; 60, 40; 90, 1");
m_FinishGens.push_back(new cDungeonRoomsFinisher(*m_HeightGen, Seed, GridSize, MaxSize, MinSize, HeightDistrib));
}
else if (NoCaseCompare(*itr, "Ice") == 0)
{
m_FinishGens.push_back(new cFinishGenIce);

View File

@ -0,0 +1,279 @@
// DungeonRoomsFinisher.cpp
// Declares the cDungeonRoomsFinisher class representing the finisher that generates dungeon rooms
#include "Globals.h"
#include "DungeonRoomsFinisher.h"
#include "../FastRandom.h"
/** Height, in blocks, of the internal dungeon room open space. This many air blocks Y-wise. */
static const int ROOM_HEIGHT = 4;
////////////////////////////////////////////////////////////////////////////////
// cDungeonRoom:
class cDungeonRoom :
public cGridStructGen::cStructure
{
typedef cGridStructGen::cStructure super;
public:
cDungeonRoom(
int a_GridX, int a_GridZ,
int a_OriginX, int a_OriginZ,
int a_HalfSizeX, int a_HalfSizeZ,
int a_FloorHeight,
cNoise & a_Noise
) :
super(a_GridX, a_GridZ, a_OriginX, a_OriginZ),
m_StartX(a_OriginX - a_HalfSizeX),
m_EndX(a_OriginX + a_HalfSizeX),
m_StartZ(a_OriginZ - a_HalfSizeZ),
m_EndZ(a_OriginZ + a_HalfSizeZ),
m_FloorHeight(a_FloorHeight)
{
/*
Pick coords next to the wall for the chests.
This is done by indexing the possible coords, picking any one for the first chest
and then picking another position for the second chest that is not adjacent to the first pos
*/
int rnd = a_Noise.IntNoise2DInt(a_OriginX, a_OriginZ) / 7;
int SizeX = m_EndX - m_StartX - 1;
int SizeZ = m_EndZ - m_StartZ - 1;
int NumPositions = 2 * SizeX + 2 * SizeZ;
int FirstChestPos = rnd % NumPositions; // The corner positions are a bit more likely, but we don't mind
rnd = rnd / 512;
int SecondChestPos = (FirstChestPos + 2 + (rnd % (NumPositions - 3))) % NumPositions;
m_Chest1 = DecodeChestCoords(FirstChestPos, SizeX, SizeZ);
m_Chest2 = DecodeChestCoords(SecondChestPos, SizeX, SizeZ);
}
protected:
// The X range of the room, start inclusive, end exclusive:
int m_StartX, m_EndX;
// The Z range of the room, start inclusive, end exclusive:
int m_StartZ, m_EndZ;
/** The Y coord of the floor of the room */
int m_FloorHeight;
/** The (absolute) coords of the first chest. The Y coord represents the chest's Meta value (facing). */
Vector3i m_Chest1;
/** The (absolute) coords of the second chest. The Y coord represents the chest's Meta value (facing). */
Vector3i m_Chest2;
/** Decodes the position index along the room walls into a proper 2D position for a chest. */
Vector3i DecodeChestCoords(int a_PosIdx, int a_SizeX, int a_SizeZ)
{
if (a_PosIdx < a_SizeX)
{
// Return a coord on the ZM side of the room:
return Vector3i(m_StartX + a_PosIdx + 1, E_META_CHEST_FACING_ZP, m_StartZ + 1);
}
a_PosIdx -= a_SizeX;
if (a_PosIdx < a_SizeZ)
{
// Return a coord on the XP side of the room:
return Vector3i(m_EndX - 1, E_META_CHEST_FACING_XM, m_StartZ + a_PosIdx + 1);
}
a_PosIdx -= a_SizeZ;
if (a_PosIdx < a_SizeX)
{
// Return a coord on the ZP side of the room:
return Vector3i(m_StartX + a_PosIdx + 1, E_META_CHEST_FACING_ZM, m_StartZ + 1);
}
a_PosIdx -= a_SizeX;
// Return a coord on the XM side of the room:
return Vector3i(m_StartX + 1, E_META_CHEST_FACING_XP, m_StartZ + a_PosIdx + 1);
}
/** Fills the specified area of blocks in the chunk with the specified blocktype if they are one of the overwritten block types.
The coords are absolute, start coords are inclusive, end coords are exclusive. */
void ReplaceCuboid(cChunkDesc & a_ChunkDesc, int a_StartX, int a_StartY, int a_StartZ, int a_EndX, int a_EndY, int a_EndZ, BLOCKTYPE a_DstBlockType)
{
int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
int RelStartX = Clamp(a_StartX - BlockX, 0, cChunkDef::Width - 1);
int RelStartZ = Clamp(a_StartZ - BlockZ, 0, cChunkDef::Width - 1);
int RelEndX = Clamp(a_EndX - BlockX, 0, cChunkDef::Width);
int RelEndZ = Clamp(a_EndZ - BlockZ, 0, cChunkDef::Width);
for (int y = a_StartY; y < a_EndY; y++)
{
for (int z = RelStartZ; z < RelEndZ; z++)
{
for (int x = RelStartX; x < RelEndX; x++)
{
if (cBlockInfo::CanBeTerraformed(a_ChunkDesc.GetBlockType(x, y, z)))
{
a_ChunkDesc.SetBlockType(x, y, z, a_DstBlockType);
}
} // for x
} // for z
} // for z
}
/** Fills the specified area of blocks in the chunk with a random pattern of the specified blocktypes, if they are one of the overwritten block types.
The coords are absolute, start coords are inclusive, end coords are exclusive. The first blocktype uses 75% chance, the second 25% chance. */
void ReplaceCuboidRandom(cChunkDesc & a_ChunkDesc, int a_StartX, int a_StartY, int a_StartZ, int a_EndX, int a_EndY, int a_EndZ, BLOCKTYPE a_DstBlockType1, BLOCKTYPE a_DstBlockType2)
{
int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
int RelStartX = Clamp(a_StartX - BlockX, 0, cChunkDef::Width - 1);
int RelStartZ = Clamp(a_StartZ - BlockZ, 0, cChunkDef::Width - 1);
int RelEndX = Clamp(a_EndX - BlockX, 0, cChunkDef::Width);
int RelEndZ = Clamp(a_EndZ - BlockZ, 0, cChunkDef::Width);
cFastRandom rnd;
for (int y = a_StartY; y < a_EndY; y++)
{
for (int z = RelStartZ; z < RelEndZ; z++)
{
for (int x = RelStartX; x < RelEndX; x++)
{
if (cBlockInfo::CanBeTerraformed(a_ChunkDesc.GetBlockType(x, y, z)))
{
BLOCKTYPE BlockType = (rnd.NextInt(101) < 75) ? a_DstBlockType1 : a_DstBlockType2;
a_ChunkDesc.SetBlockType(x, y, z, BlockType);
}
} // for x
} // for z
} // for z
}
/** Tries to place a chest at the specified (absolute) coords.
Does nothing if the coords are outside the chunk. */
void TryPlaceChest(cChunkDesc & a_ChunkDesc, const Vector3i & a_Chest)
{
int RelX = a_Chest.x - a_ChunkDesc.GetChunkX() * cChunkDef::Width;
int RelZ = a_Chest.z - a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
if (
(RelX < 0) || (RelX >= cChunkDef::Width) || // The X coord is not in this chunk
(RelZ < 0) || (RelZ >= cChunkDef::Width) // The Z coord is not in this chunk
)
{
return;
}
a_ChunkDesc.SetBlockTypeMeta(RelX, m_FloorHeight + 1, RelZ, E_BLOCK_CHEST, (NIBBLETYPE)a_Chest.y);
// TODO: Fill the chest with random loot
}
// cGridStructGen::cStructure override:
virtual void DrawIntoChunk(cChunkDesc & a_ChunkDesc) override
{
if (
(m_EndX < a_ChunkDesc.GetChunkX() * cChunkDef::Width) ||
(m_StartX >= a_ChunkDesc.GetChunkX() * cChunkDef::Width + cChunkDef::Width) ||
(m_EndZ < a_ChunkDesc.GetChunkZ() * cChunkDef::Width) ||
(m_StartZ >= a_ChunkDesc.GetChunkZ() * cChunkDef::Width + cChunkDef::Width)
)
{
// The chunk is not intersecting the room at all, bail out
return;
}
int b = m_FloorHeight + 1; // Bottom
int t = m_FloorHeight + 1 + ROOM_HEIGHT; // Top
ReplaceCuboidRandom(a_ChunkDesc, m_StartX, m_FloorHeight, m_StartZ, m_EndX + 1, b, m_EndZ + 1, E_BLOCK_MOSSY_COBBLESTONE, E_BLOCK_COBBLESTONE); // Floor
ReplaceCuboid(a_ChunkDesc, m_StartX + 1, b, m_StartZ + 1, m_EndX, t, m_EndZ, E_BLOCK_AIR); // Insides
// Walls:
ReplaceCuboid(a_ChunkDesc, m_StartX, b, m_StartZ, m_StartX + 1, t, m_EndZ, E_BLOCK_COBBLESTONE); // XM wall
ReplaceCuboid(a_ChunkDesc, m_EndX, b, m_StartZ, m_EndX + 1, t, m_EndZ, E_BLOCK_COBBLESTONE); // XP wall
ReplaceCuboid(a_ChunkDesc, m_StartX, b, m_StartZ, m_EndX + 1, t, m_StartZ + 1, E_BLOCK_COBBLESTONE); // ZM wall
ReplaceCuboid(a_ChunkDesc, m_StartX, b, m_EndZ, m_EndX + 1, t, m_EndZ + 1, E_BLOCK_COBBLESTONE); // ZP wall
// Place chests:
TryPlaceChest(a_ChunkDesc, m_Chest1);
TryPlaceChest(a_ChunkDesc, m_Chest2);
// Place the spawner:
int CenterX = (m_StartX + m_EndX) / 2 - a_ChunkDesc.GetChunkX() * cChunkDef::Width;
int CenterZ = (m_StartZ + m_EndZ) / 2 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
if (
(CenterX >= 0) && (CenterX < cChunkDef::Width) &&
(CenterZ >= 0) && (CenterZ < cChunkDef::Width)
)
{
a_ChunkDesc.SetBlockTypeMeta(CenterX, b, CenterZ, E_BLOCK_MOB_SPAWNER, 0);
// TODO: Set the spawned mob
}
}
} ;
////////////////////////////////////////////////////////////////////////////////
// cDungeonRoomsFinisher:
cDungeonRoomsFinisher::cDungeonRoomsFinisher(cTerrainHeightGen & a_HeightGen, int a_Seed, int a_GridSize, int a_MaxSize, int a_MinSize, const AString & a_HeightDistrib) :
super(a_Seed + 100, a_GridSize, a_GridSize, a_GridSize, a_GridSize, a_MaxSize, a_MaxSize, 1024),
m_HeightGen(a_HeightGen),
m_MaxHalfSize((a_MaxSize + 1) / 2),
m_MinHalfSize((a_MinSize + 1) / 2),
m_HeightProbability(cChunkDef::Height)
{
// Initialize the height probability distribution:
m_HeightProbability.SetDefString(a_HeightDistrib);
// Normalize the min and max size:
if (m_MinHalfSize > m_MaxHalfSize)
{
std::swap(m_MinHalfSize, m_MaxHalfSize);
}
}
cDungeonRoomsFinisher::cStructurePtr cDungeonRoomsFinisher::CreateStructure(int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ)
{
// Select a random room size in each direction:
int rnd = m_Noise.IntNoise2DInt(a_OriginX, a_OriginZ) / 7;
int HalfSizeX = m_MinHalfSize + (rnd % (m_MaxHalfSize - m_MinHalfSize + 1));
rnd = rnd / 32;
int HalfSizeZ = m_MinHalfSize + (rnd % (m_MaxHalfSize - m_MinHalfSize + 1));
rnd = rnd / 32;
// Select a random floor height for the room, based on the height generator:
int ChunkX, ChunkZ;
int RelX = a_OriginX, RelY = 0, RelZ = a_OriginZ;
cChunkDef::AbsoluteToRelative(RelX, RelY, RelZ, ChunkX, ChunkZ);
cChunkDef::HeightMap HeightMap;
m_HeightGen.GenHeightMap(ChunkX, ChunkZ, HeightMap);
int Height = cChunkDef::GetHeight(HeightMap, RelX, RelZ); // Max room height at {a_OriginX, a_OriginZ}
Height = Clamp(m_HeightProbability.MapValue(rnd % m_HeightProbability.GetSum()), 10, Height - 5);
// Create the dungeon room descriptor:
return cStructurePtr(new cDungeonRoom(a_GridX, a_GridZ, a_OriginX, a_OriginZ, HalfSizeX, HalfSizeZ, Height, m_Noise));
}

View File

@ -0,0 +1,52 @@
// DungeonRoomsFinisher.h
// Declares the cDungeonRoomsFinisher class representing the finisher that generates dungeon rooms
#pragma once
#include "GridStructGen.h"
#include "../ProbabDistrib.h"
class cDungeonRoomsFinisher :
public cGridStructGen
{
typedef cGridStructGen super;
public:
/** Creates a new dungeon room finisher.
a_HeightGen is the underlying height generator, so that the rooms can always be placed under the terrain.
a_MaxSize and a_MinSize are the maximum and minimum sizes of the room's internal (air) area, in blocks across.
a_HeightDistrib is the string defining the height distribution for the rooms (cProbabDistrib format). */
cDungeonRoomsFinisher(cTerrainHeightGen & a_HeightGen, int a_Seed, int a_GridSize, int a_MaxSize, int a_MinSize, const AString & a_HeightDistrib);
protected:
/** The height gen that is used for limiting the rooms' Y coords */
cTerrainHeightGen & m_HeightGen;
/** Maximum half-size (from center to wall) of the dungeon room's inner (air) area. Default is 3 (vanilla). */
int m_MaxHalfSize;
/** Minimum half-size (from center to wall) of the dungeon room's inner (air) area. Default is 2 (vanilla). */
int m_MinHalfSize;
/** The height probability distribution to make the spawners more common in layers 10 - 40, less common outside this range. */
cProbabDistrib m_HeightProbability;
// cGridStructGen overrides:
virtual cStructurePtr CreateStructure(int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ) override;
} ;

View File

@ -239,7 +239,13 @@ bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_Rel
cHeiGenClassic::cHeiGenClassic(int a_Seed) :
m_Seed(a_Seed),
m_Noise(a_Seed)
m_Noise(a_Seed),
m_HeightFreq1(1.0f),
m_HeightAmp1(1.0f),
m_HeightFreq2(0.5f),
m_HeightAmp2(0.5f),
m_HeightFreq3(0.1f),
m_HeightAmp3(0.1f)
{
}

View File

@ -1,41 +0,0 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Group.h"
void cGroup::AddCommand( const AString & a_Command)
{
m_Commands[ a_Command ] = true;
}
void cGroup::AddPermission( const AString & a_Permission)
{
m_Permissions[ a_Permission ] = true;
}
void cGroup::InheritFrom( cGroup* a_Group)
{
m_Inherits.remove( a_Group);
m_Inherits.push_back( a_Group);
}
void cGroup::ClearPermission()
{
m_Permissions.clear();
}

View File

@ -1,44 +0,0 @@
#pragma once
// tolua_begin
class cGroup
{
public:
// tolua_end
cGroup() {}
~cGroup() {}
// tolua_begin
void SetName( const AString & a_Name) { m_Name = a_Name; }
const AString & GetName() const { return m_Name; }
void SetColor( const AString & a_Color) { m_Color = a_Color; }
void AddCommand( const AString & a_Command);
void AddPermission( const AString & a_Permission);
void InheritFrom( cGroup* a_Group);
// tolua_end
typedef std::map< AString, bool > PermissionMap;
const PermissionMap & GetPermissions() const { return m_Permissions; }
void ClearPermission(void);
typedef std::map< AString, bool > CommandMap;
const CommandMap & GetCommands() const { return m_Commands; }
const AString & GetColor() const { return m_Color; } // tolua_export
typedef std::list< cGroup* > GroupList;
const GroupList & GetInherits() const { return m_Inherits; }
private:
AString m_Name;
AString m_Color;
PermissionMap m_Permissions;
CommandMap m_Commands;
GroupList m_Inherits;
}; // tolua_export

View File

@ -1,227 +0,0 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "GroupManager.h"
#include "Group.h"
#include "inifile/iniFile.h"
#include "ChatColor.h"
#include "Root.h"
typedef std::map< AString, cGroup* > GroupMap;
struct cGroupManager::sGroupManagerState
{
GroupMap Groups;
};
cGroupManager::~cGroupManager()
{
for (GroupMap::iterator itr = m_pState->Groups.begin(); itr != m_pState->Groups.end(); ++itr)
{
delete itr->second;
itr->second = NULL;
}
m_pState->Groups.clear();
delete m_pState;
m_pState = NULL;
}
cGroupManager::cGroupManager()
: m_pState( new sGroupManagerState)
{
LOGD("-- Loading Groups --");
if (!LoadGroups())
{
LOGWARNING("ERROR: Groups could not load!");
}
if (!CheckUsers())
{
LOGWARNING("ERROR: User file could not be found!");
}
LOGD("-- Groups Successfully Loaded --");
}
void cGroupManager::GenerateDefaultUsersIni(cIniFile & a_IniFile)
{
LOGWARN("Regenerating users.ini, all users will be reset");
a_IniFile.AddHeaderComment(" This file stores the players' groups.");
a_IniFile.AddHeaderComment(" The format is:");
a_IniFile.AddHeaderComment(" [PlayerName]");
a_IniFile.AddHeaderComment(" Groups = GroupName1, GroupName2, ...");
a_IniFile.WriteFile("users.ini");
}
bool cGroupManager::CheckUsers()
{
cIniFile IniFile;
if (!IniFile.ReadFile("users.ini"))
{
GenerateDefaultUsersIni(IniFile);
return true;
}
int NumKeys = IniFile.GetNumKeys();
for (int i = 0; i < NumKeys; i++)
{
AString Player = IniFile.GetKeyName(i);
AString Groups = IniFile.GetValue(Player, "Groups", "");
if (Groups.empty())
{
continue;
}
AStringVector Split = StringSplitAndTrim(Groups, ",");
for (AStringVector::const_iterator itr = Split.begin(), end = Split.end(); itr != end; ++itr)
{
if (!ExistsGroup(*itr))
{
LOGWARNING("The group %s for player %s was not found!", Split[i].c_str(), Player.c_str());
}
} // for itr - Split[]
} // for i - ini file keys
// Always return true for now, just but we can handle writefile fails later.
return true;
}
bool cGroupManager::LoadGroups()
{
cIniFile IniFile;
if (!IniFile.ReadFile("groups.ini"))
{
LOGWARNING("Regenerating groups.ini, all groups will be reset");
IniFile.AddHeaderComment(" This is the MCServer permissions manager groups file");
IniFile.AddHeaderComment(" It stores all defined groups such as Administrators, Players, or Moderators");
IniFile.SetValue("Owner", "Permissions", "*", true);
IniFile.SetValue("Owner", "Color", "2", true);
IniFile.SetValue("Moderator", "Permissions", "core.time, core.item, core.tpa, core.tpaccept, core.ban, core.unban, core.save-all, core.toggledownfall");
IniFile.SetValue("Moderator", "Color", "2", true);
IniFile.SetValue("Moderator", "Inherits", "Player", true);
IniFile.SetValue("Player", "Permissions", "core.portal", true);
IniFile.SetValue("Player", "Color", "f", true);
IniFile.SetValue("Player", "Inherits", "Default", true);
IniFile.SetValue("Default", "Permissions", "core.help, core.plugins, core.spawn, core.worlds, core.back, core.motd, core.build, core.locate, core.viewdistance", true);
IniFile.SetValue("Default", "Color", "f", true);
IniFile.WriteFile("groups.ini");
}
int NumKeys = IniFile.GetNumKeys();
for (int i = 0; i < NumKeys; i++)
{
AString KeyName = IniFile.GetKeyName(i);
cGroup * Group = GetGroup(KeyName.c_str());
Group->ClearPermission(); // Needed in case the groups are reloaded.
LOGD("Loading group %s", KeyName.c_str());
Group->SetName(KeyName);
AString Color = IniFile.GetValue(KeyName, "Color", "-");
if ((Color != "-") && (Color.length() >= 1))
{
Group->SetColor(AString(cChatColor::Delimiter) + Color[0]);
}
else
{
Group->SetColor(cChatColor::White);
}
AString Commands = IniFile.GetValue(KeyName, "Commands", "");
if (!Commands.empty())
{
AStringVector Split = StringSplitAndTrim(Commands, ",");
for (size_t i = 0; i < Split.size(); i++)
{
Group->AddCommand(Split[i]);
}
}
AString Permissions = IniFile.GetValue(KeyName, "Permissions", "");
if (!Permissions.empty())
{
AStringVector Split = StringSplitAndTrim(Permissions, ",");
for (size_t i = 0; i < Split.size(); i++)
{
Group->AddPermission(Split[i]);
}
}
AString Groups = IniFile.GetValue(KeyName, "Inherits", "");
if (!Groups.empty())
{
AStringVector Split = StringSplitAndTrim(Groups, ",");
for (size_t i = 0; i < Split.size(); i++)
{
Group->InheritFrom(GetGroup(Split[i].c_str()));
}
}
}
// Always return true, we can handle writefile fails later.
return true;
}
bool cGroupManager::ExistsGroup( const AString & a_Name)
{
GroupMap::iterator itr = m_pState->Groups.find( a_Name);
return ( itr != m_pState->Groups.end());
}
cGroup* cGroupManager::GetGroup( const AString & a_Name)
{
GroupMap::iterator itr = m_pState->Groups.find( a_Name);
if (itr != m_pState->Groups.end())
{
return itr->second;
}
cGroup* Group = new cGroup();
m_pState->Groups[a_Name] = Group;
return Group;
}

View File

@ -1,36 +0,0 @@
#pragma once
class cGroup;
class cGroupManager
{
public:
bool ExistsGroup(const AString & a_Name);
cGroup * GetGroup(const AString & a_Name);
bool LoadGroups();
bool CheckUsers();
/** Writes the default header to the specified ini file, and saves it as "users.ini". */
static void GenerateDefaultUsersIni(cIniFile & a_IniFile);
private:
friend class cRoot;
cGroupManager();
~cGroupManager();
struct sGroupManagerState;
sGroupManagerState * m_pState;
} ;

View File

@ -15,7 +15,8 @@
cHTTPConnection::cHTTPConnection(cHTTPServer & a_HTTPServer) :
m_HTTPServer(a_HTTPServer),
m_State(wcsRecvHeaders),
m_CurrentRequest(NULL)
m_CurrentRequest(NULL),
m_CurrentRequestBodyRemaining(0)
{
// LOGD("HTTP: New connection at %p", this);
}

View File

@ -15,7 +15,9 @@
cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks) :
m_Callbacks(a_Callbacks),
m_IsValid(true)
m_IsValid(true),
m_IsCurrentPartFile(false),
m_FileHasBeenAnnounced(false)
{
if (a_Request.GetMethod() == "GET")
{
@ -55,7 +57,9 @@ cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callba
cHTTPFormParser::cHTTPFormParser(eKind a_Kind, const char * a_Data, size_t a_Size, cCallbacks & a_Callbacks) :
m_Callbacks(a_Callbacks),
m_Kind(a_Kind),
m_IsValid(true)
m_IsValid(true),
m_IsCurrentPartFile(false),
m_FileHasBeenAnnounced(false)
{
Parse(a_Data, a_Size);
}

View File

@ -106,7 +106,7 @@ int cInventory::AddItem(const cItem & a_Item, bool a_AllowNewStacks, bool a_tryT
// When the item is a armor, try to set it directly to the armor slot.
if (ItemCategory::IsArmor(a_Item.m_ItemType))
{
for (size_t i = 0; i < (size_t)m_ArmorSlots.GetNumSlots(); i++)
for (int i = 0; i < m_ArmorSlots.GetNumSlots(); i++)
{
if (m_ArmorSlots.GetSlot(i).IsEmpty() && cSlotAreaArmor::CanPlaceArmorInSlot(i, a_Item))
{

View File

@ -73,6 +73,8 @@ public:
HEIGHTTYPE * m_HeightMap; // 3x3 chunks of height map, organized as a single XZY blob of data (instead of 3x3 XZY blobs)
cReader(BLOCKTYPE * a_BlockTypes, HEIGHTTYPE * a_HeightMap) :
m_ReadingChunkX(0),
m_ReadingChunkZ(0),
m_MaxHeight(0),
m_BlockTypes(a_BlockTypes),
m_HeightMap(a_HeightMap)
@ -89,7 +91,9 @@ public:
cLightingThread::cLightingThread(void) :
super("cLightingThread"),
m_World(NULL)
m_World(NULL),
m_MaxHeight(0),
m_NumSeeds(0)
{
}

View File

@ -31,7 +31,7 @@ Linearly interpolates values in the array between the equidistant anchor points
Works in-place (input is already present at the correct output coords)
Uses templates to make it possible for the compiler to further optimizer the loops
*/
template<
template <
int SizeX, int SizeY, // Dimensions of the array
int AnchorStepX, int AnchorStepY,
typename TYPE
@ -83,7 +83,7 @@ void LinearUpscale2DArrayInPlace(TYPE * a_Array)
Linearly interpolates values in the array between the equidistant anchor points (upscales).
Works on two arrays, input is packed and output is to be completely constructed.
*/
template<typename TYPE> void LinearUpscale2DArray(
template <typename TYPE> void LinearUpscale2DArray(
TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY
int a_SrcSizeX, int a_SrcSizeY, ///< Dimensions of the src array
TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1)
@ -153,7 +153,7 @@ template<typename TYPE> void LinearUpscale2DArray(
Linearly interpolates values in the array between the equidistant anchor points (upscales).
Works on two arrays, input is packed and output is to be completely constructed.
*/
template<typename TYPE> void LinearUpscale3DArray(
template <typename TYPE> void LinearUpscale3DArray(
TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY x a_SrcSizeZ
int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Dimensions of the src array
TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) x (a_SrcSizeZ * a_UpscaleZ + 1)

View File

@ -65,7 +65,7 @@
{
case cLogger::llRegular:
{
// Gray on black
// Gray on black
Attrib = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
break;
}
@ -93,7 +93,7 @@
virtual void SetDefaultLogColour() override
{
{
SetConsoleTextAttribute(m_Console, m_DefaultConsoleAttrib);
}
@ -119,13 +119,13 @@
{
case cLogger::llRegular:
{
// Whatever the console default is
// Whatever the console default is
printf("\x1b[0m");
break;
break;
}
case cLogger::llInfo:
{
// Yellow on black
// Yellow on black
printf("\x1b[33;1m");
break;
}
@ -133,7 +133,7 @@
{
// Red on black
printf("\x1b[31;1m");
break;
break;
}
case cLogger::llError:
{
@ -147,7 +147,7 @@
virtual void SetDefaultLogColour() override
{
// Whatever the console default is
// Whatever the console default is
printf("\x1b[0m");
}
};

View File

@ -1024,7 +1024,7 @@ void cMonster::HandleDaylightBurning(cChunk & a_Chunk)
(a_Chunk.GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand
(GetWorld()->GetTimeOfDay() < (12000 + 1000)) && // It is nighttime
!IsOnFire() && // Not already burning
GetWorld()->IsWeatherWetAt(POSX_TOINT, POSZ_TOINT) // Not raining
GetWorld()->IsWeatherSunnyAt(POSX_TOINT, POSZ_TOINT) // Not raining
)
{
// Burn for 100 ticks, then decide again

View File

@ -146,6 +146,8 @@ cCubicCell2D::cCubicCell2D(
) :
m_Noise(a_Noise),
m_WorkRnds(&m_Workspace1),
m_CurFloorX(0),
m_CurFloorY(0),
m_Array(a_Array),
m_SizeX(a_SizeX),
m_SizeY(a_SizeY),
@ -300,6 +302,9 @@ cCubicCell3D::cCubicCell3D(
) :
m_Noise(a_Noise),
m_WorkRnds(&m_Workspace1),
m_CurFloorX(0),
m_CurFloorY(0),
m_CurFloorZ(0),
m_Array(a_Array),
m_SizeX(a_SizeX),
m_SizeY(a_SizeY),

View File

@ -20,7 +20,7 @@ cQueueFuncs and is used as the default behavior.
*/
/// This empty struct allows for the callback functions to be inlined
template<class T>
template <class T>
struct cQueueFuncs
{
public:

View File

@ -16,6 +16,7 @@ cSslContext::cSslContext(void) :
m_IsValid(false),
m_HasHandshaken(false)
{
memset(&m_Ssl, 0, sizeof(m_Ssl));
}

View File

@ -10,6 +10,7 @@
#include "inifile/iniFile.h"
#include "json/json.h"
#include "PolarSSL++/BlockingSslClientSocket.h"
#include "../RankManager.h"
@ -300,6 +301,7 @@ void cMojangAPI::AddPlayerNameToUUIDMapping(const AString & a_PlayerName, const
cCSLock Lock(m_CSUUIDToName);
m_UUIDToName[UUID] = sProfile(a_PlayerName, UUID, "", "", Now);
}
NotifyNameUUID(a_PlayerName, a_UUID);
}
@ -322,6 +324,7 @@ void cMojangAPI::AddPlayerProfile(const AString & a_PlayerName, const AString &
cCSLock Lock(m_CSUUIDToProfile);
m_UUIDToProfile[UUID] = sProfile(a_PlayerName, UUID, a_Properties, Now);
}
NotifyNameUUID(a_PlayerName, a_UUID);
}
@ -655,11 +658,11 @@ void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames)
}
// Store the returned results into cache:
size_t JsonCount = root.size();
Json::Value::UInt JsonCount = root.size();
Int64 Now = time(NULL);
{
cCSLock Lock(m_CSNameToUUID);
for (size_t idx = 0; idx < JsonCount; ++idx)
for (Json::Value::UInt idx = 0; idx < JsonCount; ++idx)
{
Json::Value & Val = root[idx];
AString JsonName = Val.get("name", "").asString();
@ -669,13 +672,14 @@ void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames)
continue;
}
m_NameToUUID[StrToLower(JsonName)] = sProfile(JsonName, JsonUUID, "", "", Now);
NotifyNameUUID(JsonName, JsonUUID);
} // for idx - root[]
} // cCSLock (m_CSNameToUUID)
// Also cache the UUIDToName:
{
cCSLock Lock(m_CSUUIDToName);
for (size_t idx = 0; idx < JsonCount; ++idx)
for (Json::Value::UInt idx = 0; idx < JsonCount; ++idx)
{
Json::Value & Val = root[idx];
AString JsonName = Val.get("name", "").asString();
@ -792,6 +796,21 @@ void cMojangAPI::CacheUUIDToProfile(const AString & a_UUID)
cCSLock Lock(m_CSNameToUUID);
m_NameToUUID[StrToLower(PlayerName)] = sProfile(PlayerName, a_UUID, Properties, Now);
}
NotifyNameUUID(PlayerName, a_UUID);
}
void cMojangAPI::NotifyNameUUID(const AString & a_PlayerName, const AString & a_UUID)
{
// Notify the rank manager:
cCSLock Lock(m_CSRankMgr);
if (m_RankMgr != NULL)
{
m_RankMgr->NotifyNameUUID(a_PlayerName, a_UUID);
}
}

View File

@ -11,6 +11,13 @@
#include <time.h>
// fwd: ../RankManager.h"
class cRankManager;
namespace Json
{
class Value;
@ -38,8 +45,6 @@ public:
Returns true if all was successful, false on failure. */
static bool SecureRequest(const AString & a_ServerName, const AString & a_Request, AString & a_Response);
// tolua_begin
/** Normalizes the given UUID to its short form (32 bytes, no dashes, lowercase).
Logs a warning and returns empty string if not a UUID.
Note: only checks the string's length, not the actual content. */
@ -50,8 +55,6 @@ public:
Note: only checks the string's length, not the actual content. */
static AString MakeUUIDDashed(const AString & a_UUID);
// tolua_end
/** Converts a player name into a UUID.
The UUID will be empty on error.
If a_UseOnlyCached is true, the function only consults the cached values.
@ -85,7 +88,10 @@ public:
/** Called by the Authenticator to add a profile that it has received from authenticating a user. Adds
the profile to the respective mapping caches and updtes their datetime stamp to now. */
void AddPlayerProfile(const AString & a_PlayerName, const AString & a_UUID, const Json::Value & a_Properties);
/** Sets the m_RankMgr that is used for name-uuid notifications. Accepts NULL to remove the binding. */
void SetRankManager(cRankManager * a_RankManager) { m_RankMgr = a_RankManager; }
protected:
/** Holds data for a single player profile. */
struct sProfile
@ -165,6 +171,12 @@ protected:
/** Protects m_UUIDToProfile against simultaneous multi-threaded access. */
cCriticalSection m_CSUUIDToProfile;
/** The rank manager that is notified of the name-uuid pairings. May be NULL. Protected by m_CSRankMgr. */
cRankManager * m_RankMgr;
/** Protects m_RankMgr agains simultaneous multi-threaded access. */
cCriticalSection m_CSRankMgr;
/** Loads the caches from a disk storage. */
@ -182,6 +194,10 @@ protected:
UUIDs that are not valid will not be added into the cache.
ASSUMEs that a_UUID is a lowercased short UUID. */
void CacheUUIDToProfile(const AString & a_UUID);
/** Called for each name-uuid pairing that is discovered.
If assigned, notifies the m_RankManager of the event. */
void NotifyNameUUID(const AString & a_PlayerName, const AString & a_PlayerUUID);
} ; // tolua_export

View File

@ -1824,7 +1824,11 @@ void cProtocol172::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffe
void cProtocol172::HandlePacketLoginStart(cByteBuffer & a_ByteBuffer)
{
AString Username;
a_ByteBuffer.ReadVarUTF8String(Username);
if (!a_ByteBuffer.ReadVarUTF8String(Username))
{
m_Client->Kick("Bad username");
return;
}
if (!m_Client->HandleHandshake(Username))
{

1837
src/RankManager.cpp Normal file

File diff suppressed because it is too large Load Diff

246
src/RankManager.h Normal file
View File

@ -0,0 +1,246 @@
// RankManager.h
// Declares the cRankManager class that represents the rank manager responsible for assigning permissions and message visuals to players
#pragma once
#include "SQLiteCpp/Database.h"
#include "SQLiteCpp/Transaction.h"
class cMojangAPI;
class cRankManager
{
public:
/** Acquire this lock to perform mass changes.
Improves performance by wrapping everything into a transaction.
Makes sure that no other thread is accessing the DB. */
class cMassChangeLock
{
public:
cMassChangeLock(cRankManager & a_RankManager) :
m_Lock(a_RankManager.m_CS),
m_Transaction(a_RankManager.m_DB)
{
}
~cMassChangeLock()
{
m_Transaction.commit();
}
protected:
cCSLock m_Lock;
SQLite::Transaction m_Transaction;
};
/** Creates the rank manager. Needs to be initialized before other use. */
cRankManager(void);
~cRankManager();
/** Initializes the rank manager. Performs migration and default-setting if no data is found in the DB.
The a_MojangAPI param is used when migrating from old ini files, to look up player UUIDs. */
void Initialize(cMojangAPI & a_MojangAPI);
/** Returns the name of the rank that the specified player has assigned to them.
If the player has no rank assigned, returns an empty string (NOT the default rank). */
AString GetPlayerRankName(const AString & a_PlayerUUID);
/** Returns the names of Groups that the specified player has assigned to them. */
AStringVector GetPlayerGroups(const AString & a_PlayerUUID);
/** Returns the permissions that the specified player has assigned to them.
If the player has no rank assigned to them, returns the default rank's permissions. */
AStringVector GetPlayerPermissions(const AString & a_PlayerUUID);
/** Returns the names of groups that the specified rank has assigned to it.
Returns an empty vector if the rank doesn't exist. */
AStringVector GetRankGroups(const AString & a_RankName);
/** Returns the permissions that the specified group has assigned to it.
Returns an empty vector if the group doesn't exist. */
AStringVector GetGroupPermissions(const AString & a_GroupName);
/** Returns all permissions that the specified rank has assigned to it, through all its groups.
Returns an empty vector if the rank doesn't exist. Any non-existent groups are ignored. */
AStringVector GetRankPermissions(const AString & a_RankName);
/** Returns the names of all defined ranks. */
AStringVector GetAllRanks(void);
/** Returns the names of all permission groups. */
AStringVector GetAllGroups(void);
/** Returns all the distinct permissions that are stored in the DB. */
AStringVector GetAllPermissions(void);
/** Returns the message visuals (prefix, postfix, color) for the specified player.
Returns true if the visuals were read from the DB, false if not (player not found etc). */
bool GetPlayerMsgVisuals(
const AString & a_PlayerUUID,
AString & a_MsgPrefix,
AString & a_MsgSuffix,
AString & a_MsgNameColorCode
);
/** Adds a new rank. No action if the rank already exists. */
void AddRank(
const AString & a_RankName,
const AString & a_MsgPrefix,
const AString & a_MsgSuffix,
const AString & a_MsgNameColorCode
);
/** Adds a new permission group. No action if such a group already exists. */
void AddGroup(const AString & a_GroupName);
/** Bulk-adds groups. Group names that already exist are silently skipped. */
void AddGroups(const AStringVector & a_GroupNames);
/** Adds the specified permission group to the specified rank.
Fails if the rank or group names are not found.
Returns true if successful, false on error. */
bool AddGroupToRank(const AString & a_GroupName, const AString & a_RankName);
/** Adds the specified permission to the specified permission group.
Fails if the permission group name is not found.
Returns true if successful, false on error. */
bool AddPermissionToGroup(const AString & a_Permission, const AString & a_GroupName);
/** Adds the specified permissions to the specified permission group.
Fails if the permission group name is not found.
Returns true if successful, false on error. */
bool AddPermissionsToGroup(const AStringVector & a_Permissions, const AString & a_GroupName);
/** Removes the specified rank.
All players assigned to that rank will be re-assigned to a_ReplacementRankName.
If a_ReplacementRankName is empty or not a valid rank, the player will be removed from the DB,
which means they will receive the default rank the next time they are queried.
If the rank being removed is the default rank, the default will be changed to the replacement
rank; the operation fails if there's no replacement. */
void RemoveRank(const AString & a_RankName, const AString & a_ReplacementRankName);
/** Removes the specified group completely.
The group will first be removed from all ranks using it, and then removed itself. */
void RemoveGroup(const AString & a_GroupName);
/** Removes the specified group from the specified rank.
The group will stay defined, even if no rank is using it. */
void RemoveGroupFromRank(const AString & a_GroupName, const AString & a_RankName);
/** Removes the specified permission from the specified group. */
void RemovePermissionFromGroup(const AString & a_Permission, const AString & a_GroupName);
/** Renames the specified rank. No action if the rank name is not found.
Fails if the new name is already used.
Updates the cached m_DefaultRank if the default rank is being renamed.
Returns true on success, false on failure. */
bool RenameRank(const AString & a_OldName, const AString & a_NewName);
/** Renames the specified group. No action if the rank name is not found.
Fails if the new name is already used.
Returns true on success, false on failure. */
bool RenameGroup(const AString & a_OldName, const AString & a_NewName);
/** Sets the specified player's rank.
If the player already had rank assigned to them, it is overwritten with the new rank and name.
Note that this doesn't change the cPlayer if the player is already connected, you need to update all the
cPlayer instances manually.
The PlayerName is provided for reference, so that GetRankPlayerNames() can work. */
void SetPlayerRank(const AString & a_PlayerUUID, const AString & a_PlayerName, const AString & a_RankName);
/** Removes the player's rank assignment. The player is left without a rank.
Note that this doesn't change the cPlayer instances for the already connected players, you need to update
all the instances manually.
No action if the player has no rank assigned to them already. */
void RemovePlayerRank(const AString & a_PlayerUUID);
/** Sets the message visuals of an existing rank. No action if the rank name is not found. */
void SetRankVisuals(
const AString & a_RankName,
const AString & a_MsgPrefix,
const AString & a_MsgSuffix,
const AString & a_MsgNameColorCode
);
/** Returns the message visuals of an existing rank.
Returns true if successful, false on error (rank doesn't exist). */
bool GetRankVisuals(
const AString & a_RankName,
AString & a_MsgPrefix,
AString & a_MsgSuffix,
AString & a_MsgNameColorCode
);
/** Returns true iff the specified rank exists in the DB. */
bool RankExists(const AString & a_RankName);
/** Returns true iff the specified group exists in the DB. */
bool GroupExists(const AString & a_GroupName);
/** Returns true iff the specified player has a rank assigned to them in the DB. */
bool IsPlayerRankSet(const AString & a_PlayerUUID);
/** Returns true iff the specified rank contains the specified group. */
bool IsGroupInRank(const AString & a_GroupName, const AString & a_RankName);
/** Returns true iff the specified group contains the specified permission. */
bool IsPermissionInGroup(const AString & a_Permission, const AString & a_GroupName);
/** Called by cMojangAPI whenever the playername-uuid pairing is discovered. Updates the DB. */
void NotifyNameUUID(const AString & a_PlayerName, const AString & a_UUID);
/** Sets the specified rank as the default rank.
Returns true on success, false on failure (rank not found). */
bool SetDefaultRank(const AString & a_RankName);
/** Returns the name of the default rank. */
const AString & GetDefaultRank(void) const { return m_DefaultRank; }
protected:
/** The database storage for all the data. Protected by m_CS. */
SQLite::Database m_DB;
/** The name of the default rank. Kept as a cache so that queries for it don't need to go through the DB. */
AString m_DefaultRank;
/** The mutex protecting m_DB and m_DefaultRank against multi-threaded access. */
cCriticalSection m_CS;
/** Set to true once the manager is initialized. */
bool m_IsInitialized;
/** The MojangAPI instance that is used for translating playernames to UUIDs.
Set in Initialize(), may be NULL. */
cMojangAPI * m_MojangAPI;
/** Returns true if all the DB tables are empty, indicating a fresh new install. */
bool AreDBTablesEmpty(void);
/** Returns true iff the specified DB table is empty.
If there's an error while querying, returns false. */
bool IsDBTableEmpty(const AString & a_TableName);
/** Creates a default set of ranks / groups / permissions. */
void CreateDefaults(void);
} ;

View File

@ -6,7 +6,6 @@
#include "World.h"
#include "WebAdmin.h"
#include "FurnaceRecipe.h"
#include "GroupManager.h"
#include "CraftingRecipes.h"
#include "Bindings/PluginManager.h"
#include "MonsterConfig.h"
@ -47,7 +46,6 @@ cRoot::cRoot(void) :
m_InputThread(NULL),
m_Server(NULL),
m_MonsterConfig(NULL),
m_GroupManager(NULL),
m_CraftingRecipes(NULL),
m_FurnaceRecipe(NULL),
m_WebAdmin(NULL),
@ -161,7 +159,7 @@ void cRoot::Start(void)
m_WebAdmin->Init();
LOGD("Loading settings...");
m_GroupManager = new cGroupManager();
m_RankManager.Initialize(m_MojangAPI);
m_CraftingRecipes = new cCraftingRecipes;
m_FurnaceRecipe = new cFurnaceRecipe();
@ -240,8 +238,6 @@ void cRoot::Start(void)
LOGD("Unloading recipes...");
delete m_FurnaceRecipe; m_FurnaceRecipe = NULL;
delete m_CraftingRecipes; m_CraftingRecipes = NULL;
LOGD("Forgetting groups...");
delete m_GroupManager; m_GroupManager = NULL;
LOGD("Unloading worlds...");
UnloadWorlds();
@ -555,17 +551,6 @@ void cRoot::SaveAllChunks(void)
void cRoot::ReloadGroups(void)
{
LOG("Reload groups ...");
m_GroupManager->LoadGroups();
m_GroupManager->CheckUsers();
}
void cRoot::BroadcastChat(const AString & a_Message, eMessageType a_ChatPrefix)
{
for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr)

View File

@ -5,6 +5,7 @@
#include "Protocol/MojangAPI.h"
#include "HTTPServer/HTTPServer.h"
#include "Defines.h"
#include "RankManager.h"
@ -13,7 +14,6 @@
// fwd:
class cThread;
class cMonsterConfig;
class cGroupManager;
class cCraftingRecipes;
class cFurnaceRecipe;
class cWebAdmin;
@ -78,7 +78,6 @@ public:
cMonsterConfig * GetMonsterConfig(void) { return m_MonsterConfig; }
cGroupManager * GetGroupManager (void) { return m_GroupManager; } // tolua_export
cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export
cFurnaceRecipe * GetFurnaceRecipe (void) { return m_FurnaceRecipe; } // Exported in ManualBindings.cpp with quite a different signature
@ -89,6 +88,7 @@ public:
cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export
cAuthenticator & GetAuthenticator (void) { return m_Authenticator; }
cMojangAPI & GetMojangAPI (void) { return m_MojangAPI; }
cRankManager & GetRankManager (void) { return m_RankManager; }
/** Queues a console command for execution through the cServer class.
The command will be executed in the tick thread
@ -122,9 +122,6 @@ public:
/// Saves all chunks in all worlds
void SaveAllChunks(void); // tolua_export
/// Reloads all the groups
void ReloadGroups(void); // tolua_export
/// Calls the callback for each player in all worlds
bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
@ -187,13 +184,13 @@ private:
cServer * m_Server;
cMonsterConfig * m_MonsterConfig;
cGroupManager * m_GroupManager;
cCraftingRecipes * m_CraftingRecipes;
cFurnaceRecipe * m_FurnaceRecipe;
cWebAdmin * m_WebAdmin;
cPluginManager * m_PluginManager;
cAuthenticator m_Authenticator;
cMojangAPI m_MojangAPI;
cRankManager m_RankManager;
cHTTPServer m_HTTPServer;
bool m_bStop;

View File

@ -11,7 +11,6 @@
#include "World.h"
#include "ChunkDef.h"
#include "Bindings/PluginManager.h"
#include "GroupManager.h"
#include "ChatColor.h"
#include "Entities/Player.h"
#include "Inventory.h"
@ -117,7 +116,9 @@ cServer::cServer(void) :
m_MaxPlayers(0),
m_bIsHardcore(false),
m_TickThread(*this),
m_ShouldAuthenticate(false)
m_ShouldAuthenticate(false),
m_ShouldLoadOfflinePlayerData(false),
m_ShouldLoadNamedPlayerData(true)
{
}
@ -469,25 +470,17 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
PrintHelp(split, a_Output);
return;
}
if (split[0] == "reload")
{
cPluginManager::Get()->ReloadPlugins();
cRoot::Get()->ReloadGroups();
return;
}
if (split[0] == "reloadplugins")
else if (split[0] == "reload")
{
cPluginManager::Get()->ReloadPlugins();
return;
}
if (split[0] == "reloadgroups")
else if (split[0] == "reloadplugins")
{
cRoot::Get()->ReloadGroups();
a_Output.Out("Groups reloaded!");
a_Output.Finished();
cPluginManager::Get()->ReloadPlugins();
return;
}
if (split[0] == "load")
else if (split[0] == "load")
{
if (split.size() > 1)
{
@ -502,8 +495,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
return;
}
}
if (split[0] == "unload")
else if (split[0] == "unload")
{
if (split.size() > 1)
{
@ -519,21 +511,21 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
}
// There is currently no way a plugin can do these (and probably won't ever be):
if (split[0].compare("chunkstats") == 0)
else if (split[0].compare("chunkstats") == 0)
{
cRoot::Get()->LogChunkStats(a_Output);
a_Output.Finished();
return;
}
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
if (split[0].compare("dumpmem") == 0)
else if (split[0].compare("dumpmem") == 0)
{
LeakFinderXmlOutput Output("memdump.xml");
DumpUsedMemory(&Output);
return;
}
if (split[0].compare("killmem") == 0)
else if (split[0].compare("killmem") == 0)
{
for (;;)
{
@ -542,7 +534,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
}
#endif
if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output))
else if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output))
{
a_Output.Finished();
return;

View File

@ -120,7 +120,7 @@ public: // tolua_export
const AString & GetPublicKeyDER(void) const { return m_PublicKeyDER; }
/** Returns true if authentication has been turned on in server settings. */
bool ShouldAuthenticate(void) const { return m_ShouldAuthenticate; }
bool ShouldAuthenticate(void) const { return m_ShouldAuthenticate; } // tolua_export
/** Returns true if offline UUIDs should be used to load data for players whose normal UUIDs cannot be found.
Loaded from the settings.ini [PlayerData].LoadOfflinePlayerData setting. */

View File

@ -5,6 +5,7 @@
#include "Globals.h"
#include "SetChunkData.h"
#include "BlockEntities/BlockEntity.h"
@ -13,6 +14,9 @@
cSetChunkData::cSetChunkData(int a_ChunkX, int a_ChunkZ, bool a_ShouldMarkDirty) :
m_ChunkX(a_ChunkX),
m_ChunkZ(a_ChunkZ),
m_IsLightValid(false),
m_IsHeightMapValid(false),
m_AreBiomesValid(false),
m_ShouldMarkDirty(a_ShouldMarkDirty)
{
}
@ -113,3 +117,35 @@ void cSetChunkData::CalculateHeightMap(void)
void cSetChunkData::RemoveInvalidBlockEntities(void)
{
for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end();)
{
BLOCKTYPE EntityBlockType = (*itr)->GetBlockType();
BLOCKTYPE WorldBlockType = cChunkDef::GetBlock(m_BlockTypes, (*itr)->GetRelX(), (*itr)->GetPosY(), (*itr)->GetRelZ());
if (EntityBlockType != WorldBlockType)
{
// Bad blocktype, remove the block entity:
LOGD("Block entity blocktype mismatch at {%d, %d, %d}: entity for blocktype %s(%d) in block %s(%d). Deleting the block entity.",
(*itr)->GetPosX(), (*itr)->GetPosY(), (*itr)->GetPosZ(),
ItemTypeToString(EntityBlockType).c_str(), EntityBlockType,
ItemTypeToString(WorldBlockType).c_str(), WorldBlockType
);
cBlockEntityList::iterator itr2 = itr;
itr2++;
m_BlockEntities.erase(itr);
delete *itr;
itr = itr2;
}
else
{
// Good blocktype, keep the block entity:
++itr;
}
} // for itr - m_BlockEntities[]
}

View File

@ -92,6 +92,9 @@ public:
/** Calculates the heightmap based on the contained blocktypes and marks it valid. */
void CalculateHeightMap(void);
/** Removes the block entities that don't have a proper blocktype at their corresponding coords. */
void RemoveInvalidBlockEntities(void);
protected:
int m_ChunkX;
int m_ChunkZ;

View File

@ -98,7 +98,7 @@ int cVanillaFluidSimulator::CalculateFlowCost(cChunk * a_Chunk, int a_RelX, int
}
// Check if block below is passable
if (!a_Chunk->UnboundedRelGetBlock(a_RelX, a_RelY - 1, a_RelZ, BlockType, BlockMeta))
if ((a_RelY > 0) && !a_Chunk->UnboundedRelGetBlock(a_RelX, a_RelY - 1, a_RelZ, BlockType, BlockMeta))
{
return Cost;
}

View File

@ -9,6 +9,7 @@
#pragma once
#include <string>
#include <limits>
@ -98,6 +99,68 @@ extern int GetBEInt(const char * a_Mem);
/// Writes four bytes to the specified memory location so that they interpret as BigEndian int
extern void SetBEInt(char * a_Mem, Int32 a_Value);
/// Parses any integer type. Checks bounds and returns errors out of band.
template <class T>
bool StringToInteger(const AString & a_str, T & a_Num)
{
size_t i = 0;
bool positive = true;
T result = 0;
if (a_str[0] == '+')
{
i++;
}
else if (a_str[0] == '-')
{
i++;
positive = false;
}
if (positive)
{
for (size_t size = a_str.size(); i < size; i++)
{
if ((a_str[i] < '0') || (a_str[i] > '9'))
{
return false;
}
if (std::numeric_limits<T>::max() / 10 < result)
{
return false;
}
result *= 10;
T digit = a_str[i] - '0';
if (std::numeric_limits<T>::max() - digit < result)
{
return false;
}
result += digit;
}
}
else
{
for (size_t size = a_str.size(); i < size; i++)
{
if ((a_str[i] < '0') || (a_str[i] > '9'))
{
return false;
}
if (std::numeric_limits<T>::min() / 10 > result)
{
return false;
}
result *= 10;
T digit = a_str[i] - '0';
if (std::numeric_limits<T>::min() + digit > result)
{
return false;
}
result -= digit;
}
}
a_Num = result;
return true;
}
// If you have any other string helper functions, declare them here

View File

@ -251,8 +251,38 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin
m_TimeOfDay(0),
m_LastTimeUpdate(0),
m_SkyDarkness(0),
m_GameMode(gmNotSet),
m_bEnabledPVP(false),
m_IsDeepSnowEnabled(false),
m_ShouldLavaSpawnFire(true),
m_VillagersShouldHarvestCrops(true),
m_SimulatorManager(NULL),
m_SandSimulator(NULL),
m_WaterSimulator(NULL),
m_LavaSimulator(NULL),
m_FireSimulator(NULL),
m_RedstoneSimulator(NULL),
m_MaxPlayers(10),
m_ChunkMap(NULL),
m_bAnimals(true),
m_Weather(eWeather_Sunny),
m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :)
m_MaxCactusHeight(3),
m_MaxSugarcaneHeight(4),
m_IsCactusBonemealable(false),
m_IsCarrotsBonemealable(true),
m_IsCropsBonemealable(true),
m_IsGrassBonemealable(true),
m_IsMelonStemBonemealable(true),
m_IsMelonBonemealable(true),
m_IsPotatoesBonemealable(true),
m_IsPumpkinStemBonemealable(true),
m_IsPumpkinBonemealable(true),
m_IsSaplingBonemealable(true),
m_IsSugarcaneBonemealable(false),
m_bCommandBlocksEnabled(true),
m_bUseChatPrefixes(false),
m_TNTShrapnelLevel(slNone),
m_Scoreboard(this),
m_MapManager(this),
m_GeneratorCallbacks(*this),
@ -407,7 +437,7 @@ void cWorld::InitializeSpawn(void)
int ViewDist = IniFile.GetValueSetI("SpawnPosition", "PregenerateDistance", DefaultViewDist);
IniFile.WriteFile(m_IniFileName);
LOG("Preparing spawn area in world \"%s\"...", m_WorldName.c_str());
LOG("Preparing spawn area in world \"%s\", %d x %d chunks, total %d chunks...", m_WorldName.c_str(), ViewDist, ViewDist, ViewDist * ViewDist);
for (int x = 0; x < ViewDist; x++)
{
for (int z = 0; z < ViewDist; z++)
@ -3454,14 +3484,16 @@ void cWorld::cChunkGeneratorCallbacks::OnChunkGenerated(cChunkDesc & a_ChunkDesc
cChunkDef::BlockNibbles BlockMetas;
a_ChunkDesc.CompressBlockMetas(BlockMetas);
m_World->QueueSetChunkData(cSetChunkDataPtr(new cSetChunkData(
cSetChunkDataPtr SetChunkData(new cSetChunkData(
a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(),
a_ChunkDesc.GetBlockTypes(), BlockMetas,
NULL, NULL, // We don't have lighting, chunk will be lighted when needed
&a_ChunkDesc.GetHeightMap(), &a_ChunkDesc.GetBiomeMap(),
a_ChunkDesc.GetEntities(), a_ChunkDesc.GetBlockEntities(),
true
)));
));
SetChunkData->RemoveInvalidBlockEntities();
m_World->QueueSetChunkData(SetChunkData);
}

View File

@ -76,7 +76,9 @@ public:
cFastNBTTag(eTagType a_Type, int a_Parent) :
m_Type(a_Type),
m_NameStart(0),
m_NameLength(0),
m_DataStart(0),
m_DataLength(0),
m_Parent(a_Parent),
m_PrevSibling(-1),
@ -88,7 +90,9 @@ public:
cFastNBTTag(eTagType a_Type, int a_Parent, int a_PrevSibling) :
m_Type(a_Type),
m_NameStart(0),
m_NameLength(0),
m_DataStart(0),
m_DataLength(0),
m_Parent(a_Parent),
m_PrevSibling(a_PrevSibling),

View File

@ -396,7 +396,7 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT
} // for y
//*/
m_World->QueueSetChunkData(cSetChunkDataPtr(new cSetChunkData(
cSetChunkDataPtr SetChunkData(new cSetChunkData(
a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ,
BlockTypes, MetaData,
IsLightValid ? BlockLight : NULL,
@ -404,7 +404,8 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT
NULL, Biomes,
Entities, BlockEntities,
false
)));
));
m_World->QueueSetChunkData(SetChunkData);
return true;
}
@ -581,64 +582,32 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
{
continue;
}
int sID = a_NBT.FindChildByName(Child, "id");
if (sID < 0)
// Get the BlockEntity's position
int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, Child, x, y, z))
{
LOGWARNING("Bad block entity, missing the coords. Will be ignored.");
continue;
}
int RelX = x, RelY = y, RelZ = z, ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(RelX, RelY, RelZ, ChunkX, ChunkZ);
if (RelY == 2)
{
LOGD("HERE");
}
// Load the proper BlockEntity type based on the block type:
BLOCKTYPE BlockType = cChunkDef::GetBlock(a_BlockTypes, RelX, RelY, RelZ);
NIBBLETYPE BlockMeta = cChunkDef::GetNibble(a_BlockMetas, RelX, RelY, RelZ);
std::auto_ptr<cBlockEntity> be(LoadBlockEntityFromNBT(a_NBT, Child, x, y, z, BlockType, BlockMeta));
if (be.get() == NULL)
{
continue;
}
if (strncmp(a_NBT.GetData(sID), "Beacon", a_NBT.GetDataLength(sID)) == 0)
{
LoadBeaconFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "Chest", a_NBT.GetDataLength(sID)) == 0)
{
LoadChestFromNBT(a_BlockEntities, a_NBT, Child, E_BLOCK_CHEST);
}
else if (strncmp(a_NBT.GetData(sID), "Control", a_NBT.GetDataLength(sID)) == 0)
{
LoadCommandBlockFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "Dropper", a_NBT.GetDataLength(sID)) == 0)
{
LoadDropperFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "FlowerPot", a_NBT.GetDataLength(sID)) == 0)
{
LoadFlowerPotFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "Furnace", a_NBT.GetDataLength(sID)) == 0)
{
LoadFurnaceFromNBT(a_BlockEntities, a_NBT, Child, a_BlockTypes, a_BlockMetas);
}
else if (strncmp(a_NBT.GetData(sID), "Hopper", a_NBT.GetDataLength(sID)) == 0)
{
LoadHopperFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "Music", a_NBT.GetDataLength(sID)) == 0)
{
LoadNoteFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "RecordPlayer", a_NBT.GetDataLength(sID)) == 0)
{
LoadJukeboxFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "Sign", a_NBT.GetDataLength(sID)) == 0)
{
LoadSignFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "Skull", a_NBT.GetDataLength(sID)) == 0)
{
LoadMobHeadFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "Trap", a_NBT.GetDataLength(sID)) == 0)
{
LoadDispenserFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "TrappedChest", a_NBT.GetDataLength(sID)) == 0)
{
LoadChestFromNBT(a_BlockEntities, a_NBT, Child, E_BLOCK_TRAPPED_CHEST);
}
// TODO: Other block entities
// Add the BlockEntity to the loaded data:
a_BlockEntities.push_back(be.release());
} // for Child - tag children
}
@ -646,6 +615,52 @@ 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)
{
// Load the specific BlockEntity type:
switch (a_BlockType)
{
// Specific entity loaders:
case E_BLOCK_BEACON: return LoadBeaconFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_CHEST: return LoadChestFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_CHEST);
case E_BLOCK_COMMAND_BLOCK: return LoadCommandBlockFromNBT(a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_DISPENSER: return LoadDispenserFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_DROPPER: return LoadDropperFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_FLOWER_POT: return LoadFlowerPotFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_FURNACE: return LoadFurnaceFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FURNACE, a_BlockMeta);
case E_BLOCK_HEAD: return LoadMobHeadFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_HOPPER: return LoadHopperFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_JUKEBOX: return LoadJukeboxFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_LIT_FURNACE: return LoadFurnaceFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_LIT_FURNACE, a_BlockMeta);
case E_BLOCK_NOTE_BLOCK: return LoadNoteBlockFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_SIGN_POST: return LoadSignFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SIGN_POST);
case E_BLOCK_TRAPPED_CHEST: return LoadChestFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_TRAPPED_CHEST);
case E_BLOCK_WALLSIGN: return LoadSignFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WALLSIGN);
// Blocktypes that have block entities but don't load their contents from disk:
case E_BLOCK_ENDER_CHEST: return NULL;
}
// All the other blocktypes should have no entities assigned to them. Report an error:
// Get the "id" tag:
int TagID = a_NBT.FindChildByName(a_Tag, "id");
AString TypeName("<unknown>");
if (TagID >= 0)
{
TypeName.assign(a_NBT.GetData(TagID), (size_t)a_NBT.GetDataLength(TagID));
}
LOGINFO("WorldLoader(%s): Block entity mismatch: block type %s (%d), type \"%s\", at {%d, %d, %d}; the entity will be lost.",
m_World->GetName().c_str(),
ItemTypeToString(a_BlockType).c_str(), a_BlockType, TypeName.c_str(),
a_BlockX, a_BlockY, a_BlockZ
);
return NULL;
}
bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx)
{
int Type = a_NBT.FindChildByName(a_TagIdx, "id");
@ -656,7 +671,6 @@ bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_
a_Item.m_ItemType = a_NBT.GetShort(Type);
if (a_Item.m_ItemType < 0)
{
LOGD("Encountered an item with negative type (%d). Replacing with an empty item.", a_NBT.GetShort(Type));
a_Item.Empty();
return true;
}
@ -754,16 +768,46 @@ void cWSSAnvil::LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a
void cWSSAnvil::LoadBeaconFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
bool cWSSAnvil::CheckBlockEntityType(const cParsedNBT & a_NBT, int a_TagIdx, const char * a_ExpectedType)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
// Check if the given tag is a compound:
if (a_NBT.GetType(a_TagIdx) != TAG_Compound)
{
return;
return false;
}
std::auto_ptr<cBeaconEntity> Beacon(new cBeaconEntity(x, y, z, m_World));
// Get the "id" tag:
int TagID = a_NBT.FindChildByName(a_TagIdx, "id");
if (TagID < 0)
{
return false;
}
// Compare the value:
if (strncmp(a_NBT.GetData(TagID), a_ExpectedType, (size_t)a_NBT.GetDataLength(TagID)) == 0)
{
return true;
}
LOGWARNING("Block entity type mismatch: exp \"%s\", got \"%s\".",
a_ExpectedType,
AString(a_NBT.GetData(TagID), (size_t)a_NBT.GetDataLength(TagID)).c_str()
);
return false;
}
cBlockEntity * cWSSAnvil::LoadBeaconFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
// Check if the data has a proper type:
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Beacon"))
{
return NULL;
}
std::auto_ptr<cBeaconEntity> Beacon(new cBeaconEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int CurrentLine = a_NBT.FindChildByName(a_TagIdx, "Levels");
if (CurrentLine >= 0)
@ -790,88 +834,128 @@ void cWSSAnvil::LoadBeaconFromNBT(cBlockEntityList & a_BlockEntities, const cPar
LoadItemGridFromNBT(Beacon->GetContents(), a_NBT, Items);
}
a_BlockEntities.push_back(Beacon.release());
return Beacon.release();
}
void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_ChestType)
cBlockEntity * cWSSAnvil::LoadChestFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_ChestBlockType)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
// Check if the data has a proper type:
// TODO: Does vanilla use "TrappedChest" or not? MCWiki says no, but previous code says yes
// Ref.: http://minecraft.gamepedia.com/Trapped_Chest
// https://github.com/mc-server/MCServer/blob/d0551e2e0a98a28f31a88d489d17b408e4a7d38d/src/WorldStorage/WSSAnvil.cpp#L637
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Chest") && !CheckBlockEntityType(a_NBT, a_TagIdx, "TrappedChest"))
{
return;
return NULL;
}
int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{
return; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this
return NULL; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this
}
std::auto_ptr<cChestEntity> Chest(new cChestEntity(x, y, z, m_World, a_ChestType));
std::auto_ptr<cChestEntity> Chest(new cChestEntity(a_BlockX, a_BlockY, a_BlockZ, m_World, a_ChestBlockType));
LoadItemGridFromNBT(Chest->GetContents(), a_NBT, Items);
a_BlockEntities.push_back(Chest.release());
return Chest.release();
}
void cWSSAnvil::LoadDispenserFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
cBlockEntity * cWSSAnvil::LoadCommandBlockFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
// Check if the data has a proper type:
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Control"))
{
return;
return NULL;
}
std::auto_ptr<cCommandBlockEntity> CmdBlock(new cCommandBlockEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int currentLine = a_NBT.FindChildByName(a_TagIdx, "Command");
if (currentLine >= 0)
{
CmdBlock->SetCommand(a_NBT.GetString(currentLine));
}
currentLine = a_NBT.FindChildByName(a_TagIdx, "SuccessCount");
if (currentLine >= 0)
{
CmdBlock->SetResult(a_NBT.GetInt(currentLine));
}
currentLine = a_NBT.FindChildByName(a_TagIdx, "LastOutput");
if (currentLine >= 0)
{
CmdBlock->SetLastOutput(a_NBT.GetString(currentLine));
}
// TODO 2014-01-18 xdot: Figure out what TrackOutput is and parse it.
return CmdBlock.release();
}
cBlockEntity * cWSSAnvil::LoadDispenserFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
// Check if the data has a proper type:
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Trap"))
{
return NULL;
}
int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{
return; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this
return NULL; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this
}
std::auto_ptr<cDispenserEntity> Dispenser(new cDispenserEntity(x, y, z, m_World));
std::auto_ptr<cDispenserEntity> Dispenser(new cDispenserEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
LoadItemGridFromNBT(Dispenser->GetContents(), a_NBT, Items);
a_BlockEntities.push_back(Dispenser.release());
return Dispenser.release();
}
void cWSSAnvil::LoadDropperFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
cBlockEntity * cWSSAnvil::LoadDropperFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
// Check if the data has a proper type:
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Dropper"))
{
return;
return NULL;
}
int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{
return; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this
return NULL; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this
}
std::auto_ptr<cDropperEntity> Dropper(new cDropperEntity(x, y, z, m_World));
std::auto_ptr<cDropperEntity> Dropper(new cDropperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
LoadItemGridFromNBT(Dropper->GetContents(), a_NBT, Items);
a_BlockEntities.push_back(Dropper.release());
return Dropper.release();
}
void cWSSAnvil::LoadFlowerPotFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
cBlockEntity * cWSSAnvil::LoadFlowerPotFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
// Check if the data has a proper type:
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "FlowerPot"))
{
return;
return NULL;
}
std::auto_ptr<cFlowerPotEntity> FlowerPot(new cFlowerPotEntity(x, y, z, m_World));
std::auto_ptr<cFlowerPotEntity> FlowerPot(new cFlowerPotEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
short ItemType = 0, ItemData = 0;
int currentLine = a_NBT.FindChildByName(a_TagIdx, "Item");
@ -887,37 +971,28 @@ void cWSSAnvil::LoadFlowerPotFromNBT(cBlockEntityList & a_BlockEntities, const c
}
FlowerPot->SetItem(cItem(ItemType, 1, ItemData));
a_BlockEntities.push_back(FlowerPot.release());
return FlowerPot.release();
}
void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas)
cBlockEntity * cWSSAnvil::LoadFurnaceFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
// Check if the data has a proper type:
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Furnace"))
{
return;
return NULL;
}
int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{
return; // Make it an empty furnace - the chunk loader will provide an empty cFurnaceEntity for this
return NULL; // Make it an empty furnace - the chunk loader will provide an empty cFurnaceEntity for this
}
// Convert coords to relative:
int RelX = x;
int RelZ = z;
int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(RelX, y, RelZ, ChunkX, ChunkZ);
// Create the furnace entity, with proper BlockType and BlockMeta info:
BLOCKTYPE BlockType = cChunkDef::GetBlock(a_BlockTypes, RelX, y, RelZ);
NIBBLETYPE BlockMeta = cChunkDef::GetNibble(a_BlockMetas, RelX, y, RelZ);
std::auto_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(x, y, z, BlockType, BlockMeta, m_World));
std::auto_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, m_World));
// Load slots:
for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child))
@ -954,86 +1029,121 @@ void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cPa
// Restart cooking:
Furnace->ContinueCooking();
a_BlockEntities.push_back(Furnace.release());
return Furnace.release();
}
void cWSSAnvil::LoadHopperFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
cBlockEntity * cWSSAnvil::LoadHopperFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
// Check if the data has a proper type:
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Hopper"))
{
return;
return NULL;
}
int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{
return; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this
return NULL; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this
}
std::auto_ptr<cHopperEntity> Hopper(new cHopperEntity(x, y, z, m_World));
std::auto_ptr<cHopperEntity> Hopper(new cHopperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
LoadItemGridFromNBT(Hopper->GetContents(), a_NBT, Items);
a_BlockEntities.push_back(Hopper.release());
return Hopper.release();
}
void cWSSAnvil::LoadJukeboxFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
cBlockEntity * cWSSAnvil::LoadJukeboxFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
// Check if the data has a proper type:
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "RecordPlayer"))
{
return;
return NULL;
}
std::auto_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(x, y, z, m_World));
std::auto_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int Record = a_NBT.FindChildByName(a_TagIdx, "Record");
if (Record >= 0)
{
Jukebox->SetRecord(a_NBT.GetInt(Record));
}
a_BlockEntities.push_back(Jukebox.release());
return Jukebox.release();
}
void cWSSAnvil::LoadNoteFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
cBlockEntity * cWSSAnvil::LoadMobHeadFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
// Check if the data has a proper type:
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Skull"))
{
return;
return NULL;
}
std::auto_ptr<cNoteEntity> Note(new cNoteEntity(x, y, z, m_World));
std::auto_ptr<cMobHeadEntity> MobHead(new cMobHeadEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int currentLine = a_NBT.FindChildByName(a_TagIdx, "SkullType");
if (currentLine >= 0)
{
MobHead->SetType(static_cast<eMobHeadType>(a_NBT.GetByte(currentLine)));
}
currentLine = a_NBT.FindChildByName(a_TagIdx, "Rot");
if (currentLine >= 0)
{
MobHead->SetRotation(static_cast<eMobHeadRotation>(a_NBT.GetByte(currentLine)));
}
currentLine = a_NBT.FindChildByName(a_TagIdx, "ExtraType");
if (currentLine >= 0)
{
MobHead->SetOwner(a_NBT.GetString(currentLine));
}
return MobHead.release();
}
cBlockEntity * cWSSAnvil::LoadNoteBlockFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
// Check if the data has a proper type:
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Music"))
{
return NULL;
}
std::auto_ptr<cNoteEntity> NoteBlock(new cNoteEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int note = a_NBT.FindChildByName(a_TagIdx, "note");
if (note >= 0)
{
Note->SetPitch(a_NBT.GetByte(note));
NoteBlock->SetPitch(a_NBT.GetByte(note));
}
a_BlockEntities.push_back(Note.release());
return NoteBlock.release();
}
void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
cBlockEntity * cWSSAnvil::LoadSignFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
// Check if the data has a proper type:
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Sign"))
{
return;
return NULL;
}
std::auto_ptr<cSignEntity> Sign(new cSignEntity(E_BLOCK_SIGN_POST, x, y, z, m_World));
std::auto_ptr<cSignEntity> Sign(new cSignEntity(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, m_World));
int currentLine = a_NBT.FindChildByName(a_TagIdx, "Text1");
if (currentLine >= 0)
@ -1059,79 +1169,7 @@ void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParse
Sign->SetLine(3, a_NBT.GetString(currentLine));
}
a_BlockEntities.push_back(Sign.release());
}
void cWSSAnvil::LoadMobHeadFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
{
return;
}
std::auto_ptr<cMobHeadEntity> MobHead(new cMobHeadEntity(x, y, z, m_World));
int currentLine = a_NBT.FindChildByName(a_TagIdx, "SkullType");
if (currentLine >= 0)
{
MobHead->SetType(static_cast<eMobHeadType>(a_NBT.GetByte(currentLine)));
}
currentLine = a_NBT.FindChildByName(a_TagIdx, "Rot");
if (currentLine >= 0)
{
MobHead->SetRotation(static_cast<eMobHeadRotation>(a_NBT.GetByte(currentLine)));
}
currentLine = a_NBT.FindChildByName(a_TagIdx, "ExtraType");
if (currentLine >= 0)
{
MobHead->SetOwner(a_NBT.GetString(currentLine));
}
a_BlockEntities.push_back(MobHead.release());
}
void cWSSAnvil::LoadCommandBlockFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
{
return;
}
std::auto_ptr<cCommandBlockEntity> CmdBlock(new cCommandBlockEntity(x, y, z, m_World));
int currentLine = a_NBT.FindChildByName(a_TagIdx, "Command");
if (currentLine >= 0)
{
CmdBlock->SetCommand(a_NBT.GetString(currentLine));
}
currentLine = a_NBT.FindChildByName(a_TagIdx, "SuccessCount");
if (currentLine >= 0)
{
CmdBlock->SetResult(a_NBT.GetInt(currentLine));
}
currentLine = a_NBT.FindChildByName(a_TagIdx, "LastOutput");
if (currentLine >= 0)
{
CmdBlock->SetLastOutput(a_NBT.GetString(currentLine));
}
// TODO 2014-01-18 xdot: Figure out what TrackOutput is and parse it.
a_BlockEntities.push_back(CmdBlock.release());
return Sign.release();
}

View File

@ -125,6 +125,10 @@ protected:
/// Loads the chunk's BlockEntities from NBT data (a_Tag is the Level\\TileEntities list tag; may be -1)
void LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntitites, const cParsedNBT & a_NBT, int a_Tag, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas);
/** Loads the data for a block entity from the specified NBT tag.
Returns the loaded block entity, or NULL upon failure. */
cBlockEntity * LoadBlockEntityFromNBT(const cParsedNBT & a_NBT, int a_Tag, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
/// Loads a cItem contents from the specified NBT tag; returns true if successful. Doesn't load the Slot tag
bool LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx);
@ -134,18 +138,21 @@ protected:
*/
void LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a_NBT, int a_ItemsTagIdx, int s_SlotOffset = 0);
void LoadBeaconFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_ChestType);
void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadFlowerPotFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas);
void LoadHopperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMobHeadFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadCommandBlockFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
/** Returns true iff the "id" child tag inside the specified tag equals the specified expected type. */
bool CheckBlockEntityType(const cParsedNBT & a_NBT, int a_TagIdx, const char * a_ExpectedType);
cBlockEntity * LoadBeaconFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * LoadChestFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_ChestBlockType);
cBlockEntity * LoadCommandBlockFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * LoadDispenserFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * LoadDropperFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * LoadFlowerPotFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * LoadFurnaceFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
cBlockEntity * LoadHopperFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * LoadJukeboxFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * LoadMobHeadFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * LoadNoteBlockFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * LoadSignFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_SignBlockType);
void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, size_t a_IDTagLength);