1
0

Merge branch 'master' into BlockEntitys

This commit is contained in:
Howaner 2014-03-15 02:46:18 +01:00
commit 8316ae330e
139 changed files with 3426 additions and 1293 deletions

View File

@ -3,7 +3,13 @@ compiler:
- gcc
- clang
# Build MCServer
script: cmake . -DCMAKE_BUILD_TYPE=RELEASE -DBUILD_TOOLS=1 -DSELF_TEST=1 && make -j 2 && cd MCServer/ && (echo stop | ./MCServer)
script: cmake . -DBUILD_TOOLS=1 -DSELF_TEST=1 && make -j 2 && cd MCServer/ && (echo stop | $MCSERVER_PATH)
env:
- TRAVIS_MCSERVER_BUILD_TYPE=RELEASE MCSERVER_PATH=./MCServer
- TRAVIS_MCSERVER_BUILD_TYPE=DEBUG MCSERVER_PATH=./MCServer_debug
- TRAVIS_MCSERVER_BUILD_TYPE=RELEASE TRAVIS_MCSERVER_FORCE32=1 MCSERVER_PATH=./MCServer
- TRAVIS_MCSERVER_BUILD_TYPE=DEBUG TRAVIS_MCSERVER_FORCE32=1 MCSERVER_PATH=./MCServer_debug
# Notification Settings
notifications:

View File

@ -3,6 +3,17 @@ cmake_minimum_required (VERSION 2.6)
# Without this, the MSVC variable isn't defined for MSVC builds ( http://www.cmake.org/pipermail/cmake/2011-November/047130.html )
enable_language(CXX C)
# These env variables are used for configuring Travis CI builds.
# See https://github.com/mc-server/MCServer/pull/767
if(DEFINED ENV{TRAVIS_MCSERVER_BUILD_TYPE})
message("Setting build type to $ENV{TRAVIS_MCSERVER_BUILD_TYPE}")
set(CMAKE_BUILD_TYPE $ENV{TRAVIS_MCSERVER_BUILD_TYPE})
endif()
if(DEFINED ENV{TRAVIS_MCSERVER_FORCE32})
set(FORCE32 $ENV{TRAVIS_MCSERVER_FORCE32})
endif()
# This has to be done before any flags have been set up.
if(${BUILD_TOOLS})
add_subdirectory(Tools/MCADefrag/)

View File

@ -1,7 +1,7 @@
Code Stuff
----------
* We use C++03
* We use C++03 with some C++11 extensions (ask if you think that something would be useful)
* Use the provided wrappers for OS stuff:
- Threading is done by inheriting from `cIsThread`, thread synchronization through `cCriticalSection`, `cSemaphore` and `cEvent`, file access and filesystem operations through the `cFile` class, high-precision timers through `cTimer`, high-precision sleep through `cSleep`
* No magic numbers, use named constants:
@ -22,8 +22,10 @@ Code Stuff
- This helps prevent mistakes such as `if (a & 1 == 0)`
* White space is free, so use it freely
- "freely" as in "plentifully", not "arbitrarily"
* All `case` statements inside a `switch` need an extra indent.
* Each and every control statement deserves its braces. This helps maintainability later on when the file is edited, lines added or removed - the control logic doesn't break so easily.
- The only exception: a `switch` statement with all `case` statements being a single short statement is allowed to use the short brace-less form.
- These two rules really mean that indent is governed by braces
* Add an empty last line in all source files (GCC and GIT can complain otherwise)
* Use doxy-comments for functions in the header file, format as `/** Description */`
* Use spaces after the comment markers: `// Comment` instead of `//Comment`

6
MCServer/.gitignore vendored
View File

@ -19,10 +19,8 @@ schematics
*.pdb
memdump*
*.grab
Galleries.cfg
Galleries.example.cfg
Galleries.sqlite
ProtectionAreas.sqlite
*.cfg
*.sqlite
helgrind.log
valgrind.log
motd.txt

View File

@ -114,7 +114,7 @@ g_APIDesc =
GetBlockSkyLight = { Params = "BlockX, BlockY, BlockZ", Return = "NIBBLETYPE", Notes = "Returns the skylight at the specified absolute coords" },
GetBlockType = { Params = "BlockX, BlockY, BlockZ", Return = "BLOCKTYPE", Notes = "Returns the block type at the specified absolute coords" },
GetBlockTypeMeta = { Params = "BlockX, BlockY, BlockZ", Return = "BLOCKTYPE, NIBBLETYPE", Notes = "Returns the block type and meta at the specified absolute coords" },
GetDataTypes = { Params = "", Return = "number", Notes = "Returns the mask of datatypes that the objectis currently holding" },
GetDataTypes = { Params = "", Return = "number", Notes = "Returns the mask of datatypes that the object is currently holding" },
GetOrigin = { Params = "", Return = "OriginX, OriginY, OriginZ", Notes = "Returns the origin coords of where the area was read from." },
GetOriginX = { Params = "", Return = "number", Notes = "Returns the origin x-coord" },
GetOriginY = { Params = "", Return = "number", Notes = "Returns the origin y-coord" },
@ -129,6 +129,7 @@ g_APIDesc =
GetSizeY = { Params = "", Return = "number", Notes = "Returns the size of the held data in the y-axis" },
GetSizeZ = { Params = "", Return = "number", Notes = "Returns the size of the held data in the z-axis" },
GetVolume = { Params = "", Return = "number", Notes = "Returns the volume of the area - the total number of blocks stored within." },
GetWEOffset = { Params = "", Return = "{{Vector3i}}", Notes = "Returns the WE offset, a data value sometimes stored in the schematic files. MCServer doesn't use this value, but provides access to it using this method. The default is {0, 0, 0}."},
HasBlockLights = { Params = "", Return = "bool", Notes = "Returns true if current datatypes include blocklight" },
HasBlockMetas = { Params = "", Return = "bool", Notes = "Returns true if current datatypes include block metas" },
HasBlockSkyLights = { Params = "", Return = "bool", Notes = "Returns true if current datatypes include skylight" },
@ -178,6 +179,11 @@ g_APIDesc =
SetRelBlockSkyLight = { Params = "RelBlockX, RelBlockY, RelBlockZ, SkyLight", Return = "", Notes = "Sets the skylight at the specified relative coords" },
SetRelBlockType = { Params = "RelBlockX, RelBlockY, RelBlockZ, BlockType", Return = "", Notes = "Sets the block type at the specified relative coords" },
SetRelBlockTypeMeta = { Params = "RelBlockX, RelBlockY, RelBlockZ, BlockType, BlockMeta", Return = "", Notes = "Sets the block type and meta at the specified relative coords" },
SetWEOffset =
{
{ Params = "{{Vector3i|Offset}}", Return = "", Notes = "Sets the WE offset, a data value sometimes stored in the schematic files. Mostly used for WorldEdit. MCServer doesn't use this value, but provides access to it using this method." },
{ Params = "OffsetX, OffsetY, OffsetZ", Return = "", Notes = "Sets the WE offset, a data value sometimes stored in the schematic files. Mostly used for WorldEdit. MCServer doesn't use this value, but provides access to it using this method." },
},
Write =
{
{ Params = "World, {{Vector3i|MinPoint}}, DataTypes", Return = "bool", Notes = "Writes the area into World at the specified coords, returns true if successful" },
@ -417,6 +423,7 @@ g_APIDesc =
SetUseDefaultFinish = { Params = "bool", Return = "", Notes = "Sets the chunk to use default finishers or not" },
SetUseDefaultHeight = { Params = "bool", Return = "", Notes = "Sets the chunk to use default height generator or not" },
SetUseDefaultStructures = { Params = "bool", Return = "", Notes = "Sets the chunk to use default structures or not" },
UpdateHeightmap = { Params = "", Return = "", Notes = "Updates the heightmap to match current contents. The plugins should do that if they modify the contents and don't modify the heightmap accordingly; MCServer expects (and checks in Debug mode) that the heightmap matches the contents when the cChunkDesc is returned from a plugin." },
WriteBlockArea = { Params = "{{cBlockArea|BlockArea}}, MinRelX, MinRelY, MinRelZ", Return = "", Notes = "Writes data from the block area into the chunk" },
},
AdditionalInfo =
@ -466,6 +473,7 @@ end
Functions =
{
GetLocale = { Params = "", Return = "Locale", Notes = "Returns the locale string that the client sends as part of the protocol handshake. Can be used to provide localized strings." },
GetPing = { Params = "", Return = "number", Notes = "Returns the ping time, in ms" },
GetPlayer = { Params = "", Return = "{{cPlayer|cPlayer}}", Notes = "Returns the player object connected to this client. Note that this may be nil, for example if the player object is not yet spawned." },
GetUniqueID = { Params = "", Return = "number", Notes = "Returns the UniqueID of the client used to identify the client in the server" },
@ -474,6 +482,7 @@ end
HasPluginChannel = { Params = "ChannelName", Return = "bool", Notes = "Returns true if the client has registered to receive messages on the specified plugin channel." },
Kick = { Params = "Reason", Return = "", Notes = "Kicks the user with the specified reason" },
SendPluginMessage = { Params = "Channel, Message", Return = "", Notes = "Sends the plugin message on the specified channel." },
SetLocale = { Params = "Locale", Return = "", Notes = "Sets the locale that MCServer keeps on record. Initially the locale is initialized in protocol handshake, this function allows plugins to override the stored value (but only server-side and only until the user disconnects)." },
SetUsername = { Params = "Name", Return = "", Notes = "Sets the username" },
SetViewDistance = { Params = "ViewDistance", Return = "", Notes = "Sets the viewdistance (number of chunks loaded for the player in each direction)" },
SendBlockChange = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta", Return = "", Notes = "Sends a BlockChange packet to the client. This can be used to create fake blocks only for that player." },
@ -877,7 +886,6 @@ cFile:Delete("/usr/bin/virus.exe");
SetColor = { Return = "" },
GetColor = { Return = "string" },
AddCommand = { Return = "" },
HasCommand = { Return = "bool" },
AddPermission = { Return = "" },
InheritFrom = { Return = "" },
},
@ -1144,7 +1152,6 @@ These ItemGrids are available in the API and can be manipulated by the plugins,
IsEnchantable = { Params = "", Return = "bool", Notes = "Returns true if the item is enchantable" },
IsFullStack = { Params = "", Return = "bool", Notes = "Returns true if the item is stacked up to its maximum stacking" },
IsSameType = { Params = "cItem", Return = "bool", Notes = "Returns true if the item in the parameter is of the same ItemType as the one stored in the object. This is true even if the two items have different enchantments" },
IsStackableWith = { Params = "cItem", Return = "bool", Notes = "Returns true if the item in the parameter is stackable with the one stored in the object. Two items with different enchantments cannot be stacked" },
IsBothNameAndLoreEmpty = { Params = "", Return = "bool", Notes = "Returns if both the custom name and lore are not set." },
IsCustomNameEmpty = { Params = "", Return = "bool", Notes = "Returns if the custom name of the cItem is empty." },
IsLoreEmpty = { Params = "", Return = "", Notes = "Returns if the lore of the cItem is empty." },
@ -1650,7 +1657,6 @@ a_Player:OpenWindow(Window);
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." },
CanUseCommand = { Params = "Command", Return = "bool", Notes = "Returns true if the player is allowed to use the specified command." },
CloseWindow = { Params = "[CanRefuse]", Return = "", Notes = "Closes the currently open UI window. If CanRefuse is true (default), the window may refuse the closing." },
CloseWindowIfID = { Params = "WindowID, [CanRefuse]", Return = "", Notes = "Closes the currently open UI window if its ID matches the given ID. If CanRefuse is true (default), the window may refuse the closing." },
DeltaExperience = { Params = "DeltaXP", Return = "", Notes = "Adds or removes XP from the current XP amount. Won't allow XP to go negative. Returns the new experience, -1 on error (XP overflow)." },
@ -1727,7 +1733,6 @@ a_Player:OpenWindow(Window);
SetSprint = { Params = "IsSprinting", Return = "", Notes = "Sets whether the player is sprinting or not." },
SetSprintingMaxSpeed = { Params = "SprintingMaxSpeed", Return = "", Notes = "Sets the sprinting maximum speed (as reported by the 1.6.1+ protocols)" },
SetVisible = { Params = "IsVisible", Return = "", Notes = "Sets the player visibility to other players" },
TossItem = { Params = "DraggedItem, [Amount], [CreateType], [CreateDamage]", Return = "", Notes = "FIXME: This function will be rewritten, avoid it. It tosses an item, either from the inventory, dragged in hand (while in UI window) or a newly created one." },
XpForLevel = { Params = "XPLevel", Return = "number", Notes = "(STATIC) Returns the total amount of XP needed for the specified XP level. Inverse of CalcLevelFromXp()." },
},
Constants =
@ -1789,13 +1794,13 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
},
BindCommand =
{
{ Params = "Command, Permission, Callback, HelpString", Return = "", Notes = "(STATIC) Binds an in-game command with the specified callback function, permission and help string. By common convention, providing an empty string for HelpString will hide the command from the /help display." },
{ Params = "Command, Permission, Callback, HelpString", Return = "", Notes = "Binds an in-game command with the specified callback function, permission and help string. By common convention, providing an empty string for HelpString will hide the command from the /help display." },
{ Params = "Command, Permission, Callback, HelpString", Return = "[bool]", Notes = "(STATIC) Binds an in-game command with the specified callback function, permission and help string. By common convention, providing an empty string for HelpString will hide the command from the /help display. Returns true if successful, logs to console and returns no value on error." },
{ Params = "Command, Permission, Callback, HelpString", Return = "[bool]", Notes = "Binds an in-game command with the specified callback function, permission and help string. By common convention, providing an empty string for HelpString will hide the command from the /help display. Returns true if successful, logs to console and returns no value on error." },
},
BindConsoleCommand =
{
{ Params = "Command, Callback, HelpString", Return = "", Notes = "(STATIC) Binds a console command with the specified callback function and help string. By common convention, providing an empty string for HelpString will hide the command from the \"help\" console command." },
{ Params = "Command, Callback, HelpString", Return = "", Notes = "Binds a console command with the specified callback function and help string. By common convention, providing an empty string for HelpString will hide the command from the \"help\" console command." },
{ Params = "Command, Callback, HelpString", Return = "[bool]", Notes = "(STATIC) Binds a console command with the specified callback function and help string. By common convention, providing an empty string for HelpString will hide the command from the \"help\" console command. Returns true if successful, logs to console and returns no value on error." },
{ Params = "Command, Callback, HelpString", Return = "[bool]", Notes = "Binds a console command with the specified callback function and help string. By common convention, providing an empty string for HelpString will hide the command from the \"help\" console command. Returns true if successful, logs to console and returns no value on error." },
},
CallPlugin = { Params = "PluginName, FunctionName, [FunctionArgs...]", Return = "[FunctionRets]", Notes = "(STATIC) Calls the specified function in the specified plugin, passing all the given arguments to it. If it succeeds, it returns all the values returned by that function. If it fails, returns no value at all. Note that only strings, numbers, bools, nils and classes can be used for parameters and return values; tables and functions cannot be copied across plugins." },
DisablePlugin = { Params = "PluginName", Return = "bool", Notes = "Disables a plugin specified by its name. Returns true if the plugin was disabled, false if it wasn't found or wasn't active." },
@ -2624,7 +2629,8 @@ end
]],
Functions =
{
AddFaceDirection = {Params = "BlockX, BlockY, BlockZ, BlockFace, [IsInverse]", Return = "BlockX, BlockY, BlockZ", Notes = "Returns the coords of a block adjacent to the specified block through the specified {{Globals#BlockFace|face}}"},
AddFaceDirection = {Params = "BlockX, BlockY, BlockZ, BlockFace, [IsInverse]", Return = "BlockX, BlockY, BlockZ", Notes = "Returns the coords of a block adjacent to the specified block through the specified {{Globals#BlockFaces|face}}"},
BlockFaceToString = { Params = "{{Globals#BlockFaces|eBlockFace}}", Return = "string", Notes = "Returns the string representation of the {{Globals#BlockFaces|eBlockFace}} constant. Uses the axis-direction-based names, such as BLOCK_FACE_XP." },
BlockStringToType = {Params = "BlockTypeString", Return = "BLOCKTYPE", Notes = "Returns the block type parsed from the given string"},
ClickActionToString = {Params = "{{Globals#ClickAction|ClickAction}}", Return = "string", Notes = "Returns a string description of the ClickAction enumerated value"},
DamageTypeToString = {Params = "{{Globals#DamageType|DamageType}}", Return = "string", Notes = "Converts the {{Globals#DamageType|DamageType}} enumerated value to a string representation "},
@ -2643,9 +2649,12 @@ end
LOGINFO = {Params = "string", Notes = "Logs a text into the server console using 'info' severity (yellow text)"},
LOGWARN = {Params = "string", Notes = "Logs a text into the server console using 'warning' severity (red text); OBSOLETE, use LOGWARNING() instead"},
LOGWARNING = {Params = "string", Notes = "Logs a text into the server console using 'warning' severity (red text)"},
MirrorBlockFaceY = { Params = "{{Globals#BlockFaces|eBlockFace}}", Return = "{{Globals#BlockFaces|eBlockFace}}", Notes = "Returns the {{Globals#BlockFaces|eBlockFace}} that corresponds to the given {{Globals#BlockFaces|eBlockFace}} after mirroring it around the Y axis (or rotating 180 degrees around it)." },
NoCaseCompare = {Params = "string, string", Return = "number", Notes = "Case-insensitive string comparison; returns 0 if the strings are the same"},
NormalizeAngleDegrees = { Params = "AngleDegrees", Return = "AngleDegrees", Notes = "Returns the angle, wrapped into the [-180, +180) range." },
ReplaceString = {Params = "full-string, to-be-replaced-string, to-replace-string", Notes = "Replaces *each* occurence of to-be-replaced-string in full-string with to-replace-string"},
RotateBlockFaceCCW = { Params = "{{Globals#BlockFaces|eBlockFace}}", Return = "{{Globals#BlockFaces|eBlockFace}}", Notes = "Returns the {{Globals#BlockFaces|eBlockFace}} that corresponds to the given {{Globals#BlockFaces|eBlockFace}} after rotating it around the Y axis 90 degrees counter-clockwise." },
RotateBlockFaceCW = { Params = "{{Globals#BlockFaces|eBlockFace}}", Return = "{{Globals#BlockFaces|eBlockFace}}", Notes = "Returns the {{Globals#BlockFaces|eBlockFace}} that corresponds to the given {{Globals#BlockFaces|eBlockFace}} after rotating it around the Y axis 90 degrees clockwise." },
StringSplit = {Params = "string, SeperatorsString", Return = "array table of strings", Notes = "Seperates string into multiple by splitting every time any of the characters in SeperatorsString is encountered."},
StringSplitAndTrim = {Params = "string, SeperatorsString", Return = "array table of strings", Notes = "Seperates string into multiple by splitting every time any of the characters in SeperatorsString is encountered. Each of the separate strings is trimmed (whitespace removed from the beginning and end of the string)"},
StringToBiome = {Params = "string", Return = "{{Globals#BiomeTypes|BiomeType}}", Notes = "Converts a string representation to a {{Globals#BiomeTypes|BiomeType}} enumerated value"},
@ -2692,7 +2701,13 @@ end
Include = "^BLOCK_FACE_.*",
TextBefore = [[
These constants are used to describe individual faces of the block. They are used when the
client is interacting with a block, or when the {{cLineBlockTracer}} hits a block, etc.
client is interacting with a block in the {{OnPlayerBreakingBlock|HOOK_PLAYER_BREAKING_BLOCK}},
{{OnPlayerBrokenBlock|HOOK_PLAYER_BROKEN_BLOCK}}, {{OnPlayerLeftClick|HOOK_PLAYER_LEFT_CLICK}},
{{OnPlayerPlacedBlock|HOOK_PLAYER_PLACED_BLOCK}}, {{OnPlayerPlacingBlock|HOOK_PLAYER_PLACING_BLOCK}},
{{OnPlayerRightClick|HOOK_PLAYER_RIGHT_CLICK}}, {{OnPlayerUsedBlock|HOOK_PLAYER_USED_BLOCK}},
{{OnPlayerUsedItem|HOOK_PLAYER_USED_ITEM}}, {{OnPlayerUsingBlock|HOOK_PLAYER_USING_BLOCK}},
and {{OnPlayerUsingItem|HOOK_PLAYER_USING_ITEM}} hooks, or when the {{cLineBlockTracer}} hits a
block, etc.
]],
},
ClickAction =

View File

@ -76,6 +76,7 @@ return
DifY = { Params = "", Return = "number", Notes = "Returns the difference between the two Y coords (Y-size minus 1). Assumes sorted." },
DifZ = { Params = "", Return = "number", Notes = "Returns the difference between the two Z coords (Z-size minus 1). Assumes sorted." },
DoesIntersect = { Params = "OtherCuboid", Return = "bool", Notes = "Returns true if this cuboid has at least one voxel in common with OtherCuboid. Note that edges are considered inclusive. Assumes both sorted." },
Engulf = { Params = "{{Vector3i|Point}}", Return = "", Notes = "If needed, expands the cuboid to include the specified point. Doesn't shrink. Assumes sorted. " },
Expand = { Params = "SubMinX, AddMaxX, SubMinY, AddMaxY, SubMinZ, AddMaxZ", Return = "", Notes = "Expands the cuboid by the specified amount in each direction. Works on unsorted cuboids as well. NOTE: this function doesn't check for underflows." },
GetVolume = { Params = "", Return = "number", Notes = "Returns the volume of the cuboid, in blocks. Note that the volume considers both coords inclusive. Works on unsorted cuboids, too." },
IsCompletelyInside = { Params = "OuterCuboid", Return = "bool", Notes = "Returns true if this cuboid is completely inside (in all directions) in OuterCuboid. Assumes both sorted." },
@ -308,6 +309,7 @@ end
},
Equals = { Params = "Vector3i", Return = "bool", Notes = "Returns true if this vector is exactly the same as the specified vector." },
Length = { Params = "", Return = "number", Notes = "Returns the (euclidean) length of this vector." },
Move = { Params = "X, Y, Z", Return = "", Notes = "Moves the vector by the specified amount in each axis direction." },
Set = { Params = "x, y, z", Return = "", Notes = "Sets all the coords of the vector at once" },
SqrLength = { Params = "", Return = "number", Notes = "Returns the (euclidean) length of this vector, squared. This operation is slightly less computationally expensive than Length(), while it conserves some properties of Length(), such as comparison." },
},

View File

@ -9,22 +9,6 @@
-- Global variables:
g_Plugin = nil;
g_PluginFolder = "";
g_TrackedPages = {}; -- List of tracked pages, to be checked later whether they exist. Each item is an array of referring pagenames.
g_Stats = -- Statistics about the documentation
{
NumTotalClasses = 0,
NumUndocumentedClasses = 0,
NumTotalFunctions = 0,
NumUndocumentedFunctions = 0,
NumTotalConstants = 0,
NumUndocumentedConstants = 0,
NumTotalVariables = 0,
NumUndocumentedVariables = 0,
NumTotalHooks = 0,
NumUndocumentedHooks = 0,
NumTrackedLinks = 0,
NumInvalidLinks = 0,
}
@ -33,15 +17,34 @@ g_Stats = -- Statistics about the documentation
function Initialize(Plugin)
g_Plugin = Plugin;
Plugin:SetName("APIDump");
Plugin:SetVersion(1);
g_PluginFolder = Plugin:GetLocalFolder();
LOG("Initialising " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
g_PluginFolder = Plugin:GetLocalFolder();
cPluginManager:BindConsoleCommand("api", HandleCmdApi, "Dumps the Lua API docs into the API/ subfolder")
g_Plugin:AddWebTab("APIDump", HandleWebAdminDump)
-- TODO: Add a WebAdmin tab that has a Dump button
return true
end
function HandleCmdApi(a_Split)
DumpApi()
end
function DumpApi()
LOG("Dumping the API...")
-- Load the API descriptions from the Classes and Hooks subfolders:
-- This needs to be done each time the command is invoked because the export modifies the tables' contents
dofile(g_PluginFolder .. "/APIDesc.lua")
if (g_APIDesc.Classes == nil) then
g_APIDesc.Classes = {};
end
@ -51,6 +54,24 @@ function Initialize(Plugin)
LoadAPIFiles("/Classes/", g_APIDesc.Classes);
LoadAPIFiles("/Hooks/", g_APIDesc.Hooks);
-- Reset the stats:
g_TrackedPages = {}; -- List of tracked pages, to be checked later whether they exist. Each item is an array of referring pagenames.
g_Stats = -- Statistics about the documentation
{
NumTotalClasses = 0,
NumUndocumentedClasses = 0,
NumTotalFunctions = 0,
NumUndocumentedFunctions = 0,
NumTotalConstants = 0,
NumUndocumentedConstants = 0,
NumTotalVariables = 0,
NumUndocumentedVariables = 0,
NumTotalHooks = 0,
NumUndocumentedHooks = 0,
NumTrackedLinks = 0,
NumInvalidLinks = 0,
}
-- dump all available API functions and objects:
-- DumpAPITxt();
@ -58,7 +79,6 @@ function Initialize(Plugin)
DumpAPIHtml();
LOG("APIDump finished");
return true
end
@ -67,6 +87,9 @@ end
function LoadAPIFiles(a_Folder, a_DstTable)
assert(type(a_Folder) == "string")
assert(type(a_DstTable) == "table")
local Folder = g_PluginFolder .. a_Folder;
for idx, fnam in ipairs(cFile:GetFolderContents(Folder)) do
local FileName = Folder .. fnam;
@ -317,6 +340,11 @@ end
function DumpAPIHtml()
LOG("Dumping all available functions and constants to API subfolder...");
-- Create the output folder
if not(cFile:IsFolder("API")) then
cFile:CreateFolder("API");
end
LOG("Copying static files..");
cFile:CreateFolder("API/Static");
local localFolder = g_Plugin:GetLocalFolder();
@ -366,11 +394,6 @@ function DumpAPIHtml()
ReadDescriptions(API);
ReadHooks(Hooks);
-- Create the output folder
if not(cFile:IsFolder("API")) then
cFile:CreateFolder("API");
end
-- Create a "class index" file, write each class as a link to that file,
-- then dump class contents into class-specific file
LOG("Writing HTML files...");
@ -1428,3 +1451,18 @@ end
function HandleWebAdminDump(a_Request)
if (a_Request.PostParams["Dump"] ~= nil) then
DumpApi()
end
return
[[
<p>Pressing the button will generate the API dump on the server. Note that this can take some time.</p>
<form method="POST"><input type="submit" name="Dump" value="Dump the API"/></form>
]]
end

@ -1 +1 @@
Subproject commit 3b416b07a339b3abcbc127070d56eea05b05373d
Subproject commit 013a32a7fb3c8a6cfe0aef892d4c7394d4e1be59

View File

@ -39,6 +39,7 @@
#
# Need to list each of the four log types, otherwise all logs would get converted into apple planks (^0)
ApplePlanks, 4 = AppleLog, *
ConiferPlanks, 4 = ConiferLog, *
BirchPlanks, 4 = BirchLog, *
@ -434,6 +435,55 @@ GoldNugget, 9 = GoldIngot, *
EnchantmentTable = Obsidian, 1:3, 2:3, 3:3, 2:2 | Diamond, 1:2, 3:2 | Book, 2:1
#******************************************************#
# Fireworks & Co.
# (Best not to add non-vanilla items to this as it will cause internal firework data handling code to log warnings)
# Ballistic firework rockets - plain and with firework star, all with 1 - 3 gunpowder
FireworkRocket = Paper, * | Gunpowder, *
FireworkRocket = Paper, * | Gunpowder, * | Gunpowder, *
FireworkRocket = Paper, * | Gunpowder, * | Gunpowder, * | Gunpowder, *
FireworkRocket = FireworkStar, * | Paper, * | Gunpowder, *
FireworkRocket = FireworkStar, * | Paper, * | Gunpowder, * | Gunpowder, *
FireworkRocket = FireworkStar, * | Paper, * | Gunpowder, * | Gunpowder, * | Gunpowder, *
# Radioactive firework stars
# Plain powder and dye
FireworkStar = Gunpowder, * | Dye ^-1, *
# Powder and effect, with effect combining
FireworkStar = Gunpowder, * | Dye ^-1, * | Diamond, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Glowdust, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Glowdust, * | Diamond, *
# Powder and shape (no shape combining possible)
FireworkStar = Gunpowder, * | Dye ^-1, * | Firecharge, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Goldnugget, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Feather, *
FireworkStar = Gunpowder, * | Dye ^-1, * | SkeletonHead ^-1, *
# Power and shape (no shape combining possible), combined with effect
FireworkStar = Gunpowder, * | Dye ^-1, * | Firecharge, * | Diamond, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Firecharge, * | Glowdust, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Goldnugget, * | Diamond, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Goldnugget, * | Glowdust, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Feather, * | Diamond, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Feather, * | Glowdust, *
FireworkStar = Gunpowder, * | Dye ^-1, * | SkeletonHead ^-1, * | Diamond, *
FireworkStar = Gunpowder, * | Dye ^-1, * | SkeletonHead ^-1, * | Glowdust, *
# Power and shape (no shape combining possible), combined with effect (with effect combining)
FireworkStar = Gunpowder, * | Dye ^-1, * | Firecharge, * | Glowdust, * | Diamond, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Goldnugget, * | Glowdust, * | Diamond, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Feather, * | Glowdust, * | Diamond, *
FireworkStar = Gunpowder, * | Dye ^-1, * | SkeletonHead ^-1, * | Glowdust, * | Diamond, *
# Star fade colour-change
FireworkStar = FireworkStar, * | Dye ^-1, *
FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, *
FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, *
FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, *
FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, *
FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, *
FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, *
FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, *

View File

@ -182,14 +182,23 @@ macro(set_exe_flags)
string(REPLACE "-w" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
string(REPLACE "-w" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
string(REPLACE "-w" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
add_flags_cxx("-Wall -Wextra")
add_flags_cxx("-Wall -Wextra -Wno-unused-parameter -Wno-error=switch")
# we support non-IEEE 754 fpus so can make no guarentees about error
add_flags_cxx("-ffast-math")
# clang does not provide the __extern_always_inline macro and a part of libm depends on this when using fast-math
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# clang does not provide the __extern_always_inline macro and a part of libm depends on this when using fast-math
add_flags_cxx("-D__extern_always_inline=inline")
add_flags_cxx("-Werror -Weverything -Wno-c++98-compat-pedantic -Wno-string-conversion")
add_flags_cxx("-Wno-extra-semi -Wno-error=switch-enum -Wno-documentation")
add_flags_cxx("-Wno-error=sign-conversion -Wno-error=conversion -Wno-padded")
add_flags_cxx("-Wno-error=deprecated -Wno-error=weak-vtables -Wno-error=float-equal")
add_flags_cxx("-Wno-error=missing-prototypes -Wno-error=non-virtual-dtor")
add_flags_cxx("-Wno-error=covered-switch-default -Wno-error=shadow")
add_flags_cxx("-Wno-error=exit-time-destructors -Wno-error=missing-variable-declarations")
add_flags_cxx("-Wno-error=global-constructors -Wno-implicit-fallthrough")
add_flags_cxx("-Wno-error=unreachable-code")
endif()
endif()

View File

@ -37,7 +37,8 @@
// Some portability macros :)
#define stricmp strcasecmp
#define FORMATSTRING(formatIndex,va_argsIndex)
#else
#error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler"
@ -58,6 +59,8 @@
#define ALIGN_8
#define ALIGN_16
*/
#define FORMATSTRING(formatIndex,va_argsIndex) __attribute__((format (printf, formatIndex, va_argsIndex)))
#endif

View File

@ -131,8 +131,6 @@
} \
}
#define MAX_ENC_LEN 1024
@ -473,14 +471,14 @@ bool cConnection::SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a
bool cConnection::SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryptor, const char * a_Data, int a_Size, const char * a_Peer)
bool cConnection::SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryptor, const char * a_Data, size_t a_Size, const char * a_Peer)
{
DataLog(a_Data, a_Size, "Encrypting %d bytes to %s", a_Size, a_Peer);
const Byte * Data = (const Byte *)a_Data;
while (a_Size > 0)
{
Byte Buffer[64 KiB];
int NumBytes = (a_Size > sizeof(Buffer)) ? sizeof(Buffer) : a_Size;
size_t NumBytes = (a_Size > sizeof(Buffer)) ? sizeof(Buffer) : a_Size;
a_Encryptor.ProcessData(Buffer, Data, NumBytes);
bool res = SendData(a_Socket, (const char *)Buffer, NumBytes, a_Peer);
if (!res)
@ -2263,7 +2261,9 @@ bool cConnection::HandleServerSpawnObjectVehicle(void)
HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw);
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, DataIndicator);
AString ExtraData;
short VelocityX, VelocityY, VelocityZ;
short VelocityX = 0;
short VelocityY = 0;
short VelocityZ = 0;
if (DataIndicator != 0)
{
HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SpeedX);
@ -2697,7 +2697,7 @@ bool cConnection::ParseMetadata(cByteBuffer & a_Buffer, AString & a_Metadata)
a_Metadata.push_back(x);
while (x != 0x7f)
{
int Index = ((unsigned)((unsigned char)x)) & 0x1f; // Lower 5 bits = index
// int Index = ((unsigned)((unsigned char)x)) & 0x1f; // Lower 5 bits = index
int Type = ((unsigned)((unsigned char)x)) >> 5; // Upper 3 bits = type
int Length = 0;
switch (Type)
@ -2772,7 +2772,7 @@ void cConnection::LogMetadata(const AString & a_Metadata, size_t a_IndentCount)
{
int Index = ((unsigned)((unsigned char)a_Metadata[pos])) & 0x1f; // Lower 5 bits = index
int Type = ((unsigned)((unsigned char)a_Metadata[pos])) >> 5; // Upper 3 bits = type
int Length = 0;
// int Length = 0;
switch (Type)
{
case 0:
@ -2827,7 +2827,7 @@ void cConnection::LogMetadata(const AString & a_Metadata, size_t a_IndentCount)
ASSERT(!"Cannot parse item description from metadata");
return;
}
int After = bb.GetReadableSpace();
// int After = bb.GetReadableSpace();
int BytesConsumed = BytesLeft - bb.GetReadableSpace();
Log("%sslot[%d] = %s (%d bytes)", Indent.c_str(), Index, ItemDesc.c_str(), BytesConsumed);

View File

@ -109,7 +109,7 @@ protected:
bool SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a_Peer);
/// Sends data to the specfied socket, after encrypting it using a_Encryptor. If sending fails, prints a fail message using a_Peer and returns false
bool SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryptor, const char * a_Data, int a_Size, const char * a_Peer);
bool SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryptor, const char * a_Data, size_t a_Size, const char * a_Peer);
/// Sends data to the specfied socket, after encrypting it using a_Encryptor. If sending fails, prints a fail message using a_Peer and returns false
bool SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer);

View File

@ -37,6 +37,8 @@
// Some portability macros :)
#define stricmp strcasecmp
#define FORMATSTRING(formatIndex,va_argsIndex)
#else
@ -59,6 +61,9 @@
#define ALIGN_16
*/
#define FORMATSTRING(formatIndex,va_argsIndex) __attribute__((format (printf, formatIndex, va_argsIndex)))
#endif
@ -233,4 +238,4 @@ public:
#define LOGERROR printf
#define LOGINFO printf
#define LOGWARNING printf
#define LOGWARNING printf

View File

@ -11,6 +11,7 @@ typedef unsigned int UInt32;
typedef unsigned short UInt16;
$cfile "../Vector3.h"
$cfile "../ChunkDef.h"
$cfile "../BiomeDef.h"
@ -62,10 +63,6 @@ $cfile "../BlockEntities/MobHeadEntity.h"
$cfile "../BlockEntities/FlowerPotEntity.h"
$cfile "../WebAdmin.h"
$cfile "../Root.h"
$cfile "../Vector3f.h"
$cfile "../Vector3d.h"
$cfile "../Vector3i.h"
$cfile "../Matrix4f.h"
$cfile "../Cuboid.h"
$cfile "../BoundingBox.h"
$cfile "../Tracer.h"
@ -97,4 +94,10 @@ typedef unsigned char Byte;
// Aliases
$renaming Vector3<double> @ Vector3d
$renaming Vector3<float> @ Vector3f
$renaming Vector3<int> @ Vector3i

View File

@ -94,12 +94,20 @@ void cLuaState::Create(void)
}
m_LuaState = lua_open();
luaL_openlibs(m_LuaState);
m_IsOwned = true;
}
void cLuaState::RegisterAPILibs(void)
{
tolua_AllToLua_open(m_LuaState);
ManualBindings::Bind(m_LuaState);
DeprecatedBindings::Bind(m_LuaState);
luaopen_lsqlite3(m_LuaState);
luaopen_lxp(m_LuaState);
m_IsOwned = true;
}
@ -677,6 +685,7 @@ void cLuaState::Push(Vector3i * a_Vector)
void cLuaState::Push(void * a_Ptr)
{
UNUSED(a_Ptr);
ASSERT(IsValid());
// Investigate the cause of this - what is the callstack?
@ -733,10 +742,6 @@ void cLuaState::GetStackValue(int a_StackPos, AString & a_Value)
{
a_Value.assign(data, len);
}
else
{
a_Value.clear();
}
}
@ -1280,7 +1285,9 @@ void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header)
{
UNUSED(a_Header); // The param seems unused when compiling for release, so the compiler warns
LOGD((a_Header != NULL) ? a_Header : "Lua C API Stack contents:");
// Format string consisting only of %s is used to appease the compiler
LOGD("%s",(a_Header != NULL) ? a_Header : "Lua C API Stack contents:");
for (int i = lua_gettop(a_LuaState); i > 0; i--)
{
AString Value;

View File

@ -29,6 +29,8 @@ extern "C"
#include "lua/src/lauxlib.h"
}
#include "../Vector3.h"
@ -52,7 +54,6 @@ class cWebAdmin;
struct HTTPTemplateRequest;
class cTNTEntity;
class cCreeper;
class Vector3i;
class cHopperEntity;
class cBlockEntity;
@ -139,9 +140,14 @@ public:
/** Allows this object to be used in the same way as a lua_State *, for example in the LuaLib functions */
operator lua_State * (void) { return m_LuaState; }
/** Creates the m_LuaState, if not closed already. This state will be automatically closed in the destructor */
/** Creates the m_LuaState, if not closed already. This state will be automatically closed in the destructor.
The regular Lua libs are registered, but the MCS API is not registered (so that Lua can be used as
lite-config as well), use RegisterAPILibs() to do that. */
void Create(void);
/** Registers all the API libraries that MCS provides into m_LuaState. */
void RegisterAPILibs(void);
/** Closes the m_LuaState, if not closed already */
void Close(void);
@ -194,7 +200,7 @@ public:
void Push(const HTTPTemplateRequest * a_Request);
void Push(cTNTEntity * a_TNTEntity);
void Push(Vector3i * a_Vector);
void Push(void * a_Ptr);
NORETURNDEBUG void Push(void * a_Ptr);
void Push(cHopperEntity * a_Hopper);
void Push(cBlockEntity * a_BlockEntity);

View File

@ -1497,7 +1497,8 @@ static int tolua_cPluginManager_BindCommand(lua_State * L)
}
Plugin->BindCommand(Command, FnRef);
return 0;
lua_pushboolean(L, true);
return 1;
}
@ -1521,7 +1522,10 @@ static int tolua_cPluginManager_BindConsoleCommand(lua_State * L)
// Read the arguments to this API call:
tolua_Error tolua_err;
int idx = 1;
if (tolua_isusertype(L, 1, "cPluginManager", 0, &tolua_err))
if (
tolua_isusertype(L, 1, "cPluginManager", 0, &tolua_err) ||
tolua_isusertable(L, 1, "cPluginManager", 0, &tolua_err)
)
{
idx++;
}
@ -1561,7 +1565,8 @@ static int tolua_cPluginManager_BindConsoleCommand(lua_State * L)
}
Plugin->BindConsoleCommand(Command, FnRef);
return 0;
lua_pushboolean(L, true);
return 1;
}

View File

@ -75,6 +75,7 @@ bool cPluginLua::Initialize(void)
if (!m_LuaState.IsValid())
{
m_LuaState.Create();
m_LuaState.RegisterAPILibs();
// Inject the identification global variables into the state:
lua_pushlightuserdata(m_LuaState, this);

View File

@ -168,6 +168,7 @@ cBlockArea::cBlockArea(void) :
m_SizeX(0),
m_SizeY(0),
m_SizeZ(0),
m_WEOffset(0, 0, 0),
m_BlockTypes(NULL),
m_BlockMetas(NULL),
m_BlockLight(NULL),
@ -254,6 +255,24 @@ void cBlockArea::Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
void cBlockArea::SetWEOffset(int a_OffsetX, int a_OffsetY, int a_OffsetZ)
{
m_WEOffset.Set(a_OffsetX, a_OffsetY, a_OffsetZ);
}
void cBlockArea::SetWEOffset(const Vector3i & a_Offset)
{
m_WEOffset.Set(a_Offset.x, a_Offset.y, a_Offset.z);
}
void cBlockArea::SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ)
{
m_OriginX = a_OriginX;

View File

@ -13,13 +13,13 @@
#pragma once
#include "ForEachChunkProvider.h"
#include "Vector3.h"
// fwd:
class cCuboid;
class Vector3i;
@ -209,6 +209,8 @@ public:
void SetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight);
void SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight);
void SetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight);
void SetWEOffset (int a_OffsetX, int a_OffsetY, int a_OffsetZ);
void SetWEOffset (const Vector3i & a_Offset);
// Getters:
BLOCKTYPE GetRelBlockType (int a_RelX, int a_RelY, int a_RelZ) const;
@ -219,6 +221,7 @@ public:
NIBBLETYPE GetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ) const;
NIBBLETYPE GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const;
NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ) const;
const Vector3i & GetWEOffset (void) const {return m_WEOffset;}
void SetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
void SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
@ -299,6 +302,10 @@ protected:
int m_SizeY;
int m_SizeZ;
/** An extra data value sometimes stored in the .schematic file. Used mainly by the WorldEdit plugin.
cBlockArea doesn't use this value in any way. */
Vector3i m_WEOffset;
BLOCKTYPE * m_BlockTypes;
NIBBLETYPE * m_BlockMetas; // Each meta is stored as a separate byte for faster access
NIBBLETYPE * m_BlockLight; // Each light value is stored as a separate byte for faster access

View File

@ -42,8 +42,6 @@ cBlockInfo::~cBlockInfo()
cBlockInfo & cBlockInfo::Get(BLOCKTYPE a_Type)
{
ASSERT(a_Type < 256);
return ms_Info[a_Type];
}
@ -271,6 +269,11 @@ void cBlockInfo::Initialize(void)
ms_Info[E_BLOCK_VINES ].m_IsSnowable = false;
ms_Info[E_BLOCK_WALLSIGN ].m_IsSnowable = false;
ms_Info[E_BLOCK_WATER ].m_IsSnowable = false;
ms_Info[E_BLOCK_RAIL ].m_IsSnowable = false;
ms_Info[E_BLOCK_ACTIVATOR_RAIL ].m_IsSnowable = false;
ms_Info[E_BLOCK_POWERED_RAIL ].m_IsSnowable = false;
ms_Info[E_BLOCK_DETECTOR_RAIL ].m_IsSnowable = false;
ms_Info[E_BLOCK_COBWEB ].m_IsSnowable = false;
// Blocks that don't drop without a special tool:
@ -309,6 +312,10 @@ void cBlockInfo::Initialize(void)
ms_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_RequiresSpecialTool = true;
ms_Info[E_BLOCK_STONE_SLAB ].m_RequiresSpecialTool = true;
ms_Info[E_BLOCK_VINES ].m_RequiresSpecialTool = true;
ms_Info[E_BLOCK_FURNACE ].m_RequiresSpecialTool = true;
ms_Info[E_BLOCK_LIT_FURNACE ].m_RequiresSpecialTool = true;
ms_Info[E_BLOCK_ANVIL ].m_RequiresSpecialTool = true;
ms_Info[E_BLOCK_ENCHANTMENT_TABLE ].m_RequiresSpecialTool = true;
// Nonsolid blocks:

View File

@ -4,6 +4,7 @@
#include "BlockHandler.h"
#include "ChunkInterface.h"
#include "WorldInterface.h"
#include "MetaRotater.h"
#include "../Entities/Player.h"
@ -11,11 +12,11 @@
class cBlockBedHandler :
public cBlockHandler
public cMetaRotater<cBlockHandler, 0x3, 0x02, 0x03, 0x00, 0x01, true>
{
public:
cBlockBedHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType)
: cMetaRotater<cBlockHandler, 0x3, 0x02, 0x03, 0x00, 0x01,true>(a_BlockType)
{
}

View File

@ -2,16 +2,17 @@
#include "BlockHandler.h"
#include "Chunk.h"
#include "MetaRotater.h"
class cBlockButtonHandler :
public cBlockHandler
public cMetaRotater<cBlockHandler, 0x07, 0x04, 0x01, 0x03, 0x02, true>
{
public:
cBlockButtonHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType)
: cMetaRotater<cBlockHandler, 0x07, 0x04, 0x01, 0x03, 0x02, true>(a_BlockType)
{
}

View File

@ -4,17 +4,18 @@
#include "BlockEntity.h"
#include "../BlockArea.h"
#include "../Entities/Player.h"
#include "MetaRotater.h"
class cBlockChestHandler :
public cBlockEntityHandler
public cMetaRotater<cBlockEntityHandler, 0x07, 0x04, 0x01, 0x03, 0x02, true>
{
public:
cBlockChestHandler(BLOCKTYPE a_BlockType)
: cBlockEntityHandler(a_BlockType)
: cMetaRotater<cBlockEntityHandler, 0x07, 0x04, 0x01, 0x03, 0x02, true>(a_BlockType)
{
}

View File

@ -3,17 +3,18 @@
#include "BlockHandler.h"
#include "BlockRedstoneRepeater.h"
#include "MetaRotater.h"
class cBlockComparatorHandler :
public cBlockHandler
public cMetaRotater<cBlockHandler, 0x03, 0x00, 0x01, 0x02, 0x03, true>
{
public:
cBlockComparatorHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType)
: cMetaRotater<cBlockHandler, 0x03, 0x00, 0x01, 0x02, 0x03, true>(a_BlockType)
{
}

View File

@ -9,7 +9,7 @@
cBlockDoorHandler::cBlockDoorHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType)
: super(a_BlockType)
{
}

View File

@ -4,13 +4,15 @@
#include "BlockHandler.h"
#include "../Entities/Player.h"
#include "Chunk.h"
#include "MetaRotater.h"
class cBlockDoorHandler :
public cBlockHandler
public cMetaRotater<cBlockHandler, 0x03, 0x01, 0x02, 0x03, 0x00, true>
{
typedef cMetaRotater<cBlockHandler, 0x03, 0x01, 0x02, 0x03, 0x00, true> super;
public:
cBlockDoorHandler(BLOCKTYPE a_BlockType);
@ -168,6 +170,60 @@ public:
}
virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
{
if (a_Meta & 0x08)
{
return a_Meta;
}
else
{
return super::MetaRotateCCW(a_Meta);
}
}
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
{
if (a_Meta & 0x08)
{
return a_Meta;
}
else
{
return super::MetaRotateCW(a_Meta);
}
}
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
{
if (a_Meta & 0x08)
{
return a_Meta;
}
else
{
return super::MetaMirrorXY(a_Meta);
}
}
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
{
if (a_Meta & 0x08)
{
return a_Meta;
}
else
{
return super::MetaMirrorYZ(a_Meta);
}
}
} ;

View File

@ -6,17 +6,18 @@
#pragma once
#include "../Piston.h"
#include "MetaRotater.h"
class cBlockDropSpenserHandler :
public cBlockEntityHandler
public cMetaRotater<cBlockEntityHandler, 0x07, 0x02, 0x05, 0x03, 0x04>
{
public:
cBlockDropSpenserHandler(BLOCKTYPE a_BlockType) :
cBlockEntityHandler(a_BlockType)
cMetaRotater<cBlockEntityHandler, 0x07, 0x02, 0x05, 0x03, 0x04>(a_BlockType)
{
}
@ -34,6 +35,20 @@ public:
a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetYaw(), a_Player->GetPitch());
return true;
}
virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override
{
// Bit 0x08 is a flag. Lowest three bits are position. 0x08 == 1000
NIBBLETYPE OtherMeta = a_Meta & 0x08;
// Mirrors defined by by a table. (Source, mincraft.gamepedia.com) 0x07 == 0111
switch (a_Meta & 0x07)
{
case 0x00: return 0x01 + OtherMeta; // Down -> Up
case 0x01: return 0x00 + OtherMeta; // Up -> Down
}
// Not Facing Up or Down; No change.
return a_Meta;
}
} ;

View File

@ -2,17 +2,17 @@
#pragma once
#include "BlockEntity.h"
#include "MetaRotater.h"
class cBlockEnderchestHandler :
public cBlockEntityHandler
public cMetaRotater<cBlockEntityHandler, 0x07, 0x02, 0x05, 0x03, 0x04>
{
public:
cBlockEnderchestHandler(BLOCKTYPE a_BlockType)
: cBlockEntityHandler(a_BlockType)
: cMetaRotater<cBlockEntityHandler, 0x07, 0x02, 0x05, 0x03, 0x04>(a_BlockType)
{
}

View File

@ -2,17 +2,17 @@
#pragma once
#include "BlockHandler.h"
#include "MetaRotater.h"
class cBlockFenceGateHandler :
public cBlockHandler
public cMetaRotater<cBlockHandler, 0x03, 0x02, 0x03, 0x00, 0x01, true>
{
public:
cBlockFenceGateHandler(BLOCKTYPE a_BlockType) :
cBlockHandler(a_BlockType)
cMetaRotater<cBlockHandler, 0x03, 0x02, 0x03, 0x00, 0x01, true>(a_BlockType)
{
}

View File

@ -146,6 +146,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_NETHER_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_NETHER_PORTAL: return new cBlockPortalHandler (a_BlockType);
case E_BLOCK_NETHER_WART: return new cBlockNetherWartHandler (a_BlockType);
case E_BLOCK_NETHER_QUARTZ_ORE: return new cBlockOreHandler (a_BlockType);
case E_BLOCK_NEW_LEAVES: return new cBlockNewLeavesHandler (a_BlockType);
case E_BLOCK_NEW_LOG: return new cBlockSidewaysHandler (a_BlockType);
case E_BLOCK_NOTE_BLOCK: return new cBlockNoteHandler (a_BlockType);

View File

@ -23,6 +23,8 @@ class cBlockHandler
{
public:
cBlockHandler(BLOCKTYPE a_BlockType);
virtual ~cBlockHandler() {}
/// Called when the block gets ticked either by a random tick or by a queued tick.
/// Note that the coords are chunk-relative!

View File

@ -2,17 +2,17 @@
#pragma once
#include "BlockHandler.h"
#include "MetaRotater.h"
class cBlockStairsHandler :
public cBlockHandler
public cMetaRotater<cBlockHandler, 0x03, 0x03, 0x00, 0x02, 0x01, true>
{
public:
cBlockStairsHandler(BLOCKTYPE a_BlockType) :
cBlockHandler(a_BlockType)
cMetaRotater<cBlockHandler, 0x03, 0x03, 0x00, 0x02, 0x01, true>(a_BlockType)
{
}
@ -25,6 +25,14 @@ public:
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
UNUSED(a_ChunkInterface);
UNUSED(a_BlockX);
UNUSED(a_BlockY);
UNUSED(a_BlockZ);
UNUSED(a_CursorX);
UNUSED(a_CursorY);
UNUSED(a_CursorZ);
UNUSED(a_BlockMeta);
a_BlockType = m_BlockType;
a_BlockMeta = RotationToMetaData(a_Player->GetYaw());
switch (a_BlockFace)
@ -96,54 +104,6 @@ public:
}
virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
{
// Bits 3 and 4 stay, the rest is swapped around according to a table:
NIBBLETYPE TopBits = (a_Meta & 0x0c);
switch (a_Meta & 0x03)
{
case 0x00: return TopBits | 0x03; // East -> North
case 0x01: return TopBits | 0x02; // West -> South
case 0x02: return TopBits | 0x00; // South -> East
case 0x03: return TopBits | 0x01; // North -> West
}
// Not reachable, but to avoid a compiler warning:
return 0;
}
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
{
// Bits 3 and 4 stay, the rest is swapped around according to a table:
NIBBLETYPE TopBits = (a_Meta & 0x0c);
switch (a_Meta & 0x03)
{
case 0x00: return TopBits | 0x02; // East -> South
case 0x01: return TopBits | 0x03; // West -> North
case 0x02: return TopBits | 0x01; // South -> West
case 0x03: return TopBits | 0x00; // North -> East
}
// Not reachable, but to avoid a compiler warning:
return 0;
}
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
{
// Bits 3 and 4 stay, the rest is swapped around according to a table:
NIBBLETYPE TopBits = (a_Meta & 0x0c);
switch (a_Meta & 0x03)
{
case 0x00: return TopBits | 0x00; // East -> East
case 0x01: return TopBits | 0x01; // West -> West
case 0x02: return TopBits | 0x03; // South -> North
case 0x03: return TopBits | 0x02; // North -> South
}
// Not reachable, but to avoid a compiler warning:
return 0;
}
virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override
{
// Toggle bit 3:
@ -151,20 +111,6 @@ public:
}
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
{
// Bits 3 and 4 stay, the rest is swapped around according to a table:
NIBBLETYPE TopBits = (a_Meta & 0x0c);
switch (a_Meta & 0x03)
{
case 0x00: return TopBits | 0x01; // East -> West
case 0x01: return TopBits | 0x00; // West -> East
case 0x02: return TopBits | 0x02; // South -> South
case 0x03: return TopBits | 0x03; // North -> North
}
// Not reachable, but to avoid a compiler warning:
return 0;
}
} ;

View File

@ -2,17 +2,17 @@
#include "BlockHandler.h"
#include "../Chunk.h"
#include "MetaRotater.h"
class cBlockTorchHandler :
public cBlockHandler
public cMetaRotater<cBlockHandler, 0x7, 0x4, 0x1, 0x3, 0x2>
{
public:
cBlockTorchHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType)
: cMetaRotater<cBlockHandler, 0x7, 0x4, 0x1, 0x3, 0x2>(a_BlockType)
{
}
@ -185,67 +185,6 @@ public:
{
return "step.wood";
}
virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
{
// Bit 4 stays, the rest is swapped around according to a table:
NIBBLETYPE TopBits = (a_Meta & 0x08);
switch (a_Meta & 0x07)
{
case 0x01: return TopBits | 0x04; // East -> North
case 0x02: return TopBits | 0x03; // West -> South
case 0x03: return TopBits | 0x01; // South -> East
case 0x04: return TopBits | 0x02; // North -> West
default: return a_Meta; // Floor -> Floor
}
}
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
{
// Bit 4 stays, the rest is swapped around according to a table:
NIBBLETYPE TopBits = (a_Meta & 0x08);
switch (a_Meta & 0x07)
{
case 0x01: return TopBits | 0x03; // East -> South
case 0x02: return TopBits | 0x04; // West -> North
case 0x03: return TopBits | 0x02; // South -> West
case 0x04: return TopBits | 0x01; // North -> East
default: return a_Meta; // Floor -> Floor
}
}
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
{
// Bit 4 stays, the rest is swapped around according to a table:
NIBBLETYPE TopBits = (a_Meta & 0x08);
switch (a_Meta & 0x07)
{
case 0x03: return TopBits | 0x04; // South -> North
case 0x04: return TopBits | 0x03; // North -> South
default: return a_Meta; // Keep the rest
}
}
// Mirroring around the XZ plane doesn't make sense for floor torches,
// the others stay the same, so let's keep all the metas the same.
// The base class does tht for us, no need to override MetaMirrorXZ()
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
{
// Bit 4 stays, the rest is swapped around according to a table:
NIBBLETYPE TopBits = (a_Meta & 0x08);
switch (a_Meta & 0x07)
{
case 0x01: return TopBits | 0x02; // East -> West
case 0x02: return TopBits | 0x01; // West -> East
default: return a_Meta; // Keep the rest
}
}
} ;

View File

@ -1,8 +1,7 @@
#pragma once
#include "BlockHandler.h"
#include "MetaRotater.h"
@ -24,6 +23,10 @@ public:
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
UNUSED(a_Player);
UNUSED(a_CursorX);
UNUSED(a_CursorY);
UNUSED(a_CursorZ);
// TODO: Disallow placement where the vine doesn't attach to something properly
BLOCKTYPE BlockType = 0;
NIBBLETYPE BlockMeta;
@ -162,11 +165,17 @@ public:
return false;
}
virtual void OnUpdate(cWorld * a_World, int X, int Y, int Z)
virtual void OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_BlockPluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
{
if (a_World->GetBlock(X, Y - 1, Z) == E_BLOCK_AIR)
UNUSED(a_ChunkInterface);
UNUSED(a_WorldInterface);
UNUSED(a_BlockPluginInterface);
BLOCKTYPE Block;
a_Chunk.UnboundedRelGetBlockType(a_RelX, a_RelY - 1, a_RelZ, Block);
if (Block == E_BLOCK_AIR)
{
a_World->SetBlock(X, Y - 1, Z, E_BLOCK_VINES, a_World->GetBlockMeta(X, Y, Z));
a_Chunk.UnboundedRelSetBlock(a_RelX, a_RelY - 1, a_RelZ, E_BLOCK_VINES, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
}
}
@ -180,8 +189,8 @@ public:
{
return ((a_Meta << 1) | (a_Meta >> 3)) & 0x0f; // Rotate bits to the left
}
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
{
// Bits 2 and 4 stay, bits 1 and 3 swap
@ -194,6 +203,7 @@ public:
// Bits 1 and 3 stay, bits 2 and 4 swap
return ((a_Meta & 0x05) | ((a_Meta & 0x02) << 2) | ((a_Meta & 0x08) >> 2));
}
} ;

120
src/Blocks/MetaRotater.h Normal file
View File

@ -0,0 +1,120 @@
// MetaRotater.h
// Provides a mixin for rotations and reflections
#pragma once
// MSVC generates warnings for the templated AssertIfNotMatched parameter conditions, so disable it:
#ifdef _MSC_VER
#pragma warning(disable: 4127) // Conditional expression is constant
#endif
/*
Provides a mixin for rotations and reflections following the standard pattern of apply mask then use case.
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>
class cMetaRotater : public Base
{
public:
cMetaRotater(BLOCKTYPE a_BlockType) :
Base(a_BlockType)
{}
virtual ~cMetaRotater() {}
virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override;
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override;
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override;
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override;
};
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotater<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaRotateCW(NIBBLETYPE a_Meta)
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
switch (a_Meta & BitMask)
{
case South: return West | OtherMeta;
case West: return North | OtherMeta;
case North: return East | OtherMeta;
case East: return South | OtherMeta;
}
if (AssertIfNotMatched)
{
ASSERT(!"Invalid Meta value");
}
return a_Meta;
}
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotater<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaRotateCCW(NIBBLETYPE a_Meta)
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
switch (a_Meta & BitMask)
{
case South: return East | OtherMeta;
case East: return North | OtherMeta;
case North: return West | OtherMeta;
case West: return South | OtherMeta;
}
if (AssertIfNotMatched)
{
ASSERT(!"Invalid Meta value");
}
return a_Meta;
}
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotater<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaMirrorXY(NIBBLETYPE a_Meta)
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
switch (a_Meta & BitMask)
{
case South: return North | OtherMeta;
case North: return South | OtherMeta;
}
// Not Facing North or South; No change.
return a_Meta;
}
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotater<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaMirrorYZ(NIBBLETYPE a_Meta)
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
switch (a_Meta & BitMask)
{
case West: return East | OtherMeta;
case East: return West | OtherMeta;
}
// Not Facing East or West; No change.
return a_Meta;
}

View File

@ -10,7 +10,7 @@
#if SELF_TEST
#ifdef SELF_TEST
/** A simple self-test that is executed on program start, used to verify bbox functionality */
static class SelfTest_BoundingBox

View File

@ -8,7 +8,7 @@
#pragma once
#include "Vector3d.h"
#include "Vector3.h"
#include "Defines.h"

View File

@ -44,10 +44,10 @@
#if 0
#ifdef SELF_TEST
/// Self-test of the VarInt-reading and writing code
class cByteBufferSelfTest
static class cByteBufferSelfTest
{
public:
cByteBufferSelfTest(void)
@ -62,11 +62,11 @@ public:
cByteBuffer buf(50);
buf.Write("\x05\xac\x02\x00", 4);
UInt32 v1;
assert(buf.ReadVarInt(v1) && (v1 == 5));
assert_test(buf.ReadVarInt(v1) && (v1 == 5));
UInt32 v2;
assert(buf.ReadVarInt(v2) && (v2 == 300));
assert_test(buf.ReadVarInt(v2) && (v2 == 300));
UInt32 v3;
assert(buf.ReadVarInt(v3) && (v3 == 0));
assert_test(buf.ReadVarInt(v3) && (v3 == 0));
}
void TestWrite(void)
@ -77,8 +77,8 @@ public:
buf.WriteVarInt(0);
AString All;
buf.ReadAll(All);
assert(All.size() == 4);
assert(memcmp(All.data(), "\x05\xac\x02\x00", All.size()) == 0);
assert_test(All.size() == 4);
assert_test(memcmp(All.data(), "\x05\xac\x02\x00", All.size()) == 0);
}
void TestWrap(void)
@ -86,18 +86,18 @@ public:
cByteBuffer buf(3);
for (int i = 0; i < 1000; i++)
{
int FreeSpace = buf.GetFreeSpace();
assert(buf.GetReadableSpace() == 0);
assert(FreeSpace > 0);
assert(buf.Write("a", 1));
assert(buf.CanReadBytes(1));
assert(buf.GetReadableSpace() == 1);
size_t FreeSpace = buf.GetFreeSpace();
assert_test(buf.GetReadableSpace() == 0);
assert_test(FreeSpace > 0);
assert_test(buf.Write("a", 1));
assert_test(buf.CanReadBytes(1));
assert_test(buf.GetReadableSpace() == 1);
unsigned char v = 0;
assert(buf.ReadByte(v));
assert(v == 'a');
assert(buf.GetReadableSpace() == 0);
assert_test(buf.ReadByte(v));
assert_test(v == 'a');
assert_test(buf.GetReadableSpace() == 0);
buf.CommitRead();
assert(buf.GetFreeSpace() == FreeSpace); // We're back to normal
assert_test(buf.GetFreeSpace() == FreeSpace); // We're back to normal
}
}
@ -171,21 +171,22 @@ cByteBuffer::~cByteBuffer()
bool cByteBuffer::Write(const char * a_Bytes, int a_Count)
bool cByteBuffer::Write(const char * a_Bytes, size_t a_Count)
{
CHECK_THREAD;
CheckValid();
// Store the current free space for a check after writing:
int CurFreeSpace = GetFreeSpace();
int CurReadableSpace = GetReadableSpace();
int WrittenBytes = 0;
size_t CurFreeSpace = GetFreeSpace();
size_t CurReadableSpace = GetReadableSpace();
size_t WrittenBytes = 0;
if (CurFreeSpace < a_Count)
{
return false;
}
int TillEnd = m_BufferSize - m_WritePos;
ASSERT(m_BufferSize >= m_WritePos);
size_t TillEnd = m_BufferSize - m_WritePos;
if (TillEnd <= a_Count)
{
// Need to wrap around the ringbuffer end
@ -216,16 +217,20 @@ bool cByteBuffer::Write(const char * a_Bytes, int a_Count)
int cByteBuffer::GetFreeSpace(void) const
size_t cByteBuffer::GetFreeSpace(void) const
{
CHECK_THREAD;
CheckValid();
if (m_WritePos >= m_DataStart)
{
// Wrap around the buffer end:
ASSERT(m_BufferSize >= m_WritePos);
ASSERT((m_BufferSize - m_WritePos + m_DataStart) >= 1);
return m_BufferSize - m_WritePos + m_DataStart - 1;
}
// Single free space partition:
ASSERT(m_BufferSize >= m_WritePos);
ASSERT(m_BufferSize - m_WritePos >= 1);
return m_DataStart - m_WritePos - 1;
}
@ -234,10 +239,12 @@ int cByteBuffer::GetFreeSpace(void) const
/// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes()
int cByteBuffer::GetUsedSpace(void) const
size_t cByteBuffer::GetUsedSpace(void) const
{
CHECK_THREAD;
CheckValid();
ASSERT(m_BufferSize >= GetFreeSpace());
ASSERT((m_BufferSize - GetFreeSpace()) >= 1);
return m_BufferSize - GetFreeSpace() - 1;
}
@ -246,16 +253,18 @@ int cByteBuffer::GetUsedSpace(void) const
/// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already)
int cByteBuffer::GetReadableSpace(void) const
size_t cByteBuffer::GetReadableSpace(void) const
{
CHECK_THREAD;
CheckValid();
if (m_ReadPos > m_WritePos)
{
// Wrap around the buffer end:
ASSERT(m_BufferSize >= m_ReadPos);
return m_BufferSize - m_ReadPos + m_WritePos;
}
// Single readable space partition:
ASSERT(m_WritePos >= m_ReadPos);
return m_WritePos - m_ReadPos ;
}
@ -263,7 +272,7 @@ int cByteBuffer::GetReadableSpace(void) const
bool cByteBuffer::CanReadBytes(int a_Count) const
bool cByteBuffer::CanReadBytes(size_t a_Count) const
{
CHECK_THREAD;
CheckValid();
@ -274,7 +283,7 @@ bool cByteBuffer::CanReadBytes(int a_Count) const
bool cByteBuffer::CanWriteBytes(int a_Count) const
bool cByteBuffer::CanWriteBytes(size_t a_Count) const
{
CHECK_THREAD;
CheckValid();
@ -450,7 +459,7 @@ bool cByteBuffer::ReadVarUTF8String(AString & a_Value)
}
if (Size > MAX_STRING_SIZE)
{
LOGWARNING("%s: String too large: %llu (%llu KiB)", __FUNCTION__, Size, Size / 1024);
LOGWARNING("%s: String too large: %u (%u KiB)", __FUNCTION__, Size, Size / 1024);
}
return ReadString(a_Value, (int)Size);
}
@ -650,15 +659,14 @@ bool cByteBuffer::WriteLEInt(int a_Value)
bool cByteBuffer::ReadBuf(void * a_Buffer, int a_Count)
bool cByteBuffer::ReadBuf(void * a_Buffer, size_t a_Count)
{
CHECK_THREAD;
CheckValid();
ASSERT(a_Count >= 0);
NEEDBYTES(a_Count);
char * Dst = (char *)a_Buffer; // So that we can do byte math
int BytesToEndOfBuffer = m_BufferSize - m_ReadPos;
ASSERT(BytesToEndOfBuffer >= 0); // Sanity check
ASSERT(m_BufferSize >= m_ReadPos);
size_t BytesToEndOfBuffer = m_BufferSize - m_ReadPos;
if (BytesToEndOfBuffer <= a_Count)
{
// Reading across the ringbuffer end, read the first part and adjust parameters:
@ -684,14 +692,14 @@ bool cByteBuffer::ReadBuf(void * a_Buffer, int a_Count)
bool cByteBuffer::WriteBuf(const void * a_Buffer, int a_Count)
bool cByteBuffer::WriteBuf(const void * a_Buffer, size_t a_Count)
{
CHECK_THREAD;
CheckValid();
ASSERT(a_Count >= 0);
PUTBYTES(a_Count);
char * Src = (char *)a_Buffer; // So that we can do byte math
int BytesToEndOfBuffer = m_BufferSize - m_WritePos;
ASSERT(m_BufferSize >= m_ReadPos);
size_t BytesToEndOfBuffer = m_BufferSize - m_WritePos;
if (BytesToEndOfBuffer <= a_Count)
{
// Reading across the ringbuffer end, read the first part and adjust parameters:
@ -714,22 +722,22 @@ bool cByteBuffer::WriteBuf(const void * a_Buffer, int a_Count)
bool cByteBuffer::ReadString(AString & a_String, int a_Count)
bool cByteBuffer::ReadString(AString & a_String, size_t a_Count)
{
CHECK_THREAD;
CheckValid();
ASSERT(a_Count >= 0);
NEEDBYTES(a_Count);
a_String.clear();
a_String.reserve(a_Count);
int BytesToEndOfBuffer = m_BufferSize - m_ReadPos;
ASSERT(BytesToEndOfBuffer >= 0); // Sanity check
ASSERT(m_BufferSize >= m_ReadPos);
size_t BytesToEndOfBuffer = m_BufferSize - m_ReadPos;
if (BytesToEndOfBuffer <= a_Count)
{
// Reading across the ringbuffer end, read the first part and adjust parameters:
if (BytesToEndOfBuffer > 0)
{
a_String.assign(m_Buffer + m_ReadPos, BytesToEndOfBuffer);
ASSERT(a_Count >= BytesToEndOfBuffer);
a_Count -= BytesToEndOfBuffer;
}
m_ReadPos = 0;
@ -759,7 +767,7 @@ bool cByteBuffer::ReadUTF16String(AString & a_String, int a_NumChars)
{
return false;
}
RawBEToUTF8((short *)(RawData.data()), a_NumChars, a_String);
RawBEToUTF8(RawData.data(), a_NumChars, a_String);
return true;
}
@ -767,11 +775,10 @@ bool cByteBuffer::ReadUTF16String(AString & a_String, int a_NumChars)
bool cByteBuffer::SkipRead(int a_Count)
bool cByteBuffer::SkipRead(size_t a_Count)
{
CHECK_THREAD;
CheckValid();
ASSERT(a_Count >= 0);
if (!CanReadBytes(a_Count))
{
return false;
@ -809,6 +816,7 @@ bool cByteBuffer::ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes)
size_t num = (a_NumBytes > sizeof(buf)) ? sizeof(buf) : a_NumBytes;
VERIFY(ReadBuf(buf, num));
VERIFY(a_Dst.Write(buf, num));
ASSERT(a_NumBytes >= num);
a_NumBytes -= num;
}
return true;
@ -846,13 +854,15 @@ void cByteBuffer::ReadAgain(AString & a_Out)
// Used by ProtoProxy to repeat communication twice, once for parsing and the other time for the remote party
CHECK_THREAD;
CheckValid();
int DataStart = m_DataStart;
size_t DataStart = m_DataStart;
if (m_ReadPos < m_DataStart)
{
// Across the ringbuffer end, read the first part and adjust next part's start:
ASSERT(m_BufferSize >= m_DataStart);
a_Out.append(m_Buffer + m_DataStart, m_BufferSize - m_DataStart);
DataStart = 0;
}
ASSERT(m_ReadPos >= DataStart);
a_Out.append(m_Buffer + DataStart, m_ReadPos - DataStart);
}
@ -860,7 +870,7 @@ void cByteBuffer::ReadAgain(AString & a_Out)
void cByteBuffer::AdvanceReadPos(int a_Count)
void cByteBuffer::AdvanceReadPos(size_t a_Count)
{
CHECK_THREAD;
CheckValid();

View File

@ -31,25 +31,25 @@ public:
~cByteBuffer();
/// Writes the bytes specified to the ringbuffer. Returns true if successful, false if not
bool Write(const char * a_Bytes, int a_Count);
bool Write(const char * a_Bytes, size_t a_Count);
/// Returns the number of bytes that can be successfully written to the ringbuffer
int GetFreeSpace(void) const;
size_t GetFreeSpace(void) const;
/// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes()
int GetUsedSpace(void) const;
size_t GetUsedSpace(void) const;
/// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already)
int GetReadableSpace(void) const;
size_t GetReadableSpace(void) const;
/// Returns the current data start index. For debugging purposes.
int GetDataStart(void) const { return m_DataStart; }
size_t GetDataStart(void) const { return m_DataStart; }
/// Returns true if the specified amount of bytes are available for reading
bool CanReadBytes(int a_Count) const;
bool CanReadBytes(size_t a_Count) const;
/// Returns true if the specified amount of bytes are available for writing
bool CanWriteBytes(int a_Count) const;
bool CanWriteBytes(size_t a_Count) const;
// Read the specified datatype and advance the read pointer; return true if successfully read:
bool ReadChar (char & a_Value);
@ -92,19 +92,19 @@ public:
bool WriteLEInt (int a_Value);
/// Reads a_Count bytes into a_Buffer; returns true if successful
bool ReadBuf(void * a_Buffer, int a_Count);
bool ReadBuf(void * a_Buffer, size_t a_Count);
/// Writes a_Count bytes into a_Buffer; returns true if successful
bool WriteBuf(const void * a_Buffer, int a_Count);
bool WriteBuf(const void * a_Buffer, size_t a_Count);
/// Reads a_Count bytes into a_String; returns true if successful
bool ReadString(AString & a_String, int a_Count);
bool ReadString(AString & a_String, size_t a_Count);
/// Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String
bool ReadUTF16String(AString & a_String, int a_NumChars);
/// Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer
bool SkipRead(int a_Count);
bool SkipRead(size_t a_Count);
/// Reads all available data into a_Data
void ReadAll(AString & a_Data);
@ -126,18 +126,18 @@ public:
protected:
char * m_Buffer;
int m_BufferSize; // Total size of the ringbuffer
size_t m_BufferSize; // Total size of the ringbuffer
#ifdef _DEBUG
volatile unsigned long m_ThreadID; // Thread that is currently accessing the object, checked via cSingleThreadAccessChecker
#endif // _DEBUG
int m_DataStart; // Where the data starts in the ringbuffer
int m_WritePos; // Where the data ends in the ringbuffer
int m_ReadPos; // Where the next read will start in the ringbuffer
size_t m_DataStart; // Where the data starts in the ringbuffer
size_t m_WritePos; // Where the data ends in the ringbuffer
size_t m_ReadPos; // Where the next read will start in the ringbuffer
/// Advances the m_ReadPos by a_Count bytes
void AdvanceReadPos(int a_Count);
void AdvanceReadPos(size_t a_Count);
} ;

View File

@ -1,4 +1,3 @@
cmake_minimum_required (VERSION 2.8.2)
project (MCServer)
@ -10,7 +9,6 @@ set(FOLDERS OSSupport HTTPServer Items Blocks Protocol Generating)
set(FOLDERS ${FOLDERS} WorldStorage Mobs Entities Simulator UI BlockEntities)
if (NOT MSVC)
#Bindings needs to reference other folders so are done here
@ -67,7 +65,6 @@ if (NOT MSVC)
Inventory.h
Item.h
ItemGrid.h
Matrix4f.h
Mobs/Monster.h
OSSupport/File.h
Root.h
@ -75,9 +72,7 @@ if (NOT MSVC)
StringUtils.h
Tracer.h
UI/Window.h
Vector3d.h
Vector3f.h
Vector3i.h
Vector3.h
WebAdmin.h
World.h
)

View File

@ -9,7 +9,7 @@
#pragma once
#include "Vector3i.h"
#include "Vector3.h"
#include "BiomeDef.h"
@ -62,16 +62,12 @@ typedef unsigned char HEIGHTTYPE;
class cChunkDef
{
public:
enum
{
// Chunk dimensions:
Width = 16,
Height = 256,
NumBlocks = Width * Height * Width,
/// If the data is collected into a single buffer, how large it needs to be:
BlockDataSize = cChunkDef::NumBlocks * 2 + (cChunkDef::NumBlocks / 2), // 2.5 * numblocks
} ;
// Chunk dimensions:
static const int Width = 16;
static const int Height = 256;
static const int NumBlocks = Width * Height * Width;
/// If the data is collected into a single buffer, how large it needs to be:
static const int BlockDataSize = cChunkDef::NumBlocks * 2 + (cChunkDef::NumBlocks / 2); // 2.5 * numblocks
/// The type used for any heightmap operations and storage; idx = x + Width * z; Height points to the highest non-air block in the column
typedef HEIGHTTYPE HeightMap[Width * Width];
@ -116,7 +112,7 @@ public:
}
inline static unsigned int MakeIndex(int x, int y, int z )
inline static int MakeIndex(int x, int y, int z )
{
if (
(x < Width) && (x > -1) &&
@ -132,7 +128,7 @@ public:
}
inline static unsigned int MakeIndexNoCheck(int x, int y, int z)
inline static int MakeIndexNoCheck(int x, int y, int z)
{
#if AXIS_ORDER == AXIS_ORDER_XZY
// For some reason, NOT using the Horner schema is faster. Weird.
@ -255,7 +251,7 @@ public:
ASSERT(!"cChunkDef::SetNibble(): index out of range!");
return;
}
a_Buffer[a_BlockIdx / 2] = (
a_Buffer[a_BlockIdx / 2] = static_cast<NIBBLETYPE>(
(a_Buffer[a_BlockIdx / 2] & (0xf0 >> ((a_BlockIdx & 1) * 4))) | // The untouched nibble
((a_Nibble & 0x0f) << ((a_BlockIdx & 1) * 4)) // The nibble being set
);
@ -275,20 +271,20 @@ public:
}
int Index = MakeIndexNoCheck(x, y, z);
a_Buffer[Index / 2] = (
a_Buffer[Index / 2] = static_cast<NIBBLETYPE>(
(a_Buffer[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble
((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set
);
}
inline static char GetNibble(const NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos )
inline static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos )
{
return GetNibble(a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z );
}
inline static void SetNibble(NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos, char a_Value )
inline static void SetNibble(NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos, NIBBLETYPE a_Value )
{
SetNibble( a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, a_Value );
}
@ -306,6 +302,9 @@ The virtual methods are called in the same order as they're declared here.
class cChunkDataCallback abstract
{
public:
virtual ~cChunkDataCallback() {}
/** Called before any other callbacks to inform of the current coords
(only in processes where multiple chunks can be processed, such as cWorld::ForEachChunkInRect()).
If false is returned, the chunk is skipped.
@ -432,6 +431,9 @@ Used primarily for entity moving while both chunks are locked.
class cClientDiffCallback
{
public:
virtual ~cClientDiffCallback() {}
/// Called for clients that are in Chunk1 and not in Chunk2,
virtual void Removed(cClientHandle * a_Client) = 0;
@ -492,6 +494,9 @@ typedef std::vector<cChunkCoords> cChunkCoordsVector;
class cChunkCoordCallback
{
public:
virtual ~cChunkCoordCallback() {}
virtual void Call(int a_ChunkX, int a_ChunkZ) = 0;
} ;

View File

@ -22,9 +22,6 @@
#include "Blocks/BlockSlab.h"
#include "Blocks/ChunkInterface.h"
#include "Vector3f.h"
#include "Vector3d.h"
#include "Root.h"
#include "Authenticator.h"
@ -36,22 +33,12 @@
#define AddPistonDir(x, y, z, dir, amount) switch (dir) { case 0: (y)-=(amount); break; case 1: (y)+=(amount); break;\
case 2: (z)-=(amount); break; case 3: (z)+=(amount); break;\
case 4: (x)-=(amount); break; case 5: (x)+=(amount); break; }
/** If the number of queued outgoing packets reaches this, the client will be kicked */
#define MAX_OUTGOING_PACKETS 2000
/** Maximum number of explosions to send this tick, server will start dropping if exceeded */
#define MAX_EXPLOSIONS_PER_TICK 20
/** Maximum number of block change interactions a player can perform per tick - exceeding this causes a kick */
#define MAX_BLOCK_CHANGE_INTERACTIONS 20
/** How many ticks before the socket is closed after the client is destroyed (#31) */
static const int TICKS_BEFORE_CLOSE = 20;
@ -96,8 +83,8 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) :
m_ShouldCheckDownloaded(false),
m_NumExplosionsThisTick(0),
m_UniqueID(0),
m_Locale("en_GB"),
m_HasSentPlayerChunk(false)
m_HasSentPlayerChunk(false),
m_Locale("en_GB")
{
m_Protocol = new cProtocolRecognizer(this);
@ -555,12 +542,25 @@ void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString
}
else if (a_Channel == "REGISTER")
{
if (HasPluginChannel(a_Channel))
{
SendPluginMessage("UNREGISTER", a_Channel);
return; // Can't register again if already taken - kinda defeats the point of plugin messaging!
}
RegisterPluginChannels(BreakApartPluginChannels(a_Message));
}
else if (a_Channel == "UNREGISTER")
{
UnregisterPluginChannels(BreakApartPluginChannels(a_Message));
}
else if (!HasPluginChannel(a_Channel))
{
// Ignore if client sent something but didn't register the channel first
LOGD("Player %s sent a plugin message on channel \"%s\", but didn't REGISTER it first", GetUsername().c_str(), a_Channel.c_str());
SendPluginMessage("UNREGISTER", a_Channel);
return;
}
cPluginManager::Get()->CallHookPluginMessage(*this, a_Channel, a_Message);
}
@ -687,6 +687,14 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status
);
m_NumBlockChangeInteractionsThisTick++;
if (!CheckBlockInteractionsRate())
{
Kick("Too many blocks were destroyed per unit time - hacked client?");
return;
}
cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
if (PlgMgr->CallHookPlayerLeftClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status))
{
@ -694,12 +702,6 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
return;
}
if (!CheckBlockInteractionsRate())
{
// Too many interactions per second, simply ignore. Probably a hacked client, so don't even send bak the block
return;
}
switch (a_Status)
{
@ -877,7 +879,7 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo
LOGD("Prevented a dig/aim bug in the client (finish {%d, %d, %d} vs start {%d, %d, %d}, HSD: %s)",
a_BlockX, a_BlockY, a_BlockZ,
m_LastDigBlockX, m_LastDigBlockY, m_LastDigBlockZ,
m_HasStartedDigging
(m_HasStartedDigging ? "True" : "False")
);
return;
}
@ -931,7 +933,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
cBlockHandler * BlockHandler = cBlockInfo::GetHandler(BlockType);
BlockHandler->OnCancelRightClick(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
if (a_BlockFace > -1)
if (a_BlockFace != BLOCK_FACE_NONE)
{
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
@ -942,7 +944,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
if (!CheckBlockInteractionsRate())
{
LOGD("Too many block interactions, aborting placement");
Kick("Too many blocks were placed/interacted with per unit time - hacked client?");
return;
}
@ -1453,7 +1455,7 @@ bool cClientHandle::HandleHandshake(const AString & a_Username)
void cClientHandle::HandleEntityAction(int a_EntityID, char a_ActionID)
void cClientHandle::HandleEntityCrouch(int a_EntityID, bool a_IsCrouching)
{
if (a_EntityID != m_Player->GetUniqueID())
{
@ -1461,35 +1463,37 @@ void cClientHandle::HandleEntityAction(int a_EntityID, char a_ActionID)
return;
}
switch (a_ActionID)
m_Player->SetCrouch(a_IsCrouching);
}
void cClientHandle::HandleEntityLeaveBed(int a_EntityID)
{
if (a_EntityID != m_Player->GetUniqueID())
{
case 1: // Crouch
{
m_Player->SetCrouch(true);
break;
}
case 2: // Uncrouch
{
m_Player->SetCrouch(false);
break;
}
case 3: // Leave bed
{
m_Player->GetWorld()->BroadcastEntityAnimation(*m_Player, 2);
break;
}
case 4: // Start sprinting
{
m_Player->SetSprint(true);
break;
}
case 5: // Stop sprinting
{
m_Player->SetSprint(false);
SendPlayerMaxSpeed();
break;
}
// We should only receive entity actions from the entity that is performing the action
return;
}
m_Player->GetWorld()->BroadcastEntityAnimation(*m_Player, 2);
}
void cClientHandle::HandleEntitySprinting(int a_EntityID, bool a_IsSprinting)
{
if (a_EntityID != m_Player->GetUniqueID())
{
// We should only receive entity actions from the entity that is performing the action
return;
}
m_Player->SetSprint(a_IsSprinting);
}
@ -1619,28 +1623,12 @@ bool cClientHandle::CheckBlockInteractionsRate(void)
{
ASSERT(m_Player != NULL);
ASSERT(m_Player->GetWorld() != NULL);
/*
// TODO: _X 2012_11_01: This needs a total re-thinking and rewriting
int LastActionCnt = m_Player->GetLastBlockActionCnt();
if ((m_Player->GetWorld()->GetTime() - m_Player->GetLastBlockActionTime()) < 0.1)
if (m_NumBlockChangeInteractionsThisTick > MAX_BLOCK_CHANGE_INTERACTIONS)
{
// Limit the number of block interactions per tick
m_Player->SetLastBlockActionTime(); //Player tried to interact with a block. Reset last block interation time.
m_Player->SetLastBlockActionCnt(LastActionCnt + 1);
if (m_Player->GetLastBlockActionCnt() > MAXBLOCKCHANGEINTERACTIONS)
{
// Kick if more than MAXBLOCKCHANGEINTERACTIONS per tick
LOGWARN("Player %s tried to interact with a block too quickly! (could indicate bot) Was Kicked.", m_Username.c_str());
Kick("You're a baaaaaad boy!");
return false;
}
return false;
}
else
{
m_Player->SetLastBlockActionCnt(0); // Reset count
m_Player->SetLastBlockActionTime(); // Player tried to interact with a block. Reset last block interation time.
}
*/
return true;
}
@ -1713,8 +1701,9 @@ void cClientHandle::Tick(float a_Dt)
}
}
// Reset explosion counter:
// Reset explosion & block change counters:
m_NumExplosionsThisTick = 0;
m_NumBlockChangeInteractionsThisTick = 0;
}

View File

@ -12,7 +12,7 @@
#define CCLIENTHANDLE_H_INCLUDED
#include "Defines.h"
#include "Vector3d.h"
#include "Vector3.h"
#include "OSSupport/SocketThreads.h"
#include "ChunkDef.h"
#include "ByteBuffer.h"
@ -46,7 +46,6 @@ class cClientHandle : // tolua_export
public cSocketThreads::cCallback
{ // tolua_export
public:
static const int MAXBLOCKCHANGEINTERACTIONS = 20; // 5 didn't help, 10 still doesn't work in Creative, 20 seems to have done the trick
#if defined(ANDROID_NDK)
static const int DEFAULT_VIEW_DISTANCE = 4; // The default ViewDistance (used when no value is set in Settings.ini)
@ -188,7 +187,9 @@ public:
void HandleChat (const AString & a_Message);
void HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem);
void HandleDisconnect (const AString & a_Reason);
void HandleEntityAction (int a_EntityID, char a_ActionID);
void HandleEntityCrouch (int a_EntityID, bool a_IsCrouching);
void HandleEntityLeaveBed (int a_EntityID);
void HandleEntitySprinting (int a_EntityID, bool a_IsSprinting);
/** Called when the protocol handshake has been received (for protocol versions that support it;
otherwise the first instant when a username is received).
@ -319,6 +320,9 @@ private:
/** Number of explosions sent this tick */
int m_NumExplosionsThisTick;
/** Number of place or break interactions this tick */
int m_NumBlockChangeInteractionsThisTick;
static int s_ClientCount;
int m_UniqueID;

View File

@ -51,7 +51,7 @@ void cLogCommandOutputCallback::Finished(void)
{
case '\n':
{
LOG(m_Buffer.substr(last, i - last).c_str());
LOG("%s", m_Buffer.substr(last, i - last).c_str());
last = i + 1;
break;
}
@ -59,7 +59,7 @@ void cLogCommandOutputCallback::Finished(void)
} // for i - m_Buffer[]
if (last < len)
{
LOG(m_Buffer.substr(last).c_str());
LOG("%s", m_Buffer.substr(last).c_str());
}
// Clear the buffer for the next command output:

View File

@ -17,7 +17,7 @@ public:
virtual ~cCommandOutputCallback() {}; // Force a virtual destructor in subclasses
/// Syntax sugar function, calls Out() with Printf()-ed parameters; appends a "\n"
void Out(const char * a_Fmt, ...);
void Out(const char * a_Fmt, ...) FORMATSTRING(2, 3);
/// Called when the command wants to output anything; may be called multiple times
virtual void Out(const AString & a_Text) = 0;

View File

@ -10,7 +10,7 @@
#if SELF_TEST
#ifdef SELF_TEST
/** A simple self-test that verifies that the composite chat parser is working properly. */
class SelfTest_CompositeChat
@ -32,15 +32,15 @@ public:
cCompositeChat Msg;
Msg.ParseText("Testing @2color codes and http://links parser");
const cCompositeChat::cParts & Parts = Msg.GetParts();
assert(Parts.size() == 4);
assert(Parts[0]->m_PartType == cCompositeChat::ptText);
assert(Parts[1]->m_PartType == cCompositeChat::ptText);
assert(Parts[2]->m_PartType == cCompositeChat::ptUrl);
assert(Parts[3]->m_PartType == cCompositeChat::ptText);
assert(Parts[0]->m_Style == "");
assert(Parts[1]->m_Style == "@2");
assert(Parts[2]->m_Style == "@2");
assert(Parts[3]->m_Style == "@2");
assert_test(Parts.size() == 4);
assert_test(Parts[0]->m_PartType == cCompositeChat::ptText);
assert_test(Parts[1]->m_PartType == cCompositeChat::ptText);
assert_test(Parts[2]->m_PartType == cCompositeChat::ptUrl);
assert_test(Parts[3]->m_PartType == cCompositeChat::ptText);
assert_test(Parts[0]->m_Style == "");
assert_test(Parts[1]->m_Style == "@2");
assert_test(Parts[2]->m_Style == "@2");
assert_test(Parts[3]->m_Style == "@2");
}
void TestParser2(void)
@ -48,15 +48,15 @@ public:
cCompositeChat Msg;
Msg.ParseText("@3Advanced stuff: @5overriding color codes and http://links.with/@4color-in-them handling");
const cCompositeChat::cParts & Parts = Msg.GetParts();
assert(Parts.size() == 4);
assert(Parts[0]->m_PartType == cCompositeChat::ptText);
assert(Parts[1]->m_PartType == cCompositeChat::ptText);
assert(Parts[2]->m_PartType == cCompositeChat::ptUrl);
assert(Parts[3]->m_PartType == cCompositeChat::ptText);
assert(Parts[0]->m_Style == "@3");
assert(Parts[1]->m_Style == "@5");
assert(Parts[2]->m_Style == "@5");
assert(Parts[3]->m_Style == "@5");
assert_test(Parts.size() == 4);
assert_test(Parts[0]->m_PartType == cCompositeChat::ptText);
assert_test(Parts[1]->m_PartType == cCompositeChat::ptText);
assert_test(Parts[2]->m_PartType == cCompositeChat::ptUrl);
assert_test(Parts[3]->m_PartType == cCompositeChat::ptText);
assert_test(Parts[0]->m_Style == "@3");
assert_test(Parts[1]->m_Style == "@5");
assert_test(Parts[2]->m_Style == "@5");
assert_test(Parts[3]->m_Style == "@5");
}
void TestParser3(void)
@ -64,11 +64,11 @@ public:
cCompositeChat Msg;
Msg.ParseText("http://links.starting the text");
const cCompositeChat::cParts & Parts = Msg.GetParts();
assert(Parts.size() == 2);
assert(Parts[0]->m_PartType == cCompositeChat::ptUrl);
assert(Parts[1]->m_PartType == cCompositeChat::ptText);
assert(Parts[0]->m_Style == "");
assert(Parts[1]->m_Style == "");
assert_test(Parts.size() == 2);
assert_test(Parts[0]->m_PartType == cCompositeChat::ptUrl);
assert_test(Parts[1]->m_PartType == cCompositeChat::ptText);
assert_test(Parts[0]->m_Style == "");
assert_test(Parts[1]->m_Style == "");
}
void TestParser4(void)
@ -76,11 +76,11 @@ public:
cCompositeChat Msg;
Msg.ParseText("links finishing the text: http://some.server");
const cCompositeChat::cParts & Parts = Msg.GetParts();
assert(Parts.size() == 2);
assert(Parts[0]->m_PartType == cCompositeChat::ptText);
assert(Parts[1]->m_PartType == cCompositeChat::ptUrl);
assert(Parts[0]->m_Style == "");
assert(Parts[1]->m_Style == "");
assert_test(Parts.size() == 2);
assert_test(Parts[0]->m_PartType == cCompositeChat::ptText);
assert_test(Parts[1]->m_PartType == cCompositeChat::ptUrl);
assert_test(Parts[0]->m_Style == "");
assert_test(Parts[1]->m_Style == "");
}
void TestParser5(void)
@ -88,9 +88,9 @@ public:
cCompositeChat Msg;
Msg.ParseText("http://only.links");
const cCompositeChat::cParts & Parts = Msg.GetParts();
assert(Parts.size() == 1);
assert(Parts[0]->m_PartType == cCompositeChat::ptUrl);
assert(Parts[0]->m_Style == "");
assert_test(Parts.size() == 1);
assert_test(Parts[0]->m_PartType == cCompositeChat::ptUrl);
assert_test(Parts[0]->m_Style == "");
}
} gTest;

View File

@ -1,4 +1,4 @@

// CraftingRecipes.cpp
// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes
@ -192,7 +192,9 @@ void cCraftingGrid::Dump(void)
{
for (int y = 0; y < m_Height; y++) for (int x = 0; x < m_Width; x++)
{
#ifdef _DEBUG
int idx = x + m_Width * y;
#endif
LOGD("Slot (%d, %d): Type %d, health %d, count %d",
x, y, m_Items[idx].m_ItemType, m_Items[idx].m_ItemDamage, m_Items[idx].m_ItemCount
);
@ -338,7 +340,7 @@ void cCraftingRecipes::LoadRecipes(void)
}
AddRecipeLine(LineNum, Recipe);
} // for itr - Split[]
LOG("Loaded %d crafting recipes", m_Recipes.size());
LOG("Loaded " SIZE_T_FMT " crafting recipes", m_Recipes.size());
}
@ -762,9 +764,94 @@ cCraftingRecipes::cRecipe * cCraftingRecipes::MatchRecipe(const cItem * a_Crafti
Recipe->m_Ingredients.push_back(*itrS);
}
Recipe->m_Ingredients.insert(Recipe->m_Ingredients.end(), MatchedSlots.begin(), MatchedSlots.end());
// We use Recipe instead of a_Recipe because we want the wildcard ingredients' slot numbers as well, which was just added previously
HandleFireworks(a_CraftingGrid, Recipe.get(), a_GridStride, a_OffsetX, a_OffsetY);
return Recipe.release();
}
void cCraftingRecipes::HandleFireworks(const cItem * a_CraftingGrid, cCraftingRecipes::cRecipe * a_Recipe, int a_GridStride, int a_OffsetX, int a_OffsetY)
{
// TODO: add support for more than one dye in the recipe
// A manual and temporary solution (listing everything) is in crafting.txt for fade colours, but a programmatic solutions needs to be done for everything else
if (a_Recipe->m_Result.m_ItemType == E_ITEM_FIREWORK_ROCKET)
{
for (cRecipeSlots::const_iterator itr = a_Recipe->m_Ingredients.begin(); itr != a_Recipe->m_Ingredients.end(); ++itr)
{
switch (itr->m_Item.m_ItemType)
{
case E_ITEM_FIREWORK_STAR:
{
// Result was a rocket, found a star - copy star data to rocket data
int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY);
a_Recipe->m_Result.m_FireworkItem.CopyFrom(a_CraftingGrid[GridID].m_FireworkItem);
break;
}
case E_ITEM_GUNPOWDER:
{
// Gunpowder - increase flight time
a_Recipe->m_Result.m_FireworkItem.m_FlightTimeInTicks += 20;
break;
}
case E_ITEM_PAPER: break;
default: LOG("Unexpected item in firework rocket a_Recipe, was the crafting file fireworks section changed?"); break;
}
}
}
else if (a_Recipe->m_Result.m_ItemType == E_ITEM_FIREWORK_STAR)
{
std::vector<int> DyeColours;
bool FoundStar = false;
for (cRecipeSlots::const_iterator itr = a_Recipe->m_Ingredients.begin(); itr != a_Recipe->m_Ingredients.end(); ++itr)
{
switch (itr->m_Item.m_ItemType)
{
case E_ITEM_FIREWORK_STAR:
{
// Result was star, found another star - probably adding fade colours, but copy data over anyhow
FoundStar = true;
int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY);
a_Recipe->m_Result.m_FireworkItem.CopyFrom(a_CraftingGrid[GridID].m_FireworkItem);
break;
}
case E_ITEM_DYE:
{
int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY);
DyeColours.push_back(cFireworkItem::GetVanillaColourCodeFromDye(a_CraftingGrid[GridID].m_ItemDamage));
break;
}
case E_ITEM_GUNPOWDER: break;
case E_ITEM_DIAMOND: a_Recipe->m_Result.m_FireworkItem.m_HasTrail = true; break;
case E_ITEM_GLOWSTONE_DUST: a_Recipe->m_Result.m_FireworkItem.m_HasFlicker = true; break;
case E_ITEM_FIRE_CHARGE: a_Recipe->m_Result.m_FireworkItem.m_Type = 1; break;
case E_ITEM_GOLD_NUGGET: a_Recipe->m_Result.m_FireworkItem.m_Type = 2; break;
case E_ITEM_FEATHER: a_Recipe->m_Result.m_FireworkItem.m_Type = 4; break;
case E_ITEM_HEAD: a_Recipe->m_Result.m_FireworkItem.m_Type = 3; break;
default: LOG("Unexpected item in firework star a_Recipe, was the crafting file fireworks section changed?"); break; // ermahgerd BARD ardmins
}
}
if (FoundStar && (!DyeColours.empty()))
{
// Found a star and a dye? Fade colours.
a_Recipe->m_Result.m_FireworkItem.m_FadeColours = DyeColours;
}
else if (!DyeColours.empty())
{
// Only dye? Normal colours.
a_Recipe->m_Result.m_FireworkItem.m_Colours = DyeColours;
}
}
}

View File

@ -165,6 +165,9 @@ protected:
/// Checks if the grid matches the specified recipe, offset by the specified offsets. Returns a matched cRecipe * if so, or NULL if not matching. Caller must delete the return value!
cRecipe * MatchRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride, const cRecipe * a_Recipe, int a_OffsetX, int a_OffsetY);
/** Searches for anything firework related, and does the data setting if appropriate */
void HandleFireworks(const cItem * a_CraftingGrid, cCraftingRecipes::cRecipe * a_Recipe, int a_GridStride, int a_OffsetX, int a_OffsetY);
} ;

View File

@ -72,6 +72,9 @@ int cCuboid::GetVolume(void) const
bool cCuboid::DoesIntersect(const cCuboid & a_Other) const
{
ASSERT(IsSorted());
ASSERT(a_Other.IsSorted());
// In order for cuboids to intersect, each of their coord intervals need to intersect
return (
DoIntervalsIntersect(p1.x, p2.x, a_Other.p1.x, a_Other.p2.x) &&
@ -86,6 +89,9 @@ bool cCuboid::DoesIntersect(const cCuboid & a_Other) const
bool cCuboid::IsCompletelyInside(const cCuboid & a_Outer) const
{
ASSERT(IsSorted());
ASSERT(a_Outer.IsSorted());
return (
(p1.x >= a_Outer.p1.x) &&
(p2.x <= a_Outer.p2.x) &&
@ -197,3 +203,37 @@ bool cCuboid::IsSorted(void) const
void cCuboid::Engulf(const Vector3i & a_Point)
{
if (a_Point.x < p1.x)
{
p1.x = a_Point.x;
}
else if (a_Point.x > p2.x)
{
p2.x = a_Point.x;
}
if (a_Point.y < p1.y)
{
p1.y = a_Point.y;
}
else if (a_Point.y > p2.y)
{
p2.y = a_Point.y;
}
if (a_Point.z < p1.z)
{
p1.z = a_Point.z;
}
else if (a_Point.z > p2.z)
{
p2.z = a_Point.z;
}
}

View File

@ -1,8 +1,7 @@
#pragma once
#include "Vector3i.h"
#include "Vector3d.h"
#include "Vector3.h"
@ -34,7 +33,8 @@ public:
Works on unsorted cuboids, too. */
int GetVolume(void) const;
/** Returns true if the cuboids have at least one voxel in common. Both coords are considered inclusive. */
/** Returns true if the cuboids have at least one voxel in common. Both coords are considered inclusive.
Assumes both cuboids are sorted. */
bool DoesIntersect(const cCuboid & a_Other) const;
bool IsInside(const Vector3i & v) const
@ -64,7 +64,8 @@ public:
);
}
/** Returns true if this cuboid is completely inside the specifie cuboid (in all 6 coords) */
/** Returns true if this cuboid is completely inside the specifie cuboid (in all 6 coords).
Assumes both cuboids are sorted. */
bool IsCompletelyInside(const cCuboid & a_Outer) const;
/** Moves the cuboid by the specified offsets in each direction */
@ -72,7 +73,7 @@ public:
/** Expands the cuboid by the specified amount in each direction.
Works on unsorted cuboids as well.
Note that this function doesn't check for underflows. */
Note that this function doesn't check for underflows when using negative amounts. */
void Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
/** Clamps both X coords to the specified range. Works on unsorted cuboids, too. */
@ -86,6 +87,9 @@ public:
/** Returns true if the coords are properly sorted (lesser in p1, greater in p2) */
bool IsSorted(void) const;
/** If needed, expands the cuboid so that it contains the specified point. Assumes sorted. Doesn't contract. */
void Engulf(const Vector3i & a_Point);
} ;
// tolua_end

View File

@ -60,7 +60,7 @@ protected:
void CheckWorldAge(const AString & a_WorldName, Int64 a_Age);
/// Called when a deadlock is detected. Aborts the server.
void DeadlockDetected(void);
NORETURN void DeadlockDetected(void);
} ;

View File

@ -2,6 +2,7 @@
#pragma once
#include "ChatColor.h"
#include <limits>
@ -276,6 +277,26 @@ inline eBlockFace RotateBlockFaceCW(eBlockFace a_BlockFace)
/** Returns the textual representation of the BlockFace constant. */
inline AString BlockFaceToString(eBlockFace a_BlockFace)
{
switch (a_BlockFace)
{
case BLOCK_FACE_XM: return "BLOCK_FACE_XM";
case BLOCK_FACE_XP: return "BLOCK_FACE_XP";
case BLOCK_FACE_YM: return "BLOCK_FACE_YM";
case BLOCK_FACE_YP: return "BLOCK_FACE_YP";
case BLOCK_FACE_ZM: return "BLOCK_FACE_ZM";
case BLOCK_FACE_ZP: return "BLOCK_FACE_ZP";
case BLOCK_FACE_NONE: return "BLOCK_FACE_NONE";
}
return Printf("Unknown BLOCK_FACE: %d", a_BlockFace);
}
inline bool IsValidBlock(int a_BlockType)
{
if (
@ -469,7 +490,7 @@ inline void EulerToVector(double a_Pan, double a_Pitch, double & a_X, double & a
inline void VectorToEuler(double a_X, double a_Y, double a_Z, double & a_Pan, double & a_Pitch)
{
if (a_X != 0)
if (fabs(a_X) < std::numeric_limits<double>::epsilon())
{
a_Pan = atan2(a_Z, a_X) * 180 / PI - 90;
}

View File

@ -4,7 +4,7 @@
#include "../World.h"
#include "../Server.h"
#include "../Root.h"
#include "../Matrix4f.h"
#include "../Matrix4.h"
#include "../ClientHandle.h"
#include "../Chunk.h"
#include "../Simulator/FluidSimulator.h"

View File

@ -2,9 +2,7 @@
#pragma once
#include "../Item.h"
#include "../Vector3d.h"
#include "../Vector3f.h"
#include "../Vector3i.h"
#include "../Vector3.h"

View File

@ -11,7 +11,7 @@
class cFloater :
public cEntity
{
typedef cFloater super;
typedef cEntity super;
public:
//tolua_end

View File

@ -1031,9 +1031,9 @@ cMinecartWithChest::cMinecartWithChest(double a_X, double a_Y, double a_Z) :
void cMinecartWithChest::SetSlot(int a_Idx, const cItem & a_Item)
void cMinecartWithChest::SetSlot(size_t a_Idx, const cItem & a_Item)
{
ASSERT((a_Idx >= 0) && (a_Idx < ARRAYCOUNT(m_Items)));
ASSERT(a_Idx < ARRAYCOUNT(m_Items));
m_Items[a_Idx] = a_Item;
}

View File

@ -122,7 +122,7 @@ public:
const cItem & GetSlot(int a_Idx) const { return m_Items[a_Idx]; }
cItem & GetSlot(int a_Idx) { return m_Items[a_Idx]; }
void SetSlot(int a_Idx, const cItem & a_Item);
void SetSlot(size_t a_Idx, const cItem & a_Item);
protected:
@ -193,4 +193,4 @@ public:
CLASS_PROTODEF(cMinecartWithHopper);
cMinecartWithHopper(double a_X, double a_Y, double a_Z);
} ;
} ;

View File

@ -10,17 +10,11 @@
#include "../BlockEntities/BlockEntity.h"
#include "../GroupManager.h"
#include "../Group.h"
#include "../ChatColor.h"
#include "../Item.h"
#include "../Tracer.h"
#include "../Root.h"
#include "../OSSupport/Timer.h"
#include "../MersenneTwister.h"
#include "../Chunk.h"
#include "../Items/ItemHandler.h"
#include "../Vector3d.h"
#include "../Vector3f.h"
#include "../Vector3.h"
#include "inifile/iniFile.h"
#include "json/json.h"
@ -45,10 +39,7 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
, m_Inventory(*this)
, m_CurrentWindow(NULL)
, m_InventoryWindow(NULL)
, m_TimeLastPickupCheck(0.f)
, m_Color('-')
, m_LastBlockActionTime(0)
, m_LastBlockActionCnt(0)
, m_GameMode(eGameMode_NotSet)
, m_IP("")
, m_ClientHandle(a_Client)
@ -86,7 +77,6 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
m_LastPlayerListTime = t1.GetNowTime();
m_TimeLastTeleportPacket = 0;
m_TimeLastPickupCheck = 0;
m_PlayerName = a_PlayerName;
m_bDirtyPosition = true; // So chunks are streamed to player at spawn
@ -1047,27 +1037,6 @@ void cPlayer::CloseWindowIfID(char a_WindowID, bool a_CanRefuse)
void cPlayer::SetLastBlockActionTime()
{
if (m_World != NULL)
{
m_LastBlockActionTime = m_World->GetWorldAge() / 20.0f;
}
}
void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt )
{
m_LastBlockActionCnt = a_LastBlockActionCnt;
}
void cPlayer::SetGameMode(eGameMode a_GameMode)
{
if ((a_GameMode < gmMin) || (a_GameMode >= gmMax))

View File

@ -50,7 +50,7 @@ public:
/// Returns the curently equipped weapon; empty item if none
virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); }
/// Returns the currently equipped helmet; empty item if nonte
/// Returns the currently equipped helmet; empty item if none
virtual cItem GetEquippedHelmet(void) const override { return m_Inventory.GetEquippedHelmet(); }
/// Returns the currently equipped chestplate; empty item if none
@ -165,11 +165,6 @@ public:
// tolua_end
void SetIP(const AString & a_IP);
float GetLastBlockActionTime() { return m_LastBlockActionTime; }
int GetLastBlockActionCnt() { return m_LastBlockActionCnt; }
void SetLastBlockActionCnt( int );
void SetLastBlockActionTime();
// Sets the current gamemode, doesn't check validity, doesn't send update packets to client
void LoginSetGameMode(eGameMode a_GameMode);
@ -416,12 +411,8 @@ protected:
cWindow * m_CurrentWindow;
cWindow * m_InventoryWindow;
float m_TimeLastPickupCheck;
char m_Color;
float m_LastBlockActionTime;
int m_LastBlockActionCnt;
eGameMode m_GameMode;
AString m_IP;

View File

@ -214,7 +214,7 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Ve
cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed)
cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item, const Vector3d * a_Speed)
{
Vector3d Speed;
if (a_Speed != NULL)
@ -231,8 +231,15 @@ cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator,
case pkGhastFireball: return new cGhastFireballEntity (a_Creator, a_X, a_Y, a_Z, Speed);
case pkFireCharge: return new cFireChargeEntity (a_Creator, a_X, a_Y, a_Z, Speed);
case pkExpBottle: return new cExpBottleEntity (a_Creator, a_X, a_Y, a_Z, Speed);
case pkFirework: return new cFireworkEntity (a_Creator, a_X, a_Y, a_Z );
// TODO: the rest
case pkFirework:
{
if (a_Item.m_FireworkItem.m_Colours.empty())
{
return NULL;
}
return new cFireworkEntity(a_Creator, a_X, a_Y, a_Z, a_Item);
}
}
LOGWARNING("%s: Unknown projectile kind: %d", __FUNCTION__, a_Kind);
@ -276,6 +283,7 @@ AString cProjectileEntity::GetMCAClassName(void) const
case pkExpBottle: return "ThrownExpBottle";
case pkSplashPotion: return "ThrownPotion";
case pkWitherSkull: return "WitherSkull";
case pkFirework: return "Firework";
case pkFishingFloat: return ""; // Unknown, perhaps MC doesn't save this?
}
ASSERT(!"Unhandled projectile entity kind!");
@ -655,8 +663,6 @@ cThrownSnowballEntity::cThrownSnowballEntity(cEntity * a_Creator, double a_X, do
void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
// TODO: Apply damage to certain mobs (blaze etc.) and anger all mobs
Destroy();
}
@ -664,6 +670,30 @@ void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFac
void cThrownSnowballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
int TotalDamage = 0;
if (a_EntityHit.IsMob())
{
cMonster::eType MobType = ((cMonster &) a_EntityHit).GetMobType();
if (MobType == cMonster::mtBlaze)
{
TotalDamage = 3;
}
else if (MobType == cMonster::mtEnderDragon)
{
TotalDamage = 1;
}
}
a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1);
Destroy(true);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cBottleOEnchantingEntity :
@ -693,8 +723,10 @@ void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_H
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFireworkEntity :
cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z) :
super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item) :
super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25),
m_ExplodeTimer(0),
m_FireworkItem(a_Item)
{
}
@ -702,30 +734,20 @@ super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
void cFireworkEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
if ((a_HitFace != BLOCK_FACE_BOTTOM) && (a_HitFace != BLOCK_FACE_NONE))
{
return;
}
SetSpeed(0, 0, 0);
SetPosition(GetPosX(), GetPosY() - 0.5, GetPosZ());
m_IsInGround = true;
BroadcastMovementUpdate();
}
void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
int RelX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width;
int RelZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width;
int PosY = POSY_TOINT;
if ((PosY < 0) || (PosY >= cChunkDef::Height))
{
goto setspeed;
}
if (m_IsInGround)
{
if (a_Chunk.GetBlock((int)GetPosX(), (int)GetPosY() + 1, (int)GetPosZ()) == E_BLOCK_AIR)
if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) == E_BLOCK_AIR)
{
m_IsInGround = false;
}
@ -734,28 +756,35 @@ void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
return;
}
}
Vector3d PerTickSpeed = GetSpeed() / 20;
Vector3d Pos = GetPosition();
// Trace the tick's worth of movement as a line:
Vector3d NextPos = Pos + PerTickSpeed;
cProjectileTracerCallback TracerCallback(this);
if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos))
else
{
// Something has been hit, abort all other processing
return;
if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) != E_BLOCK_AIR)
{
OnHitSolidBlock(GetPosition(), BLOCK_FACE_YM);
return;
}
}
// The tracer also checks the blocks for slowdown blocks - water and lava - and stores it for later in its SlowdownCoeff
// Update the position:
SetPosition(NextPos);
setspeed:
AddSpeedY(1);
AddPosition(GetSpeed() * (a_Dt / 1000));
}
// Add slowdown and gravity effect to the speed:
Vector3d NewSpeed(GetSpeed());
NewSpeed.y += 2;
NewSpeed *= TracerCallback.GetSlowdownCoeff();
SetSpeed(NewSpeed);
void cFireworkEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
if (m_ExplodeTimer == m_FireworkItem.m_FireworkItem.m_FlightTimeInTicks)
{
m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_FIREWORK_EXPLODE);
Destroy();
}
m_ExplodeTimer++;
}

View File

@ -46,7 +46,7 @@ public:
cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height);
cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height);
static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed = NULL);
static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item, const Vector3d * a_Speed = NULL);
/// Called by the physics blocktracer when the entity hits a solid block, the hit position and the face hit (BLOCK_FACE_) is given
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace);
@ -259,6 +259,7 @@ protected:
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
// tolua_begin
@ -305,13 +306,19 @@ public:
CLASS_PROTODEF(cFireworkEntity);
cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z);
cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item);
const cItem & GetItem(void) const { return m_FireworkItem; }
protected:
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
private:
int m_ExplodeTimer;
cItem m_FireworkItem;
// tolua_begin

View File

@ -175,7 +175,7 @@ void cFurnaceRecipe::ReloadRecipes(void)
{
LOGERROR("ERROR: FurnaceRecipe, syntax error" );
}
LOG("Loaded %u furnace recipes and %u fuels", m_pState->Recipes.size(), m_pState->Fuel.size());
LOG("Loaded " SIZE_T_FMT " furnace recipes and " SIZE_T_FMT " fuels", m_pState->Recipes.size(), m_pState->Fuel.size());
}

View File

@ -116,7 +116,7 @@ void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_Chunk
// Add to queue, issue a warning if too many:
if (m_Queue.size() >= QUEUE_WARNING_LIMIT)
{
LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (%i)", a_ChunkX, a_ChunkZ, m_Queue.size());
LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (" SIZE_T_FMT ")", a_ChunkX, a_ChunkZ, m_Queue.size());
}
m_Queue.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
}
@ -180,7 +180,7 @@ BLOCKTYPE cChunkGenerator::GetIniBlock(cIniFile & a_IniFile, const AString & a_S
BLOCKTYPE Block = BlockStringToType(BlockType);
if (Block < 0)
{
LOGWARN("[&s].%s Could not parse block value \"%s\". Using default: \"%s\".", a_SectionName.c_str(), a_ValueName.c_str(), BlockType.c_str(),a_Default.c_str());
LOGWARN("[%s].%s Could not parse block value \"%s\". Using default: \"%s\".", a_SectionName.c_str(), a_ValueName.c_str(), BlockType.c_str(),a_Default.c_str());
return BlockStringToType(a_Default);
}
return Block;

View File

@ -22,6 +22,7 @@
#include "EndGen.h"
#include "MineShafts.h"
#include "Noise3DGenerator.h"
#include "POCPieceGenerator.h"
#include "Ravines.h"
@ -364,6 +365,10 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
{
m_FinishGens.push_back(new cStructGenOreNests(Seed));
}
else if (NoCaseCompare(*itr, "POCPieces") == 0)
{
m_FinishGens.push_back(new cPOCPieceGenerator(Seed));
}
else if (NoCaseCompare(*itr, "PreSimulator") == 0)
{
m_FinishGens.push_back(new cFinishGenPreSimulator);

View File

@ -69,6 +69,8 @@ public:
m_BoundingBox(a_BoundingBox)
{
}
virtual ~cMineShaft() {}
/// Returns true if this mineshaft intersects the specified cuboid
bool DoesIntersect(const cCuboid & a_Other)

View File

@ -0,0 +1,270 @@
// POCPieceGenerator.cpp
// Implements the cPOCPieceGenerator class representing a Proof-Of_Concept structure generator using the cPieceGenerator technique
// The generator generates a maze of rooms at {0, 50, 0}
#include "Globals.h"
#include "POCPieceGenerator.h"
#include "ChunkDesc.h"
/** POC pieces are simple boxes that have connectors in the middle of their walls.
Each wall has one connector, there are 3 connector types that get assigned semi-randomly.
The piece also knows how to imprint itself in a cChunkDesc, each piece has a different color glass
and each connector is uses a different color wool frame. */
class cPOCPiece :
public cPiece
{
public:
cPOCPiece(int a_SizeXZ, int a_Height) :
m_SizeXZ(a_SizeXZ),
m_Height(a_Height)
{
m_Connectors.push_back(cConnector(m_SizeXZ / 2, a_Height / 2, 0, 0, BLOCK_FACE_ZM));
m_Connectors.push_back(cConnector(m_SizeXZ / 2, a_Height / 2, m_SizeXZ - 1, 1, BLOCK_FACE_ZP));
m_Connectors.push_back(cConnector(0, a_Height / 2, m_SizeXZ / 2, 2, BLOCK_FACE_XM));
m_Connectors.push_back(cConnector(m_SizeXZ - 1, a_Height - 1, m_SizeXZ / 2, m_SizeXZ % 3, BLOCK_FACE_XP));
}
/** Imprints the piece in the specified chunk. Assumes they intersect. */
void ImprintInChunk(cChunkDesc & a_ChunkDesc, const Vector3i & a_Pos, int a_NumCCWRotations)
{
int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
Vector3i Min = a_Pos;
Min.Move(-BlockX, 0, -BlockZ);
Vector3i Max = Min;
Max.Move(m_SizeXZ - 1, m_Height - 1, m_SizeXZ - 1);
ASSERT(Min.x < cChunkDef::Width);
ASSERT(Min.z < cChunkDef::Width);
ASSERT(Max.x >= 0);
ASSERT(Max.z >= 0);
if (Min.x >= 0)
{
// Draw the XM wall:
a_ChunkDesc.FillRelCuboid(Min.x, Min.x, Min.y, Max.y, Min.z, Max.z, E_BLOCK_STAINED_GLASS, m_SizeXZ % 16);
}
if (Min.z >= 0)
{
// Draw the ZM wall:
a_ChunkDesc.FillRelCuboid(Min.x, Max.x, Min.y, Max.y, Min.z, Min.z, E_BLOCK_STAINED_GLASS, m_SizeXZ % 16);
}
if (Max.x < cChunkDef::Width)
{
// Draw the XP wall:
a_ChunkDesc.FillRelCuboid(Max.x, Max.x, Min.y, Max.y, Min.z, Max.z, E_BLOCK_STAINED_GLASS, m_SizeXZ % 16);
}
if (Max.z < cChunkDef::Width)
{
// Draw the ZP wall:
a_ChunkDesc.FillRelCuboid(Min.x, Max.x, Min.y, Max.y, Max.z, Max.z, E_BLOCK_STAINED_GLASS, m_SizeXZ % 16);
}
// Draw all the connectors:
for (cConnectors::const_iterator itr = m_Connectors.begin(), end = m_Connectors.end(); itr != end; ++itr)
{
cConnector Conn = cPiece::RotateMoveConnector(*itr, a_NumCCWRotations, a_Pos.x, a_Pos.y, a_Pos.z);
Conn.m_Pos.Move(-BlockX, 0, -BlockZ);
if (
(Conn.m_Pos.x >= 0) && (Conn.m_Pos.x < cChunkDef::Width) &&
(Conn.m_Pos.z >= 0) && (Conn.m_Pos.z < cChunkDef::Width)
)
{
a_ChunkDesc.SetBlockTypeMeta(Conn.m_Pos.x, Conn.m_Pos.y, Conn.m_Pos.z, E_BLOCK_WOOL, itr->m_Type % 16);
}
/*
// TODO: Frame the connectors
switch (itr->m_Direction)
{
case BLOCK_FACE_XM:
case BLOCK_FACE_XP:
{
// TODO
break;
}
case BLOCK_FACE_ZM:
case BLOCK_FACE_ZP:
{
// TODO
break;
}
}
*/
} // for itr - m_Connectors[]
}
protected:
int m_SizeXZ;
int m_Height;
cConnectors m_Connectors;
// cPiece overrides:
virtual cConnectors GetConnectors(void) const override
{
return m_Connectors;
}
virtual Vector3i GetSize(void) const override
{
return Vector3i(m_SizeXZ, m_Height, m_SizeXZ);
}
virtual cCuboid GetHitBox(void) const override
{
return cCuboid(0, 0, 0, m_SizeXZ - 1, m_Height - 1, m_SizeXZ - 1);
}
virtual bool CanRotateCCW(int a_NumRotations) const override
{
return true;
}
};
/*
static void DebugPieces(const cPlacedPieces & a_Pieces)
{
size_t idx = 0;
for (cPlacedPieces::const_iterator itr = a_Pieces.begin(), end = a_Pieces.end(); itr != end; ++itr, ++idx)
{
const cCuboid & HitBox = (*itr)->GetHitBox();
printf(" %u: %d rotations, {%d - %d, %d - %d}\n",
idx, (*itr)->GetNumCCWRotations(),
HitBox.p1.x, HitBox.p2.x, HitBox.p1.z, HitBox.p2.z
);
} // for itr - a_Pieces[]
}
//*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cPOCPieceGenerator:
cPOCPieceGenerator::cPOCPieceGenerator(int a_Seed) :
m_Seed(a_Seed)
{
// Prepare a vector of available pieces:
m_AvailPieces.push_back(new cPOCPiece(5, 3));
m_AvailPieces.push_back(new cPOCPiece(7, 5));
m_AvailPieces.push_back(new cPOCPiece(9, 5));
m_AvailPieces.push_back(new cPOCPiece(5, 7));
// Generate the structure:
cBFSPieceGenerator Gen(*this, a_Seed);
Gen.PlacePieces(0, 50, 0, 6, m_Pieces);
// DebugPieces(m_Pieces);
// Get the smallest cuboid encompassing the entire generated structure:
cCuboid Bounds(0, 50, 0, 0, 50, 0);
for (cPlacedPieces::const_iterator itr = m_Pieces.begin(), end = m_Pieces.end(); itr != end; ++itr)
{
Vector3i MinCoords = (*itr)->GetCoords();
Bounds.Engulf(MinCoords);
Bounds.Engulf(MinCoords + (*itr)->GetPiece().GetSize());
} // for itr - m_Pieces[]
m_Bounds = Bounds;
}
cPOCPieceGenerator::~cPOCPieceGenerator()
{
cPieceGenerator::FreePieces(m_Pieces);
}
void cPOCPieceGenerator::GenFinish(cChunkDesc & a_ChunkDesc)
{
int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
if (
(BlockX + 16 < m_Bounds.p1.x) || (BlockX > m_Bounds.p2.x) || // X coords out of bounds of the generated structure
(BlockZ + 16 < m_Bounds.p1.z) || (BlockZ > m_Bounds.p2.z) // Z coords out of bounds of the generated structure
)
{
return;
}
// Imprint each piece in the chunk:
for (cPlacedPieces::const_iterator itr = m_Pieces.begin(), end = m_Pieces.end(); itr != end; ++itr)
{
const Vector3i & Pos = (*itr)->GetCoords();
Vector3i Size = (*itr)->GetPiece().GetSize();
if (((*itr)->GetNumCCWRotations() % 2) == 1)
{
std::swap(Size.x, Size.z);
}
if (
(Pos.x >= BlockX + 16) || (Pos.x + Size.x - 1 < BlockX) ||
(Pos.z >= BlockZ + 16) || (Pos.z + Size.z - 1 < BlockZ)
)
{
// This piece doesn't intersect the chunk
continue;
}
((cPOCPiece &)(*itr)->GetPiece()).ImprintInChunk(a_ChunkDesc, Pos, (*itr)->GetNumCCWRotations());
} // for itr - m_Pieces[]
a_ChunkDesc.UpdateHeightmap();
}
cPieces cPOCPieceGenerator::GetPiecesWithConnector(int a_ConnectorType)
{
// Each piece has each connector
return m_AvailPieces;
}
cPieces cPOCPieceGenerator::GetStartingPieces(void)
{
// Any piece can be a starting piece
return m_AvailPieces;
}
void cPOCPieceGenerator::PiecePlaced(const cPiece & a_Piece)
{
UNUSED(a_Piece);
}
void cPOCPieceGenerator::Reset(void)
{
// Nothing needed
}

View File

@ -0,0 +1,54 @@
// POCPieceGenerator.h
// Declares the cPOCPieceGenerator class representing a Proof-Of_Concept structure generator using the cPieceGenerator technique
// The generator generates a maze of rooms at {0, 100, 0}
#pragma once
#include "PieceGenerator.h"
#include "ComposableGenerator.h"
class cPOCPieceGenerator :
public cFinishGen,
protected cPiecePool
{
public:
cPOCPieceGenerator(int a_Seed);
~cPOCPieceGenerator();
protected:
int m_Seed;
/** The pieces from which the generated structure is built. */
cPieces m_AvailPieces;
/** The placed pieces of the generated structure. */
cPlacedPieces m_Pieces;
/** Bounds of the complete structure, to save on processing outside chunks. */
cCuboid m_Bounds;
// cFinishGen overrides:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
// cPiecePool overrides:
virtual cPieces GetPiecesWithConnector(int a_ConnectorType) override;
virtual cPieces GetStartingPieces(void) override;
virtual void PiecePlaced(const cPiece & a_Piece) override;
virtual void Reset(void) override;
} ;

View File

@ -0,0 +1,625 @@
// PieceGenerator.cpp
// Implements the cBFSPieceGenerator class and cDFSPieceGenerator class
// representing base classes for generating structures composed of individual "pieces"
#include "Globals.h"
#include "PieceGenerator.h"
#ifdef SELF_TEST
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Self-test:
static class cPieceGeneratorSelfTest :
public cPiecePool
{
public:
cPieceGeneratorSelfTest(void)
{
// Prepare the internal state:
InitializePieces();
// Generate:
cBFSPieceGenerator Gen(*this, 0);
cPlacedPieces OutPieces;
Gen.PlacePieces(500, 50, 500, 3, OutPieces);
// Print out the pieces:
printf("OutPieces.size() = " SIZE_T_FMT "\n", OutPieces.size());
size_t idx = 0;
for (cPlacedPieces::const_iterator itr = OutPieces.begin(), end = OutPieces.end(); itr != end; ++itr, ++idx)
{
const Vector3i & Coords = (*itr)->GetCoords();
cCuboid Hitbox = (*itr)->GetHitBox();
Hitbox.Sort();
printf(SIZE_T_FMT ": {%d, %d, %d}, rot %d, hitbox {%d, %d, %d} - {%d, %d, %d} (%d * %d * %d)\n", idx,
Coords.x, Coords.y, Coords.z,
(*itr)->GetNumCCWRotations(),
Hitbox.p1.x, Hitbox.p1.y, Hitbox.p1.z,
Hitbox.p2.x, Hitbox.p2.y, Hitbox.p2.z,
Hitbox.DifX() + 1, Hitbox.DifY() + 1, Hitbox.DifZ() + 1
);
} // itr - OutPieces[]
printf("Done.\n");
// Free the placed pieces properly:
Gen.FreePieces(OutPieces);
}
~cPieceGeneratorSelfTest()
{
// Dealloc all the pieces:
for (cPieces::iterator itr = m_Pieces.begin(), end = m_Pieces.end(); itr != end; ++itr)
{
delete *itr;
}
m_Pieces.clear();
}
protected:
class cTestPiece :
public cPiece
{
int m_Size;
public:
cTestPiece(int a_Size) :
m_Size(a_Size)
{
}
virtual cConnectors GetConnectors(void) const override
{
// Each piece has 4 connectors, one of each type, plus one extra, at the center of its walls:
cConnectors res;
res.push_back(cConnector(m_Size / 2, 1, 0, 0, BLOCK_FACE_ZM));
res.push_back(cConnector(m_Size / 2, 1, m_Size - 1, 1, BLOCK_FACE_ZP));
res.push_back(cConnector(0, 1, m_Size / 2, 2, BLOCK_FACE_XM));
res.push_back(cConnector(m_Size - 1, 1, m_Size / 2, m_Size % 3, BLOCK_FACE_XP));
return res;
}
virtual Vector3i GetSize(void) const override
{
return Vector3i(m_Size, 5, m_Size);
}
virtual cCuboid GetHitBox(void) const override
{
return cCuboid(0, 0, 0, m_Size - 1, 4, m_Size - 1);
}
virtual bool CanRotateCCW(int a_NumCCWRotations) const override
{
return true;
}
};
cPieces m_Pieces;
virtual cPieces GetPiecesWithConnector(int a_ConnectorType) override
{
// Each piece contains each connector
return m_Pieces;
}
virtual cPieces GetStartingPieces(void) override
{
return m_Pieces;
}
virtual void PiecePlaced(const cPiece & a_Piece) override
{
UNUSED(a_Piece);
}
virtual void Reset(void) override
{
}
void InitializePieces(void)
{
m_Pieces.push_back(new cTestPiece(5));
m_Pieces.push_back(new cTestPiece(7));
m_Pieces.push_back(new cTestPiece(9));
}
} g_Test;
#endif // SELF_TEST
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cPiece:
Vector3i cPiece::RotatePos(const Vector3i & a_Pos, int a_NumCCWRotations) const
{
Vector3i Size = GetSize();
switch (a_NumCCWRotations)
{
case 0:
{
// No rotation needed
return a_Pos;
}
case 1:
{
// 1 CCW rotation:
return Vector3i(a_Pos.z, a_Pos.y, Size.x - a_Pos.x - 1);
}
case 2:
{
// 2 rotations ( = axis flip):
return Vector3i(Size.x - a_Pos.x - 1, a_Pos.y, Size.z - a_Pos.z - 1);
}
case 3:
{
// 1 CW rotation:
return Vector3i(Size.z - a_Pos.z - 1, a_Pos.y, a_Pos.x);
}
}
ASSERT(!"Unhandled rotation");
return a_Pos;
}
cPiece::cConnector cPiece::RotateMoveConnector(const cConnector & a_Connector, int a_NumCCWRotations, int a_MoveX, int a_MoveY, int a_MoveZ) const
{
cPiece::cConnector res(a_Connector);
// Rotate the res connector:
switch (a_NumCCWRotations)
{
case 0:
{
// No rotation needed
break;
}
case 1:
{
// 1 CCW rotation:
res.m_Direction = RotateBlockFaceCCW(res.m_Direction);
break;
}
case 2:
{
// 2 rotations ( = axis flip):
res.m_Direction = MirrorBlockFaceY(res.m_Direction);
break;
}
case 3:
{
// 1 CW rotation:
res.m_Direction = RotateBlockFaceCW(res.m_Direction);
break;
}
}
res.m_Pos = RotatePos(a_Connector.m_Pos, a_NumCCWRotations);
// Move the res connector:
res.m_Pos.x += a_MoveX;
res.m_Pos.y += a_MoveY;
res.m_Pos.z += a_MoveZ;
return res;
}
cCuboid cPiece::RotateHitBoxToConnector(
const cPiece::cConnector & a_MyConnector,
const Vector3i & a_ToConnectorPos,
int a_NumCCWRotations
) const
{
ASSERT(a_NumCCWRotations == (a_NumCCWRotations % 4));
Vector3i ConnPos = RotatePos(a_MyConnector.m_Pos, a_NumCCWRotations);
ConnPos = a_ToConnectorPos - ConnPos;
return RotateMoveHitBox(a_NumCCWRotations, ConnPos.x, ConnPos.y, ConnPos.z);
}
cCuboid cPiece::RotateMoveHitBox(int a_NumCCWRotations, int a_MoveX, int a_MoveY, int a_MoveZ) const
{
ASSERT(a_NumCCWRotations == (a_NumCCWRotations % 4));
cCuboid res = GetHitBox();
res.p1 = RotatePos(res.p1, a_NumCCWRotations);
res.p2 = RotatePos(res.p2, a_NumCCWRotations);
res.p1.Move(a_MoveX, a_MoveY, a_MoveZ);
res.p2.Move(a_MoveX, a_MoveY, a_MoveZ);
return res;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cPiece::cConnector:
cPiece::cConnector::cConnector(int a_X, int a_Y, int a_Z, int a_Type, eBlockFace a_Direction) :
m_Pos(a_X, a_Y, a_Z),
m_Type(a_Type),
m_Direction(a_Direction)
{
}
cPiece::cConnector::cConnector(const Vector3i & a_Pos, int a_Type, eBlockFace a_Direction) :
m_Pos(a_Pos),
m_Type(a_Type),
m_Direction(a_Direction)
{
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cPlacedPiece:
cPlacedPiece::cPlacedPiece(const cPlacedPiece * a_Parent, const cPiece & a_Piece, const Vector3i & a_Coords, int a_NumCCWRotations) :
m_Parent(a_Parent),
m_Piece(&a_Piece),
m_Coords(a_Coords),
m_NumCCWRotations(a_NumCCWRotations)
{
m_Depth = (m_Parent == NULL) ? 0 : (m_Parent->GetDepth() + 1);
m_HitBox = a_Piece.RotateMoveHitBox(a_NumCCWRotations, a_Coords.x, a_Coords.y, a_Coords.z);
m_HitBox.Sort();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cPieceGenerator:
cPieceGenerator::cPieceGenerator(cPiecePool & a_PiecePool, int a_Seed) :
m_PiecePool(a_PiecePool),
m_Noise(a_Seed),
m_Seed(a_Seed)
{
}
void cPieceGenerator::FreePieces(cPlacedPieces & a_PlacedPieces)
{
for (cPlacedPieces::iterator itr = a_PlacedPieces.begin(), end = a_PlacedPieces.end(); itr != end; ++itr)
{
delete *itr;
} // for itr - a_PlacedPieces[]
a_PlacedPieces.clear();
}
cPlacedPiece * cPieceGenerator::PlaceStartingPiece(int a_BlockX, int a_BlockY, int a_BlockZ, cFreeConnectors & a_OutConnectors)
{
m_PiecePool.Reset();
int rnd = m_Noise.IntNoise3DInt(a_BlockX, a_BlockY, a_BlockZ) / 7;
// Choose a random one of the starting pieces:
cPieces StartingPieces = m_PiecePool.GetStartingPieces();
cPiece * StartingPiece = StartingPieces[rnd % StartingPieces.size()];
rnd = rnd >> 16;
// Choose a random supported rotation:
int Rotations[4] = {0};
int NumRotations = 1;
for (size_t i = 1; i < ARRAYCOUNT(Rotations); i++)
{
if (StartingPiece->CanRotateCCW(i))
{
Rotations[NumRotations] = i;
NumRotations += 1;
}
}
int Rotation = Rotations[rnd % NumRotations];
cPlacedPiece * res = new cPlacedPiece(NULL, *StartingPiece, Vector3i(a_BlockX, a_BlockY, a_BlockZ), Rotation);
// Place the piece's connectors into a_OutConnectors:
const cPiece::cConnectors & Conn = StartingPiece->GetConnectors();
for (cPiece::cConnectors::const_iterator itr = Conn.begin(), end = Conn.end(); itr != end; ++itr)
{
a_OutConnectors.push_back(
cFreeConnector(res, StartingPiece->RotateMoveConnector(*itr, Rotation, a_BlockX, a_BlockY, a_BlockZ))
);
}
return res;
}
bool cPieceGenerator::TryPlacePieceAtConnector(
const cPlacedPiece & a_ParentPiece,
const cPiece::cConnector & a_Connector,
cPlacedPieces & a_OutPieces,
cPieceGenerator::cFreeConnectors & a_OutConnectors
)
{
// Translation of direction - direction -> number of CCW rotations needed:
// You need DirectionRotationTable[rot1][rot2] CCW turns to connect rot1 to rot2 (they are opposite)
static const int DirectionRotationTable[6][6] =
{
/* YM, YP, ZM, ZP, XM, XP */
/* YM */ { 0, 0, 0, 0, 0, 0},
/* YP */ { 0, 0, 0, 0, 0, 0},
/* ZM */ { 0, 0, 2, 0, 1, 3},
/* ZP */ { 0, 0, 0, 2, 3, 1},
/* XM */ { 0, 0, 3, 1, 2, 0},
/* XP */ { 0, 0, 1, 3, 0, 2},
};
// Get a list of available connections:
const int * RotTable = DirectionRotationTable[a_Connector.m_Direction];
cConnections Connections;
cPieces AvailablePieces = m_PiecePool.GetPiecesWithConnector(a_Connector.m_Type);
Connections.reserve(AvailablePieces.size());
Vector3i ConnPos = a_Connector.m_Pos; // The position at which the new connector should be placed - 1 block away from the connector
AddFaceDirection(ConnPos.x, ConnPos.y, ConnPos.z, a_Connector.m_Direction);
/*
// DEBUG:
printf("Placing piece at connector pos {%d, %d, %d}, direction %s\n", ConnPos.x, ConnPos.y, ConnPos.z, BlockFaceToString(a_Connector.m_Direction).c_str());
//*/
for (cPieces::iterator itrP = AvailablePieces.begin(), endP = AvailablePieces.end(); itrP != endP; ++itrP)
{
cPiece::cConnectors Connectors = (*itrP)->GetConnectors();
for (cPiece::cConnectors::iterator itrC = Connectors.begin(), endC = Connectors.end(); itrC != endC; ++itrC)
{
if (itrC->m_Type != a_Connector.m_Type)
{
continue;
}
// This is a same-type connector, find out how to rotate to it:
int NumCCWRotations = RotTable[itrC->m_Direction];
if (!(*itrP)->CanRotateCCW(NumCCWRotations))
{
// Doesn't support this rotation
continue;
}
if (!CheckConnection(a_Connector, ConnPos, **itrP, *itrC, NumCCWRotations, a_OutPieces))
{
// Doesn't fit in this rotation
continue;
}
Connections.push_back(cConnection(**itrP, *itrC, NumCCWRotations));
} // for itrC - Connectors[]
} // for itrP - AvailablePieces[]
if (Connections.empty())
{
// No available connections, bail out
return false;
}
// Choose a random connection from the list:
int rnd = m_Noise.IntNoise3DInt(a_Connector.m_Pos.x, a_Connector.m_Pos.y, a_Connector.m_Pos.z) / 7;
cConnection & Conn = Connections[rnd % Connections.size()];
// Place the piece:
/*
// DEBUG
printf("Chosen connector at {%d, %d, %d}, direction %s, needs %d rotations\n",
Conn.m_Connector.m_Pos.x, Conn.m_Connector.m_Pos.y, Conn.m_Connector.m_Pos.z,
BlockFaceToString(Conn.m_Connector.m_Direction).c_str(),
Conn.m_NumCCWRotations
);
//*/
Vector3i NewPos = Conn.m_Piece->RotatePos(Conn.m_Connector.m_Pos, Conn.m_NumCCWRotations);
ConnPos -= NewPos;
cPlacedPiece * PlacedPiece = new cPlacedPiece(&a_ParentPiece, *(Conn.m_Piece), ConnPos, Conn.m_NumCCWRotations);
a_OutPieces.push_back(PlacedPiece);
// Add the new piece's connectors to the list of free connectors:
cPiece::cConnectors Connectors = Conn.m_Piece->GetConnectors();
/*
// DEBUG:
printf("Adding %u connectors to the pool\n", Connectors.size() - 1);
//*/
for (cPiece::cConnectors::const_iterator itr = Connectors.begin(), end = Connectors.end(); itr != end; ++itr)
{
if (itr->m_Pos.Equals(Conn.m_Connector.m_Pos))
{
// This is the connector through which we have been connected to the parent, don't add
continue;
}
a_OutConnectors.push_back(cFreeConnector(PlacedPiece, Conn.m_Piece->RotateMoveConnector(*itr, Conn.m_NumCCWRotations, ConnPos.x, ConnPos.y, ConnPos.z)));
}
return true;
}
bool cPieceGenerator::CheckConnection(
const cPiece::cConnector & a_ExistingConnector,
const Vector3i & a_ToPos,
const cPiece & a_Piece,
const cPiece::cConnector & a_NewConnector,
int a_NumCCWRotations,
const cPlacedPieces & a_OutPieces
)
{
// For each placed piece, test the hitbox against the new piece:
cCuboid RotatedHitBox = a_Piece.RotateHitBoxToConnector(a_NewConnector, a_ToPos, a_NumCCWRotations);
RotatedHitBox.Sort();
for (cPlacedPieces::const_iterator itr = a_OutPieces.begin(), end = a_OutPieces.end(); itr != end; ++itr)
{
if ((*itr)->GetHitBox().DoesIntersect(RotatedHitBox))
{
return false;
}
}
return true;
}
//*
// DEBUG:
void cPieceGenerator::DebugConnectorPool(const cPieceGenerator::cFreeConnectors & a_ConnectorPool, size_t a_NumProcessed)
{
printf(" Connector pool: " SIZE_T_FMT " items\n", a_ConnectorPool.size() - a_NumProcessed);
size_t idx = 0;
for (cPieceGenerator::cFreeConnectors::const_iterator itr = a_ConnectorPool.begin() + a_NumProcessed, end = a_ConnectorPool.end(); itr != end; ++itr, ++idx)
{
printf(" " SIZE_T_FMT ": {%d, %d, %d}, type %d, direction %s, depth %d\n",
idx,
itr->m_Connector.m_Pos.x, itr->m_Connector.m_Pos.y, itr->m_Connector.m_Pos.z,
itr->m_Connector.m_Type,
BlockFaceToString(itr->m_Connector.m_Direction).c_str(),
itr->m_Piece->GetDepth()
);
} // for itr - a_ConnectorPool[]
}
//*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cPieceGenerator::cConnection:
cPieceGenerator::cConnection::cConnection(cPiece & a_Piece, cPiece::cConnector & a_Connector, int a_NumCCWRotations) :
m_Piece(&a_Piece),
m_Connector(a_Connector),
m_NumCCWRotations(a_NumCCWRotations)
{
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cPieceGenerator::cFreeConnector:
cPieceGenerator::cFreeConnector::cFreeConnector(cPlacedPiece * a_Piece, const cPiece::cConnector & a_Connector) :
m_Piece(a_Piece),
m_Connector(a_Connector)
{
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cBFSPieceGenerator:
cBFSPieceGenerator::cBFSPieceGenerator(cPiecePool & a_PiecePool, int a_Seed) :
super(a_PiecePool, a_Seed)
{
}
void cBFSPieceGenerator::PlacePieces(int a_BlockX, int a_BlockY, int a_BlockZ, int a_MaxDepth, cPlacedPieces & a_OutPieces)
{
a_OutPieces.clear();
cFreeConnectors ConnectorPool;
// Place the starting piece:
a_OutPieces.push_back(PlaceStartingPiece(a_BlockX, a_BlockY, a_BlockZ, ConnectorPool));
/*
// DEBUG:
printf("Placed the starting piece at {%d, %d, %d}\n", a_BlockX, a_BlockY, a_BlockZ);
cCuboid Hitbox = a_OutPieces[0]->GetHitBox();
Hitbox.Sort();
printf(" Hitbox: {%d, %d, %d} - {%d, %d, %d} (%d * %d * %d)\n",
Hitbox.p1.x, Hitbox.p1.y, Hitbox.p1.z,
Hitbox.p2.x, Hitbox.p2.y, Hitbox.p2.z,
Hitbox.DifX() + 1, Hitbox.DifY() + 1, Hitbox.DifZ() + 1
);
DebugConnectorPool(ConnectorPool, 0);
//*/
// Place pieces at the available connectors:
/*
Instead of removing them one by one from the pool, we process them sequentially and take note of the last
processed one. To save on memory, once the number of processed connectors reaches a big number, a chunk
of the connectors is removed.
*/
size_t NumProcessed = 0;
while (ConnectorPool.size() > NumProcessed)
{
cFreeConnector & Conn = ConnectorPool[NumProcessed];
if (Conn.m_Piece->GetDepth() < a_MaxDepth)
{
if (TryPlacePieceAtConnector(*Conn.m_Piece, Conn.m_Connector, a_OutPieces, ConnectorPool))
{
/*
// DEBUG:
const cPlacedPiece * NewPiece = a_OutPieces.back();
const Vector3i & Coords = NewPiece->GetCoords();
printf("Placed a new piece at {%d, %d, %d}, rotation %d\n", Coords.x, Coords.y, Coords.z, NewPiece->GetNumCCWRotations());
cCuboid Hitbox = NewPiece->GetHitBox();
Hitbox.Sort();
printf(" Hitbox: {%d, %d, %d} - {%d, %d, %d} (%d * %d * %d)\n",
Hitbox.p1.x, Hitbox.p1.y, Hitbox.p1.z,
Hitbox.p2.x, Hitbox.p2.y, Hitbox.p2.z,
Hitbox.DifX() + 1, Hitbox.DifY() + 1, Hitbox.DifZ() + 1
);
DebugConnectorPool(ConnectorPool, NumProcessed + 1);
//*/
}
}
NumProcessed++;
if (NumProcessed > 1000)
{
ConnectorPool.erase(ConnectorPool.begin(), ConnectorPool.begin() + NumProcessed);
NumProcessed = 0;
}
}
}

View File

@ -0,0 +1,247 @@
// PieceGenerator.h
// Declares the cBFSPieceGenerator class and cDFSPieceGenerator class
// representing base classes for generating structures composed of individual "pieces"
/*
Each uses a slightly different approach to generating:
- DFS extends pieces one by one until it hits the configured depth (or can't connect another piece anymore),
then starts looking at adjacent connectors (like depth-first search).
- BFS keeps a pool of currently-open connectors, chooses one at random and tries to place a piece on it,
thus possibly extending the pool of open connectors (like breadth-first search).
*/
#pragma once
#include "../Defines.h"
#include "../Cuboid.h"
#include "../Noise.h"
/** Represents a single piece. Can have multiple connectors of different types where other pieces can connect. */
class cPiece
{
public:
// Force a virtual destructor in all descendants
virtual ~cPiece() {}
struct cConnector
{
/** Position relative to the piece */
Vector3i m_Pos;
/** Type of the connector. Any arbitrary number; the generator connects only connectors of the same type. */
int m_Type;
/** Direction in which the connector is facing.
Will be matched by the opposite direction for the connecting connector. */
eBlockFace m_Direction;
cConnector(int a_X, int a_Y, int a_Z, int a_Type, eBlockFace a_Direction);
cConnector(const Vector3i & a_Pos, int a_Type, eBlockFace a_Direction);
};
typedef std::vector<cConnector> cConnectors;
/** Returns all of the available connectors that the piece has.
Each connector has a (relative) position in the piece, and a type associated with it. */
virtual cConnectors GetConnectors(void) const = 0;
/** Returns the dimensions of this piece.
The dimensions cover the entire piece, there is no block that the piece generates outside of this size. */
virtual Vector3i GetSize(void) const = 0;
/** Returns the "hitbox" of this piece.
A hitbox is what is compared and must not intersect other pieces' hitboxes when generating. */
virtual cCuboid GetHitBox(void) const = 0;
/** Returns true if the piece can be rotated CCW the specific number of 90-degree turns. */
virtual bool CanRotateCCW(int a_NumRotations) const = 0;
/** Returns a copy of the a_Pos after rotating the piece the specified number of CCW rotations. */
Vector3i RotatePos(const Vector3i & a_Pos, int a_NumCCWRotations) const;
/** Returns a copy of the connector that is rotated and then moved by the specified amounts. */
cConnector RotateMoveConnector(const cConnector & a_Connector, int a_NumCCWRotations, int a_MoveX, int a_MoveY, int a_MoveZ) const;
/** Returns the hitbox after the specified number of rotations and moved so that a_MyConnector is placed at a_ToConnectorPos. */
cCuboid RotateHitBoxToConnector(const cConnector & a_MyConnector, const Vector3i & a_ToConnectorPos, int a_NumCCWRotations) const;
/** Returns the hitbox after the specified number of CCW rotations and moved by the specified amounts. */
cCuboid RotateMoveHitBox(int a_NumCCWRotations, int a_MoveX, int a_MoveY, int a_MoveZ) const;
};
typedef std::vector<cPiece *> cPieces;
/** This class is an interface that provides pieces for the generator. It can keep track of what pieces were
placed and adjust the returned piece vectors. */
class cPiecePool
{
public:
// Force a virtual destructor in all descendants:
virtual ~cPiecePool() {}
/** Returns a list of pieces that contain the specified connector type.
The cPiece pointers returned are managed by the pool and the caller doesn't free them. */
virtual cPieces GetPiecesWithConnector(int a_ConnectorType) = 0;
/** Returns the pieces that should be used as the starting point.
Multiple starting points are supported, one of the returned piece will be chosen. */
virtual cPieces GetStartingPieces(void) = 0;
/** Called after a piece is placed, to notify the pool that it has been used.
The pool may adjust the pieces it will return the next time. */
virtual void PiecePlaced(const cPiece & a_Piece) = 0;
/** Called when the pool has finished the current structure and should reset any piece-counters it has
for a new structure. */
virtual void Reset(void) = 0;
};
/** Represents a single piece that has been placed to specific coords in the world. */
class cPlacedPiece
{
public:
cPlacedPiece(const cPlacedPiece * a_Parent, const cPiece & a_Piece, const Vector3i & a_Coords, int a_NumCCWRotations);
const cPiece & GetPiece (void) const { return *m_Piece; }
const Vector3i & GetCoords (void) const { return m_Coords; }
int GetNumCCWRotations(void) const { return m_NumCCWRotations; }
const cCuboid & GetHitBox (void) const { return m_HitBox; }
int GetDepth (void) const { return m_Depth; }
protected:
const cPlacedPiece * m_Parent;
const cPiece * m_Piece;
Vector3i m_Coords;
int m_NumCCWRotations;
cCuboid m_HitBox;
int m_Depth;
};
typedef std::vector<cPlacedPiece *> cPlacedPieces;
class cPieceGenerator
{
public:
cPieceGenerator(cPiecePool & a_PiecePool, int a_Seed);
/** Cleans up all the memory used by the placed pieces.
Call this utility function instead of freeing the items on your own. */
static void FreePieces(cPlacedPieces & a_PlacedPieces);
protected:
/** The type used for storing a connection from one piece to another, while building the piece tree. */
struct cConnection
{
cPiece * m_Piece; // The piece being connected
cPiece::cConnector m_Connector; // The piece's connector being used (relative non-rotated coords)
int m_NumCCWRotations; // Number of rotations necessary to match the two connectors
cConnection(cPiece & a_Piece, cPiece::cConnector & a_Connector, int a_NumCCWRotations);
};
typedef std::vector<cConnection> cConnections;
/** The type used for storing a pool of connectors that will be attempted to expand by another piece. */
struct cFreeConnector
{
cPlacedPiece * m_Piece;
cPiece::cConnector m_Connector;
cFreeConnector(cPlacedPiece * a_Piece, const cPiece::cConnector & a_Connector);
};
typedef std::vector<cFreeConnector> cFreeConnectors;
cPiecePool & m_PiecePool;
cNoise m_Noise;
int m_Seed;
/** Selects a starting piece and places it, including the rotations.
Also puts the piece's connectors in a_OutConnectors. */
cPlacedPiece * PlaceStartingPiece(int a_BlockX, int a_BlockY, int a_BlockZ, cFreeConnectors & a_OutConnectors);
/** Tries to place a new piece at the specified (placed) connector. Returns true if successful. */
bool TryPlacePieceAtConnector(
const cPlacedPiece & a_ParentPiece, // The existing piece to a new piece should be placed
const cPiece::cConnector & a_Connector, // The existing connector (world-coords) to which a new piece should be placed
cPlacedPieces & a_OutPieces, // Already placed pieces, to be checked for intersections
cFreeConnectors & a_OutConnectors // List of free connectors to which the new connectors will be placed
);
/** Checks if the specified piece would fit with the already-placed pieces, using the specified connector
and number of CCW rotations.
a_ExistingConnector is in world-coords and is already rotated properly
a_ToPos is the world-coords position on which the new connector should be placed (1 block away from a_ExistingConnector, in its Direction)
a_NewConnector is in the original (non-rotated) coords.
Returns true if the piece fits, false if not. */
bool CheckConnection(
const cPiece::cConnector & a_ExistingConnector, // The existing connector
const Vector3i & a_ToPos, // The position on which the new connector should be placed
const cPiece & a_Piece, // The new piece
const cPiece::cConnector & a_NewConnector, // The connector of the new piece
int a_NumCCWRotations, // Number of rotations for the new piece to align the connector
const cPlacedPieces & a_OutPieces // All the already-placed pieces to check
);
/** DEBUG: Outputs all the connectors in the pool into stdout.
a_NumProcessed signals the number of connectors from the pool that should be considered processed (not listed). */
void DebugConnectorPool(const cPieceGenerator::cFreeConnectors & a_ConnectorPool, size_t a_NumProcessed);
} ;
class cBFSPieceGenerator :
public cPieceGenerator
{
typedef cPieceGenerator super;
public:
cBFSPieceGenerator(cPiecePool & a_PiecePool, int a_Seed);
/** Generates a placement for pieces at the specified coords.
Caller must free each individual cPlacedPiece in a_OutPieces. */
void PlacePieces(int a_BlockX, int a_BlockY, int a_BlockZ, int a_MaxDepth, cPlacedPieces & a_OutPieces);
};
class cDFSPieceGenerator :
public cPieceGenerator
{
public:
cDFSPieceGenerator(cPiecePool & a_PiecePool, int a_Seed);
/** Generates a placement for pieces at the specified coords.
Caller must free each individual cPlacedPiece in a_OutPieces. */
void PlacePieces(int a_BlockX, int a_BlockY, int a_BlockZ, cPlacedPieces & a_OutPieces);
};

View File

@ -595,7 +595,7 @@ void GetPineTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise
{
break;
}
ASSERT(LayerSize < ARRAYCOUNT(BigOs));
ASSERT((size_t)LayerSize < ARRAYCOUNT(BigOs));
PushCoordBlocks(a_BlockX, h, a_BlockZ, a_OtherBlocks, BigOs[LayerSize].Coords, BigOs[LayerSize].Count, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
h--;
}

View File

@ -38,6 +38,15 @@
// No alignment needed in MSVC
#define ALIGN_8
#define ALIGN_16
#define FORMATSTRING(formatIndex, va_argsIndex)
// MSVC has its own custom version of zu format
#define SIZE_T_FMT "%Iu"
#define SIZE_T_FMT_PRECISION(x) "%" #x "Iu"
#define SIZE_T_FMT_HEX "%Ix"
#define NORETURN __declspec(noreturn)
#elif defined(__GNUC__)
@ -56,6 +65,14 @@
// Some portability macros :)
#define stricmp strcasecmp
#define FORMATSTRING(formatIndex, va_argsIndex) __attribute__((format (printf, formatIndex, va_argsIndex)))
#define SIZE_T_FMT "%zu"
#define SIZE_T_FMT_PRECISION(x) "%" #x "zu"
#define SIZE_T_FMT_HEX "%zx"
#define NORETURN __attribute((__noreturn__))
#else
@ -81,8 +98,15 @@
#endif
#ifdef _DEBUG
#define NORETURNDEBUG NORETURN
#else
#define NORETURNDEBUG
#endif
#include <stddef.h>
// Integral types with predefined sizes:
typedef long long Int64;
@ -96,8 +120,23 @@ typedef unsigned short UInt16;
typedef unsigned char Byte;
// If you get an error about specialization check the size of integral types
template <typename T, size_t Size, bool x = sizeof(T) == Size>
class SizeChecker;
template <typename T, size_t Size>
class SizeChecker<T, Size, true>
{
T v;
};
template class SizeChecker<Int64, 8>;
template class SizeChecker<Int32, 4>;
template class SizeChecker<Int16, 2>;
template class SizeChecker<UInt64, 8>;
template class SizeChecker<UInt32, 4>;
template class SizeChecker<UInt16, 2>;
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for any class that shouldn't allow copying itself
@ -179,7 +218,7 @@ typedef unsigned char Byte;
#include <memory>
#include <set>
#include <queue>
#include <limits>
@ -220,9 +259,10 @@ typedef unsigned char Byte;
// Pretty much the same as ASSERT() but stays in Release builds
#define VERIFY( x ) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), exit(1), 0 ) )
// Same as assert but in all Self test builds
#ifdef SELF_TEST
#define assert_test(x) ( !!(x) || (assert(!#x), exit(1), 0))
#endif
/// A generic interface used mainly in ForEach() functions
template <typename Type> class cItemCallback
@ -246,6 +286,14 @@ T Clamp(T a_Value, T a_Min, T a_Max)
#ifndef TOLUA_TEMPLATE_BIND
#define TOLUA_TEMPLATE_BIND(x)
#endif
// Common headers (part 2, with macros):
#include "ChunkDef.h"
#include "BiomeDef.h"
@ -254,5 +302,3 @@ T Clamp(T a_Value, T a_Min, T a_Max)
#include "Entities/Effects.h"

View File

@ -1,4 +1,4 @@

#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Item.h"
@ -139,6 +139,16 @@ void cItem::GetJson(Json::Value & a_OutValue) const
{
a_OutValue["Lore"] = m_Lore;
}
if ((m_ItemType == E_ITEM_FIREWORK_ROCKET) || (m_ItemType == E_ITEM_FIREWORK_STAR))
{
a_OutValue["Flicker"] = m_FireworkItem.m_HasFlicker;
a_OutValue["Trail"] = m_FireworkItem.m_HasTrail;
a_OutValue["Type"] = m_FireworkItem.m_Type;
a_OutValue["FlightTimeInTicks"] = m_FireworkItem.m_FlightTimeInTicks;
a_OutValue["Colours"] = m_FireworkItem.ColoursToString(m_FireworkItem);
a_OutValue["FadeColours"] = m_FireworkItem.FadeColoursToString(m_FireworkItem);
}
}
}
@ -157,6 +167,16 @@ void cItem::FromJson(const Json::Value & a_Value)
m_Enchantments.AddFromString(a_Value.get("ench", "").asString());
m_CustomName = a_Value.get("Name", "").asString();
m_Lore = a_Value.get("Lore", "").asString();
if ((m_ItemType == E_ITEM_FIREWORK_ROCKET) || (m_ItemType == E_ITEM_FIREWORK_STAR))
{
m_FireworkItem.m_HasFlicker = a_Value.get("Flicker", false).asBool();
m_FireworkItem.m_HasTrail = a_Value.get("Trail", false).asBool();
m_FireworkItem.m_Type = (NIBBLETYPE)a_Value.get("Type", 0).asInt();
m_FireworkItem.m_FlightTimeInTicks = (short)a_Value.get("FlightTimeInTicks", 0).asInt();
m_FireworkItem.ColoursFromString(a_Value.get("Colours", "").asString(), m_FireworkItem);
m_FireworkItem.FadeColoursFromString(a_Value.get("FadeColours", "").asString(), m_FireworkItem);
}
}
}
@ -196,7 +216,7 @@ cItem * cItems::Get(int a_Idx)
{
if ((a_Idx < 0) || (a_Idx >= (int)size()))
{
LOGWARNING("cItems: Attempt to get an out-of-bounds item at index %d; there are currently %d items. Returning a nil.", a_Idx, size());
LOGWARNING("cItems: Attempt to get an out-of-bounds item at index %d; there are currently " SIZE_T_FMT " items. Returning a nil.", a_Idx, size());
return NULL;
}
return &at(a_Idx);
@ -210,7 +230,7 @@ void cItems::Set(int a_Idx, const cItem & a_Item)
{
if ((a_Idx < 0) || (a_Idx >= (int)size()))
{
LOGWARNING("cItems: Attempt to set an item at an out-of-bounds index %d; there are currently %d items. Not setting.", a_Idx, size());
LOGWARNING("cItems: Attempt to set an item at an out-of-bounds index %d; there are currently " SIZE_T_FMT " items. Not setting.", a_Idx, size());
return;
}
at(a_Idx) = a_Item;
@ -224,7 +244,7 @@ void cItems::Delete(int a_Idx)
{
if ((a_Idx < 0) || (a_Idx >= (int)size()))
{
LOGWARNING("cItems: Attempt to delete an item at an out-of-bounds index %d; there are currently %d items. Ignoring.", a_Idx, size());
LOGWARNING("cItems: Attempt to delete an item at an out-of-bounds index %d; there are currently " SIZE_T_FMT " items. Ignoring.", a_Idx, size());
return;
}
erase(begin() + a_Idx);
@ -238,7 +258,7 @@ void cItems::Set(int a_Idx, short a_ItemType, char a_ItemCount, short a_ItemDama
{
if ((a_Idx < 0) || (a_Idx >= (int)size()))
{
LOGWARNING("cItems: Attempt to set an item at an out-of-bounds index %d; there are currently %d items. Not setting.", a_Idx, size());
LOGWARNING("cItems: Attempt to set an item at an out-of-bounds index %d; there are currently " SIZE_T_FMT " items. Not setting.", a_Idx, size());
return;
}
at(a_Idx) = cItem(a_ItemType, a_ItemCount, a_ItemDamage);

View File

@ -11,6 +11,7 @@
#include "Defines.h"
#include "Enchantments.h"
#include "WorldStorage/FireworksSerializer.h"
@ -38,7 +39,8 @@ public:
m_ItemCount(0),
m_ItemDamage(0),
m_CustomName(""),
m_Lore("")
m_Lore(""),
m_FireworkItem()
{
}
@ -57,7 +59,8 @@ public:
m_ItemDamage (a_ItemDamage),
m_Enchantments(a_Enchantments),
m_CustomName (a_CustomName),
m_Lore (a_Lore)
m_Lore (a_Lore),
m_FireworkItem()
{
if (!IsValidItem(m_ItemType))
{
@ -77,7 +80,8 @@ public:
m_ItemDamage (a_CopyFrom.m_ItemDamage),
m_Enchantments(a_CopyFrom.m_Enchantments),
m_CustomName (a_CopyFrom.m_CustomName),
m_Lore (a_CopyFrom.m_Lore)
m_Lore (a_CopyFrom.m_Lore),
m_FireworkItem(a_CopyFrom.m_FireworkItem)
{
}
@ -90,6 +94,7 @@ public:
m_Enchantments.Clear();
m_CustomName = "";
m_Lore = "";
m_FireworkItem.EmptyData();
}
@ -115,7 +120,8 @@ public:
(m_ItemDamage == a_Item.m_ItemDamage) &&
(m_Enchantments == a_Item.m_Enchantments) &&
(m_CustomName == a_Item.m_CustomName) &&
(m_Lore == a_Item.m_Lore)
(m_Lore == a_Item.m_Lore) &&
m_FireworkItem.IsEqualTo(a_Item.m_FireworkItem)
);
}
@ -177,6 +183,8 @@ public:
cEnchantments m_Enchantments;
AString m_CustomName;
AString m_Lore;
cFireworkItem m_FireworkItem;
};
// tolua_end

View File

@ -21,6 +21,9 @@ class cItemHandler
public:
cItemHandler(int a_ItemType);
// Force virtual destructor
virtual ~cItemHandler() {}
/// Called when the player tries to use the item (right mouse button). Return false to make the item unusable. DEFAULT: False
virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir);

View File

@ -19,17 +19,13 @@ public:
{
switch(m_ItemType)
{
case E_ITEM_WOODEN_PICKAXE:
case E_ITEM_GOLD_PICKAXE:
return 1;
case E_ITEM_STONE_PICKAXE:
return 2;
case E_ITEM_IRON_PICKAXE:
return 3;
case E_ITEM_DIAMOND_PICKAXE:
return 4;
default:
return 0;
case E_ITEM_WOODEN_PICKAXE: return 1;
case E_ITEM_GOLD_PICKAXE: return 1;
case E_ITEM_STONE_PICKAXE: return 2;
case E_ITEM_IRON_PICKAXE: return 3;
case E_ITEM_DIAMOND_PICKAXE: return 4;
default: return 0;
}
}
@ -61,6 +57,10 @@ public:
return PickaxeLevel() >= 2;
}
case E_BLOCK_ANVIL:
case E_BLOCK_ENCHANTMENT_TABLE:
case E_BLOCK_FURNACE:
case E_BLOCK_LIT_FURNACE:
case E_BLOCK_COAL_ORE:
case E_BLOCK_STONE:
case E_BLOCK_COBBLESTONE:

View File

@ -35,7 +35,7 @@ public:
Vector3d Pos = a_Player->GetThrowStartPos();
Vector3d Speed = a_Player->GetLookVector() * m_SpeedCoeff;
a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, &Speed);
a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, a_Player->GetEquippedItem(), &Speed);
return true;
}
@ -127,13 +127,13 @@ public:
return false;
}
a_World->CreateProjectile(a_BlockX + 0.5, a_BlockY + 1, a_BlockZ + 0.5, m_ProjectileKind, a_Player, a_Player->GetEquippedItem());
if (!a_Player->IsGameModeCreative())
{
a_Player->GetInventory().RemoveOneEquippedItem();
}
a_World->CreateProjectile(a_BlockX + 0.5, a_BlockY + 1, a_BlockZ + 0.5, m_ProjectileKind, a_Player, 0);
return true;
}

View File

@ -13,12 +13,6 @@
/// If more than this many chunks are in the queue, a warning is printed to the log
#define WARN_ON_QUEUE_SIZE 800
/// Chunk data callback that takes the chunk data and puts them into cLightingThread's m_BlockTypes[] / m_HeightMap[]:
class cReader :

View File

@ -5,7 +5,7 @@
#include "Globals.h"
#include "LineBlockTracer.h"
#include "Vector3d.h"
#include "Vector3.h"
#include "World.h"
#include "Chunk.h"

View File

@ -118,7 +118,7 @@ void cLog::Log(const char * a_Format, va_list argList)
AString Line;
#ifdef _DEBUG
Printf(Line, "[%04x|%02d:%02d:%02d] %s", cIsThread::GetCurrentID(), timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, Message.c_str());
Printf(Line, "[%04lx|%02d:%02d:%02d] %s", cIsThread::GetCurrentID(), timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, Message.c_str());
#else
Printf(Line, "[%02d:%02d:%02d] %s", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, Message.c_str());
#endif

View File

@ -14,8 +14,8 @@ private:
public:
cLog(const AString & a_FileName);
~cLog();
void Log(const char * a_Format, va_list argList);
void Log(const char * a_Format, ...);
void Log(const char * a_Format, va_list argList) FORMATSTRING(2, 0);
void Log(const char * a_Format, ...) FORMATSTRING(2, 3);
// tolua_begin
void SimpleLog(const char * a_String);
void OpenLog(const char * a_FileName);

View File

@ -21,10 +21,10 @@ public: // tolua_export
~cMCLogger(); // tolua_export
void Log(const char* a_Format, va_list a_ArgList);
void Info(const char* a_Format, va_list a_ArgList);
void Warn(const char* a_Format, va_list a_ArgList);
void Error(const char* a_Format, va_list a_ArgList);
void Log(const char* a_Format, va_list a_ArgList) FORMATSTRING(2, 0);
void Info(const char* a_Format, va_list a_ArgList) FORMATSTRING(2, 0);
void Warn(const char* a_Format, va_list a_ArgList) FORMATSTRING(2, 0);
void Error(const char* a_Format, va_list a_ArgList) FORMATSTRING(2, 0);
void LogSimple(const char* a_Text, int a_LogType = 0 ); // tolua_export
@ -57,10 +57,10 @@ private:
extern void LOG(const char* a_Format, ...);
extern void LOGINFO(const char* a_Format, ...);
extern void LOGWARN(const char* a_Format, ...);
extern void LOGERROR(const char* a_Format, ...);
extern void LOG(const char* a_Format, ...) FORMATSTRING(1, 2);
extern void LOGINFO(const char* a_Format, ...) FORMATSTRING(1, 2);
extern void LOGWARN(const char* a_Format, ...) FORMATSTRING(1, 2);
extern void LOGERROR(const char* a_Format, ...) FORMATSTRING(1, 2);

View File

@ -64,7 +64,7 @@ public:
unsigned int GetPixelX(void) const { return m_PixelX; }
unsigned int GetPixelZ(void) const { return m_PixelZ; }
int GetRot(void) const { return m_Rot; }
unsigned int GetRot(void) const { return m_Rot; }
eType GetType(void) const { return m_Type; }

224
src/Matrix4.h Normal file
View File

@ -0,0 +1,224 @@
#pragma once
#define _USE_MATH_DEFINES // Enable non-standard math defines (MSVC)
#include <math.h>
template <typename T>
// tolua_begin
class Matrix4
{
TOLUA_TEMPLATE_BIND((T, float, double))
// tolua_end
public:
T cell[16];
// tolua_begin
inline Matrix4(void)
{
Identity();
}
inline Matrix4(const Matrix4 & a_Rhs)
{
*this = a_Rhs;
}
inline Matrix4 & operator = (const Matrix4 & a_Rhs)
{
for (unsigned int i = 0; i < 16; ++i)
{
cell[i] = a_Rhs.cell[i];
}
return *this;
}
inline T & operator [] (int a_N)
{
ASSERT(a_N < 16);
return cell[a_N];
}
inline void Identity()
{
cell[1] = cell[2] = cell[3] = cell[4] = 0;
cell[6] = cell[7] = cell[8] = cell[9] = 0;
cell[11] = cell[12] = cell[13] = cell[14] = 0;
cell[0] = cell[5] = cell[10] = cell[15] = 1;
}
inline void Init(const Vector3<T> & a_Pos, T a_RX, T a_RY, T a_RZ)
{
Matrix4<T> t;
t.RotateX(a_RZ);
RotateY(a_RY);
Concatenate(t);
t.RotateZ(a_RX);
Concatenate(t);
Translate(a_Pos);
}
inline void RotateX(T a_RX)
{
T sx = (T) sin(a_RX * M_PI / 180);
T cx = (T) cos(a_RX * M_PI / 180);
Identity();
cell[5] = cx; cell[6] = sx;
cell[9] = -sx; cell[10] = cx;
}
inline void RotateY(T a_RY)
{
T sy = (T) sin(a_RY * M_PI / 180);
T cy = (T) cos(a_RY * M_PI / 180);
Identity();
cell[0] = cy; cell[2] = -sy;
cell[8] = sy; cell[10] = cy;
}
inline void RotateZ(T a_RZ)
{
T sz = (T) sin(a_RZ * M_PI / 180);
T cz = (T) cos(a_RZ * M_PI / 180);
Identity();
cell[0] = cz; cell[1] = sz;
cell[4] = -sz; cell[5] = cz;
}
inline void Translate(const Vector3<T> & a_Pos)
{
cell[3] += a_Pos.x;
cell[7] += a_Pos.y;
cell[11] += a_Pos.z;
}
inline void SetTranslation(const Vector3<T> & a_Pos)
{
cell[3] = a_Pos.x;
cell[7] = a_Pos.y;
cell[11] = a_Pos.z;
}
inline void Concatenate(const Matrix4 & m2)
{
Matrix4 res;
for (unsigned int c = 0; c < 4; ++c)
{
for (unsigned int r = 0; r < 4; ++r)
{
res.cell[r * 4 + c] = (
cell[r * 4 + 0] * m2.cell[c + 0] +
cell[r * 4 + 1] * m2.cell[c + 4] +
cell[r * 4 + 2] * m2.cell[c + 8] +
cell[r * 4 + 3] * m2.cell[c + 12]
);
}
}
*this = res;
}
inline Vector3<T> Transform(const Vector3<T> & v) const
{
T x = cell[0] * v.x + cell[1] * v.y + cell[2] * v.z + cell[3];
T y = cell[4] * v.x + cell[5] * v.y + cell[6] * v.z + cell[7];
T z = cell[8] * v.x + cell[9] * v.y + cell[10] * v.z + cell[11];
return Vector3<T>(x, y, z);
}
inline void Invert(void)
{
Matrix4 t;
T tx = -cell[3];
T ty = -cell[7];
T tz = -cell[11];
for (unsigned int h = 0; h < 3; ++h)
{
for (unsigned int v = 0; v < 3; ++v)
{
t.cell[h + v * 4] = cell[v + h * 4];
}
}
for (unsigned int i = 0; i < 11; ++i)
{
cell[i] = t.cell[i];
}
cell[3] = tx * cell[0] + ty * cell[1] + tz * cell[2];
cell[7] = tx * cell[4] + ty * cell[5] + tz * cell[6];
cell[11] = tx * cell[8] + ty * cell[9] + tz * cell[10];
}
inline Vector3<T> GetXColumn(void) const
{
return Vector3<T>(cell[0], cell[1], cell[2]);
}
inline Vector3<T> GetYColumn(void) const
{
return Vector3<T>(cell[4], cell[5], cell[6]);
}
inline Vector3<T> GetZColumn(void) const
{
return Vector3<T>(cell[8], cell[9], cell[10]);
}
inline void SetXColumn(const Vector3<T> & a_X)
{
cell[0] = a_X.x;
cell[1] = a_X.y;
cell[2] = a_X.z;
}
inline void SetYColumn(const Vector3<T> & a_Y)
{
cell[4] = a_Y.x;
cell[5] = a_Y.y;
cell[6] = a_Y.z;
}
inline void SetZColumn(const Vector3<T> & a_Z)
{
cell[8] = a_Z.x;
cell[9] = a_Z.y;
cell[10] = a_Z.z;
}
};
// tolua_end
// tolua_begin
typedef Matrix4<double> Matrix4d;
typedef Matrix4<float> Matrix4f;
// tolua_end

View File

@ -1,4 +0,0 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
// _X: empty file??

View File

@ -1,225 +0,0 @@
#pragma once
#define _USE_MATH_DEFINES
#include <math.h>
#include "Vector3f.h"
class Matrix4f
{
public:
enum
{
TX=3,
TY=7,
TZ=11,
D0=0, D1=5, D2=10, D3=15,
SX=D0, SY=D1, SZ=D2,
W=D3
};
Matrix4f() { Identity(); }
float& operator [] ( int a_N ) { return cell[a_N]; }
void Identity()
{
cell[1] = cell[2] = cell[TX] = cell[4] = cell[6] = cell[TY] =
cell[8] = cell[9] = cell[TZ] = cell[12] = cell[13] = cell[14] = 0;
cell[D0] = cell[D1] = cell[D2] = cell[W] = 1;
}
void Init( Vector3f a_Pos, float a_RX, float a_RY, float a_RZ )
{
Matrix4f t;
t.RotateX( a_RZ );
RotateY( a_RY );
Concatenate( t );
t.RotateZ( a_RX );
Concatenate( t );
Translate( a_Pos );
}
void RotateX( float a_RX )
{
float sx = (float)sin( a_RX * M_PI / 180 );
float cx = (float)cos( a_RX * M_PI / 180 );
Identity();
cell[5] = cx, cell[6] = sx, cell[9] = -sx, cell[10] = cx;
}
void RotateY( float a_RY )
{
float sy = (float)sin( a_RY * M_PI / 180 );
float cy = (float)cos( a_RY * M_PI / 180 );
Identity ();
cell[0] = cy, cell[2] = -sy, cell[8] = sy, cell[10] = cy;
}
void RotateZ( float a_RZ )
{
float sz = (float)sin( a_RZ * M_PI / 180 );
float cz = (float)cos( a_RZ * M_PI / 180 );
Identity ();
cell[0] = cz, cell[1] = sz, cell[4] = -sz, cell[5] = cz;
}
void Translate( Vector3f a_Pos ) { cell[TX] += a_Pos.x; cell[TY] += a_Pos.y; cell[TZ] += a_Pos.z; }
void SetTranslation( Vector3f a_Pos ) { cell[TX] = a_Pos.x; cell[TY] = a_Pos.y; cell[TZ] = a_Pos.z; }
void Concatenate( const Matrix4f& m2 )
{
Matrix4f res;
int c;
for ( c = 0; c < 4; c++ ) for ( int r = 0; r < 4; r++ )
res.cell[r * 4 + c] = cell[r * 4] * m2.cell[c] +
cell[r * 4 + 1] * m2.cell[c + 4] +
cell[r * 4 + 2] * m2.cell[c + 8] +
cell[r * 4 + 3] * m2.cell[c + 12];
for ( c = 0; c < 16; c++ ) cell[c] = res.cell[c];
}
Vector3f Transform( const Vector3f& v ) const
{
float x = cell[0] * v.x + cell[1] * v.y + cell[2] * v.z + cell[3];
float y = cell[4] * v.x + cell[5] * v.y + cell[6] * v.z + cell[7];
float z = cell[8] * v.x + cell[9] * v.y + cell[10] * v.z + cell[11];
return Vector3f( x, y, z );
}
void Invert()
{
Matrix4f t;
int h, i;
float tx = -cell[3], ty = -cell[7], tz = -cell[11];
for ( h = 0; h < 3; h++ ) for ( int v = 0; v < 3; v++ ) t.cell[h + v * 4] = cell[v + h * 4];
for ( i = 0; i < 11; i++ ) cell[i] = t.cell[i];
cell[3] = tx * cell[0] + ty * cell[1] + tz * cell[2];
cell[7] = tx * cell[4] + ty * cell[5] + tz * cell[6];
cell[11] = tx * cell[8] + ty * cell[9] + tz * cell[10];
}
Vector3f GetXColumn() { return Vector3f( cell[0], cell[1], cell[2] ); }
Vector3f GetYColumn() { return Vector3f( cell[4], cell[5], cell[6] ); }
Vector3f GetZColumn() { return Vector3f( cell[8], cell[9], cell[10] ); }
void SetXColumn( const Vector3f & a_X )
{
cell[0] = a_X.x;
cell[1] = a_X.y;
cell[2] = a_X.z;
}
void SetYColumn( const Vector3f & a_Y )
{
cell[4] = a_Y.x;
cell[5] = a_Y.y;
cell[6] = a_Y.z;
}
void SetZColumn( const Vector3f & a_Z )
{
cell[8] = a_Z.x;
cell[9] = a_Z.y;
cell[10] = a_Z.z;
}
float cell[16];
};
class Matrix4d
{
public:
enum
{
TX=3,
TY=7,
TZ=11,
D0=0, D1=5, D2=10, D3=15,
SX=D0, SY=D1, SZ=D2,
W=D3
};
Matrix4d() { Identity(); }
double& operator [] ( int a_N ) { return cell[a_N]; }
void Identity()
{
cell[1] = cell[2] = cell[TX] = cell[4] = cell[6] = cell[TY] =
cell[8] = cell[9] = cell[TZ] = cell[12] = cell[13] = cell[14] = 0;
cell[D0] = cell[D1] = cell[D2] = cell[W] = 1;
}
void Init( Vector3f a_Pos, double a_RX, double a_RY, double a_RZ )
{
Matrix4d t;
t.RotateX( a_RZ );
RotateY( a_RY );
Concatenate( t );
t.RotateZ( a_RX );
Concatenate( t );
Translate( a_Pos );
}
void RotateX( double a_RX )
{
double sx = (double)sin( a_RX * M_PI / 180 );
double cx = (double)cos( a_RX * M_PI / 180 );
Identity();
cell[5] = cx, cell[6] = sx, cell[9] = -sx, cell[10] = cx;
}
void RotateY( double a_RY )
{
double sy = (double)sin( a_RY * M_PI / 180 );
double cy = (double)cos( a_RY * M_PI / 180 );
Identity ();
cell[0] = cy, cell[2] = -sy, cell[8] = sy, cell[10] = cy;
}
void RotateZ( double a_RZ )
{
double sz = (double)sin( a_RZ * M_PI / 180 );
double cz = (double)cos( a_RZ * M_PI / 180 );
Identity ();
cell[0] = cz, cell[1] = sz, cell[4] = -sz, cell[5] = cz;
}
void Translate( Vector3d a_Pos ) { cell[TX] += a_Pos.x; cell[TY] += a_Pos.y; cell[TZ] += a_Pos.z; }
void SetTranslation( Vector3d a_Pos ) { cell[TX] = a_Pos.x; cell[TY] = a_Pos.y; cell[TZ] = a_Pos.z; }
void Concatenate( const Matrix4d & m2 )
{
Matrix4d res;
int c;
for ( c = 0; c < 4; c++ ) for ( int r = 0; r < 4; r++ )
res.cell[r * 4 + c] = cell[r * 4] * m2.cell[c] +
cell[r * 4 + 1] * m2.cell[c + 4] +
cell[r * 4 + 2] * m2.cell[c + 8] +
cell[r * 4 + 3] * m2.cell[c + 12];
for ( c = 0; c < 16; c++ ) cell[c] = res.cell[c];
}
Vector3d Transform( const Vector3d & v ) const
{
double x = cell[0] * v.x + cell[1] * v.y + cell[2] * v.z + cell[3];
double y = cell[4] * v.x + cell[5] * v.y + cell[6] * v.z + cell[7];
double z = cell[8] * v.x + cell[9] * v.y + cell[10] * v.z + cell[11];
return Vector3d( x, y, z );
}
void Invert()
{
Matrix4d t;
int h, i;
double tx = -cell[3], ty = -cell[7], tz = -cell[11];
for ( h = 0; h < 3; h++ ) for ( int v = 0; v < 3; v++ ) t.cell[h + v * 4] = cell[v + h * 4];
for ( i = 0; i < 11; i++ ) cell[i] = t.cell[i];
cell[3] = tx * cell[0] + ty * cell[1] + tz * cell[2];
cell[7] = tx * cell[4] + ty * cell[5] + tz * cell[6];
cell[11] = tx * cell[8] + ty * cell[9] + tz * cell[10];
}
Vector3d GetXColumn() { return Vector3d( cell[0], cell[1], cell[2] ); }
Vector3d GetYColumn() { return Vector3d( cell[4], cell[5], cell[6] ); }
Vector3d GetZColumn() { return Vector3d( cell[8], cell[9], cell[10] ); }
void SetXColumn( const Vector3d & a_X )
{
cell[0] = a_X.x;
cell[1] = a_X.y;
cell[2] = a_X.z;
}
void SetYColumn( const Vector3d & a_Y )
{
cell[4] = a_Y.x;
cell[5] = a_Y.y;
cell[6] = a_Y.z;
}
void SetZColumn( const Vector3d & a_Z )
{
cell[8] = a_Z.x;
cell[9] = a_Z.y;
cell[10] = a_Z.z;
}
double cell[16];
} ;

View File

@ -62,7 +62,7 @@
class MTRand {
// Data
public:
typedef long uint32; // unsigned integer type, at least 32 bits
typedef UInt32 uint32; // unsigned integer type, at least 32 bits
enum { N = 624 }; // length of state vector
enum { SAVE = N + 1 }; // length of array for save()
@ -72,7 +72,7 @@ protected:
uint32 state[N]; // internal state
uint32 *pNext; // next value to get from state
int left; // number of values left before reload needed
uint32 left; // number of values left before reload needed
// Methods
public:
@ -164,7 +164,7 @@ inline void MTRand::initialize( const uint32 seed )
// only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto.
uint32 *s = state;
uint32 *r = state;
int i = 1;
uint32 i = 1;
*s++ = seed & 0xffffffffUL;
for( ; i < N; ++i )
{
@ -205,9 +205,9 @@ inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength )
// in each element are discarded.
// Just call seed() if you want to get array from /dev/urandom
initialize(19650218UL);
int i = 1;
uint32 i = 1;
uint32 j = 0;
int k = ( (uint32)N > seedLength ? (uint32)N : seedLength );
uint32 k = ( (uint32)N > seedLength ? (uint32)N : seedLength );
for( ; k; --k )
{
state[i] =

View File

@ -2,7 +2,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Bat.h"
#include "../Vector3d.h"
#include "../Vector3.h"
#include "../Chunk.h"

View File

@ -68,17 +68,28 @@ void cSheep::OnRightClicked(cPlayer & a_Player)
void cSheep::Tick(float a_Dt, cChunk & a_Chunk)
{
// The sheep should not move when he's eating so only handle the physics.
super::Tick(a_Dt, a_Chunk);
int PosX = POSX_TOINT;
int PosY = POSY_TOINT - 1;
int PosZ = POSZ_TOINT;
if ((PosY <= 0) || (PosY > cChunkDef::Height))
{
return;
}
if (m_TimeToStopEating > 0)
{
HandlePhysics(a_Dt, a_Chunk);
m_bMovingToDestination = false; // The sheep should not move when he's eating
m_TimeToStopEating--;
if (m_TimeToStopEating == 0)
{
if (m_World->GetBlock((int) GetPosX(), (int) GetPosY() - 1, (int) GetPosZ()) == E_BLOCK_GRASS)
if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS) // Make sure grass hasn't been destroyed in the meantime
{
// The sheep ate the grass so we change it to dirt.
m_World->SetBlock((int) GetPosX(), (int) GetPosY() - 1, (int) GetPosZ(), E_BLOCK_DIRT, 0);
// The sheep ate the grass so we change it to dirt
m_World->SetBlock(PosX, PosY, PosZ, E_BLOCK_DIRT, 0);
GetWorld()->BroadcastSoundParticleEffect(2001, PosX, PosY, PosX, E_BLOCK_GRASS);
m_IsSheared = false;
m_World->BroadcastEntityMetadata(*this);
}
@ -86,12 +97,11 @@ void cSheep::Tick(float a_Dt, cChunk & a_Chunk)
}
else
{
super::Tick(a_Dt, a_Chunk);
if (m_World->GetTickRandomNumber(600) == 1)
{
if (m_World->GetBlock((int) GetPosX(), (int) GetPosY() - 1, (int) GetPosZ()) == E_BLOCK_GRASS)
if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS)
{
m_World->BroadcastEntityStatus(*this, 10);
m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_SHEEP_EATING);
m_TimeToStopEating = 40;
}
}

View File

@ -2,7 +2,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Squid.h"
#include "../Vector3d.h"
#include "../Vector3.h"
#include "../Chunk.h"

View File

@ -3,14 +3,6 @@
#include "Noise.h"
#if NOISE_USE_SSE
#include <smmintrin.h> //_mm_mul_epi32
#endif
#define FAST_FLOOR(x) (((x) < 0) ? (((int)x) - 1) : ((int)x))

View File

@ -70,7 +70,7 @@ bool cBlockingTCPLink::Connect(const char * iAddress, unsigned int iPort)
}
}
server.sin_addr.s_addr = *((unsigned long *)hp->h_addr);
memcpy(&server.sin_addr.s_addr,hp->h_addr, hp->h_length);
server.sin_family = AF_INET;
server.sin_port = htons( (unsigned short)iPort);
if (connect(m_Socket, (struct sockaddr *)&server, sizeof(server)))

View File

@ -131,7 +131,7 @@ public:
/** Returns the list of all items in the specified folder (files, folders, nix pipes, whatever's there). */
static AStringVector GetFolderContents(const AString & a_Folder); // Exported in ManualBindings.cpp
int Printf(const char * a_Fmt, ...);
int Printf(const char * a_Fmt, ...) FORMATSTRING(2, 3);
/** Flushes all the bufferef output into the file (only when writing) */
void Flush(void);

View File

@ -34,7 +34,7 @@ protected:
public:
cIsThread(const AString & iThreadName);
~cIsThread();
virtual ~cIsThread();
/// Starts the thread; returns without waiting for the actual start
bool Start(void);

View File

@ -307,7 +307,8 @@ bool cSocket::ConnectIPv4(const AString & a_HostNameOrAddr, unsigned short a_Por
CloseSocket();
return false;
}
addr = *((unsigned long*)hp->h_addr);
// Should be optimised to a single word copy
memcpy(&addr, hp->h_addr, hp->h_length);
}
sockaddr_in server;

View File

@ -54,7 +54,7 @@ bool cSocketThreads::AddClient(const cSocket & a_Socket, cCallback * a_Client)
}
// No thread has free space, create a new one:
LOGD("Creating a new cSocketThread (currently have %d)", m_Threads.size());
LOGD("Creating a new cSocketThread (currently have " SIZE_T_FMT ")", m_Threads.size());
cSocketThread * Thread = new cSocketThread(this);
if (!Thread->Start())
{

View File

@ -103,7 +103,7 @@ private:
public:
cSocketThread(cSocketThreads * a_Parent);
~cSocketThread();
virtual ~cSocketThread();
// All these methods assume parent's m_CS is locked
bool HasEmptySlot(void) const {return m_NumSlots < MAX_SLOTS; }

View File

@ -52,7 +52,7 @@ public:
virtual ~cProtocol() {}
/// Called when client sends some data
virtual void DataReceived(const char * a_Data, int a_Size) = 0;
virtual void DataReceived(const char * a_Data, size_t a_Size) = 0;
// Sending stuff to clients (alphabetically sorted):
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) = 0;

View File

@ -1186,7 +1186,7 @@ void cProtocol125::SendData(const char * a_Data, int a_Size)
void cProtocol125::DataReceived(const char * a_Data, int a_Size)
void cProtocol125::DataReceived(const char * a_Data, size_t a_Size)
{
if (!m_ReceivedData.Write(a_Data, a_Size))
{
@ -1269,19 +1269,6 @@ int cProtocol125::ParsePacket(unsigned char a_PacketType)
#define HANDLE_PACKET_PARSE(Packet) \
{ \
int res = Packet.Parse(m_ReceivedData); \
if (res < 0) \
{ \
return res; \
} \
}
int cProtocol125::ParseArmAnim(void)
{
HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
@ -1375,7 +1362,16 @@ int cProtocol125::ParseEntityAction(void)
{
HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
HANDLE_PACKET_READ(ReadChar, char, ActionID);
m_Client->HandleEntityAction(EntityID, ActionID);
switch (ActionID)
{
case 1: m_Client->HandleEntityCrouch(EntityID, true); break; // Crouch
case 2: m_Client->HandleEntityCrouch(EntityID, false); break; // Uncrouch
case 3: m_Client->HandleEntityLeaveBed(EntityID); break; // Leave Bed
case 4: m_Client->HandleEntitySprinting(EntityID, true); break; // Start sprinting
case 5: m_Client->HandleEntitySprinting(EntityID, false); break; // Stop sprinting
}
return PARSE_OK;
}

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