diff options
220 files changed, 11521 insertions, 4314 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d359f9004..9ce4c9ff3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,6 +19,7 @@ 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" + * Please leave the first line of all files blank, to get around an IDE bug. Copyright diff --git a/GNUmakefile b/GNUmakefile index f139b3d39..338470592 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -21,8 +21,11 @@ # Macros # +# allow user to override compiler +# if no compiler is specified make specifies cc +ifeq ($(CC),cc) CC = /usr/bin/g++ - +endif all: MCServer/MCServer @@ -82,6 +85,15 @@ endif endif +################################################### +# Fix Crypto++ warnings in clang + +ifeq ($(shell $(CXX) --version 2>&1 | grep -i -c "clang version"),0) +CC_OPTIONS += -Wno-tautological-compare +CXX_OPTIONS += -Wno-tautological-compare +disableasm = 1 +endif + ################################################### # Set the link libraries based on the OS @@ -133,7 +145,6 @@ endif INCLUDE = -I.\ -Isource\ -Isource/md5\ - -IWebServer\ -Isource/items\ -Isource/blocks\ -Itolua++-1.0.93/src/lib\ @@ -155,7 +166,7 @@ INCLUDE = -I.\ # 2012_11_08 _X: Removed: squirrel_3_0_1_stable -SOURCES := $(shell find CryptoPP lua-5.1.4 jsoncpp-src-0.5.0 zlib-1.2.7 source tolua++-1.0.93 iniFile WebServer expat '(' -name '*.cpp' -o -name '*.c' ')') +SOURCES := $(shell find CryptoPP lua-5.1.4 jsoncpp-src-0.5.0 zlib-1.2.7 source tolua++-1.0.93 iniFile expat '(' -name '*.cpp' -o -name '*.c' ')') SOURCES := $(filter-out %minigzip.c %lua.c %tolua.c %toluabind.c %LeakFinder.cpp %StackWalker.cpp %example.c,$(SOURCES)) OBJECTS := $(patsubst %.c,$(BUILDDIR)%.o,$(SOURCES)) OBJECTS := $(patsubst %.cpp,$(BUILDDIR)%.o,$(OBJECTS)) diff --git a/MCServer/.gitignore b/MCServer/.gitignore index ac226a77d..d9d869986 100644 --- a/MCServer/.gitignore +++ b/MCServer/.gitignore @@ -4,7 +4,9 @@ MCServer logs players world* +API/ API.txt +API_wiki.txt *.dat schematics *.schematic @@ -14,4 +16,6 @@ memdump* *.grab ProtectionAreas.sqlite helgrind.log +valgrind.log motd.txt +*.deuser diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua new file mode 100644 index 000000000..12665888b --- /dev/null +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -0,0 +1,2562 @@ + +-- APIDesc.lua + +-- Contains the API objects' descriptions + + + + +g_APIDesc = +{ + Classes = + { + --[[ + -- What the APIDump plugin understands / how to document stuff: + ExampleClassName = + { + Desc = "Description, exported as the first paragraph of the class page. Usually enclosed within double brackets." + + Functions = + { + FunctionName = { Params = "Parameter list", Return = "Return values list", Notes = "Notes" ), + OverloadedFunctionName = -- When a function supports multiple parameter variants + { + { Params = "Parameter list 1", Return = "Return values list 1", Notes = "Notes 1" }, + { Params = "Parameter list 2", Return = "Return values list 2", Notes = "Notes 2" }, + } + } , + + Constants = + { + ConstantName = { Notes = "Notes about the constant" }, + } , + + AdditionalInfo = -- Paragraphs to be exported after the function definitions table + { + { + Header = "Header 1", + Contents = "Contents of the additional section 1", + }, + { + Header = "Header 2", + Contents = "Contents of the additional section 2", + } + }, + + Inherits = "ParentClassName", -- Only present if the class inherits from another API class + }, + ]]-- + + cArrowEntity = + { + Desc = [[ + Represents the arrow when it is shot from the bow. A subclass of the {{cProjectileEntity}}. + ]], + + Functions = + { + CanPickup = { Params = "{{cPlayer|Player}}", Return = "bool", Notes = "Returns true if the specified player can pick the arrow when it's on the ground" }, + GetDamageCoeff = { Params = "", Return = "number", Notes = "Returns the damage coefficient stored within the arrow. The damage dealt by this arrow is multiplied by this coeff" }, + GetPickupState = { Params = "", Return = "PickupState", Notes = "Returns the pickup state (one of the psXXX constants, above)" }, + IsCritical = { Params = "", Return = "bool", Notes = "Returns true if the arrow should deal critical damage. Based on the bow charge when the arrow was shot." }, + SetDamageCoeff = { Params = "number", Return = "", Notes = "Sets the damage coefficient. The damage dealt by this arrow is multiplied by this coeff" }, + SetIsCritical = { Params = "bool", Return = "", Notes = "Sets the IsCritical flag on the arrow. Critical arrow deal additional damage" }, + SetPickupState = { Params = "PickupState", Return = "", Notes = "Sets the pickup state (one of the psXXX constants, above)" }, + }, + + Constants = + { + psInCreative = { Notes = "The arrow can be picked up only by players in creative gamemode" }, + psInSurvivalOrCreative = { Notes = "The arrow can be picked up by players in survival or creative gamemode" }, + psNoPickup = { Notes = "The arrow cannot be picked up at all" }, + }, + + Inherits = "cProjectileEntity", + }, + + cBlockArea = + { + Desc = [[ + This class is used when multiple adjacent blocks are to be manipulated. Because of chunking + and multithreading, manipulating single blocks using {{cWorld|cWorld:SetBlock}}() is a rather + time-consuming operation (locks for exclusive access need to be obtained, chunk lookup is done + for each block), so whenever you need to manipulate multiple adjacent blocks, it's better to wrap + the operation into a cBlockArea access. cBlockArea is capable of reading / writing across chunk + boundaries, has no chunk lookups for get and set operations and is not subject to multithreading + locking (because it is not shared among threads).</p> + <p> + cBlockArea remembers its origin (MinX, MinY, MinZ coords in the Read() call) and therefore supports + absolute as well as relative get / set operations. Despite that, the contents of a cBlockArea can + be written back into the world at any coords.</p> + <p> + cBlockArea can hold any combination of the following datatypes:<ul> + <li>block types</li> + <li>block metas</li> + <li>blocklight</li> + <li>skylight</li> + </ul> + Read() and Write() functions have parameters that tell the class which datatypes to read / write. + Note that a datatype that has not been read cannot be written (FIXME).</p> + <p> + Typical usage:<ul> + <li>Create cBlockArea object</li> + <li>Read an area from the world / load from file / create anew</li> + <li>Modify blocks inside cBlockArea</li> + <li>Write the area back to a world / save to file</li> + </ul></p> + ]], + Functions = + { + constructor = { Params = "", Return = "cBlockArea", Notes = "Creates a new empty cBlockArea object" }, + Clear = { Params = "", Return = "", Notes = "Clears the object, resets it to zero size" }, + CopyFrom = { Params = "BlockAreaSrc", Return = "", Notes = "Copies contents from BlockAreaSrc into self" }, + CopyTo = { Params = "BlockAreaDst", Return = "", Notes = "Copies contents from self into BlockAreaDst." }, + Create = { Params = "SizeX, SizeY, SizeZ, [DataTypes]", Return = "", Notes = "Initializes this BlockArea to an empty area of the specified size and origin of {0, 0, 0}. Any previous contents are lost." }, + Crop = { Params = "AddMinX, SubMaxX, AddMinY, SubMaxY, AddMinZ, SubMaxZ", Return = "", Notes = "Crops the specified number of blocks from each border. Modifies the size of this blockarea object." }, + DumpToRawFile = { Params = "FileName", Return = "", Notes = "Dumps the raw data into a file. For debugging purposes only." }, + Expand = { Params = "SubMinX, AddMaxX, SubMinY, AddMaxY, SubMinZ, AddMaxZ", Return = "", Notes = "Expands the specified number of blocks from each border. Modifies the size of this blockarea object. New blocks created with this operation are filled with zeroes." }, + Fill = { Params = "DataTypes, BlockType, [BlockMeta], [BlockLight], [BlockSkyLight]", Return = "", Notes = "Fills the entire block area with the same values, specified. Uses the DataTypes param to determine which content types are modified." }, + FillRelCuboid = { Params = "MinRelX, MaxRelX, MinRelY, MaxRelY, MinRelZ, MaxRelZ, DataTypes, BlockType, [BlockMeta], [BlockLight], [BlockSkyLight]", Return = "", Notes = "Fills the specified cuboid with the same values (like Fill() )." }, + GetBlockLight = { Params = "BlockX, BlockY, BlockZ", Return = "NIBBLETYPE", Notes = "Returns the blocklight at the specified absolute coords" }, + GetBlockMeta = { Params = "BlockX, BlockY, BlockZ", Return = "NIBBLETYPE", Notes = "Returns the block meta at the specified absolute coords" }, + 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" }, + GetOriginX = { Params = "", Return = "number", Notes = "Returns the origin x-coord" }, + GetOriginY = { Params = "", Return = "number", Notes = "Returns the origin y-coord" }, + GetOriginZ = { Params = "", Return = "number", Notes = "Returns the origin z-coord" }, + GetRelBlockLight = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "NIBBLETYPE", Notes = "Returns the blocklight at the specified relative coords" }, + GetRelBlockMeta = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "NIBBLETYPE", Notes = "Returns the block meta at the specified relative coords" }, + GetRelBlockSkyLight = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "NIBBLETYPE", Notes = "Returns the skylight at the specified relative coords" }, + GetRelBlockType = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "BLOCKTYPE", Notes = "Returns the block type at the specified relative coords" }, + GetRelBlockTypeMeta = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "NIBBLETYPE", Notes = "Returns the block type and meta at the specified relative coords" }, + GetSizeX = { Params = "", Return = "number", Notes = "Returns the size of the held data in the x-axis" }, + 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" }, + 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" }, + HasBlockTypes = { Params = "", Return = "bool", Notes = "Returns true if current datatypes include block types" }, + LoadFromSchematicFile = { Params = "FileName", Return = "", Notes = "Clears current content and loads new content from the specified schematic file. Returns true if successful. Returns false and logs error if unsuccessful, old content is preserved in such a case." }, + Merge = { Params = "BlockAreaSrc, RelX, RelY, RelZ, Strategy", Return = "", Notes = "Merges BlockAreaSrc into this object at the specified relative coords, using the specified strategy" }, + MirrorXY = { Params = "", Return = "", Notes = "Mirrors this block area around the XY plane. Modifies blocks' metas (if present) to match (i. e. furnaces facing the opposite direction)." }, + MirrorXYNoMeta = { Params = "", Return = "", Notes = "Mirrors this block area around the XY plane. Doesn't modify blocks' metas." }, + MirrorXZ = { Params = "", Return = "", Notes = "Mirrors this block area around the XZ plane. Modifies blocks' metas (if present)" }, + MirrorXZNoMeta = { Params = "", Return = "", Notes = "Mirrors this block area around the XZ plane. Doesn't modify blocks' metas." }, + MirrorYZ = { Params = "", Return = "", Notes = "Mirrors this block area around the YZ plane. Modifies blocks' metas (if present)" }, + MirrorYZNoMeta = { Params = "", Return = "", Notes = "Mirrors this block area around the YZ plane. Doesn't modify blocks' metas." }, + Read = { Params = "World, MinX, MaxX, MinY, MaxY, MinZ, MaxZ, DataTypes", Return = "bool", Notes = "Reads the area from World, returns true if successful" }, + RelLine = { Params = "RelX1, RelY1, RelZ1, RelX2, RelY2, RelZ2, DataTypes, BlockType, [BlockMeta], [BlockLight], [BlockSkyLight]", Return = "", Notes = "Draws a line between the two specified points. Sets only datatypes specified by DataTypes." }, + RotateCCW = { Params = "", Return = "", Notes = "Rotates the block area around the Y axis, counter-clockwise (east -> north). Modifies blocks' metas (if present) to match." }, + RotateCCWNoMeta = { Params = "", Return = "", Notes = "Rotates the block area around the Y axis, counter-clockwise (east -> north). Doesn't modify blocks' metas." }, + RotateCW = { Params = "", Return = "", Notes = "Rotates the block area around the Y axis, clockwise (north -> east). Modifies blocks' metas (if present) to match." }, + RotateCWNoMeta = { Params = "", Return = "", Notes = "Rotates the block area around the Y axis, clockwise (north -> east). Doesn't modify blocks' metas." }, + SaveToSchematicFile = { Params = "FileName", Return = "", Notes = "Saves the current contents to a schematic file. Returns true if successful." }, + SetBlockLight = { Params = "BlockX, BlockY, BlockZ, BlockLight", Return = "", Notes = "Sets the blocklight at the specified absolute coords" }, + SetBlockMeta = { Params = "BlockX, BlockY, BlockZ, BlockMeta", Return = "", Notes = "Sets the block meta at the specified absolute coords" }, + SetBlockSkyLight = { Params = "BlockX, BlockY, BlockZ, SkyLight", Return = "", Notes = "Sets the skylight at the specified absolute coords" }, + SetBlockType = { Params = "BlockX, BlockY, BlockZ, BlockType", Return = "", Notes = "Sets the block type at the specified absolute coords" }, + SetBlockTypeMeta = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta", Return = "", Notes = "Sets the block type and meta at the specified absolute coords" }, + SetOrigin = { Params = "OriginX, OriginY, OriginZ", Return = "", Notes = "Resets the origin for the absolute coords. Only affects how absolute coords are translated into relative coords." }, + SetRelBlockLight = { Params = "RelBlockX, RelBlockY, RelBlockZ, BlockLight", Return = "", Notes = "Sets the blocklight at the specified relative coords" }, + SetRelBlockMeta = { Params = "RelBlockX, RelBlockY, RelBlockZ, BlockMeta", Return = "", Notes = "Sets the block meta at the specified relative coords" }, + 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" }, + Write = { Params = "World, MinX, MinY, MinZ, DataTypes", Return = "bool", Notes = "Writes the area into World at the specified coords, returns true if successful" }, + }, + Constants = + { + baTypes = { Notes = "Operation should work on block types" }, + baMetas = { Notes = "Operations should work on block metas" }, + baLight = { Notes = "Operations should work on block (emissive) light" }, + baSkyLight = { Notes = "Operations should work on skylight" }, + msOverwrite = { Notes = "Src overwrites anything in Dst" }, + msFillAir = { Notes = "Dst is overwritten by Src only where Src has air blocks" }, + msImprint = { Notes = "Src overwrites Dst anywhere where Dst has non-air blocks" }, + msLake = { Notes = "Special mode for merging lake images" }, + }, + + AdditionalInfo = + { + { + Header = "Merge strategies", + Contents = + [[ + <p>The strategy parameter specifies how individual blocks are combined together, using the table below. + </p> + <table class="inline"> + <tbody><tr> + <th colspan="2">area block</th><th colspan="3">result</th> + </tr> + <tr> + <th> this </th><th> Src </th><th> msOverwrite </th><th> msFillAir </th><th> msImprint </th> + </tr> + <tr> + <td> air </td><td> air </td><td> air </td><td> air </td><td> air </td> + </tr> + <tr> + <td> A </td><td> air </td><td> air </td><td> A </td><td> A </td> + </tr> + <tr> + <td> air </td><td> B </td><td> B </td><td> B </td><td> B </td> + </tr> + <tr> + <td> A </td><td> B </td><td> B </td><td> A </td><td> B </td> + </tr> + </tbody></table> + + <p> + So to sum up: + <ol> + <li class="level1">msOverwrite completely overwrites all blocks with the Src's blocks</li> + <li class="level1">msFillAir overwrites only those blocks that were air</li> + <li class="level1">msImprint overwrites with only those blocks that are non-air</li> + </ol> + </p> + + <p> + Special strategies: + </p> + + <p> + <strong>msLake</strong> (evaluate top-down, first match wins): + </p> + <table><tbody><tr> + <th colspan="2"> area block </th><th> </th><th> Notes </th> + </tr><tr> + <th> this </th><th> Src </th><th> result </th><th> </th> + </tr><tr> + <td> A </td><td> sponge </td><td> A </td><td> Sponge is the NOP block </td> + </tr><tr> + <td> * </td><td> air </td><td> air </td><td> Air always gets hollowed out, even under the oceans </td> + </tr><tr> + <td> water </td><td> * </td><td> water </td><td> Water is never overwritten </td> + </tr><tr> + <td> lava </td><td> * </td><td> lava </td><td> Lava is never overwritten </td> + </tr><tr> + <td> * </td><td> water </td><td> water </td><td> Water always overwrites anything </td> + </tr><tr> + <td> * </td><td> lava </td><td> lava </td><td> Lava always overwrites anything </td> + </tr><tr> + <td> dirt </td><td> stone </td><td> stone </td><td> Stone overwrites dirt </td> + </tr><tr> + <td> grass </td><td> stone </td><td> stone </td><td> ... and grass </td> + </tr><tr> + <td> mycelium </td><td> stone </td><td> stone </td><td> ... and mycelium </td> + </tr><tr> + <td> A </td><td> stone </td><td> A </td><td> ... but nothing else </td> + </tr><tr> + <td> A </td><td> * </td><td> A </td><td> Everything else is left as it is </td> + </tr> + </tbody></table> + ]], + }, -- Merge strategies + }, -- AdditionalInfo + }, -- cBlockArea + + cBlockEntity = + { + Desc = [[ + Block entities are simply blocks in the world that have persistent data, such as the text for a sign + or contents of a chest. All block entities are also saved in the chunk data of the chunk they reside in. + The cBlockEntity class acts as a common ancestor for all the individual block entities. + ]], + + Functions = + { + GetBlockType = { Params = "", Return = "BLOCKTYPE", Notes = "Returns the blocktype which is represented by this blockentity. This is the primary means of type-identification" }, + GetChunkX = { Params = "", Return = "number", Notes = "Returns the chunk X-coord of the block entity's chunk" }, + GetChunkZ = { Params = "", Return = "number", Notes = "Returns the chunk Z-coord of the block entity's chunk" }, + GetPosX = { Params = "", Return = "number", Notes = "Returns the block X-coord of the block entity's block" }, + GetPosY = { Params = "", Return = "number", Notes = "Returns the block Y-coord of the block entity's block" }, + GetPosZ = { Params = "", Return = "number", Notes = "Returns the block Z-coord of the block entity's block" }, + GetRelX = { Params = "", Return = "number", Notes = "Returns the relative X coord of the block entity's block within the chunk" }, + GetRelZ = { Params = "", Return = "number", Notes = "Returns the relative Z coord of the block entity's block within the chunk" }, + GetWorld = { Params = "", Return = "{{cWorld|cWorld}}", Notes = "Returns the world to which the block entity belongs" }, + }, + Constants = + { + }, + }, + + cBlockEntityWithItems = + { + Desc = [[ + This class is a common ancestor for all {{cBlockEntity|block entities}} that provide item storage. + Internally, the object has a {{cItemGrid|cItemGrid}} object for storing the items; this ItemGrid is + accessible through the API. The storage is a grid of items, items in it can be addressed either by a slot + number, or by XY coords within the grid. If a UI window is opened for this block entity, the item storage + is monitored for changes and the changes are immediately sent to clients of the UI window. + ]], + + Inherits = "cBlockEntity", + + Functions = + { + GetContents = { Params = "", Return = "{{cItemGrid|cItemGrid}}", Notes = "Returns the cItemGrid object representing the items stored within this block entity" }, + GetSlot = + { + { Params = "SlotNum", Return = "{{cItem|cItem}}", Notes = "Returns the cItem for the specified slot number. Returns nil for invalid slot numbers" }, + { Params = "X, Y", Return = "{{cItem|cItem}}", Notes = "Returns the cItem for the specified slot coords. Returns nil for invalid slot coords" }, + }, + SetSlot = + { + { Params = "SlotNum, {{cItem|cItem}}", Return = "", Notes = "Sets the cItem for the specified slot number. Ignored if invalid slot number" }, + { Params = "X, Y, {{cItem|cItem}}", Return = "", Notes = "Sets the cItem for the specified slot coords. Ignored if invalid slot coords" }, + }, + }, + Constants = + { + }, + }, + + cBoundingBox = + { + Desc = [[ + Represents two sets of coordinates, minimum and maximum for each direction; thus defining an + axis-aligned cuboid with floating-point boundaries. It supports operations changing the size and + position of the box, as well as querying whether a point or another BoundingBox is inside the box.</p> + <p> + All the points within the coordinate limits (inclusive the edges) are considered "inside" the box. + However, for intersection purposes, if the intersection is "sharp" in any coord (min1 == max2, i. e. + zero volume), the boxes are considered non-intersecting.</p> + ]], + Functions = + { + constructor = + { + { Params = "MinX, MaxX, MinY, MaxY, MinZ, MaxZ", Return = "cBoundingBox", Notes = "Creates a new bounding box with the specified edges" }, + { Params = "{{Vector3d|Min}}, {{Vector3d|Max}}", Return = "cBoundingBox", Notes = "Creates a new bounding box with the coords specified as two vectors" }, + { Params = "{{Vector3d|Pos}}, Radius, Height", Return = "cBoundingBox", Notes = "Creates a new bounding box from the position given and radius (X/Z) and height. Radius is added from X/Z to calculate the maximum coords and subtracted from X/Z to get the minimum; minimum Y is set to Pos.y and maxumim Y to Pos.y plus Height. This corresponds with how {{cEntity|entities}} are represented in Minecraft." }, + { Params = "OtherBoundingBox", Return = "cBoundingBox", Notes = "Creates a new copy of the given bounding box. Same result can be achieved by using a simple assignment." }, + }, + CalcLineIntersection = { Params = "{{Vector3d|LineStart}}, {{Vector3d|LinePt2}}", Return = "DoesIntersect, LineCoeff, Face", Notes = "Calculates the intersection of a ray (half-line), given by two of its points, with the bounding box. Returns false if the line doesn't intersect the bounding box, or true, together with coefficient of the intersection (how much of the difference between the two ray points is needed to reach the intersection), and the face of the box which is intersected.<br /><b>TODO</b>: Lua binding for this function is wrong atm." }, + DoesIntersect = { Params = "OtherBoundingBox", Return = "bool", Notes = "Returns true if the two bounding boxes have an intersection of nonzero volume." }, + Expand = { Params = "ExpandX, ExpandY, ExpandZ", Return = "", Notes = "Expands this bounding box by the specified amount in each direction (so the box becomes larger by 2 * Expand in each axis)." }, + IsInside = + { + { Params = "{{Vector3d|Point}}", Return = "bool", Notes = "Returns true if the specified point is inside (including on the edge) of the box." }, + { Params = "PointX, PointY, PointZ", Return = "bool", Notes = "Returns true if the specified point is inside (including on the edge) of the box." }, + { Params = "OtherBoundingBox", Return = "bool", Notes = "Returns true if OtherBoundingBox is inside of this box." }, + { Params = "{{Vector3d|OtherBoxMin}}, {{Vector3d|OtherBoxMax}}", Return = "bool", Notes = "Returns true if the other bounding box, specified by its 2 corners, is inside of this box." }, + }, + Move = + { + { Params = "OffsetX, OffsetY, OffsetZ", Return = "", Notes = "Moves the bounding box by the specified offset in each axis" }, + { Params = "{{Vector3d|Offset}}", Return = "", Notes = "Moves the bounding box by the specified offset in each axis" }, + }, + Union = { Params = "OtherBoundingBox", Return = "cBoundingBox", Notes = "Returns the smallest bounding box that contains both OtherBoundingBox and this bounding box. Note that unlike the strict geometrical meaning of \"union\", this operation actually returns a cBoundingBox." }, + }, + Constants = {}, + }, + + cChatColor = + { + Desc = [[ + A wrapper class for constants representing colors or effects. + ]], + + Functions = + { + MakeColor = { Params = "ColorCodeConstant", Return = "string", Notes = "Creates the complete color-code-sequence from the color or effect constant" }, + }, + Constants = + { + Color = { Notes = "The first character of the color-code-sequence, §" }, + Delimiter = { Notes = "The first character of the color-code-sequence, §" }, + Random = { Notes = "Random letters and symbols animate instead of the text" }, + Plain = { Notes = "Resets all formatting to normal" }, + }, + }, + + cChestEntity = + { + Desc = [[ + A chest entity is a {{cBlockEntityWithItems|cBlockEntityWithItems}} descendant that represents a chest + in the world. Note that doublechests consist of two separate cChestEntity objects, they do not collaborate + in any way.</p> + <p> + The chest entity can be created by the plugins only in the {{OnChunkGenerating}} and + {{OnChunkGenerated}} hooks, as part of the new chunk being generated. Plugins may generate chests + with contents in this way.</p> + <p> + To manipulate a chest already in the game, you need to use {{cWorld}}'s callback mechanism with + either DoWithChestAt() or ForEachChestInChunk() function. See the code example below + ]], + + Inherits = "cBlockEntityWithItems", + + Functions = + { + constructor = { Params = "BlockX, BlockY, BlockZ", Return = "cChestEntity", Notes = "Creates a new cChestEntity object. To be used only in the chunk generating hooks {{OnChunkGenerating}} and {{OnChunkGenerated}}." }, + }, + Constants = + { + ContentsHeight = { Notes = "Height of the contents' {{cItemGrid|ItemGrid}}, as required by the parent class, {{cBlockEntityWithItems}}" }, + ContentsWidth = { Notes = "Width of the contents' {{cItemGrid|ItemGrid}}, as required by the parent class, {{cBlockEntityWithItems}}" }, + }, + AdditionalInfo = + { + { + Header = "Code example", + Contents = [[ + The following example code sets the top-left item of each chest in the same chunk as Player to + 64 * diamond: +<pre class="prettyprint lang-lua"> +-- Player is a {{cPlayer}} object instance +local World = Player:GetWorld(); +World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(), + function (ChestEntity) + ChestEntity:SetSlot(0, 0, cItem(E_ITEM_DIAMOND, 64)); + end +); +</pre> + ]], + }, + }, -- AdditionalInfo + }, + + cChunkDesc = + { + Desc = [[ + The cChunkDesc class is a container for chunk data while the chunk is being generated. As such, it is + only used as a parameter for the {{OnChunkGenerating|OnChunkGenerating}} and + {{OnChunkGenerated|OnChunkGenerated}} hooks and cannot be constructed on its own. Plugins can use this + class in both those hooks to manipulate generated chunks. + ]], + + Functions = + { + FillBlocks = { Params = "BlockType, BlockMeta", Return = "", Notes = "Fills the entire chunk with the specified blocks" }, + FillRelCuboid = + { + { Params = "{{cCuboid|RelCuboid}}, BlockType, BlockMeta", Return = "", Notes = "Fills the cuboid, specified in relative coords, by the specified block type and block meta. The cuboid may reach outside of the chunk, only the part intersecting with this chunk is filled." }, + { Params = "MinRelX, MaxRelX, MinRelY, MaxRelY, MinRelZ, MaxRelZ, BlockType, BlockMeta", Return = "", Notes = "Fills the cuboid, specified in relative coords, by the specified block type and block meta. The cuboid may reach outside of the chunk, only the part intersecting with this chunk is filled." }, + }, + FloorRelCuboid = + { + { Params = "{{cCuboid|RelCuboid}}, BlockType, BlockMeta", Return = "", Notes = "Fills those blocks of the cuboid (specified in relative coords) that are considered non-floor (air, water) with the specified block type and meta. Cuboid may reach outside the chunk, only the part intersecting with this chunk is filled." }, + { Params = "MinRelX, MaxRelX, MinRelY, MaxRelY, MinRelZ, MaxRelZ, BlockType, BlockMeta", Return = "", Notes = "Fills those blocks of the cuboid (specified in relative coords) that are considered non-floor (air, water) with the specified block type and meta. Cuboid may reach outside the chunk, only the part intersecting with this chunk is filled." }, + }, + GetBiome = { Params = "RelX, RelZ", Return = "EMCSBiome", Notes = "Returns the biome at the specified relative coords" }, + GetBlockMeta = { Params = "RelX, RelY, RelZ", Return = "NIBBLETYPE", Notes = "Returns the block meta at the specified relative coords" }, + GetBlockType = { Params = "RelX, RelY, RelZ", Return = "BLOCKTYPE", Notes = "Returns the block type at the specified relative coords" }, + GetBlockTypeMeta = { Params = "RelX, RelY, RelZ", Return = "BLOCKTYPE, NIBBLETYPE", Notes = "Returns the block type and meta at the specified relative coords" }, + GetChunkX = { Params = "", Return = "number", Notes = "Returns the X coord of the chunk contained." }, + GetChunkZ = { Params = "", Return = "number", Notes = "Returns the Z coord of the chunk contained." }, + GetHeight = { Params = "RelX, RelZ", Return = "number", Notes = "Returns the height at the specified relative coords" }, + GetMaxHeight = { Params = "", Return = "number", Notes = "Returns the maximum height contained in the heightmap." }, + IsUsingDefaultBiomes = { Params = "", Return = "bool", Notes = "Returns true if the chunk is set to use default biome generator" }, + IsUsingDefaultComposition = { Params = "", Return = "bool", Notes = "Returns true if the chunk is set to use default composition generator" }, + IsUsingDefaultFinish = { Params = "", Return = "bool", Notes = "Returns true if the chunk is set to use default finishers" }, + IsUsingDefaultHeight = { Params = "", Return = "bool", Notes = "Returns true if the chunk is set to use default height generator" }, + IsUsingDefaultStructures = { Params = "", Return = "bool", Notes = "Returns true if the chunk is set to use default structures" }, + RandomFillRelCuboid = + { + { Params = "{{cCuboid|RelCuboid}}, BlockType, BlockMeta, RandomSeed, ChanceOutOf10k", Return = "", Notes = "Fills the specified relative cuboid with block type and meta in random locations. RandomSeed is used for the random number genertion (same seed produces same results); ChanceOutOf10k specifies the density (how many out of every 10000 blocks should be filled). Cuboid may reach outside the chunk, only the part intersecting with this chunk is filled." }, + { Params = "MinRelX, MaxRelX, MinRelY, MaxRelY, MinRelZ, MaxRelZ, BlockType, BlockMeta, RandomSeed, ChanceOutOf10k", Return = "", Notes = "Fills the specified relative cuboid with block type and meta in random locations. RandomSeed is used for the random number genertion (same seed produces same results); ChanceOutOf10k specifies the density (how many out of every 10000 blocks should be filled). Cuboid may reach outside the chunk, only the part intersecting with this chunk is filled." }, + }, + ReadBlockArea = { Params = "{{cBlockArea|BlockArea}}, MinRelX, MaxRelX, MinRelY, MaxRelY, MinRelZ, MaxRelZ", Return = "", Notes = "Reads data from the chunk into the block area object. Block types and metas are processed." }, + ReplaceRelCuboid = + { + { Params = "{{cCuboid|RelCuboid}}, SrcType, SrcMeta, DstType, DstMeta", Return = "", Notes = "Replaces all SrcType+SrcMeta blocks in the cuboid (specified in relative coords) with DstType+DstMeta blocks. Cuboid may reach outside the chunk, only the part intersecting with this chunk is filled." }, + { Params = "MinRelX, MaxRelX, MinRelY, MaxRelY, MinRelZ, MaxRelZ, SrcType, SrcMeta, DstType, DstMeta", Return = "", Notes = "Replaces all SrcType+SrcMeta blocks in the cuboid (specified in relative coords) with DstType+DstMeta blocks. Cuboid may reach outside the chunk, only the part intersecting with this chunk is filled." }, + }, + SetBiome = { Params = "RelX, RelZ, EMCSBiome", Return = "", Notes = "Sets the biome at the specified relative coords" }, + SetBlockMeta = { Params = "RelX, RelY, RelZ, BlockMeta", Return = "", Notes = "Sets the block meta at the specified relative coords" }, + SetBlockType = { Params = "RelX, RelY, RelZ, BlockType", Return = "", Notes = "Sets the block type at the specified relative coords" }, + SetBlockTypeMeta = { Params = "RelX, RelY, RelZ, BlockType, BlockMeta", Return = "", Notes = "Sets the block type and meta at the specified relative coords" }, + SetHeight = { Params = "RelX, RelZ, Height", Return = "", Notes = "Sets the height at the specified relative coords" }, + SetUseDefaultBiomes = { Params = "bool", Return = "", Notes = "Sets the chunk to use default biome generator or not" }, + SetUseDefaultComposition = { Params = "bool", Return = "", Notes = "Sets the chunk to use default composition generator or not" }, + 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" }, + WriteBlockArea = { Params = "{{cBlockArea|BlockArea}}, MinRelX, MinRelY, MinRelZ", Return = "", Notes = "Writes data from the block area into the chunk" }, + }, + Constants = + { + }, + }, + + cClientHandle = + { + Desc = [[ + A cClientHandle represents the technical aspect of a connected player - their game client + connection. Internally, it handles all the incoming and outgoing packets, the chunks that are to be + sent to the client, ping times etc. + ]], + + Functions = + { + 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" }, + GetUsername = { Params = "", Return = "string", Notes = "Returns the username that the client has provided" }, + GetViewDistance = { Params = "", Return = "number", Notes = "Returns the viewdistance (number of chunks loaded for the player in each direction)" }, + Kick = { Params = "Reason", Return = "", Notes = "Kicks the user with the specified reason" }, + 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." }, + }, + Constants = + { + MAX_VIEW_DISTANCE = { Notes = "The maximum value of the view distance" }, + MIN_VIEW_DISTANCE = { Notes = "The minimum value of the view distance" }, + }, + }, + + cCraftingGrid = + { + Desc = [[ + cCraftingGrid represents the player's crafting grid. It is used in + {{OnCraftingNoRecipe|OnCraftingNoRecipe}}, {{OnPostCrafting|OnPostCrafting}} and + {{OnPreCrafting|OnPreCrafting}} hooks. Plugins may use it to inspect the items the player placed + on their crafting grid.</p> + <p> + Also, an object of this type is used in {{cCraftingRecipe}}'s ConsumeIngredients() function for + specifying the exact number of ingredients to consume in that recipe; plugins may use this to + apply the crafting recipe.</p> + ]], + + Functions = + { + constructor = { Params = "Width, Height", Return = "cCraftingGrid", Notes = "Creates a new CraftingGrid object. This new crafting grid is not related to any player, but may be needed for {{cCraftingRecipe}}'s ConsumeIngredients function." }, + Clear = { Params = "", Return = "", Notes = "Clears the entire grid" }, + ConsumeGrid = { Params = "{{cCraftingGrid|CraftingGrid}}", Return = "", Notes = "Consumes items specified in CraftingGrid from the current contents. Used internally by {{cCraftingRecipe}}'s ConsumeIngredients() function, but available to plugins, too." }, + Dump = { Params = "", Return = "", Notes = "DEBUG build: Dumps the contents of the grid to the log. RELEASE build: no action" }, + GetHeight = { Params = "", Return = "number", Notes = "Returns the height of the grid" }, + GetItem = { Params = "x, y", Return = "{{cItem|cItem}}", Notes = "Returns the item at the specified coords" }, + GetWidth = { Params = "", Return = "number", Notes = "Returns the width of the grid" }, + SetItem = + { + { Params = "x, y, {{cItem|cItem}}", Return = "", Notes = "Sets the item at the specified coords" }, + { Params = "x, y, ItemType, ItemCount, ItemDamage", Return = "", Notes = "Sets the item at the specified coords" }, + }, + }, + Constants = + { + }, + }, + + cCraftingRecipe = + { + Desc = [[ + This class is used to represent a crafting recipe, either a built-in one, or one created dynamically in a plugin. It is used only as a parameter for {{OnCraftingNoRecipe|OnCraftingNoRecipe}}, {{OnPostCrafting|OnPostCrafting}} and {{OnPreCrafting|OnPreCrafting}} hooks. Plugins may use it to inspect or modify a crafting recipe that a player views in their crafting window, either at a crafting table or the survival inventory screen. +</p> + <p>Internally, the class contains a {{cCraftingGrid}} for the ingredients and a {{cItem}} for the result. +]], + Functions = + { + Clear = { Params = "", Return = "", Notes = "Clears the entire recipe, both ingredients and results" }, + ConsumeIngredients = { Params = "CraftingGrid", Return = "", Notes = "Consumes ingredients specified in the given {{cCraftingGrid|cCraftingGrid}} class" }, + Dump = { Params = "", Return = "", Notes = "DEBUG build: dumps ingredients and result into server log. RELEASE build: no action" }, + GetIngredient = { Params = "x, y", Return = "{{cItem|cItem}}", Notes = "Returns the ingredient stored in the recipe at the specified coords" }, + GetIngredientsHeight = { Params = "", Return = "number", Notes = "Returns the height of the ingredients' grid" }, + GetIngredientsWidth = { Params = "", Return = "number", Notes = "Returns the width of the ingredients' grid" }, + GetResult = { Params = "", Return = "{{cItem|cItem}}", Notes = "Returns the result of the recipe" }, + SetIngredient = + { + { Params = "x, y, {{cItem|cItem}}", Return = "", Notes = "Sets the ingredient at the specified coords" }, + { Params = "x, y, ItemType, ItemCount, ItemDamage", Return = "", Notes = "Sets the ingredient at the specified coords" }, + }, + SetResult = + { + { Params = "{{cItem|cItem}}", Return = "", Notes = "Sets the result item" }, + { Params = "ItemType, ItemCount, ItemDamage", Return = "", Notes = "Sets the result item" }, + }, + }, + Constants = + { + }, + }, + + cCuboid = + { + Desc = [[ + cCuboid offers some native support for integral-boundary cuboids. A cuboid internally consists of + two {{Vector3i}}s. By default the cuboid doesn't make any assumptions about the defining points, + but for most of the operations in the cCuboid class, the p1 member variable is expected to be the + minima and the p2 variable the maxima. The Sort() function guarantees this condition.</p> + <p> + The Cuboid considers both its edges inclusive.</p> + ]], + Functions = + { + constructor = + { + { Params = "OtheCuboid", Return = "cCuboid", Notes = "Creates a new Cuboid object as a copy of OtherCuboid" }, + { Params = "{{Vector3i|Point1}}, {{Vector3i|Point2}}", Return = "cCuboid", Notes = "Creates a new Cuboid object with the specified points as its corners." }, + { Params = "X, Y, Z", Return = "cCuboid", Notes = "Creates a new Cuboid object with the specified point as both its corners (the cuboid has a size of 1 in each direction)." }, + { Params = "X1, Y1, Z1, X2, Y2, Z2", Return = "cCuboid", Notes = "Creates a new Cuboid object with the specified points as its corners." }, + }, + Assign = { Params = "X1, Y1, Z1, X2, Y2, Z2", Return = "", Notes = "Assigns all the coords stored in the cuboid. Sort-state is ignored." }, + DifX = { Params = "", Return = "number", Notes = "Returns the difference between the two X coords (X-size minus 1). Assumes sorted." }, + 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." }, + IsCompletelyInside = { Params = "OuterCuboid", Return = "bool", Notes = "Returns true if this cuboid is completely inside (in all directions) in OuterCuboid. Assumes both sorted." }, + IsInside = + { + { Params = "X, Y, Z", Return = "bool", Notes = "Returns true if the specified point (integral coords) is inside this cuboid. Assumes sorted." }, + { Params = "{{Vector3i|Point}}", Return = "bool", Notes = "Returns true if the specified point (integral coords) is inside this cuboid. Assumes sorted." }, + { Params = "{{Vector3d|Point}}", Return = "bool", Notes = "Returns true if the specified point (floating-point coords) is inside this cuboid. Assumes sorted." }, + }, + IsSorted = { Params = "", Return = "bool", Notes = "Returns true if this cuboid is sorted" }, + Move = { Params = "OffsetX, OffsetY, OffsetZ", Return = "", Notes = "Adds the specified offsets to each respective coord, effectively moving the Cuboid. Sort-state is ignored." }, + Sort = { Params = "", Return = "" , Notes = "Sorts the internal representation so that p1 contains the lesser coords and p2 contains the greater coords." }, + }, + Variables = + { + p1 = { Notes = "{{Vector3i}} of one corner. Usually the lesser of the two coords in each set" }, + p2 = { Notes = "{{Vector3i}} of the other corner. Usually the larger of the two coords in each set" }, + }, + }, + + cDispenserEntity = + { + Desc = [[This class represents a dispenser block entity in the world. Most of this block entity's functionality is implemented in the {{cDropSpenserEntity|cDropSpenserEntity}} class that represents the behavior common with a {{cDropperEntity|dropper}} entity. +</p> + <p>An object of this class can be created from scratch when generating chunks ({{OnChunkGenerated|OnChunkGenerated}} and {{OnChunkGenerating|OnChunkGenerating}} hooks). +]], + Functions = + { + constructor = { Params = "BlockX, BlockY, BlockZ", Return = "cDispenserEntity", Notes = "Creates a new cDispenserEntity at the specified coords" }, + }, + Constants = + { + }, + Inherits = "cDropSpenserEntity", + }, + + cDropperEntity = + { + Desc = [[This class represents a dropper block entity in the world. Most of this block entity's functionality is implemented in the {{cDropSpenserEntity|cDropSpenserEntity}} class that represents the behavior common with the {{cDispenserEntity|dispenser}} entity. +</p> + <p>An object of this class can be created from scratch when generating chunks ({{OnChunkGenerated|OnChunkGenerated}} and {{OnChunkGenerating|OnChunkGenerating}} hooks). +]], + Functions = + { + constructor = { Params = "BlockX, BlockY, BlockZ", Return = "cDropperEntity", Notes = "Creates a new cDropperEntity at the specified coords" }, + }, + Constants = + { + }, + Inherits = "cDropSpenserEntity", + }, + + cDropSpenserEntity = + { + Desc = [[This is a class that implements behavior common to both {{cDispenserEntity|dispensers}} and {{cDropperEntity|droppers}}. +]], + Functions = + { + Activate = { Params = "", Return = "", Notes = "Sets the block entity to dropspense an item in the next tick" }, + AddDropSpenserDir = { Params = "BlockX, BlockY, BlockZ, BlockMeta", Return = "BlockX, BlockY, BlockZ", Notes = "Adjusts the block coords to where the dropspenser items materialize" }, + SetRedstonePower = { Params = "IsPowered", Return = "", Notes = "Sets the redstone status of the dropspenser. If the redstone power goes from off to on, the dropspenser will be activated" }, + }, + Constants = + { + ContentsWidth = { Notes = "Width (X) of the {{cItemGrid}} representing the contents" }, + ContentsHeight = { Notes = "Height (Y) of the {{cItemGrid}} representing the contents" }, + }, + + Inherits = "cBlockEntity"; + }, + + cEnchantments = + { + Desc = [[This class is the storage for enchantments for a single {{cItem|cItem}} object, through its m_Enchantments member variable. Although it is possible to create a standalone object of this class, it is not yet used in any API directly. +</p> + <p>Enchantments can be initialized either programmatically by calling the individual functions (SetLevel()), or by using a string description of the enchantment combination. This string description is in the form "id=lvl;id=lvl;...;id=lvl;", where id is either a numerical ID of the enchantment, or its textual representation from the table below, and lvl is the desired enchantment level. The class can also create its string description from its current contents; however that string description will only have the numerical IDs. +]], + Functions = + { + constructor = + { + { Params = "", Return = "cEnchantments", Notes = "Creates a new empty cEnchantments object" }, + { Params = "StringSpec", Return = "cEnchantments", Notes = "Creates a new cEnchantments object filled with enchantments based on the string description" }, + }, + operator_eq = { Params = "OtherEnchantments", Return = "bool", Notes = "Returns true if this enchantments object has the same enchantments as OtherEnchantments." }, + AddFromString = { Params = "StringSpec", Return = "", Notes = "Adds the enchantments in the string description into the object. If a specified enchantment already existed, it is overwritten." }, + Clear = { Params = "", Return = "", Notes = "Removes all enchantments" }, + GetLevel = { Params = "EnchantmentNumID", Return = "number", Notes = "Returns the level of the specified enchantment stored in this object; 0 if not stored" }, + IsEmpty = { Params = "", Return = "bool", Notes = "Returns true if the object stores no enchantments" }, + SetLevel = { Params = "EnchantmentNumID, Level", Return = "", Notes = "Sets the level for the specified enchantment, adding it if not stored before or removing it if level < = 0" }, + StringToEnchantmentID = { Params = "EnchantmentTextID", Return = "number", Notes = "(static) Returns the enchantment numerical ID, -1 if not understood. Case insensitive" }, + ToString = { Params = "", Return = "string", Notes = "Returns the string description of all the enchantments stored in this object, in numerical-ID form" }, + }, + Constants = + { + }, + }, + + cEntity = + { + Desc = [[ + A cEntity object represents an object in the world, it has a position and orientation. cEntity is an + abstract class, and can not be instantiated directly, instead, all entities are implemented as + subclasses. The cEntity class works as the common interface for the operations that all (most) + entities support.</p> + <p> + All cEntity objects have an Entity Type so it can be determined what kind of entity it is + efficiently. Entities also have a class inheritance awareness, they know their class name, + their parent class' name and can decide if there is a class within their inheritance chain. + Since these functions operate on strings, they are slightly slower than checking the entity type + directly, on the other hand, they are more specific directly. To check if the entity is a spider, + you need to call IsMob(), then cast the object to {{cMonster}} and finally compare + {{cMonster}}:GetMonsterType() to mtSpider. GetClass(), on the other hand, returns "cSpider" + directly.</p> + <p> + Note that you should not store a cEntity object between two hooks' calls, because MCServer may + despawn / remove that entity in between the calls. If you need to refer to an entity later, use its + UniqueID and {{cWorld|cWorld}}'s entity manipulation functions DoWithEntityByID(), ForEachEntity() + or ForEachEntityInChunk() to access the entity again.</p> + ]], + Functions = + { + AddPosition = + { + { Params = "OffsetX, OffsetY, OffsetZ", Return = "", Notes = "Moves the entity by the specified amount in each axis direction" }, + { Params = "{{Vector3d|Offset}}", Return = "", Notes = "Moves the entity by the specified amount in each direction" }, + }, + AddPosX = { Params = "OffsetX", Return = "", Notes = "Moves the entity by the specified amount in the X axis direction" }, + AddPosY = { Params = "OffsetY", Return = "", Notes = "Moves the entity by the specified amount in the Y axis direction" }, + AddPosZ = { Params = "OffsetZ", Return = "", Notes = "Moves the entity by the specified amount in the Z axis direction" }, + AddSpeed = + { + { Params = "AddX, AddY, AddZ", Return = "", Notes = "Adds the specified amount of speed in each axis direction." }, + { Params = "{{Vector3d|Add}}", Return = "", Notes = "Adds the specified amount of speed in each axis direction." }, + }, + AddSpeedX = { Params = "AddX", Return = "", Notes = "Adds the specified amount of speed in the X axis direction." }, + AddSpeedY = { Params = "AddY", Return = "", Notes = "Adds the specified amount of speed in the Y axis direction." }, + AddSpeedZ = { Params = "AddZ", Return = "", Notes = "Adds the specified amount of speed in the Z axis direction." }, + Destroy = { Params = "", Return = "", Notes = "Schedules the entity to be destroyed" }, + GetArmorCoverAgainst = { Params = "AttackerEntity, DamageType, RawDamage", Return = "number", Notes = "Returns the number of hitpoints out of RawDamage that the currently equipped armor would cover. See {{TakeDamageInfo}} for more information on attack damage." }, + GetChunkX = { Params = "", Return = "number", Notes = "Returns the X-coord of the chunk in which the entity is placed" }, + GetChunkZ = { Params = "", Return = "number", Notes = "Returns the Z-coord of the chunk in which the entity is placed" }, + GetClass = { Params = "", Return = "string", Notes = "Returns the classname of the entity, such as \"cSpider\" or \"cPickup\"" }, + GetClassStatic = { Params = "", Return = "string", Notes = "Returns the entity classname that this class implements. Each descendant overrides this function. Is static" }, + GetEntityType = { Params = "", Return = "eEntityType", Notes = "Returns the type of the entity, one of the etXXX constants. Note that to check specific entity type, you should use one of the IsXXX functions instead of comparing the value returned by this call." }, + GetEquippedBoots = { Params = "", Return = "{{cItem}}", Notes = "Returns the boots that the entity has equipped. Returns an empty cItem if no boots equipped or not applicable." }, + GetEquippedChestplate = { Params = "", Return = "{{cItem}}", Notes = "Returns the chestplate that the entity has equipped. Returns an empty cItem if no chestplate equipped or not applicable." }, + GetEquippedHelmet = { Params = "", Return = "{{cItem}}", Notes = "Returns the helmet that the entity has equipped. Returns an empty cItem if no helmet equipped or not applicable." }, + GetEquippedLeggings = { Params = "", Return = "{{cItem}}", Notes = "Returns the leggings that the entity has equipped. Returns an empty cItem if no leggings equipped or not applicable." }, + GetEquippedWeapon = { Params = "", Return = "{{cItem}}", Notes = "Returns the weapon that the entity has equipped. Returns an empty cItem if no weapon equipped or not applicable." }, + GetGravity = { Params = "", Return = "number", Notes = "Returns the number that is used as the gravity for physics simulation. 1G (9.78) by default." }, + GetHeadYaw = { Params = "", Return = "number", Notes = "Returns the pitch of the entity's head (FIXME: Rename to GetHeadPitch() )." }, + GetHealth = { Params = "", Return = "number", Notes = "Returns the current health of the entity." }, + GetHeight = { Params = "", Return = "number", Notes = "Returns the height (Y size) of the entity" }, + GetKnockbackAmountAgainst = { Params = "ReceiverEntity", Return = "number", Notes = "Returns the amount of knockback that the currently equipped items would cause when attacking the ReceiverEntity." }, + GetLookVector = { Params = "", Return = "Vector3f", Notes = "Returns the vector that defines the direction in which the entity is looking" }, + GetMass = { Params = "", Return = "number", Notes = "Returns the mass of the entity. Currently unused." }, + GetMaxHealth = { Params = "", Return = "number", Notes = "Returns the maximum number of hitpoints this entity is allowed to have." }, + GetParentClass = { Params = "", Return = "string", Notes = "Returns the name of the direct parent class for this entity" }, + GetPitch = { Params = "", Return = "number", Notes = "Returns the pitch (nose-down rotation) of the entity" }, + GetPosition = { Params = "", Return = "Vector3d", Notes = "Returns the entity's pivot position as a 3D vector" }, + GetPosX = { Params = "", Return = "number", Notes = "Returns the X-coord of the entity's pivot" }, + GetPosY = { Params = "", Return = "number", Notes = "Returns the Y-coord of the entity's pivot" }, + GetPosZ = { Params = "", Return = "number", Notes = "Returns the Z-coord of the entity's pivot" }, + GetRawDamageAgainst = { Params = "ReceiverEntity", Return = "number", Notes = "Returns the raw damage that this entity's equipment would cause when attacking the ReceiverEntity. This includes this entity's weapon {{cEnchantments|enchantments}}, but excludes the receiver's armor or potion effects. See {{TakeDamageInfo}} for more information on attack damage." }, + GetRoll = { Params = "", Return = "number", Notes = "Returns the roll (sideways rotation) of the entity. Currently unused." }, + GetRot = { Params = "", Return = "{{Vector3f}}", Notes = "Returns the entire rotation vector (Yaw, Pitch, Roll)" }, + GetRotation = { Params = "", Return = "number", Notes = "Returns the yaw (direction) of the entity. FIXME: Rename to GetYaw()." }, + GetSpeed = { Params = "", Return = "Vector3d", Notes = "Returns the complete speed vector of the entity" }, + GetSpeedX = { Params = "", Return = "number", Notes = "Returns the X-part of the speed vector" }, + GetSpeedY = { Params = "", Return = "number", Notes = "Returns the Y-part of the speed vector" }, + GetSpeedZ = { Params = "", Return = "number", Notes = "Returns the Z-part of the speed vector" }, + GetUniqueID = { Params = "", Return = "number", Notes = "Returns the ID that uniquely identifies the entity within the running server. Note that this ID is not persisted to the data files." }, + GetWidth = { Params = "", Return = "number", Notes = "Returns the width (X and Z size) of the entity." }, + GetWorld = { Params = "", Return = "{{cWorld|cWorld}}", Notes = "Returns the world where the entity resides" }, + Heal = { Params = "Hitpoints", Return = "", Notes = "Heals the specified number of hitpoints. Hitpoints is expected to be a positive number." }, + IsA = { Params = "ClassName", Return = "bool", Notes = "Returns true if the entity class is a descendant of the specified class name, or the specified class itself" }, + IsBoat = { Params = "", Return = "bool", Notes = "Returns true if the entity is a {{cBoat|boat}}." }, + IsCrouched = { Params = "", Return = "bool", Notes = "Returns true if the entity is crouched. Always false for entities that don't support crouching." }, + IsDestroyed = { Params = "", Return = "bool", Notes = "Returns true if the entity has been destroyed and is awaiting removal from the internal structures." }, + IsMinecart = { Params = "", Return = "bool", Notes = "Returns true if the entity represents a {{cMinecart|minecart}}" }, + IsMob = { Params = "", Return = "bool", Notes = "Returns true if the entity represents any {{cMonster|mob}}." }, + IsOnFire = { Params = "", Return = "bool", Notes = "Returns true if the entity is on fire" }, + IsPickup = { Params = "", Return = "bool", Notes = "Returns true if the entity represents a {{cPickup|pickup}}." }, + IsPlayer = { Params = "", Return = "bool", Notes = "Returns true if the entity represents a {{cPlayer|player}}" }, + IsRclking = { Params = "", Return = "bool", Notes = "Currently unimplemented" }, + IsRiding = { Params = "", Return = "bool", Notes = "Returns true if the entity is attached to (riding) another entity." }, + IsSprinting = { Params = "", Return = "bool", Notes = "Returns true if the entity is sprinting. Entities that cannot sprint return always false" }, + IsTNT = { Params = "", Return = "bool", Notes = "Returns true if the entity represents a {{cTNTEntity|TNT entity}}" }, + KilledBy = { Notes = "FIXME: Remove this from API" }, + SetGravity = { Params = "Gravity", Return = "", Notes = "Sets the number that is used as the gravity for physics simulation. 1G (9.78) by default." }, + SetHeadYaw = { Params = "HeadPitch", Return = "", Notes = "Sets the head pitch (FIXME: Rename to SetHeadPitch() )." }, + SetHealth = { Params = "Hitpoints", Return = "", Notes = "Sets the entity's health to the specified amount of hitpoints. Doesn't broadcast any hurt animation. Doesn't kill the entity if health drops below zero. Use the TakeDamage() function instead for taking damage." }, + SetHeight = { Params = "", Return = "", Notes = "FIXME: Remove this from API" }, + SetMass = { Params = "Mass", Return = "", Notes = "Sets the mass of the entity. Currently unused." }, + SetMaxHealth = { Params = "MaxHitpoints", Return = "", Notes = "Sets the maximum hitpoints of the entity. If current health is above MaxHitpoints, it is capped to MaxHitpoints." }, + SetPitch = { Params = "number", Return = "", Notes = "Sets the pitch (nose-down rotation) of the entity" }, + SetPitchFromSpeed = { Params = "", Return = "", Notes = "Sets the entity pitch to match its speed (entity looking forwards as it moves)" }, + SetPosition = + { + { Params = "PosX, PosY, PosZ", Return = "", Notes = "Sets all three coords of the entity's pivot" }, + { Params = "{{Vector3d|Vector3d}}", Return = "", Notes = "Sets all three coords of the entity's pivot" }, + }, + SetPosX = { Params = "number", Return = "", Notes = "Sets the X-coord of the entity's pivot" }, + SetPosY = { Params = "number", Return = "", Notes = "Sets the Y-coord of the entity's pivot" }, + SetPosZ = { Params = "number", Return = "", Notes = "Sets the Z-coord of the entity's pivot" }, + SetRoll = { Params = "number", Return = "", Notes = "Sets the roll (sideways rotation) of the entity. Currently unused." }, + SetRot = { Params = "{{Vector3f|Rotation}}", Return = "", Notes = "Sets the entire rotation vector (Yaw, Pitch, Roll)" }, + SetRotation = { Params = "number", Return = "", Notes = "Sets the yaw (direction) of the entity. FIXME: Rename to SetYaw()." }, + SetRotationFromSpeed = { Params = "", Return = "", Notes = "Sets the entity's yaw to match its current speed (entity looking forwards as it moves). (FIXME: Rename to SetYawFromSpeed)" }, + SetSpeed = + { + { Params = "SpeedX, SpeedY, SpeedZ", Return = "", Notes = "Sets the current speed of the entity" }, + { Params = "{{Vector3d|Speed}}", Return = "", Notes = "Sets the current speed of the entity" }, + }, + SetSpeedX = { Params = "SpeedX", Return = "", Notes = "Sets the X component of the entity speed" }, + SetSpeedY = { Params = "SpeedY", Return = "", Notes = "Sets the Y component of the entity speed" }, + SetSpeedZ = { Params = "SpeedZ", Return = "", Notes = "Sets the Z component of the entity speed" }, + SetWidth = { Params = "", Return = "", Notes = "FIXME: Remove this from API" }, + StartBurning = { Params = "NumTicks", Return = "", Notes = "Sets the entity on fire for the specified number of ticks. If entity is on fire already, makes it burn for either NumTicks or the number of ticks left from the previous fire, whichever is larger." }, + SteerVehicle = { Params = "ForwardAmount, SidewaysAmount", Return = "", Notes = "Applies the specified steering to the vehicle this entity is attached to. Ignored if not attached to any entity." }, + StopBurning = { Params = "", Return = "", Notes = "Extinguishes the entity fire, cancels all fire timers." }, + TakeDamage = + { + { Params = "AttackerEntity", Return = "", Notes = "Causes this entity to take damage that AttackerEntity would inflict. Includes their weapon and this entity's armor." }, + { Params = "DamageType, AttackerEntity, RawDamage, KnockbackAmount", Return = "", Notes = "Causes this entity to take damage of the specified type, from the specified attacker (may be nil). The final damage is calculated from RawDamage using the currently equipped armor." }, + { Params = "DamageType, ArrackerEntity, RawDamage, FinalDamage, KnockbackAmount", Return = "", Notes = "Causes this entity to take damage of the specified type, from the specified attacker (may be nil). The values are wrapped into a {{TakeDamageInfo}} structure and applied directly." }, + }, + TeleportToCoords = { Params = "PosX, PosY, PosZ", Return = "", Notes = "Teleports the entity to the specified coords." }, + TeleportToEntity = { Params = "DestEntity", Return = "", Notes = "Teleports this entity to the specified destination entity." }, + }, + Constants = + { + etBoat = { Notes = "The entity is a {{cBoat}}" }, + etEntity = { Notes = "No further specialization available" }, + etFallingBlock = { Notes = "The entity is a {{cFallingBlock}}" }, + etMob = { Notes = "The entity is a {{cMonster}} descendant" }, + etMonster = { Notes = "The entity is a {{cMonster}} descendant" }, + etMinecart = { Notes = "The entity is a {{cMinecart}} descendant" }, + etPlayer = { Notes = "The entity is a {{cPlayer}}" }, + etPickup = { Notes = "The entity is a {{cPickup}}" }, + etProjectile = { Notes = "The entity is a {{cProjectile}} descendant" }, + etTNT = { Notes = "The entity is a {{cTNTEntity}}" }, + }, + }, + + cFile = + { + Desc = [[ + Provides helper functions for manipulating and querying the filesystem. Most functions are called + directly on the cFile class itself: +<pre class="prettyprint lang-lua"> +cFile:Delete("/usr/bin/virus.exe"); +</pre></p> + ]], + + Functions = + { + Copy = { Params = "SrcFileName, DstFileName", Return = "bool", Notes = "Copies a single file to a new destination. Returns true if successful. Fails if the destination already exists." }, + CreateFolder = { Params = "FolderName", Return = "bool", Notes = "Creates a new folder. Returns true if successful." }, + Delete = { Params = "FileName", Return = "bool", Notes = "Deletes the specified file. Returns true if successful." }, + Exists = { Params = "FileName", Return = "bool", Notes = "Returns true if the specified file exists." }, + GetSize = { Params = "FileName", Return = "number", Notes = "Returns the size of the file, or -1 on failure." }, + IsFile = { Params = "Path", Return = "bool", Notes = "Returns true if the specified path points to an existing file." }, + IsFolder = { Params = "Path", Return = "bool", Notes = "Returns true if the specified path points to an existing folder." }, + Rename = { Params = "OrigPath, NewPath", Return = "bool", Notes = "Renames a file or a folder. Returns true if successful. Undefined result if NewPath already exists." }, + }, + + }, + + cFireChargeEntity = + { + Desc = "", + Functions = {}, + Constants = {}, + Inherits = "cProjectileEntity", + } , + + cFurnaceEntity = + { + Desc = [[This class represents a furnace block entity in the world. An object of this class can be created from scratch when generating chunks ({{OnChunkGenerated|OnChunkGenerated}} and {{OnChunkGenerating|OnChunkGenerating}} hooks) +]], + Functions = + { + constructor = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta", Return = "cFurnaceEntity", Notes = "Creates a new cFurnaceEntity at the specified coords and the specified block type / meta" }, + GetCookTimeLeft = { Params = "", Return = "number", Notes = "Returns the time until the current item finishes cooking, in ticks" }, + GetFuelBurnTimeLeft = { Params = "", Return = "number", Notes = "Returns the time until the current fuel is depleted, in ticks" }, + GetFuelSlot = { Params = "", Return = "{{cItem|cItem}}", Notes = "Returns the item in the fuel slot" }, + GetInputSlot = { Params = "", Return = "{{cItem|cItem}}", Notes = "Returns the item in the input slot" }, + GetOutputSlot = { Params = "", Return = "{{cItem|cItem}}", Notes = "Returns the item in the output slot" }, + GetTimeCooked = { Params = "", Return = "number", Notes = "Returns the time that the current item has been cooking, in ticks" }, + HasFuelTimeLeft = { Params = "", Return = "bool", Notes = "Returns true if there's time before the current fuel is depleted" }, + SetFuelSlot = { Params = "{{cItem|cItem}}", Return = "", Notes = "Sets the item in the fuel slot" }, + SetInputSlot = { Params = "{{cItem|cItem}}", Return = "", Notes = "Sets the item in the input slot" }, + SetOutputSlot = { Params = "{{cItem|cItem}}", Return = "", Notes = "Sets the item in the output slot" }, + }, + Constants = + { + fsInput = { Notes = "Index of the input slot, when using the GetSlot() / SetSlot() functions" }, + fsFuel = { Notes = "Index of the fuel slot, when using the GetSlot() / SetSlot() functions" }, + fsOutput = { Notes = "Index of the output slot, when using the GetSlot() / SetSlot() functions" }, + ContentsWidth = { Notes = "Width (X) of the {{cItemGrid|cItemGrid}} representing the contents" }, + ContentsHeight = { Notes = "Height (Y) of the {{cItemGrid|cItemGrid}} representing the contents" }, + }, + Inherits = "cBlockEntityWithItems" + }, + + cGhastFireballEntity = + { + Desc = "", + Functions = {}, + Constants = {}, + Inherits = "cProjectileEntity", + } , + + cGroup = + { + Desc = [[cGroup is a group {{cPlayer|cPlayer}}'s can be in. Groups define the permissions players have, and optionally the color of their name in the chat. +]], + Functions = + { + SetName = { Return = "" }, + GetName = { Return = "string" }, + SetColor = { Return = "" }, + GetColor = { Return = "string" }, + AddCommand = { Return = "" }, + HasCommand = { Return = "bool" }, + AddPermission = { Return = "" }, + InheritFrom = { Return = "" }, + }, + Constants = + { + }, + }, + + cIniFile = + { + Desc = [[The cIniFile is a class that makes it simple to read from and write to INI files. MCServer uses mostly INI files for settings and options. +]], + Functions = + { + constructor = { Return = "{{cIniFile|cIniFile}}" }, + CaseSensitive = { Return = "" }, + CaseInsensitive = { Return = "" }, + Path = { Return = "" }, + Path = { Return = "string" }, + SetPath = { Return = "" }, + ReadFile = { Return = "bool" }, + WriteFile = { Return = "bool" }, + Erase = { Return = "" }, + Clear = { Return = "" }, + Reset = { Return = "" }, + FindKey = { Notes = "long i" }, + FindValue = { Notes = "long i" }, + NumKeys = { Notes = "unsigned i" }, + GetNumKeys = { Notes = "unsigned i" }, + AddKeyName = { Notes = "unsigned int" }, + KeyName = { Notes = "Stri" }, + GetKeyName = { Notes = "Stri" }, + NumValues = { Notes = "unsigned int" }, + GetNumValues = { Notes = "unsigned int" }, + NumValues = { Notes = "unsigned int" }, + GetNumValues = { Notes = "unsigned int" }, + ValueName = { Notes = "Stri" }, + GetValueName = { Notes = "Stri" }, + ValueName = { Notes = "Stri" }, + GetValueName = { Notes = "Stri" }, + GetValue = { Notes = "Stri" }, + GetValue = { Notes = "Stri" }, + GetValueI = { Notes = "i" }, + GetValueB = { Notes = "bo" }, + GetValueF = { Notes = "doub" }, + GetValueSet = { Notes = "Stri" }, + GetValueSetI = { Notes = "i" }, + GetValueSetB = { Notes = "bo" }, + GetValueSetF = { Notes = "doub" }, + SetValue = { Return = "bool" }, + SetValue = { Return = "bool" }, + SetValueI = { Return = "bool" }, + SetValueB = { Return = "bool" }, + SetValueF = { Return = "bool" }, + DeleteValueByID = { Return = "bool" }, + DeleteValue = { Return = "bool" }, + DeleteKey = { Return = "bool" }, + NumHeaderComments = { Notes = "unsigned int" }, + HeaderComment = { Return = "" }, + HeaderComment = { Notes = "Stri" }, + DeleteHeaderComment = { Return = "bool" }, + DeleteHeaderComments = { Return = "" }, + NumKeyComments = { Notes = "unsigned i" }, + NumKeyComments = { Notes = "unsigned i" }, + KeyComment = { Return = "bool" }, + KeyComment = { Return = "bool" }, + KeyComment = { Notes = "Stri" }, + KeyComment = { Notes = "Stri" }, + DeleteKeyComment = { Return = "bool" }, + DeleteKeyComment = { Return = "bool" }, + DeleteKeyComments = { Return = "bool" }, + DeleteKeyComments = { Return = "bool" }, + }, + Constants = + { + }, + }, + + cInventory = + { + Desc = [[This object is used to store the items that a {{cPlayer|cPlayer}} has. It also keeps track of what item the player has currently selected in their hotbar. +Internally, the class uses three {{cItemGrid|cItemGrid}} objects to store the contents: +<li>Armor</li> +<li>Inventory</li> +<li>Hotbar</li> +These ItemGrids are available in the API and can be manipulated by the plugins, too. +]], + Functions = + { + AddItem = { Params = "{{cItem|cItem}}, [AllowNewStacks]", Return = "number", Notes = "Adds an item to the storage; if AllowNewStacks is true (default), will also create new stacks in empty slots. Returns the number of items added" }, + AddItems = { Params = "{{cItems|cItems}}, [AllowNewStacks]", Return = "number", Notes = "Same as AddItem, but for several items at once" }, + ChangeSlotCount = { Params = "SlotNum, AddToCount", Return = "number", Notes = "Adds AddToCount to the count of items in the specified slot. If the slot was empty, ignores the call. Returns the new count in the slot, or -1 if invalid SlotNum" }, + Clear = { Params = "", Return = "", Notes = "Empties all slots" }, + CopyToItems = { Params = "{{cItems|cItems}}", Return = "", Notes = "Copies all non-empty slots into the cItems object provided; original cItems contents are preserved" }, + DamageEquippedItem = { Params = "[DamageAmount]", Return = "bool", Notes = "Adds the specified damage (1 by default) to the currently equipped it" }, + DamageItem = { Params = "SlotNum, [DamageAmount]", Return = "bool", Notes = "Adds the specified damage (1 by default) to the specified item, returns true if the item reached its max damage and should be destroyed" }, + GetArmorGrid = { Params = "", Return = "{{cItemGrid|cItemGrid}}", Notes = "Returns the ItemGrid representing the armor grid (1 x 4 slots)" }, + GetArmorSlot = { Params = "ArmorSlotNum", Return = "{{cItem|cItem}}", Notes = "Returns the specified armor slot contents. Note that the returned item is read-only" }, + GetEquippedBoots = { Params = "", Return = "{{cItem|cItem}}", Notes = "Returns the item in the \"boots\" slot of the armor grid. Note that the returned item is read-only" }, + GetEquippedChestplate = { Params = "", Return = "{{cItem|cItem}}", Notes = "Returns the item in the \"chestplate\" slot of the armor grid. Note that the returned item is read-only" }, + GetEquippedHelmet = { Params = "", Return = "{{cItem|cItem}}", Notes = "Returns the item in the \"helmet\" slot of the armor grid. Note that the returned item is read-only" }, + GetEquippedItem = { Params = "", Return = "{{cItem|cItem}}", Notes = "Returns the currently selected item from the hotbar. Note that the returned item is read-only" }, + GetEquippedLeggings = { Params = "", Return = "{{cItem|cItem}}", Notes = "Returns the item in the \"leggings\" slot of the armor grid. Note that the returned item is read-only" }, + GetEquippedSlotNum = { Params = "", Return = "number", Notes = "Returns the hotbar slot number for the currently selected item" }, + GetHotbarGrid = { Params = "", Return = "{{cItemGrid|cItemGrid}}", Notes = "Returns the ItemGrid representing the hotbar grid (9 x 1 slots)" }, + GetHotbarSlot = { Params = "HotBarSlotNum", Return = "{{cItem|cItem}}", Notes = "Returns the specified hotbar slot contents. Note that the returned item is read-only" }, + GetInventoryGrid = { Params = "", Return = "{{cItemGrid|cItemGrid}}", Notes = "Returns the ItemGrid representing the main inventory (9 x 3 slots)" }, + GetInventorySlot = { Params = "InventorySlotNum", Return = "{{cItem|cItem}}", Notes = "Returns the specified main inventory slot contents. Note that the returned item is read-only" }, + GetOwner = { Params = "", Return = "{{cPlayer|cPlayer}}", Notes = "Returns the player whose inventory this object represents" }, + GetSlot = { Params = "SlotNum", Return = "{{cItem|cItem}}", Notes = "Returns the contents of the specified slot. Note that the returned item is read-only" }, + HasItems = { Params = "{{cItem|cItem}}", Return = "bool", Notes = "Returns true if there are at least as many items of the specified type as in the parameter" }, + HowManyCanFit = { Params = "{{cItem|cItem}}", Return = "number", Notes = "Returns the number of the specified items that can fit in the storage, including empty slots" }, + HowManyItems = { Params = "{{cItem|cItem}}", Return = "number", Notes = "Returns the number of the specified items that are currently stored" }, + RemoveOneEquippedItem = { Params = "", Return = "", Notes = "Removes one item from the hotbar's currently selected slot" }, + SetArmorSlot = { Params = "ArmorSlotNum, {{cItem|cItem}}", Return = "", Notes = "Sets the specified armor slot contents" }, + SetEquippedSlotNum = { Params = "EquippedSlotNum", Return = "", Notes = "Sets the currently selected hotbar slot number" }, + SetHotbarSlot = { Params = "HotbarSlotNum, {{cItem|cItem}}", Return = "", Notes = "Sets the specified hotbar slot contents" }, + SetInventorySlot = { Params = "InventorySlotNum, {{cItem|cItem}}", Return = "", Notes = "Sets the specified main inventory slot contents" }, + SetSlot = { Params = "SlotNum, {{cItem|cItem}}", Return = "", Notes = "Sets the specified slot contents" }, + }, + Constants = + { + invArmorCount = { Notes = "Number of slots in the Armor part" }, + invArmorOffset = { Notes = "Starting slot number of the Armor part" }, + invInventoryCount = { Notes = "Number of slots in the main inventory part" }, + invInventoryOffset = { Notes = "Starting slot number of the main inventory part" }, + invHotbarCount = { Notes = "Number of slots in the Hotbar part" }, + invHotbarOffset = { Notes = "Starting slot number of the Hotbar part" }, + invNumSlots = { Notes = "Total number of slots in a cInventory" }, + }, + }, + + cItem = + { + Desc = [[ + cItem is what defines an item or stack of items in the game, it contains the item ID, damage, + quantity and enchantments. Each slot in a {{cInventory}} class or a {{cItemGrid}} class is a cItem + and each {{cPickup}} contains a cItem. The enchantments are contained in a separate + {{cEnchantments}} class and are accessible through the m_Enchantments variable. + ]], + + Functions = + { + constructor = + { + { Params = "", Return = "cItem", Notes = "Creates a new empty cItem object" }, + { Params = "ItemType, Count, Damage, EnchantmentString", Return = "cItem", Notes = "Creates a new cItem object of the specified type, count (1 by default), damage (0 by default) and enchantments (non-enchanted by default)" }, + { Params = "cItem", Return = "cItem", Notes = "Creates an exact copy of the cItem object in the parameter" }, + } , + AddCount = { Params = "AmountToAdd", Return = "cItem", Notes = "Adds the specified amount to the item count. Returns self (useful for chaining)." }, + Clear = { Params = "", Return = "", Notes = "Resets the instance to an empty item" }, + CopyOne = { Params = "", Return = "cItem", Notes = "Creates a copy of this object, with its count set to 1" }, + DamageItem = { Params = "[Amount]", Return = "bool", Notes = "Adds the specified damage. Returns true when damage reaches max value and the item should be destroyed (but doesn't destroy the item)" }, + Empty = { Params = "", Return = "", Notes = "Resets the instance to an empty item" }, + GetMaxDamage = { Params = "", Return = "number", Notes = "Returns the maximum value for damage that this item can get before breaking; zero if damage is not accounted for for this item type" }, + IsDamageable = { Params = "", Return = "bool", Notes = "Returns true if this item does account for its damage" }, + IsEmpty = { Params = "", Return = "bool", Notes = "Returns true if this object represents an empty item (zero count or invalid ID)" }, + IsEqual = { Params = "cItem", Return = "bool", Notes = "Returns true if the item in the parameter is the same as the one stored in the object (type, damage and enchantments)" }, + 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" }, + IsStackableWith = { Params = "cItem", Return = "bool", Notes = "Returns true if the item in the parameter is stackable with the one stored in the object" }, + }, + Constants = + { + }, + }, + + cItemGrid = + { + Desc = [[This class represents a 2D array of items. It is used as the underlying storage and API for all cases that use a grid of items: +<li>Chest contents</li> +<li>(TODO) Chest minecart contents</li> +<li>{{cDispenserEntity|Dispenser|| contents</li> +<li>{{cDropperEntity|Dropper}} contents</li> +<li>{{cFurnaceEntity|Furnace}} contents (?)</li> +<li>{{cHopperEntity|Hopper}} contents</li> +<li>(TODO) Hopper minecart contents</li> +<li>Player Inventory areas</li> +<li>(TODO) Trapped chest contents</li> +</p> + <p>The items contained in this object are accessed either by a pair of XY coords, or a slot number (x + Width * y). There are functions available for converting between the two formats. +]], + Functions = + { + AddItem = { Params = "{{cItem|cItem}}, [AllowNewStacks]", Return = "number", Notes = "Adds an item to the storage; if AllowNewStacks is true (default), will also create new stacks in empty slots. Returns the number of items added" }, + AddItems = { Params = "{{cItems|cItems}}, [AllowNewStacks]", Return = "number", Notes = "Same as AddItem, but for several items at once" }, + ChangeSlotCount = + { + { Params = "SlotNum, AddToCount", Return = "number", Notes = "Adds AddToCount to the count of items in the specified slot. If the slot was empty, ignores the call. Returns the new count in the slot, or -1 if invalid SlotNum" }, + { Params = "X, Y, AddToCount", Return = "number", Notes = "Adds AddToCount to the count of items in the specified slot. If the slot was empty, ignores the call. Returns the new count in the slot, or -1 if invalid slot coords" }, + }, + Clear = { Params = "", Return = "", Notes = "Empties all slots" }, + CopyToItems = { Params = "{{cItems|cItems}}", Return = "", Notes = "Copies all non-empty slots into the cItems object provided; original cItems contents are preserved" }, + DamageItem = + { + { Params = "SlotNum, [DamageAmount]", Return = "bool", Notes = "Adds the specified damage (1 by default) to the specified item, returns true if the item reached its max damage and should be destroyed" }, + { Params = "X, Y, [DamageAmount]", Return = "bool", Notes = "Adds the specified damage (1 by default) to the specified item, returns true if the item reached its max damage and should be destroyed" }, + }, + EmptySlot = + { + { Params = "SlotNum", Return = "", Notes = "Destroys the item in the specified slot" }, + { Params = "X, Y", Return = "", Notes = "Destroys the item in the specified slot" }, + }, + GetFirstEmptySlot = { Params = "", Return = "number", Notes = "Returns the SlotNumber of the first empty slot, -1 if all slots are full" }, + GetHeight = { Params = "", Return = "number", Notes = "Returns the Y dimension of the grid" }, + GetLastEmptySlot = { Params = "", Return = "number", Notes = "Returns the SlotNumber of the last empty slot, -1 if all slots are full" }, + GetNextEmptySlot = { Params = "StartFrom", Return = "number", Notes = "Returns the SlotNumber of the first empty slot following StartFrom, -1 if all the following slots are full" }, + GetNumSlots = { Params = "", Return = "number", Notes = "Returns the total number of slots in the grid (Width * Height)" }, + GetSlot = + { + { Params = "SlotNumber", Return = "{{cItem|cItem}}", Notes = "Returns the item in the specified slot. Note that the item is read-only" }, + { Params = "X, Y", Return = "{{cItem|cItem}}", Notes = "Returns the item in the specified slot. Note that the item is read-only" }, + }, + GetSlotCoords = { Params = "SlotNum", Return = "number, number", Notes = "Returns the X and Y coords for the specified SlotNumber. Returns \"-1, -1\" on invalid SlotNumber" }, + GetSlotNum = { Params = "X, Y", Return = "number", Notes = "Returns the SlotNumber for the specified slot coords. Returns -1 on invalid coords" }, + GetWidth = { Params = "", Return = "number", Notes = "Returns the X dimension of the grid" }, + HasItems = { Params = "{{cItem|cItem}}", Return = "bool", Notes = "Returns true if there are at least as many items of the specified type as in the parameter" }, + HowManyCanFit = { Params = "{{cItem|cItem}}", Return = "number", Notes = "Returns the number of the specified items that can fit in the storage, including empty slots" }, + HowManyItems = { Params = "{{cItem|cItem}}", Return = "number", Notes = "Returns the number of the specified items that are currently stored" }, + IsSlotEmpty = + { + { Params = "SlotNum", Return = "bool", Notes = "Returns true if the specified slot is empty, or an invalid slot is specified" }, + { Params = "X, Y", Return = "bool", Notes = "Returns true if the specified slot is empty, or an invalid slot is specified" }, + }, + RemoveOneItem = + { + { Params = "SlotNum", Return = "{{cItem|cItem}}", Notes = "Removes one item from the stack in the specified slot and returns it as a single cItem. Empty slots are skipped and an empty item is returned" }, + { Params = "X, Y", Return = "{{cItem|cItem}}", Notes = "Removes one item from the stack in the specified slot and returns it as a single cItem. Empty slots are skipped and an empty item is returned" }, + }, + SetSlot = + { + { Params = "SlotNum, {{cItem|cItem}}", Return = "", Notes = "Sets the specified slot to the specified item" }, + { Params = "X, Y, {{cItem|cItem}}", Return = "", Notes = "Sets the specified slot to the specified item" }, + }, + }, + Constants = + { + }, + }, + + cItems = + { + Desc = [[ + This class represents a numbered collection (array) of {{cItem}} objects. The array indices start at + zero, each consecutive item gets a consecutive index. This class is used for spawning multiple + pickups or for mass manipulating an inventory. + ]], + Functions = + { + constructor = { Params = "", Return = "cItems", Notes = "Creates a new cItems object" }, + Add = + { + { Params = "{{cItem|cItem}}", Return = "", Notes = "Adds a new item to the end of the collection" }, + { Params = "ItemType, ItemCount, ItemDamage", Return = "", Notes = "Adds a new item to the end of the collection" }, + }, + Clear = { Params = "", Return = "", Notes = "Removes all items from the collection" }, + Delete = { Params = "Index", Return = "", Notes = "Deletes item at the specified index" }, + Get = { Params = "Index", Return = "{{cItem|cItem}}", Notes = "Returns the item at the specified index" }, + Set = + { + { Params = "Index, {{cItem|cItem}}", Return = "", Notes = "Sets the item at the specified index to the specified item" }, + { Params = "Index, ItemType, ItemCount, ItemDamage", Return = "", Notes = "Sets the item at the specified index to the specified item" }, + }, + Size = { Params = "", Return = "number", Notes = "Returns the number of items in the collection" }, + }, + Constants = + { + }, + }, + + cLineBlockTracer = + { + Desc = [[Objects of this class provide an easy-to-use interface to tracing lines through individual +blocks in the world. It will call the provided callbacks according to what events it encounters along the +way.</p> +<p> +For the Lua API, there's only one function exported that takes all the parameters necessary to do the +tracing. The Callbacks parameter is a table containing all the functions that will be called upon the +various events. See below for further information. + ]], + Functions = + { + Trace = { Params = "{{cWorld}}, Callbacks, StartX, StartY, StartZ, EndX, EndY, EndZ", Return = "bool", Notes = "(STATIC) Performs the trace on the specified line. Returns true if the entire trace was processed (no callback returned true)" }, + }, + + AdditionalInfo = + { + { + Header = "Callbacks", + Contents = [[ +The Callbacks in the Trace() function is a table that contains named functions. MCServer will call +individual functions from that table for the events that occur on the line - hitting a block, going out of +valid world data etc. The following table lists all the available callbacks. If the callback function is +not defined, MCServer skips it. Each function can return a bool value, if it returns true, the tracing is +aborted and Trace() returns false.</p> +<p> +<table><tr><th>Name</th><th>Parameters</th><th>Notes</th></tr> +<tr><td>OnNextBlock</td><td>BlockX, BlockY, BlockZ, BlockType, BlockMeta, EntryFace</td> + <td>Called when the ray hits a new valid block. The block type and meta is given. EntryFace is one of the + BLOCK_FACE_ constants indicating which "side" of the block got hit by the ray.</td></tr> +<tr><td>OnNextBlockNoData</td><td>BlockX, BlockY, BlockZ, EntryFace</td> + <td>Called when the ray hits a new block, but the block is in an unloaded chunk - no valid data is + available. Only the coords and the entry face are given.</td></tr> +<tr><td>OnOutOfWorld</td><td>X, Y, Z</td> + <td>Called when the ray goes outside of the world (Y-wise); the coords specify the exact exit point. Note + that for other paths than lines (considered for future implementations) the path may leave the world and + go back in again later, in such a case this callback is followed by OnIntoWorld() and further + OnNextBlock() calls.</td></tr> +<tr><td>OnIntoWorld</td><td>X, Y, Z</td> + <td>Called when the ray enters the world (Y-wise); the coords specify the exact entry point.</td></tr> +<tr><td>OnNoMoreHits</td><td> </td> + <td>Called when the path is sure not to hit any more blocks. This is the final callback, no more + callbacks are called after this function. Unlike the other callbacks, this function doesn't have a return + value.</td></tr> +<tr><td>OnNoChunk</td><td> </td> + <td>Called when the ray enters a chunk that is not loaded. This usually means that the tracing is aborted. + Unlike the other callbacks, this function doesn't have a return value.</td></tr> +</table> + ]], + }, + { + Header = "Example", + Contents = [[ +<p>The following example is taken from the Debuggers plugin. It is a command handler function for the +"/spidey" command that creates a line of cobweb blocks from the player's eyes up to 50 blocks away in +the direction they're looking, but only through the air. +<pre class="prettyprint lang-lua"> +function HandleSpideyCmd(a_Split, a_Player) + local World = a_Player:GetWorld(); + + local Callbacks = { + OnNextBlock = function(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta) + if (a_BlockType ~= E_BLOCK_AIR) then + -- abort the trace + return true; + end + World:SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COBWEB, 0); + end + }; + + local EyePos = a_Player:GetEyePosition(); + local LookVector = a_Player:GetLookVector(); + LookVector:Normalize(); -- Make the vector 1 m long + + -- Start cca 2 blocks away from the eyes + local Start = EyePos + LookVector + LookVector; + local End = EyePos + LookVector * 50; + + cLineBlockTracer.Trace(World, Callbacks, Start.x, Start.y, Start.z, End.x, End.y, End.z); + + return true; +end +</pre> +</p> + ]], + }, + }, -- AdditionalInfo + }, -- cLineBlockTracer + + cLuaWindow = + { + Desc = [[This class is used by plugins wishing to display a custom window to the player, unrelated to block entities or entities near the player. The window can be of any type and have any contents that the plugin defines. Callbacks for when the player modifies the window contents and when the player closes the window can be set. +</p> + <p>This class inherits from the {{cWindow|cWindow}} class, so all cWindow's functions and constants can be used, in addition to the cLuaWindow-specific functions listed below. +</p> + <p>The contents of this window are represented by a {{cWindow|cWindow}}:GetSlot() etc. or {{cPlayer|cPlayer}}:GetInventory() to access the player inventory. +</p> + <p>When creating a new cLuaWindow object, you need to specify both the window type and the contents' width and height. Note that MCServer accepts any combination of these, but opening a window for a player may crash their client if the contents' dimensions don't match the client's expectations. +</p> + <p>To open the window for a player, call {{cPlayer|cPlayer}}:OpenWindow(). Multiple players can open window of the same cLuaWindow object. All players see the same items in the window's contents (like chest, unlike crafting table). +]], + Functions = + { + constructor = { Params = "WindowType, ContentsWidth, ContentsHeight, Title", Return = "", Notes = "Creates a new object of this class" }, + GetContents = { Params = "", Return = "{{cItemGrid|cItemGrid}}", Notes = "Returns the cItemGrid object representing the internal storage in this window" }, + SetOnClosing = { Params = "OnClosingCallback", Return = "", Notes = "Sets the function that the window will call when it is about to be closed by a player" }, + SetOnSlotChanged = { Params = "OnSlotChangedCallback", Return = "", Notes = "Sets the function that the window will call when a slot is changed by a player" }, + }, + Constants = + { + }, + AdditionalInfo = + { + { + Header = "Callbacks", + Contents = [[ + The object calls the following functions at the appropriate time: + ]], + }, + { + Header = "OnClosing Callback", + Contents = [[ + This callback, settable via the SetOnClosing() function, will be called when the player tries to close the window, or the window is closed for any other reason (such as a player disconnecting).</p> +<pre class="prettyprint lang-lua"> +function OnWindowClosing(a_Window, a_Player, a_CanRefuse) +</pre> + <p> + The a_Window parameter is the cLuaWindow object representing the window, a_Player is the player for whom the window is about to close. a_CanRefuse specifies whether the callback can refuse the closing. If the callback returns true and a_CanRefuse is true, the window is not closed (internally, the server sends a new OpenWindow packet to the client). + ]], + }, + { + Header = "OnSlotChanged Callback", + Contents = [[ + This callback, settable via the SetOnSlotChanged() function, will be called whenever the contents of any slot in the window's contents (i. e. NOT in the player inventory!) changes.</p> +<pre class="prettyprint lang-lua"> +function OnWindowSlotChanged(a_Window, a_SlotNum) +</pre> + <p>The a_Window parameter is the cLuaWindow object representing the window, a_SlotNum is the slot number. There is no reference to a {{cPlayer}}, because the slot change needn't originate from the player action. To get or set the slot, you'll need to retrieve a cPlayer object, for example by calling {{cWorld|cWorld}}:DoWithPlayer(). + </p> + <p>Any returned values are ignored. + ]], + }, + { + Header = "Example", + Contents = [[ + This example is taken from the Debuggers plugin, used to test the API functionality. It opens a window and refuse to close it 3 times. It also logs slot changes to the server console. +<pre class="prettyprint lang-lua"> +-- Callback that refuses to close the window twice, then allows: +local Attempt = 1; +local OnClosing = function(Window, Player, CanRefuse) + Player:SendMessage("Window closing attempt #" .. Attempt .. "; CanRefuse = " .. tostring(CanRefuse)); + Attempt = Attempt + 1; + return CanRefuse and (Attempt <= 3); -- refuse twice, then allow, unless CanRefuse is set to true +end + +-- Log the slot changes: +local OnSlotChanged = function(Window, SlotNum) + LOG("Window \"" .. Window:GetWindowTitle() .. "\" slot " .. SlotNum .. " changed."); +end + +-- Set window contents: +-- a_Player is a cPlayer object received from the outside of this code fragment +local Window = cLuaWindow(cWindow.Hopper, 3, 3, "TestWnd"); +Window:SetSlot(a_Player, 0, cItem(E_ITEM_DIAMOND, 64)); +Window:SetOnClosing(OnClosing); +Window:SetOnSlotChanged(OnSlotChanged); + +-- Open the window: +a_Player:OpenWindow(Window); +</pre> + ]], + }, + }, -- AdditionalInfo + Inherits = "cWindow", + }, -- cLuaWindow + + cMonster = + { + Desc = "", + Functions = {}, + Constants = {}, + Inherits = "cPawn", + }, + + cPawn = + { + Desc = [[cPawn is a controllable pawn object, controlled by either AI or a player. cPawn inherits all functions and members of {{centity|centity}} +]], + Functions = + { + TeleportToEntity = { Return = "" }, + TeleportTo = { Return = "" }, + Heal = { Return = "" }, + TakeDamage = { Return = "" }, + KilledBy = { Return = "" }, + GetHealth = { Return = "number" }, + }, + Constants = + { + }, + Inherits = "cEntity", + }, + + cPickup = + { + Desc = [[cPickup is a pickup object representation. It is also commonly known as "drops". With this class you could create your own "drop" or modify automatically created. +]], + Functions = + { + cPickup = { Notes = "[[cPickup}}" }, + GetItem = { Notes = "{{cItem|cItem}}" }, + CollectedBy = { Return = "bool" }, + }, + Constants = + { + }, + Inherits = "cEntity", + }, + + cPlayer = + { + Desc = [[cPlayer describes a human player in the server. cPlayer inherits all functions and members of {{cPawn|cPawn}} +]], + Functions = + { + GetEyeHeight = { Return = "number" }, + GetEyePosition = { Return = "{{Vector3d|EyePositionVector}}" }, + GetFlying = { Return = "bool" }, + GetStance = { Return = "number" }, + GetInventory = { Return = "{{cInventory|Inventory}}" }, + GetGameMode = { Return = "{{eGameMode|GameMode}}", Notes = "Returns the player's gamemode. The player may have their gamemode unassigned, in which case they inherit the gamemode from the current {{cWorld|world}}.<br /> <b>NOTE:</b> Instead of comparing the value returned by this function to the gmXXX constants, use the IsGameModeXXX() functions. These functions handle the gamemode inheritance automatically."}, + GetIP = { Return = "string" }, + SetGameMode = { Return = "" }, + MoveTo = { Return = "" }, + GetClientHandle = { Return = "{{cClientHandle|ClientHandle}}" }, + SendMessage = { Return = "" }, + GetName = { Return = "String" }, + SetName = { Return = "" }, + AddToGroup = { Return = "" }, + CanUseCommand = { Return = "bool" }, + HasPermission = { Return = "bool" }, + IsInGroup = { Return = "bool" }, + GetColor = { Return = "string" }, + TossItem = { Return = "" }, + Heal = { Return = "" }, + TakeDamage = { Return = "" }, + KilledBy = { Return = "" }, + Respawn = { Return = "" }, + SetVisible = { Return = "" }, + IsVisible = { Return = "bool" }, + MoveToWorld = { Return = "bool" }, + LoadPermissionsFromDisk = { Return = "" }, + GetGroups = { Return = "list<{{cGroup|cGroup}}>" }, + GetResolvedPermissions = { Return = "string" }, + }, + Constants = + { + }, + Inherits = "cPawn", + }, + + cPlugin = + { + Desc = [[cPlugin describes a Lua plugin. This page is dedicated to new-style plugins and contain their functions. Each plugin has its own Plugin object. +]], + Functions = + { + Call = { Params = "Function name, [All the parameters divided with commas]", Notes = "This function allows you to call a function from another plugin. It can only use pass: integers, booleans, strings and usertypes (cPlayer, cEntity, cCuboid, etc.)." }, + GetDirectory = { Return = "string", Notes = "Returns the name of the folder where the plugin's files are. (APIDump)" }, + GetLocalDirectory = { Notes = "OBSOLETE use GetLocalFolder instead." }, + GetLocalFolder = { Return = "string", Notes = "Returns the path where the plugin's files are. (Plugins/APIDump)" }, + GetName = { Return = "string", Notes = "Returns the name of the plugin." }, + SetName = { Params = "string", Notes = "Sets the name of the Plugin." }, + GetVersion = { Return = "number", Notes = "Returns the version of the plugin." }, + SetVersion = { Params = "number", Notes = "Sets the version of the plugin." }, + GetFileName = { Return = "string" }, + CreateWebPlugin = { Notes = "{{cWebPlugin|cWebPlugin}}" }, + }, + Constants = + { + }, + }, + + cPluginLua = + { + Desc = "", + Functions = {}, + Constants = {}, + Inherits = "cPlugin", + }, + + cPluginManager = + { + Desc = [[ + This class is used for generic plugin-related functionality. The plugin manager has a list of all + plugins, can enable or disable plugins, manages hooks and in-game console commands.</p> + <p> + There is one instance of cPluginManager in MCServer, to get it, call either + {{cRoot|cRoot}}:Get():GetPluginManager() or cPluginManager:Get() function.</p> + <p> + Note that some functions are "static", that means that they are called using a dot operator instead + of the colon operator. For example: +<pre class="prettyprint lang-lua"> +cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage); +</pre></p> +]], + Functions = + { + AddHook = + { + { Params = "HookType, [HookFunction]", Return = "", Notes = "(STATIC) Informs the plugin manager that it should call the specified function when the specified hook event occurs. If a function is not specified, a default function name is looked up, based on the hook type" }, + { Params = "{{cPlugin|Plugin}}, HookType, [HookFunction]", Return = "", Notes = "(STATIC, <b>DEPRECATED</b>) Informs the plugin manager that it should call the specified function when the specified hook event occurs. If a function is not specified, a default function name is looked up, based on the hook type. NOTE: This format is deprecated and the server outputs a warning if it is used!" }, + }, + 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." }, + }, + 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." }, + }, + 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." }, + ExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "bool", Notes = "Executes the command as if given by the specified Player. Checks permissions. Returns true if executed." }, + ExecuteConsoleCommand = { Params = "CommandStr", Return = "bool", Notes = "Executes the command as if given on the server console. Returns true if executed." }, + FindPlugins = { Params = "", Return = "", Notes = "Refreshes the list of plugins to include all folders inside the Plugins folder (potentially new disabled plugins)" }, + ForceExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "bool", Notes = "Same as ExecuteCommand, but doesn't check permissions" }, + ForEachCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindCommand(). The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function(Command, Permission, HelpString)</pre>. If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." }, + ForEachConsoleCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindConsoleCommand(). The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function (Command, HelpString)</pre>. If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." }, + Get = { Params = "", Return = "cPluginManager", Notes = "Returns the single instance of the plugin manager" }, + GetAllPlugins = { Params = "", Return = "table", Notes = "Returns a table (dictionary) of all plugins, [name => {{cPlugin}}] pairing." }, + GetCommandPermission = { Params = "Command", Return = "Permission", Notes = "Returns the permission needed for executing the specified command" }, + GetNumPlugins = { Params = "", Return = "number", Notes = "Returns the number of plugins, including the disabled ones" }, + GetPlugin = { Params = "PluginName", Return = "{{cPlugin|cPlugin}}", Notes = "Returns a plugin handle of the specified plugin" }, + IsCommandBound = { Params = "Command", Return = "bool", Notes = "Returns true if in-game Command is already bound (by any plugin)" }, + IsConsoleCommandBound = { Params = "Command", Return = "bool", Notes = "Returns true if console Command is already bound (by any plugin)" }, + LoadPlugin = { Params = "PluginFolder", Return = "", Notes = "(<b>DEPRECATED</b>) Loads a plugin from the specified folder. NOTE: Loading plugins may be an unsafe operation and may result in a deadlock or a crash. This API is deprecated and might be removed." }, + ReloadPlugins = { Params = "", Return = "", Notes = "Reloads all active plugins" }, + }, + Constants = + { + HOOK_BLOCK_TO_PICKUPS = { Notes = "Called when a block has been dug and is being converted to pickups. The server has provided the default pickups and the plugins may modify them." }, + HOOK_CHAT = { Notes = "Called when a client sends a chat message that is not a command. The plugin may modify the chat message" }, + HOOK_CHUNK_AVAILABLE = { Notes = "Called when a chunk is loaded or generated and becomes available in the {{cWorld|world}}." }, + HOOK_CHUNK_GENERATED = { Notes = "Called after a chunk is generated. A plugin may do last modifications on the generated chunk before it is handed of to the {{cWorld|world}}." }, + HOOK_CHUNK_GENERATING = { Notes = "Called before a chunk is generated. A plugin may override some parts of the generation algorithm." }, + HOOK_CHUNK_UNLOADED = { Notes = "Called after a chunk has been unloaded from a {{cWorld|world}}." }, + HOOK_CHUNK_UNLOADING = { Notes = "Called before a chunk is unloaded from a {{cWorld|world}}. The chunk has already been saved." }, + HOOK_COLLECTING_PICKUP = { Notes = "Called when a player is about to collect a pickup." }, + HOOK_CRAFTING_NO_RECIPE = { Notes = "Called when a player has items in the crafting slots and the server cannot locate any recipe. Plugin may provide a recipe." }, + HOOK_DISCONNECT = { Notes = "Called after the player has disconnected." }, + HOOK_EXECUTE_COMMAND = { Notes = "Called when a client sends a chat message that is recognized as a command, before handing that command to the regular command handler. A plugin may stop the command from being handled. This hook is called even when the player doesn't have permissions for the command." }, + HOOK_EXPLODED = { Notes = "Called after an explosion has been processed in a {{cWorld|world}}." }, + HOOK_EXPLODING = { Notes = "Called before an explosion is processed in a {{cWorld|world}}. A plugin may alter the explosion parameters or cancel the explosion altogether." }, + HOOK_HANDSHAKE = { Notes = "Called when a Handshake packet is received from a client." }, + HOOK_HOPPER_PULLING_ITEM = { Notes = "Called when a hopper is pulling an item from the container above it." }, + HOOK_HOPPER_PUSHING_ITEM = { Notes = "Called when a hopper is pushing an item into the container it is aimed at." }, + HOOK_KILLING = { Notes = "Called when an entity has just been killed. A plugin may resurrect the entity by setting its health to above zero." }, + HOOK_LOGIN = { Notes = "Called when a Login packet is sent to the client, before the client is queued for authentication." }, + HOOK_MAX = { Notes = "The maximum TypeID of a hook. Used internally by MCS to check hook type for validity." }, + HOOK_NUM_HOOKS = { Notes = "Total number of hook types MCS supports. Used internally by MCS to check hook type for validity." }, + HOOK_PLAYER_ANIMATION = { Notes = "Called when a client send the Animation packet." }, + HOOK_PLAYER_BREAKING_BLOCK = { Notes = "Called when a player is about to break a block. A plugin may cancel the event." }, + HOOK_PLAYER_BROKEN_BLOCK = { Notes = "Called after a player has broken a block." }, + HOOK_PLAYER_EATING = { Notes = "Called when the player starts eating a held item. Plugins may abort the eating." }, + HOOK_PLAYER_JOINED = { Notes = "Called when the player entity has been created. It has not yet been fully initialized." }, + HOOK_PLAYER_LEFT_CLICK = { Notes = "Called when the client sends the LeftClick packet." }, + HOOK_PLAYER_MOVING = { Notes = "Called when the player has moved and the movement is now being applied." }, + HOOK_PLAYER_PLACED_BLOCK = { Notes = "Called when the player has just placed a block" }, + HOOK_PLAYER_PLACING_BLOCK = { Notes = "Called when the player is about to place a block. A plugin may cancel the event." }, + HOOK_PLAYER_RIGHT_CLICK = { Notes = "Called when the client sends the RightClick packet." }, + HOOK_PLAYER_RIGHT_CLICKING_ENTITY = { Notes = "Called when the client sends the UseEntity packet." }, + HOOK_PLAYER_SHOOTING = { Notes = "Called when the player releases the mouse button to fire their bow." }, + HOOK_PLAYER_SPAWNED = { Notes = "Called after the player entity has been created. The entity is fully initialized and is spawning in the {{cWorld|world}}." }, + HOOK_PLAYER_TOSSING_ITEM = { Notes = "Called when the player is tossing the held item (keypress Q)" }, + HOOK_PLAYER_USED_BLOCK = { Notes = "Called after the player has right-clicked a block" }, + HOOK_PLAYER_USED_ITEM = { Notes = "Called after the player has right-clicked with a usable item in their hand." }, + HOOK_PLAYER_USING_BLOCK = { Notes = "Called when the player is about to use (right-click) a block" }, + HOOK_PLAYER_USING_ITEM = { Notes = "Called when the player is about to right-click with a usable item in their hand." }, + HOOK_POST_CRAFTING = { Notes = "Called after a valid recipe has been chosen for the current contents of the crafting grid. Plugins may modify the recipe." }, + HOOK_PRE_CRAFTING = { Notes = "Called before a recipe is searched for the current contents of the crafting grid. Plugins may provide a recipe and cancel the built-in search." }, + HOOK_SPAWNED_ENTITY = { Notes = "Called after an entity is spawned in a {{cWorld|world}}. The entity is already part of the world." }, + HOOK_SPAWNED_MONSTER = { Notes = "Called after a mob is spawned in a {{cWorld|world}}. The mob is already part of the world." }, + HOOK_SPAWNING_ENTITY = { Notes = "Called just before an entity is spawned in a {{cWorld|world}}." }, + HOOK_SPAWNING_MONSTER = { Notes = "Called just before a mob is spawned in a {{cWorld|world}}." }, + HOOK_TAKE_DAMAGE = { Notes = "Called when an entity is taking any kind of damage. Plugins may modify the damage value, effects, source or cancel the damage." }, + HOOK_TICK = { Notes = "Called when the main server thread ticks - 20 times a second." }, + HOOK_UPDATED_SIGN = { Notes = "Called after a {{cSignEntity|sign}} text has been updated, either by a player or by any external means." }, + HOOK_UPDATING_SIGN = { Notes = "Called before a {{cSignEntity|sign}} text is updated, either by a player or by any external means." }, + HOOK_WEATHER_CHANGED = { Notes = "Called after the weather has changed." }, + HOOK_WEATHER_CHANGING = { Notes = "Called just before the weather changes" }, + HOOK_WORLD_TICK = { Notes = "Called in each world's tick thread when the game logic is about to tick (20 times a second)." }, + }, + }, + + cProjectileEntity = + { + Desc = "", + Functions = {}, + Constants = {}, + Inherits = "cEntity", + }, + + cRoot = + { + Desc = [[There is always only one cRoot object in MCServer. cRoot manages all the important objects such as {{cServer|cServer}} +]], + Functions = + { + Get = { Params = "", Return = "Root object", Notes = "This function returns the cRoot object." }, + BroadcastChat = { Params = "Message", Return = "", Notes = "Broadcasts a message to every player in the server." }, + FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "", Notes = "Calls the given callback function for the given player." }, + ForEachPlayer = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each player. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|cPlayer}})</pre>" }, + ForEachWorld = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each world. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cWorld|cWorld}})</pre>" }, + GetCraftingRecipes = { Params = "", Return = "{{cCraftingRecipe|cCraftingRecipe}}", Notes = "Returns the CraftingRecipes object" }, + GetDefaultWorld = { Params = "", Return = "{{cWorld|cWorld}}", Notes = "Returns the world object from the default world." }, + GetFurnaceRecipe = { Params = "", Return = "{{cFurnaceRecipe|cFurnaceRecipe}}", Notes = "Returns the cFurnaceRecipes object." }, + GetGroupManager = { Params = "", Return = "{{cGroupManager|cGroupManager}}", Notes = "Returns the cGroupManager object." }, + GetPluginManager = { Params = "", Return = "{{cPluginManager|cPluginManager}}", Notes = "Returns the cPluginManager object." }, + GetPrimaryServerVersion = { Params = "", Return = "number", Notes = "Returns the servers primary server version." }, + GetProtocolVersionTextFromInt = { Params = "Protocol Version", Return = "string", Notes = "Returns the Minecraft version from the given Protocol. If there is no version found, it returns 'Unknown protocol(Parameter)'" }, + GetServer = { Params = "", Return = "{{cServer|cServer}}", Notes = "Returns the cServer object." }, + GetTotalChunkCount = { Params = "", Return = "number", Notes = "Returns the amount of loaded chunks." }, + GetWebAdmin = { Params = "", Return = "{{cWebAdmin|cWebAdmin}}", Notes = "Returns the cWebAdmin object." }, + GetWorld = { Params = "WorldName", Return = "{{cWorld|cWorld}}", Notes = "Returns the cWorld object of the given world. It returns nil if there is no world with the given name." }, + QueueExecuteConsoleCommand = { Params = "Message", Return = "", Notes = "Queues a console command for execution through the cServer class. The command will be executed in the tick thread The command's output will be sent to console " .. '"stop" and "restart" commands have special handling.' }, + SaveAllChunks = { Params = "", Return = "", Notes = "Saves all the chunks in all the worlds." }, + SetPrimaryServerVersion = { Params = "Protocol Version", Return = "", Notes = "Sets the servers PrimaryServerVersion to the given protocol number." } + }, + Constants = + { + }, + }, + + cServer = + { + Desc = [[cServer is typically only used by plugins to broadcast a chat message(Now replaced by the {{cRoot|cRoot}} BroadcastChat function) to all players in the server. Natively however, cServer accepts connections from clients and adds those clients to the game. +]], + Functions = + { + GetDescription = { Return = "string", Notes = "Returns the server description set in the settings.ini." }, + GetMaxPlayers = { Return = "number", Notes = "Returns the max amount of players who can join the server." }, + SetMaxPlayers = { Params = "number", Notes = "Sets the max amount of players who can join." }, + GetNumPlayers = { Return = "number", Notes = "Returns the amount of players online." }, + GetServerID = { Return = "string", Notes = "Returns the ID of the server?" }, + }, + Constants = + { + }, + }, + + cSignEntity = + { + Desc = [[ +A sign entity represents a sign in the world. +Sign entities are saved and loaded from disk when the chunk they reside in is saved or loaded +]], + Functions = + { + }, + Constants = + { + }, + + Inherits = "cBlockEntity"; + }, + + cStringMap = + { + Desc = [[cStringMap is an object that maps strings with strings, it's also known as a dictionary +]], + Functions = + { + }, + Constants = + { + }, + }, + + cThrownEggEntity = + { + Desc = "", + Functions = {}, + Constants = {}, + Inherits = "cProjectileEntity", + }, + + cThrownEnderPearlEntity = + { + Desc = "", + Functions = {}, + Constants = {}, + Inherits = "cProjectileEntity", + }, + + cThrownSnowballEntity = + { + Desc = "", + Functions = {}, + Constants = {}, + Inherits = "cProjectileEntity", + }, + + cTracer = + { + Desc = [[A cTracer object is used to trace lines in the world. One thing you can use the cTracer for, is tracing what block a player is looking at, but you can do more with it if you want. +</p> + <p>The cTracer is still a work in progress +]], + Functions = + { + }, + Constants = + { + }, + }, + + cWebAdmin = + { + Desc = "", + Functions = {}, + Constants = {}, + }, + + cWebPlugin = + { + Desc = "", + Functions = {}, + Constants = {}, + }, + + cWindow = + { + Desc = [[This class is the common ancestor for all window classes used by MCServer. It is inherited by the {{cLuaWindow|cLuaWindow}} class that plugins use for opening custom windows. It is planned to be used for window-related hooks in the future. It implements the basic functionality of any window. +</p> + <p>Note that one cWindow object can be used for multiple players at the same time, and therefore the slot contents are player-specific (e. g. crafting grid, or player inventory). Thus the GetSlot() and SetSlot() functions need to have the {{cPlayer|cPlayer}} parameter that specifies the player for which the contents are to be queried. +]], + Functions = + { + GetWindowID = { Params = "", Return = "number", Notes = "Returns the ID of the window, as used by the network protocol" }, + GetWindowTitle = { Params = "", Return = "string", Notes = "Returns the window title that will be displayed to the player" }, + GetWindowType = { Params = "", Return = "number", Notes = "Returns the type of the window, one of the constants in the table above" }, + IsSlotInPlayerHotbar = { Params = "number", Return = "bool", Notes = "Returns true if the specified slot number is in the player hotbar" }, + IsSlotInPlayerInventory = { Params = "number", Return = "bool", Notes = "Returns true if the specified slot number is in the player's main inventory or in the hotbar. Note that this returns false for armor slots!" }, + IsSlotInPlayerMainInventory = { Params = "number", Return = "bool", Notes = "Returns true if the specified slot number is in the player's main inventory" }, + SetSlot = { Params = "{{cItem|cItem}}", Return = "", Notes = "Sets the contents of the specified slot for the specified player. Ignored if the slot number is invalid" }, + SetWindowTitle = { Params = "string", Return = "", Notes = "Sets the window title that will be displayed to the player" }, + }, + Constants = + { + Inventory = { Notes = "" }, + Chest = { Notes = "0" }, + Workbench = { Notes = "1" }, + Furnace = { Notes = "2" }, + DropSpenser = { Notes = "3" }, + Enchantment = { Notes = "4" }, + Brewery = { Notes = "5" }, + NPCTrade = { Notes = "6" }, + Beacon = { Notes = "7" }, + Anvil = { Notes = "8" }, + Hopper = { Notes = "9" }, + }, + }, + + cWorld = + { + Desc = [[ + cWorld is the game world. It is the hub of all the information managed by individual classes, + providing convenient access to them. MCServer supports multiple worlds in any combination of + world types. You can have two overworlds, three nethers etc. To enumerate all world the server + provides, use the {{cRoot}}:ForEachWorld() function.</p> + <p> + The world data is held in individual chunks. Each chunk consists of 16 (x) * 16 (z) * 256 (y) + blocks, each block is specified by its block type (8-bit) and block metadata (4-bit). + Additionally, each block has two light values calculated - skylight (how much daylight it receives) + and blocklight (how much light from light-emissive blocks it receives), both 4-bit.</p> + <p> + Each world runs several separate threads used for various housekeeping purposes, the most important + of those is the Tick thread. This thread updates the game logic 20 times per second, and it is + the thread where all the gameplay actions are evaluated. Liquid physics, entity interactions, + player ovement etc., all are applied in this thread.</p> + <p> + Additional threads include the generation thread (generates new chunks as needed, storage thread + (saves and loads chunk from the disk), lighting thread (updates block light values) and the + chunksender thread (compresses chunks to send to the clients).</p> + <p> + The world provides access to all its {{cPlayer|players}}, {{cEntity|entities}} and {{cBlockEntity|block + entities}}. Because of multithreading issues, individual objects cannot be retrieved for indefinite + handling, but rather must be modified in callbacks, within which they are guaranteed to stay valid.</p> + <p> + Physics for individual blocks are handled by the simulators. These will fire in each tick for all + blocks that have been scheduled for simulator update ("simulator wakeup"). The simulators include + liquid physics, falling blocks, fire spreading and extinguishing and redstone.</p> + <p> + Game time is also handled by the world. It provides the time-of-day and the total world age. + ]], + + Functions = + { + BroadcastChat = { Params = "Message, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Sends the Message to all players in this world, except the optional ExceptClient" }, + BroadcastSoundEffect = { Params = "SoundName, X, Y, Z, Volume, Pitch, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Sends the specified sound effect to all players in this world, except the optional ExceptClient" }, + BroadcastSoundParticleEffect = { Params = "EffectID, X, Y, Z, EffectData, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Sends the specified effect to all players in this world, except the optional ExceptClient" }, + CastThunderbolt = { Params = "X, Y, Z", Return = "", Notes = "Creates a thunderbolt at the specified coords" }, + ChangeWeather = { Params = "", Return = "", Notes = "Forces the weather to change in the next game tick. Weather is changed according to the normal rules: wSunny <-> wRain <-> wStorm" }, + CreateProjectile = { Params = "X, Y, Z, {{cProjectile|ProjectileKind}}, {{cEntity|Creator}}, [{{Vector3d|Speed}}]", Return = "", Notes = "Creates a new projectile of the specified kind at the specified coords. The projectile's creator is set to Creator (may be nil). Optional speed indicates the initial speed for the projectile." }, + DigBlock = { Params = "X, Y, Z", Return = "", Notes = "Replaces the specified block with air, without dropping the usual pickups for the block. Wakes up the simulators for the block and its neighbors." }, + DoExplosionAt = { Params = "Force, X, Y, Z, CanCauseFire, Source, SourceData", Return = "", Notes = "Creates an explosion of the specified relative force in the specified position. If CanCauseFire is set, the explosion will set blocks on fire, too. The Source parameter specifies the source of the explosion, one of the esXXX constants. The SourceData parameter is specific to each source type, usually it provides more info about the source." }, + DoWithChestAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a chest at the specified coords, calls the CallbackFunction with the {{cChestEntity}} parameter representing the chest. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cChestEntity|ChestEntity}}, [CallbackData])</pre> The function returns false if there is no chest, or if there is, it returns the bool value that the callback has returned." }, + DoWithDispenserAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a dispenser at the specified coords, calls the CallbackFunction with the {{cDispenserEntity}} parameter representing the dispenser. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cDispenserEntity|DispenserEntity}}, [CallbackData])</pre> The function returns false if there is no dispenser, or if there is, it returns the bool value that the callback has returned." }, + DoWithDropSpenserAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a dropper or a dispenser at the specified coords, calls the CallbackFunction with the {{cDropSpenserEntity}} parameter representing the dropper or dispenser. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cDropSpenserEntity|DropSpenserEntity}}, [CallbackData])</pre> Note that this can be used to access both dispensers and droppers in a similar way. The function returns false if there is neither dispenser nor dropper, or if there is, it returns the bool value that the callback has returned." }, + DoWithDropperAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a dropper at the specified coords, calls the CallbackFunction with the {{cDropperEntity}} parameter representing the dropper. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cDropperEntity|DropperEntity}}, [CallbackData])</pre> The function returns false if there is no dropper, or if there is, it returns the bool value that the callback has returned." }, + DoWithEntityByID = { Params = "EntityID, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If an entity with the specified ID exists, calls the callback with the {{cEntity}} parameter representing the entity. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}}, [CallbackData])</pre> The function returns false if the entity was not found, and it returns the same bool value that the callback has returned if the entity was found." }, + DoWithFurnaceAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a furnace at the specified coords, calls the CallbackFunction with the {{cFurnaceEntity}} parameter representing the furnace. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cFurnaceEntity|FurnaceEntity}}, [CallbackData])</pre> The function returns false if there is no furnace, or if there is, it returns the bool value that the callback has returned." }, + DoWithPlayer = { Params = "PlayerName, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a player of the specified name (exact match), calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found." }, + FastSetBlock = + { + { Params = "X, Y, Z, BlockType, BlockMeta", Return = "", Notes = "Sets the block at the specified coords, without waking up the simulators or replacing the block entities for the previous block type. Do not use if the block being replaced has a block entity tied to it!" }, + { Params = "{{Vector3i|BlockCoords}}, BlockType, BlockMeta", Return = "", Notes = "Sets the block at the specified coords, without waking up the simulators or replacing the block entities for the previous block type. Do not use if the block being replaced has a block entity tied to it!" }, + }, + FindAndDoWithPlayer = { Params = "PlayerNameHint, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a player of a name similar to the specified name (weighted-match), calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found. Note that the name matching is very loose, so it is a good idea to check the player name in the callback function." }, + ForEachChestInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each chest in the chunk. Returns true if all chests in the chunk have been processed (including when there are zero chests), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cChestEntity|ChestEntity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next chest, or true to abort the enumeration." }, + ForEachEntity = { Params = "CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each entity in the loaded world. Returns true if all the entities have been processed (including when there are zero entities), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next entity, or true to abort the enumeration." }, + ForEachEntityInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each entity in the specified chunk. Returns true if all the entities have been processed (including when there are zero entities), or false if the chunk is not loaded or the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next entity, or true to abort the enumeration." }, + ForEachFurnaceInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each furnace in the chunk. Returns true if all furnaces in the chunk have been processed (including when there are zero furnaces), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cFurnaceEntity|FurnaceEntity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next furnace, or true to abort the enumeration." }, + ForEachPlayer = { Params = "CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each player in the loaded world. Returns true if all the players have been processed (including when there are zero players), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The callback should return false or no value to continue with the next player, or true to abort the enumeration." }, + GenerateChunk = { Params = "ChunkX, ChunkZ", Return = "", Notes = "Queues the specified chunk in the chunk generator. Ignored if the chunk is already generated (use RegenerateChunk() to force chunk re-generation)." }, + GetBiomeAt = { Params = "BlockX, BlockZ", Return = "eBiome", Notes = "Returns the biome at the specified coords. Reads the biome from the chunk, if it is loaded, otherwise it uses the chunk generator to provide the biome value." }, + GetBlock = + { + { Params = "BlockX, BlockY, BlockZ", Return = "BLOCKTYPE", Notes = "Returns the block type of the block at the specified coords, or 0 if the appropriate chunk is not loaded." }, + { Params = "{{Vector3i|BlockCoords}}", Return = "BLOCKTYPE", Notes = "Returns the block type of the block at the specified coords, or 0 if the appropriate chunk is not loaded." }, + }, + GetBlockBlockLight = { Params = "BlockX, BlockY, BlockZ", Return = "number", Notes = "Returns the amount of block light at the specified coords, or 0 if the appropriate chunk is not loaded." }, + GetBlockInfo = { Params = "BlockX, BlockY, BlockZ", Return = "BlockValid, BlockType, BlockMeta, BlockSkyLight, BlockBlockLight", Notes = "Returns the complete block info for the block at the specified coords. The first value specifies if the block is in a valid loaded chunk, the other values are valid only if BlockValid is true." }, + GetBlockMeta = + { + { Params = "BlockX, BlockY, BlockZ", Return = "number", Notes = "Returns the block metadata of the block at the specified coords, or 0 if the appropriate chunk is not loaded." }, + { Params = "{{Vector3i|BlockCoords}}", Return = "number", Notes = "Returns the block metadata of the block at the specified coords, or 0 if the appropriate chunk is not loaded." }, + }, + GetBlockSkyLight = { Params = "BlockX, BlockY, BlockZ", Return = "number", Notes = "Returns the block skylight of the block at the specified coords, or 0 if the appropriate chunk is not loaded." }, + GetBlockTypeMeta = { Params = "BlockX, BlockY, BlockZ", Return = "BlockValid, BlockType, BlockMeta", Notes = "Returns the block type and metadata for the block at the specified coords. The first value specifies if the block is in a valid loaded chunk, the other values are valid only if BlockValid is true." }, + GetClassStatic = { Params = "", Return = "string", Notes = "Returns the name of the class, \"cWorld\"." }, + GetDimension = { Params = "", Return = "eDimension", Notes = "Returns the dimension of the world - dimOverworld, dimNether or dimEnd." }, + GetGameMode = { Params = "", Return = "eGameMode", Notes = "Returns the gamemode of the world - gmSurvival, gmCreative or gmAdventure." }, + GetGeneratorQueueLength = { Params = "", Return = "number", Notes = "Returns the number of chunks that are queued in the chunk generator." }, + GetHeight = { Params = "BlockX, BlockZ", Return = "number", Notes = "Returns the maximum height of the particula block column in the world. If the chunk is not loaded, it waits for it to load / generate. <b>WARNING</b>: Do not use, Use TryGetHeight() instead for a non-waiting version, otherwise you run the risk of a deadlock!" }, + GetIniFileName = { Params = "", Return = "string", Notes = "Returns the name of the world.ini file that the world uses to store the information." }, + GetLightingQueueLength = { Params = "", Return = "number", Notes = "Returns the number of chunks in the lighting thread's queue." }, + GetMaxCactusHeight = { Params = "", Return = "number", Notes = "Returns the configured maximum height to which cacti will grow naturally." }, + GetMaxSugarcaneHeight = { Params = "", Return = "number", Notes = "Returns the configured maximum height to which sugarcane will grow naturally." }, + GetName = { Params = "", Return = "string", Notes = "Returns the name of the world, as specified in the settings.ini file." }, + GetNumChunks = { Params = "", Return = "number", Notes = "Returns the number of chunks currently loaded." }, + GetSignLines = { Params = "BlockX, BlockY, BlockZ", Return = "IsValid, [Line1, Line2, Line3, Line4]", Notes = "Returns true and the lines of a sign at the specified coords, or false if there is no sign at the coords." }, + GetSpawnX = { Params = "", Return = "number", Notes = "Returns the X coord of the default spawn" }, + GetSpawnY = { Params = "", Return = "number", Notes = "Returns the Y coord of the default spawn" }, + GetSpawnZ = { Params = "", Return = "number", Notes = "Returns the Z coord of the default spawn" }, + GetStorageLoadQueueLength = { Params = "", Return = "number", Notes = "Returns the number of chunks queued up for loading" }, + GetStorageSaveQueueLength = { Params = "", Return = "number", Notes = "Returns the number of chunks queued up for saving" }, + GetTicksUntilWeatherChange = { Params = "", Return = "number", Notes = "Returns the number of ticks that will pass before the weather is changed" }, + GetTimeOfDay = { Params = "", Return = "number", Notes = "Returns the number of ticks that have passed from the sunrise, 0 .. 24000." }, + GetWeather = { Params = "", Return = "eWeather", Notes = "Returns the current weather in the world (wSunny, wRain, wStorm). To check for weather, use IsWeatherXXX() functions instead." }, + GetWorldAge = { Params = "", Return = "number", Notes = "Returns the total age of the world, in ticks. The age always grows, cannot be set by plugins and is unrelated to TimeOfDay." }, + GrowCactus = { Params = "BlockX, BlockY, BlockZ, NumBlocksToGrow", Return = "", Notes = "Grows a cactus block at the specified coords, by up to the specified number of blocks. Adheres to the world's maximum cactus growth (GetMaxCactusHeight())." }, + GrowMelonPumpkin = { Params = "BlockX, BlockY, BlockZ, StemType", Return = "", Notes = "Grows a melon or pumpkin, based on the stem type specified (assumed to be in the coords provided). Checks for normal melon / pumpkin growth conditions - stem not having another produce next to it and suitable ground below." }, + GrowRipePlant = { Params = "BlockX, BlockY, BlockZ, IsByBonemeal", Return = "bool", Notes = "Grows the plant at the specified coords. If IsByBonemeal is true, checks first if the specified plant type is bonemealable in the settings. Returns true if the plant was grown, false if not." }, + GrowSugarcane = { Params = "BlockX, BlockY, BlockZ, NumBlocksToGrow", Return = "", Notes = "Grows a sugarcane block at the specified coords, by up to the specified number of blocks. Adheres to the world's maximum sugarcane growth (GetMaxSugarcaneHeight())." }, + GrowTree = { Params = "BlockX, BlockY, BlockZ", Return = "", Notes = "Grows a tree based at the specified coords. If there is a sapling there, grows the tree based on that sapling, otherwise chooses a tree image based on the biome." }, + GrowTreeByBiome = { Params = "BlockX, BlockY, BlockZ", Return = "", Notes = "Grows a tree based at the specified coords. The tree type is picked from types available for the biome at those coords." }, + GrowTreeFromSapling = { Params = "BlockX, BlockY, BlockZ, SaplingMeta", Return = "", Notes = "Grows a tree based at the specified coords. The tree type is determined from the sapling meta (the sapling itself needn't be present)." }, + IsBlockDirectlyWatered = { Params = "BlockX, BlockY, BlockZ", Return = "bool", Notes = "Returns true if the specified block has a water block right next to it (on the X/Z axes)" }, + IsDeepSnowEnabled = { Params = "", Return = "bool", Notes = "Returns whether the configuration has DeepSnow enabled." }, + IsGameModeAdventure = { Params = "", Return = "bool", Notes = "Returns true if the current gamemode is gmAdventure." }, + IsGameModeCreative = { Params = "", Return = "bool", Notes = "Returns true if the current gamemode is gmCreative." }, + IsGameModeSurvival = { Params = "", Return = "bool", Notes = "Returns true if the current gamemode is gmSurvival." }, + IsPVPEnabled = { Params = "", Return = "bool", Notes = "Returns whether PVP is enabled in the world settings." }, + IsWeatherRain = { Params = "", Return = "bool", Notes = "Returns true if the current weather is rain." }, + IsWeatherStorm = { Params = "", Return = "bool", Notes = "Returns true if the current weather is a storm." }, + IsWeatherSunny = { Params = "", Return = "bool", Notes = "Returns true if the current weather is sunny." }, + IsWeatherWet = { Params = "", Return = "bool", Notes = "Returns true if the current weather has any precipitation (rain or storm)." }, + QueueBlockForTick = { Params = "BlockX, BlockY, BlockZ, TicksToWait", Return = "", Notes = "Queues the specified block to be ticked after the specified number of gameticks." }, + QueueSaveAllChunks = { Params = "", Return = "", Notes = "Queues all chunks to be saved in the world storage thread" }, + QueueSetBlock = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta, TickDelay", Return = "", Notes = "Queues the block to be set to the specified blocktype and meta after the specified amount of game ticks. Uses SetBlock() for the actual setting, so simulators are woken up and block entities are handled correctly." }, + RegenerateChunk = { Params = "ChunkX, ChunkZ", Return = "", Notes = "Queues the specified chunk to be re-generated, overwriting the current data. To queue a chunk for generating only if it doesn't exist, use the GenerateChunk() instead." }, + SendBlockTo = { Params = "BlockX, BlockY, BlockZ, {{cPlayer|Player}}", Return = "", Notes = "Sends the block at the specified coords to the specified player's client, as an UpdateBlock packet." }, + SetBlock = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta", Return = "", Notes = "Sets the block at the specified coords, replaces the block entities for the previous block type, creates a new block entity for the new block, if appropriate, and wakes up the simulators. This is the preferred way to set blocks, as opposed to FastSetBlock(), which is only to be used under special circumstances." }, + SetBlockMeta = + { + { Params = "BlockX, BlockY, BlockZ, BlockMeta", Return = "", Notes = "Sets the meta for the block at the specified coords." }, + { Params = "{{Vector3i|BlockCoords}}, BlockMeta", Return = "", Notes = "Sets the meta for the block at the specified coords." }, + }, + SetNextBlockTick = { Params = "BlockX, BlockY, BlockZ", Return = "", Notes = "Sets the blockticking to start at the specified block in the next tick." }, + SetSignLines = { Params = "X, Y, Z, Line1, Line2, Line3, Line4, [{{cPlayer|Player}}]", Return = "", Notes = "Sets the sign text at the specified coords. The sign-updating hooks are called for the change. The Player parameter is used to indicate the player from whom the change has come, it may be nil. Same as UpdateSign()." }, + SetTicksUntilWeatherChange = { Params = "NumTicks", Return = "", Notes = "Sets the number of ticks after which the weather will be changed." }, + SetTimeOfDay = { Params = "TimeOfDayTicks", Return = "", Notes = "Sets the time of day, expressed as number of ticks past sunrise, in the range 0 .. 24000." }, + SetWeather = { Params = "Weather", Return = "", Notes = "Sets the current weather (wSunny, wRain, wStorm) and resets the TicksUntilWeatherChange to the default value for the new weather. The normal weather-changing hooks are called for the change." }, + SpawnItemPickups = + { + { Params = "{{cItems|Pickups}}, X, Y, Z, FlyAwaySpeed", Return = "", Notes = "Spawns the specified pickups at the position specified. The FlyAway speed is used to initialize the random speed in which the pickups fly away from the spawn position." }, + { Params = "{{cItems|Pickups}}, X, Y, Z, SpeedX, SpeedY, SpeedZ", Return = "", Notes = "Spawns the specified pickups at the position specified. All the pickups fly away from the spawn position using the specified speed." }, + }, + SpawnMob = { Params = "X, Y, Z, {{cMonster|MonsterType}}", Return = "EntityID", Notes = "Spawns the specified type of mob at the specified coords. Returns the EntityID of the creates entity, or -1 on failure. " }, + SpawnPrimedTNT = { Params = "X, Y, Z, FuseTimeSecs, InitialVelocityCoeff", Return = "", Notes = "Spawns a {{cTNTEntity|primed TNT entity}} at the specified coords, with the given fuse time. The entity gets a random speed multiplied by the InitialVelocityCoeff, 1 being the default value." }, + TryGetHeight = { Params = "BlockX, BlockZ", Return = "IsValid, Height", Notes = "Returns true and height of the highest non-air block if the chunk is loaded, or false otherwise." }, + UnloadUnusedChunks = { Params = "", Return = "", Notes = "Unloads chunks that are no longer needed, and are saved. NOTE: This API is deprecated and will be removed soon." }, + UpdateSign = { Params = "X, Y, Z, Line1, Line2, Line3, Line4, [{{cPlayer|Player}}]", Return = "", Notes = "Sets the sign text at the specified coords. The sign-updating hooks are called for the change. The Player parameter is used to indicate the player from whom the change has come, it may be nil. Same as SetSignLiens()" }, + UseBlockEntity = { Params = "{{cPlayer|Player}}, BlockX, BlockY, BlockZ", Return = "", Notes = "Makes the specified Player use the block entity at the specified coords (open chest UI, etc.) If the cords are in an unloaded chunk or there's no block entity, ignores the call." }, + WakeUpSimulators = { Params = "BlockX, BlockY, BlockZ", Return = "", Notes = "Wakes up the simulators for the specified block." }, + WakeUpSimulatorsInArea = { Params = "MinBlockX, MaxBlockX, MinBlockY, MaxBlockY, MinBlockZ, MaxBlockZ", Return = "", Notes = "Wakes up the simulators for all the blocks in the specified area (edges inclusive)." }, + }, + Constants = + { + }, + AdditionalInfo = + { + { + Header = "Using callbacks", + Contents = [[ + To avoid problems with stale objects, the cWorld class will not let plugins get a direct pointer + to an {{cEntity|entity}}, {{cBlockEntity|block entity}} or a {{cPlayer|player}}. Such an object + could be modified or even destroyed by another thread while the plugin holds it, so it would be + rather unsafe.</p> + <p> + Instead, the cWorld provides access to these objects using callbacks. The plugin provides a + function that is called and receives the object as a parameter; cWorld guarantees that while + the callback is executing, the object will stay valid. If a plugin needs to "remember" the + object outside of the callback, it needs to store the entity ID, blockentity coords or player + name.</p> + <p> + The following code examples show how to use the callbacks</p> + <p> + This code teleports player Player to another player named ToName in the same world: +<pre class="prettyprint lang-lua"> +-- Player is a cPlayer object +-- ToName is a string +-- World is a cWorld object +World:ForEachPlayer( + function (a_OtherPlayer) + if (a_OtherPlayer:GetName() == ToName) then + Player:TeleportToEntity(a_OtherPlayer); + end +); +</pre></p> + <p> + This code fills each furnace in the chunk with 64 coals: +<pre class="prettyprint lang-lua"> +-- Player is a cPlayer object +-- World is a cWorld object +World:ForEachFurnaceInChunk(Player:GetChunkX(), Player:GetChunkZ(), + function (a_Furnace) + a_Furnace:SetFuelSlot(cItem(E_ITEM_COAL, 64)); + end +); +</pre></p> + <p> + This code teleports all spiders up by 100 blocks: +<pre class="prettyprint lang-lua"> +-- World is a cWorld object +World:ForEachEntity( + function (a_Entity) + if not(a_Entity:IsMob()) then + return; + end + local Monster = tolua.cast(a_Entity, "cMonster"); -- Get the cMonster out of cEntity, now that we know the entity represents one. + if (Monster:GetMobType() == cMonster.mtSpider) then + Monster:TeleportToCoords(Monster:GetPosX(), Monster:GetPosY() + 100, Monster:GetPosZ()); + end + end +); +</pre></p> + ]], + }, + }, -- AdditionalInfo + }, + + TakeDamageInfo = + { + Desc = [[The TakeDamageInfo is a struct that contains the amount of damage, and the entity that caused the damage. It is used in the {{OnTakeDamage|OnTakeDamage}}() hook and in the {{cEntity|cEntity}}'s TakeDamage() function. +]], + Functions = + { + }, + Constants = + { + }, + }, + + Vector3d = + { + Desc = [[A Vector3d object uses double precision floating point values to describe a point in space. Vector3d is part of the {{vector3|vector3}} family. +]], + Functions = + { + operator_plus = {Params = "{{Vector3d}}", Return = "{{Vector3d}}", Notes = "Returns the sum of this vector with the specified vector" }, + }, + Constants = + { + }, + }, + + Vector3f = + { + Desc = [[A Vector3f object uses floating point values to describe a point in space. Vector3f is part of the {{vector3|vector3}} family. +]], + Functions = + { + }, + Constants = + { + }, + }, + + Vector3i = + { + Desc = [[A Vector3i object uses integer values to describe a point in space. Vector3i is part of the {{vector3|vector3}} family. +]], + Functions = + { + }, + Constants = + { + }, + }, + Globals = + { + Desc = [[These functions are available directly, without a class instance. Any plugin cal call them at any time.]], + Functions = + { + AddFaceDirection = {Params = "BlockX, BlockY, BlockZ, BlockFace, Inverse", Return = "BlockX, BlockY, BlockZ", Notes = "Returns the coords of a block adjacent to the specified block through the specified face"}, + BlockStringToType = {Params = "BlockTypeString", Return = "BLOCKTYPE", Notes = "Returns the block type parsed from the given string"}, + ClickActionToString = {Params = "ClickAction", Return = "string", Notes = "Returns a string description of the ClickAction enumerated value"}, + DamageTypeToString = {Params = "{{TakeDamageInfo|eDamageType}}", Return = "string", Notes = "Converts a damage type enumerated value to a string representation "}, + EscapeString = {Params = "string", Return = "string", Notes = "Returns a copy of the string with all quotes and backslashes escaped by a backslash"}, + GetChar = {Params = "String, Pos", Return = "string", Notes = "Returns one character from the string, specified by position "}, + GetTime = {Return = "number", Notes = "Returns the current OS time, as a unix time stamp (number of seconds since Jan 1, 1970)"}, + IsValidBlock = {Params = "BlockType", Return = "bool", Notes = "Returns true if BlockType is a known block type"}, + IsValidItem = {Params = "ItemType", Return = "bool", Notes = "Returns true if ItemType is a known item type"}, + ItemToFullString = {Params = "{{cItem|cItem}}", Return = "string", Notes = "Returns the string representation of the item, in the format “ItemTypeText:ItemDamage * Count”"}, + ItemToString = {Params = "{{cItem|cItem}}", Return = "string", Notes = "Returns the string representation of the item type"}, + ItemTypeToString = {Params = "ItemType", Return = "string", Notes = "Returns the string representation of ItemType "}, + LOG = {Params = "string", Notes = "Logs a text into the server console using “normal” severity (gray text) "}, + LOGERROR = {Params = "string", Notes = "Logs a text into the server console using “error” severity (black text on red background)"}, + 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"}, + LOGWARNING = {Params = "string", Notes = "Logs a text into the server console using “warning” severity (red text)"}, + NoCaseCompare = {Params = "string, string", Return = "number", Notes = "Case-insensitive string comparison; returns 0 if the strings are the same"}, + 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"}, + StringSplit = {Params = "string, Seperator", Return = "list", Notes = "Seperates string into multiple by splitting every time Seperator is encountered."}, + StringToBiome = {Params = "string", Return = "EMCSBiome", Notes = "Converts a string representation to a biome enumerated value"}, + StringToDamageType = {Params = "string", Return = "{{TakeDamageInfo|eDamageType}}", Notes = "Converts a string representation to an {{TakeDamageInfo|eDamageType}} enumerated value "}, + StringToDimension = {Params = "string", Return = "eDimension", Notes = "Converts a string representation to an eDimension enumerated value"}, + StringToItem = {Params = "string, {{cItem|cItem}}", Return = "bool", Notes = "Parses the given string and sets the item; returns true if successful"}, + StringToMobType = {Params = "string", Return = "number", Notes = "Converts a string representation to a mob enumerated value"}, + StripColorCodes = {Params = "string", Return = "string", Notes = "Removes all control codes used by MC for colors and styles"}, + TrimString = {Params = "string", Return = "string", Notes = "Trime whitespace at both ends of the string"}, + md5 = {Params = "string", Return = "string", Notes = "converts a string to an md5 hash"}, + }, + Constants = + { + }, + }, + }, + + + Hooks = + { + HOOK_BLOCK_TO_PICKUPS = + { + CalledWhen = "A block is about to be dug ({{cPlayer|player}}, {{cEntity|entity}} or natural reason), plugins may override what pickups that will produce.", + DefaultFnName = "OnBlockToPickups", -- also used as pagename + Desc = [[ + This callback gets called whenever a block is about to be dug. This includes {{cPlayer|players}} + digging blocks, entities causing blocks to disappear ({{cTNTEntity|TNT}}, Endermen) and natural + causes (water washing away a block). Plugins may override the amount and kinds of pickups this + action produces. + ]], + Params = + { + { Name = "World", Type = "{{cWorld}}", Notes = "The world in which the block resides" }, + { Name = "Digger", Type = "{{cEntity}} descendant", Notes = "The entitycausing the digging. May be a {{cPlayer}}, {{cTNTEntity}} or even nil (natural causes)" }, + { Name = "BlockX", Type = "number", Notes = "X-coord of the block" }, + { Name = "BlockY", Type = "number", Notes = "Y-coord of the block" }, + { Name = "BlockZ", Type = "number", Notes = "Z-coord of the block" }, + { Name = "BlockType", Type = "BLOCKTYPE", Notes = "Block type of the block" }, + { Name = "BlockMeta", Type = "NIBBLETYPE", Notes = "Block meta of the block" }, + { Name = "Pickups", Type = "{{cItems}}", Notes = "Items that will be spawned as pickups" }, + }, + Returns = [[ + If the function returns false or no value, the next callback in the hook chain will be called. If + the function returns true, no other callbacks in the chain will be called.</p> + <p> + Either way, the server will then spawn pickups specified in the Pickups parameter, so to disable + pickups, you need to Clear the object first, then return true. + ]], + CodeExamples = + { + { + Title = "Modify pickups", + Desc = "This example callback function makes tall grass drop diamonds when digged by natural causes (washed away by water).", + Code = [[ +function OnBlockToPickups(a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_Pickups) + if (a_Digger ~= nil) then + -- Not a natural cause + return false; + end + if (a_BlockType ~= E_BLOCK_TALL_GRASS) then + -- Not a tall grass being washed away + return false; + end + + -- Remove all pickups suggested by MCServer: + a_Pickups:Clear(); + + -- Drop a diamond: + a_Pickups:Add(cItem(E_ITEM_DIAMOND)); + return true; +end; + ]], + }, + } , -- CodeExamples + }, -- HOOK_BLOCK_TO_PICKUPS + + HOOK_CHAT = + { + CalledWhen = "Player sends a chat message", + DefaultFnName = "OnChat", -- also used as pagename + Desc = [[ + A plugin may implement an OnChat() function and register it as a Hook to process chat messages from + the players. The function is then called for every in-game message sent from any player. Note that + commands are handled separately using a command framework API. + ]], + Params = { + { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who sent the message" }, + { Name = "Message", Type = "string", Notes = "The message" }, + }, + Returns = [[ + The plugin may return 2 values. The first is a boolean specifying whether the hook handling is to be + stopped or not. If it is false, the message is broadcast to all players in the world. If it is true, + no message is broadcast and no further action is taken.</p> + <p> + The second value is specifies the message to broadcast. This way, plugins may modify the message. If + the second value is not provided, the original message is used. + ]], + }, -- HOOK_CHAT + + HOOK_CHUNK_AVAILABLE = + { + CalledWhen = "A chunk has just been added to world, either generated or loaded. ", + DefaultFnName = "OnChunkAvailable", -- also used as pagename + Desc = [[ + This hook is called after a chunk is either generated or loaded from the disk. The chunk is + already available for manipulation using the {{cWorld}} API. This is a notification-only callback, + there is no behavior that plugins could override. + ]], + Params = + { + { Name = "World", Type = "{{cWorld}}", Notes = "The world to which the chunk belongs" }, + { Name = "ChunkX", Type = "number", Notes = "X-coord of the chunk" }, + { Name = "ChunkZ", Type = "number", Notes = "Z-coord of the chunk" }, + }, + Returns = [[ + If the function returns false or no value, the next plugin's callback is called. If the function + returns true, no other callback is called for this event. + ]], + }, -- HOOK_CHUNK_AVAILABLE + + HOOK_CHUNK_GENERATED = + { + CalledWhen = "After a chunk was generated. Notification only.", + DefaultFnName = "OnChunkGenerated", -- also used as pagename + Desc = [[ + This hook is called when world generator finished its work on a chunk. The chunk data has already + been generated and is about to be stored in the {{cWorld|world}}. A plugin may provide some + last-minute finishing touches to the generated data. Note that the chunk is not yet stored in the + world, so regular {{cWorld}} block API will not work! Instead, use the {{cChunkDesc}} object + received as the parameter.</p> + <p> + See also the {{OnChunkGenerating|HOOK_CHUNK_GENERATING}} hook. + ]], + Params = + { + { Name = "World", Type = "{{cWorld}}", Notes = "The world to which the chunk will be added" }, + { Name = "ChunkX", Type = "number", Notes = "X-coord of the chunk" }, + { Name = "ChunkZ", Type = "number", Notes = "Z-coord of the chunk" }, + { Name = "ChunkDesc", Type = "{{cChunkDesc}}", Notes = "Generated chunk data. Plugins may still modify the chunk data contained." }, + }, + Returns = [[ + If the plugin returns false or no value, MCServer will call other plugins' callbacks for this event. + If a plugin returns true, no other callback is called for this event.</p> + <p> + In either case, MCServer will then store the data from ChunkDesc as the chunk's contents in the world. + ]], + CodeExamples = + { + { + Title = "Generate emerald ore", + Desc = "This example callback function generates one block of emerald ore in each chunk, under the condition that the randomly chosen location is in an ExtremeHills biome.", + Code = [[ +function OnChunkGenerated(a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc) + -- Generate a psaudorandom value that is always the same for the same X/Z pair, but is otherwise random enough: + -- This is actually similar to how MCServer does its noise functions + local PseudoRandom = (a_ChunkX * 57 + a_ChunkZ) * 57 + 19785486 + PseudoRandom = PseudoRandom * 8192 + PseudoRandom; + PseudoRandom = ((PseudoRandom * (PseudoRandom * PseudoRandom * 15731 + 789221) + 1376312589) % 0x7fffffff; + PseudoRandom = PseudoRandom / 7; + + -- Based on the PseudoRandom value, choose a location for the ore: + local OreX = PseudoRandom % 16; + local OreY = 2 + ((PseudoRandom / 16) % 20); + local OreZ = (PseudoRandom / 320) % 16; + + -- Check if the location is in ExtremeHills: + if (a_ChunkDesc:GetBiome(OreX, OreZ) ~= biExtremeHills) then + return false; + end + + -- Only replace allowed blocks with the ore: + local CurrBlock = a_ChunDesc:GetBlockType(OreX, OreY, OreZ); + if ( + (CurrBlock == E_BLOCK_STONE) or + (CurrBlock == E_BLOCK_DIRT) or + (CurrBlock == E_BLOCK_GRAVEL) + ) then + a_ChunkDesc:SetBlockTypeMeta(OreX, OreY, OreZ, E_BLOCK_EMERALD_ORE, 0); + end +end; + ]], + }, + } , -- CodeExamples + }, -- HOOK_CHUNK_GENERATED + + HOOK_CHUNK_GENERATING = + { + CalledWhen = "A chunk is about to be generated. Plugin can override the built-in generator.", + DefaultFnName = "OnChunkGenerating", -- also used as pagename + Desc = [[ + This hook is called before the world generator starts generating a chunk. The plugin may provide + some or all parts of the generation, by-passing the built-in generator. The function is given access + to the {{cChunkDesc|ChunkDesc}} object representing the contents of the chunk. It may override parts + of the built-in generator by using the object's <i>SetUseDefaultXXX(false)</i> functions. After all + the callbacks for a chunk have been processed, the server will generate the chunk based on the + {{cChunkDesc|ChunkDesc}} description - those parts that are set for generating (by default + everything) are generated, the rest are read from the ChunkDesc object.</p> + <p> + See also the {{OnChunkGenerated|HOOK_CHUNK_GENERATED}} hook. + ]], + Params = + { + { Name = "World", Type = "{{cWorld}}", Notes = "The world to which the chunk will be added" }, + { Name = "ChunkX", Type = "number", Notes = "X-coord of the chunk" }, + { Name = "ChunkZ", Type = "number", Notes = "Z-coord of the chunk" }, + { Name = "ChunkDesc", Type = "{{cChunkDesc}}", Notes = "Generated chunk data." }, + }, + Returns = [[ + If this function returns true, the server will not call any other plugin with the same chunk. If + this function returns false, the server will call the rest of the plugins with the same chunk, + possibly overwriting the ChunkDesc's contents. + ]], + }, -- HOOK_CHUNK_GENERATING + + HOOK_CHUNK_UNLOADED = + { + CalledWhen = "A chunk has been unloaded from the memory.", + DefaultFnName = "OnChunkUnloaded", -- also used as pagename + Desc = [[ + This hook is called when a chunk is unloaded from the memory. Though technically still in memory, + the plugin should behave as if the chunk was already not present. In particular, {{cWorld}} block + API should not be used in the area of the specified chunk. + ]], + Params = + { + { Name = "World", Type = "{{cWorld}}", Notes = "The world from which the chunk is unloading" }, + { Name = "ChunkX", Type = "number", Notes = "X-coord of the chunk" }, + { Name = "ChunkZ", Type = "number", Notes = "Z-coord of the chunk" }, + }, + Returns = [[ + If the function returns false or no value, the next plugin's callback is called. If the function + returns true, no other callback is called for this event. There is no behavior that plugins could + override. + ]], + }, -- HOOK_CHUNK_UNLOADED + + HOOK_CHUNK_UNLOADING = + { + CalledWhen = " A chunk is about to be unloaded from the memory. Plugins may refuse the unload.", + DefaultFnName = "OnChunkUnloading", -- also used as pagename + Desc = [[ + MCServer calls this function when a chunk is about to be unloaded from the memory. A plugin may + force MCServer to keep the chunk in memory by returning true.</p> + <p> + FIXME: The return value should be used only for event propagation stopping, not for the actual + decision whether to unload. + ]], + Params = + { + { Name = "World", Type = "{{cWorld}}", Notes = "The world from which the chunk is unloading" }, + { Name = "ChunkX", Type = "number", Notes = "X-coord of the chunk" }, + { Name = "ChunkZ", Type = "number", Notes = "Z-coord of the chunk" }, + }, + Returns = [[ + If the function returns false or no value, the next plugin's callback is called and finally MCServer + unloads the chunk. If the function returns true, no other callback is called for this event and the + chunk is left in the memory. + ]], + }, -- HOOK_CHUNK_UNLOADING + + HOOK_COLLECTING_PICKUP = + { + CalledWhen = "Player is about to collect a pickup. Plugin can refuse / override behavior. ", + DefaultFnName = "OnCollectingPickup", -- also used as pagename + Desc = [[ + This hook is called when a player is about to collect a pickup. Plugins may refuse the action.</p> + <p> + Pickup collection happens within the world tick, so if the collecting is refused, it will be tried + again in the next world tick, as long as the player is within reach of the pickup.</p> + <p> + FIXME: There is no OnCollectedPickup() callback.</p> + <p> + FIXME: This callback is called even if the pickup doesn't fit into the player's inventory.</p> + ]], + Params = + { + { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who's collecting the pickup" }, + { Name = "Pickup", Type = "{{cPickup}}", Notes = "The pickup being collected" }, + }, + Returns = [[ + If the function returns false or no value, MCServer calls other plugins' callbacks and finally the + pickup is collected. If the function returns true, no other plugins are called for this event and + the pickup is not collected. + ]], + }, -- HOOK_COLLECTING_PICKUP + + HOOK_CRAFTING_NO_RECIPE = + { + CalledWhen = " No built-in crafting recipe is found. Plugin may provide a recipe.", + DefaultFnName = "OnCraftingNoRecipe", -- also used as pagename + Desc = [[ + This callback is called when a player places items in their {{cCraftingGrid|crafting grid}} and + MCServer cannot find a built-in {{cCraftingRecipe|recipe}} for the combination. Plugins may provide + a recipe for the ingredients given. + ]], + Params = + { + { Name = "Player", Type = "{{cPlayer}}", Notes = "The player whose crafting is reported in this hook" }, + { Name = "Grid", Type = "{{cCraftingGrid}}", Notes = "Contents of the player's crafting grid" }, + { Name = "Recipe", Type = "{{cCraftingRecipe}}", Notes = "The recipe that will be used (can be filled by plugins)" }, + }, + Returns = [[ + If the function returns false or no value, no recipe will be used. If the function returns true, no + other plugin will have their callback called for this event and MCServer will use the crafting + recipe in Recipe.</p> + <p> + FIXME: To allow plugins give suggestions and overwrite other plugins' suggestions, we should change + the behavior with returning false, so that the recipe will still be used, but fill the recipe with + empty values by default. + ]], + }, -- HOOK_CRAFTING_NO_RECIPE + + HOOK_DISCONNECT = + { + CalledWhen = "A player has explicitly disconnected.", + DefaultFnName = "OnDisconnect", -- also used as pagename + Desc = [[ + This hook is called when a client sends the disconnect packet and is about to be disconnected from + the server.</p> + <p> + Note that this callback is not called if the client drops the connection or is kicked by the + server.</p> + <p> + FIXME: There is no callback for "client destroying" that would be called in all circumstances.</p> + ]], + Params = + { + { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who has disconnected" }, + { Name = "Reason", Type = "string", Notes = "The reason that the client has sent in the disconnect packet" }, + }, + Returns = [[ + If the function returns false or no value, MCServer calls other plugins' callbacks for this event + and finally broadcasts a disconnect message to the player's world. If the function returns true, no + other plugins are called for this event and the disconnect message is not broadcast. In either case, + the player is disconnected. + ]], + }, -- HOOK_DISCONNECT + + HOOK_EXECUTE_COMMAND = + { + CalledWhen = "A player executes an in-game command, or the admin issues a console command. Note that built-in console commands are exempt to this hook - they are always performed and the hook is not called.", + DefaultFnName = "OnExecuteCommand", -- also used as pagename + Desc = [[ + A plugin may implement a callback for this hook to intercept both in-game commands executed by the + players and console commands executed by the server admin. The function is called for every in-game + command sent from any player and for those server console commands that are not built in in the + server.</p> + <p> + If the command is in-game, the first parameter to the hook function is the {{cPlayer|player}} who's + executing the command. If the command comes from the server console, the first parameter is nil. + ]], + Params = + { + { Name = "Player", Type = "{{cPlayer}}", Notes = "For in-game commands, the player who has sent the message. For console commands, nil" }, + { Name = "Command", Type = "table of strings", Notes = "The command and its parameters, broken into a table by spaces" }, + }, + Returns = [[ + If the plugin returns true, the command will be blocked and none of the remaining hook handlers will + be called. If the plugin returns false, MCServer calls all the remaining hook handlers and finally + the command will be executed. + ]], + }, -- HOOK_EXECUTE_COMMAND + + HOOK_EXPLODED = + { + CalledWhen = "An explosion has happened", + DefaultFnName = "OnExploded", -- also used as pagename + Desc = [[ + This hook is called after an explosion has been processed in a world.</p> + <p> + See also {{OnHookExploding|HOOK_EXPLODING}} for a similar hook called before the explosion.</p> + <p> + The explosion carries with it the type of its source - whether it's a creeper exploding, or TNT, + etc. It also carries the identification of the actual source. The exact type of the identification + depends on the source kind: + <table> + <tr><th>Source</th><th>SourceData Type</th><th>Notes</th></tr> + <tr><td>esPrimedTNT</td><td>{{cTNTEntity}}</td><td>An exploding primed TNT entity</td></tr> + <tr><td>esCreeper</td><td>{{cCreeper}}</td><td>An exploding creeper or charged creeper</td></tr> + <tr><td>esBed</td><td>{{Vector3i}}</td><td>A bed exploding in the Nether or in the End. The bed coords are given.</td></tr> + <tr><td>esEnderCrystal</td><td>{{Vector3i}}</td><td>An ender crystal exploding upon hit. The block coords are given.</td></tr> + <tr><td>esGhastFireball</td><td>{{cGhastFireballEntity}}</td><td>A ghast fireball hitting ground or an {{cEntity|entity}}.</td></tr> + <tr><td>esWitherSkullBlack</td><td><i>TBD</i></td><td>A black wither skull hitting ground or an {{cEntity|entity}}.</td></tr> + <tr><td>esWitherSkullBlue</td><td><i>TBD</i></td><td>A blue wither skull hitting ground or an {{cEntity|entity}}.</td></tr> + <tr><td>esWitherBirth</td><td><i>TBD</i></td><td>A wither boss being created</td></tr> + <tr><td>esOther</td><td><i>TBD</i></td><td>Any other previously unspecified type.</td></tr> + <tr><td>esPlugin</td><td>object</td><td>An explosion created by a plugin. The plugin may specify any kind of data.</td></tr> + </table></p> + ]], + Params = + { + { Name = "World", Type = "{{cWorld}}", Notes = "The world where the explosion happened" }, + { Name = "ExplosionSize", Type = "number", Notes = "The relative explosion size" }, + { Name = "CanCauseFire", Type = "bool", Notes = "True if the explosion has turned random air blocks to fire (such as a ghast fireball)" }, + { Name = "X", Type = "number", Notes = "X-coord of the explosion center" }, + { Name = "Y", Type = "number", Notes = "Y-coord of the explosion center" }, + { Name = "Z", Type = "number", Notes = "Z-coord of the explosion center" }, + { Name = "Source", Type = "eExplosionSource", Notes = "Source of the explosion. See the table above." }, + { Name = "SourceData", Type = "varies", Notes = "Additional data for the source. The exact type varies by the source. See the table above." }, + }, + Returns = [[ + If the function returns false or no value, the next plugin's callback is called. If the function + returns true, no other callback is called for this event. There is no overridable behaviour. + ]], + }, -- HOOK_EXPLODED + + HOOK_EXPLODING = + { + CalledWhen = "An explosion is about to be processed", + DefaultFnName = "OnExploding", -- also used as pagename + Desc = [[ + This hook is called before an explosion has been processed in a world.</p> + <p> + See also {{OnHookExploded|HOOK_EXPLODED}} for a similar hook called after the explosion.</p> + <p> + The explosion carries with it the type of its source - whether it's a creeper exploding, or TNT, + etc. It also carries the identification of the actual source. The exact type of the identification + depends on the source kind: + <table> + <tr><th>Source</th><th>SourceData Type</th><th>Notes</th></tr> + <tr><td>esPrimedTNT</td><td>{{cTNTEntity}}</td><td>An exploding primed TNT entity</td></tr> + <tr><td>esCreeper</td><td>{{cCreeper}}</td><td>An exploding creeper or charged creeper</td></tr> + <tr><td>esBed</td><td>{{Vector3i}}</td><td>A bed exploding in the Nether or in the End. The bed coords are given.</td></tr> + <tr><td>esEnderCrystal</td><td>{{Vector3i}}</td><td>An ender crystal exploding upon hit. The block coords are given.</td></tr> + <tr><td>esGhastFireball</td><td>{{cGhastFireballEntity}}</td><td>A ghast fireball hitting ground or an {{cEntity|entity}}.</td></tr> + <tr><td>esWitherSkullBlack</td><td><i>TBD</i></td><td>A black wither skull hitting ground or an {{cEntity|entity}}.</td></tr> + <tr><td>esWitherSkullBlue</td><td><i>TBD</i></td><td>A blue wither skull hitting ground or an {{cEntity|entity}}.</td></tr> + <tr><td>esWitherBirth</td><td><i>TBD</i></td><td>A wither boss being created</td></tr> + <tr><td>esOther</td><td><i>TBD</i></td><td>Any other previously unspecified type.</td></tr> + <tr><td>esPlugin</td><td>object</td><td>An explosion created by a plugin. The plugin may specify any kind of data.</td></tr> + </table></p> + ]], + Params = + { + { Name = "World", Type = "{{cWorld}}", Notes = "The world where the explosion happens" }, + { Name = "ExplosionSize", Type = "number", Notes = "The relative explosion size" }, + { Name = "CanCauseFire", Type = "bool", Notes = "True if the explosion will turn random air blocks to fire (such as a ghast fireball)" }, + { Name = "X", Type = "number", Notes = "X-coord of the explosion center" }, + { Name = "Y", Type = "number", Notes = "Y-coord of the explosion center" }, + { Name = "Z", Type = "number", Notes = "Z-coord of the explosion center" }, + { Name = "Source", Type = "eExplosionSource", Notes = "Source of the explosion. See the table above." }, + { Name = "SourceData", Type = "varies", Notes = "Additional data for the source. The exact type varies by the source. See the table above." }, + }, + Returns = [[ + If the function returns false or no value, the next plugin's callback is called, and finally + MCServer will process the explosion - destroy blocks and push + hurt entities. If the function + returns true, no other callback is called for this event and the explosion will not occur. + ]], + }, -- HOOK_EXPLODING + + HOOK_HANDSHAKE = + { + CalledWhen = "A client is connecting.", + DefaultFnName = "OnHandshake", -- also used as pagename + Desc = [[ + This hook is called when a client sends the Handshake packet. At this stage, only the client IP and + (unverified) username are known. Plugins may refuse access to the server based on this + information.</p> + <p> + Note that the username is not authenticated - the authentication takes place only after this hook is + processed. + ]], + Params = + { + { Name = "Client", Type = "{{cClientHandle}}", Notes = "The client handle representing the connection. Note that there's no {{cPlayer}} object for this client yet." }, + { Name = "UserName", Type = "string", Notes = "The username presented in the packet. Note that this username is unverified." }, + }, + Returns = [[ + If the function returns false, the user is let in to the server. If the function returns true, no + other plugin's callback is called, the user is kicked and the connection is closed. + ]], + }, -- HOOK_HANDSHAKE + + }, -- Hooks[] + + + IgnoreClasses = + { + "coroutine", + "debug", + "io", + "math", + "package", + "os", + "string", + "table", + }, + + IgnoreFunctions = + { + "Globals.assert", + "Globals.collectgarbage", + "Globals.xpcall", + "%a+\.__%a+", -- AnyClass.__Anything + "%a+\.\.collector", -- AnyClass..collector + "%a+\.new", -- AnyClass.new + "%a+.new_local", -- AnyClass.new_local + "%a+.delete", -- AnyClass.delete + + -- Functions global in the APIDump plugin: + "CreateAPITables", + "DumpAPIHtml", + "DumpAPITxt", + "Initialize", + "LinkifyString", + "ReadDescriptions", + "ReadHooks", + "WriteHtmlClass", + "WriteHtmlHook", + }, + + ExtraPages = + { + -- No sorting is provided for these, they will be output in the same order as defined here + { FileName = "WebWorldThreads.html", Title = "Webserver vs World threads" }, + } +} ; + + + + + diff --git a/MCServer/Plugins/APIDump/APIDump.deproj b/MCServer/Plugins/APIDump/APIDump.deproj new file mode 100644 index 000000000..9ee9170f2 --- /dev/null +++ b/MCServer/Plugins/APIDump/APIDump.deproj @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<project> + <file> + <filename>APIDesc.lua</filename> + </file> + <file> + <filename>main.lua</filename> + </file> +</project> diff --git a/MCServer/Plugins/APIDump/WebWorldThreads.html b/MCServer/Plugins/APIDump/WebWorldThreads.html new file mode 100644 index 000000000..a77209b0b --- /dev/null +++ b/MCServer/Plugins/APIDump/WebWorldThreads.html @@ -0,0 +1,64 @@ +<html> +<head> +<title>MCServer - Webserver vs World threads</title> +<script src="https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js"></script> +<script src="http://google-code-prettify.googlecode.com/svn/trunk/src/lang-lua.js"></script> +</head> +<body> + +<h1>Webserver vs World threads</h1> +<p> +This article will explain the threading issues that arise between the webserver and world threads are of concern to plugin authors.</p> +<p> +Generally, plugins that provide webadmin pages should be quite careful about their interactions. Most operations on MCServer objects requires synchronization, that MCServer provides automatically and transparently to plugins - when a block is written, the chunkmap is locked, or when an entity is being manipulated, the entity list is locked. Each plugin also has a mutex lock, so that only one thread at a time may be executing plugin code.</p> +<p> +This locking can be a source of deadlocks for plugins that are not written carefully.</p> + +<h2>Example scenario</h2> +<p>Consider the following example. A plugin provides a webadmin page that allows the admin to kick players off the server. When the admin presses the "Kick" button, the plugin calls cWorld:DoWithPlayer() with a callback to kick the player. Everything seems to be working fine now.</p> +<p> +A new feature is developed in the plugin, now the plugin adds a new in-game command so that the admins can kick players while they're playing the game. The plugin registers a command callback with cPluginManager.AddCommand(). Now there are problems bound to happen.</p> +<p> +Suppose that two admins are in, one is using the webadmin and the other is in-game. Both try to kick a player at the same time. The webadmin locks the plugin, so that it can execute the plugin code, but right at this moment the OS switches threads. The world thread locks the world so that it can access the list of in-game commands, receives the in-game command, it tries to lock the plugin. The plugin is already locked, so the world thread is put on hold. After a while, the webadmin thread is woken up again and continues processing. It tries to lock the world so that it can traverse the playerlist, but the lock is already held by the world thread. Now both threads are holding one lock each and trying to grab the other lock, and are therefore deadlocked.</p> + +<h2>How to avoid the deadlock</h2> +<p> +There are two main ways to avoid such a deadlock. The first approach is using tasks: Everytime you need to execute a task inside a world, instead of executing it, queue it, using <a href="cWorld.html">cWorld</a>:QueueTask(). This handy utility can will call the given function inside the world's TickThread, thus eliminating the deadlock, because now there's only one thread. However, this approach will not let you get data back. You cannot query the player list, or the entities, or anything - because when the task runs, the webadmin page has already been served to the browser.</p> +<p> +To accommodate this, you'll need to use the second approach - preparing and caching data in the tick thread, possibly using callbacks. This means that the plugin will have global variables that will store the data, and update those variables when the data changes; then the webserver thread will only read those variables, instead of calling the world functions. For example, if a webpage was to display the list of currently connected players, the plugin should maintain a global variable, g_WorldPlayers, which would be a table of worlds, each item being a list of currently connected players. The webadmin handler would read this variable and create the page from it; the plugin would use HOOK_PLAYER_JOINED and HOOK_DISCONNECT to update the variable.</p> + +<h2>What to avoid</h2> +<p> +Now that we know what the danger is and how to avoid it, how do we know if our code is susceptible?</p> +<p> +The general rule of thumb is to avoid calling any functions that read or write lists of things in the webserver thread. This means most ForEach() and DoWith() functions. Only <a href="cRoot.html">cRoot</a>:ForEachWorld() is safe - because the list of worlds is not expected to change, so it is not guarded by a mutex. Getting and setting world's blocks is, naturally, unsafe, as is calling other plugins, or creating entities.</p> + +<h2>Example</h2> +The Core has the facility to kick players using the web interface. It used the following code for the kicking (inside the webadmin handler): +<pre class="prettyprint lang-lua"> +local KickPlayerName = Request.Params["players-kick"] +local FoundPlayerCallback = function(Player) + if (Player:GetName() == KickPlayerName) then + Player:GetClientHandle():Kick("You were kicked from the game!") + end +end +cRoot:Get():FindAndDoWithPlayer(KickPlayerName, FoundPlayerCallback) +</pre> +The cRoot:FindAndDoWithPlayer() is unsafe and could have caused a deadlock. The new solution is queue a task; but since we don't know in which world the player is, we need to queue the task to all worlds: +<pre class="prettyprint lang-lua"> +cRoot:Get():ForEachWorld( -- For each world... + function(World) + World:QueueTask( -- ... queue a task... + function(a_World) + a_World:DoWithPlayer(KickPlayerName, -- ... to walk the playerlist... + function (a_Player) + a_Player:GetClientHandle():Kick("You were kicked from the game!") -- ... and kick the player + end + ) + end + ) + end +) +</pre> +</body> +</html>
\ No newline at end of file diff --git a/MCServer/Plugins/APIDump/main.css b/MCServer/Plugins/APIDump/main.css new file mode 100644 index 000000000..777f6d71a --- /dev/null +++ b/MCServer/Plugins/APIDump/main.css @@ -0,0 +1,28 @@ +table +{ + background-color: #fff; + border-spacing: 0px; + border-collapse: collapse; + border-color: gray; +} + +tr +{ + display: table-row; + vertical-align: inherit; + border-color: inherit; +} + +td, th +{ + display: table-cell; + vertical-align: inherit; + padding: 3px; + border: 1px solid #ccc; +} + +pre +{ + border: 1px solid #ccc; + background-color: #eee; +}
\ No newline at end of file diff --git a/MCServer/Plugins/APIDump/main.lua b/MCServer/Plugins/APIDump/main.lua index 853ff6301..8c07144f2 100644 --- a/MCServer/Plugins/APIDump/main.lua +++ b/MCServer/Plugins/APIDump/main.lua @@ -1,5 +1,15 @@ --- Global variables -PLUGIN = {}; -- Reference to own plugin object + +-- main.lua + +-- Implements the plugin entrypoint (in this case the entire plugin) + + + + + +-- Global variables: +g_Plugin = nil; +g_PluginFolder = ""; @@ -7,17 +17,21 @@ PLUGIN = {}; -- Reference to own plugin object function Initialize(Plugin) - PLUGIN = Plugin + g_Plugin = Plugin; - Plugin:SetName("APIDump") - Plugin:SetVersion(1) + Plugin:SetName("APIDump"); + Plugin:SetVersion(1); - PluginManager = cRoot:Get():GetPluginManager() LOG("Initialized " .. Plugin:GetName() .. " v." .. Plugin:GetVersion()) + + g_PluginFolder = Plugin:GetLocalFolder(); -- dump all available API functions and objects: - DumpAPI(); - + -- DumpAPITxt(); + + -- Dump all available API object in HTML format into a subfolder: + DumpAPIHtml(); + return true end @@ -25,17 +39,16 @@ end - -function DumpAPI() +function DumpAPITxt() LOG("Dumping all available functions to API.txt..."); function dump (prefix, a, Output) for i, v in pairs (a) do if (type(v) == "table") then if (GetChar(i, 1) ~= ".") then if (v == _G) then - LOG(prefix .. i .. " == _G, CYCLE, ignoring"); + -- LOG(prefix .. i .. " == _G, CYCLE, ignoring"); elseif (v == _G.package) then - LOG(prefix .. i .. " == _G.package, ignoring"); + -- LOG(prefix .. i .. " == _G.package, ignoring"); else dump(prefix .. i .. ".", v, Output) end @@ -59,3 +72,731 @@ function DumpAPI() f:close(); LOG("API.txt written."); end + + + + + +function CreateAPITables() + --[[ + We want an API table of the following shape: + local API = { + { + Name = "cCuboid", + Functions = { + {Name = "Sort"}, + {Name = "IsInside"} + }, + Constants = { + } + Descendants = {}, -- Will be filled by ReadDescriptions(), array of class APIs (references to other member in the tree) + }}, + { + Name = "cBlockArea", + Functions = { + {Name = "Clear"}, + {Name = "CopyFrom"}, + ... + } + Constants = { + {Name = "baTypes", Value = 0}, + {Name = "baMetas", Value = 1}, + ... + } + ... + }} + }; + local Globals = { + Functions = { + ... + }, + Constants = { + ... + } + }; + --]] + + local Globals = {Functions = {}, Constants = {}, Descendants = {}}; + local API = {}; + + local function Add(a_APIContainer, a_ObjName, a_ObjValue) + if (type(a_ObjValue) == "function") then + table.insert(a_APIContainer.Functions, {Name = a_ObjName}); + elseif ( + (type(a_ObjValue) == "number") or + (type(a_ObjValue) == "string") + ) then + table.insert(a_APIContainer.Constants, {Name = a_ObjName, Value = a_ObjValue}); + end + end + + local function ParseClass(a_ClassName, a_ClassObj) + local res = {Name = a_ClassName, Functions = {}, Constants = {}, Descendants = {}}; + for i, v in pairs(a_ClassObj) do + Add(res, i, v); + end + return res; + end + + for i, v in pairs(_G) do + if ( + (v ~= _G) and -- don't want the global namespace + (v ~= _G.packages) and -- don't want any packages + (v ~= _G[".get"]) and + (v ~= g_APIDesc) + ) then + if (type(v) == "table") then + table.insert(API, ParseClass(i, v)); + else + Add(Globals, i, v); + end + end + end + + return API, Globals; +end + + + + + +function DumpAPIHtml() + LOG("Dumping all available functions and constants to API subfolder..."); + + local API, Globals = CreateAPITables(); + local Hooks = {}; + local UndocumentedHooks = {}; + + -- Sort the classes by name: + table.sort(API, + function (c1, c2) + return (string.lower(c1.Name) < string.lower(c2.Name)); + end + ); + + -- Add Globals into the API: + Globals.Name = "Globals"; + table.insert(API, Globals); + + -- Extract hook constants: + for name, obj in pairs(cPluginManager) do + if (type(obj) == "number") and (name:match("HOOK_.*")) then + table.insert(Hooks, { Name = name }); + end + end + table.sort(Hooks, + function(Hook1, Hook2) + return (Hook1.Name < Hook2.Name); + end + ); + + -- Read in the descriptions: + 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 + local f = io.open("API/index.html", "w"); + if (f == nil) then + LOGINFO("Cannot output HTML API: " .. err); + return; + end + + f:write([[<html><head><title>MCServer API - index</title> + <link rel="stylesheet" type="text/css" href="main.css" /> + </head><body><h1>MCServer API - index</h1> + <p>The API reference is divided into the following sections:<ul> + <li><a href="#classes">Class index</a></li> + <li><a href="#hooks">Hooks</a></li> + <li><a href="#extra">Extra pages</a></li> + </ul></p> + <a name="classes"><h2>Class index</h2></a> + <p>The following classes are available in the MCServer Lua scripting language: + <ul> + ]]); + for i, cls in ipairs(API) do + f:write("<li><a href=\"" .. cls.Name .. ".html\">" .. cls.Name .. "</a></li>\n"); + WriteHtmlClass(cls, API); + end + f:write([[</ul></p> + <a name="hooks"><h2>Hooks</h2></a> + <p>A plugin can register to be called whenever an “interesting event” occurs. It does so by calling + <a href="cPluginManager.html">cPluginManager</a>'s AddHook() function and implementing a callback + function to handle the event.</p> + <p>A plugin can decide whether it will let the event pass through to the rest of the plugins, or hide it + from them. This is determined by the return value from the hook callback function. If the function returns + false or no value, the event is propagated further. If the function returns true, the processing is + stopped, no other plugin receives the notification (and possibly MCServer disables the default behavior + for the event). See each hook's details to see the exact behavior.</p> + <table><tr><th>Hook name</th><th>Called when</th></tr> + ]]); + for i, hook in ipairs(Hooks) do + if (hook.DefaultFnName == nil) then + -- The hook is not documented yet + f:write("<tr><td>" .. hook.Name .. "</td><td><i>(No documentation yet)</i></td></tr>\n"); + table.insert(UndocumentedHooks, hook.Name); + else + f:write("<tr><td><a href=\"" .. hook.DefaultFnName .. ".html\">" .. hook.Name .. "</a></td><td>" .. LinkifyString(hook.CalledWhen) .. "</td></tr>\n"); + WriteHtmlHook(hook); + end + end + f:write([[</table> + <a name="extra"><h2>Extra pages</h2></a> + <p>The following pages provide various extra information</p> + <ul>]]); + for i, extra in ipairs(g_APIDesc.ExtraPages) do + local SrcFileName = g_PluginFolder .. "/" .. extra.FileName; + if (cFile:Exists(SrcFileName)) then + local DstFileName = "API/" .. extra.FileName; + if (cFile:Exists(DstFileName)) then + cFile:Delete(DstFileName); + end + cFile:Copy(SrcFileName, DstFileName); + f:write("<li><a href=\"" .. extra.FileName .. "\">" .. extra.Title .. "</a></li>\n"); + else + f:write("<li>" .. extra.Title .. " <i>(file is missing)</i></li>\n"); + end + end + f:write([[</ul> + </body></html> + ]]); + f:close(); + + -- Copy the CSS file to the output folder (overwrite any existing): + cssf = io.open("API/main.css", "w"); + if (cssf ~= nil) then + cssfi = io.open(g_Plugin:GetLocalDirectory() .. "/main.css", "r"); + if (cssfi ~= nil) then + local CSS = cssfi:read("*all"); + cssf:write(CSS); + cssfi:close(); + end + cssf:close(); + end + + -- List the undocumented objects: + f = io.open("API/undocumented.lua", "w"); + if (f ~= nil) then + f:write("\n-- This is the list of undocumented API objects, automatically generated by APIDump\n\n"); + f:write("g_APIDesc =\n{\n\tClasses =\n\t{\n"); + for i, cls in ipairs(API) do + local HasFunctions = ((cls.UndocumentedFunctions ~= nil) and (#cls.UndocumentedFunctions > 0)); + local HasConstants = ((cls.UndocumentedConstants ~= nil) and (#cls.UndocumentedConstants > 0)); + if (HasFunctions or HasConstants) then + f:write("\t\t" .. cls.Name .. " =\n\t\t{\n"); + if ((cls.Desc == nil) or (cls.Desc == "")) then + f:write("\t\t\tDesc = \"\"\n"); + end + end + + if (HasFunctions) then + f:write("\t\t\tFunctions =\n\t\t\t{\n"); + table.sort(cls.UndocumentedFunctions); + for j, fn in ipairs(cls.UndocumentedFunctions) do + f:write("\t\t\t\t" .. fn .. " = { Params = \"\", Return = \"\", Notes = \"\" },\n"); + end -- for j, fn - cls.Undocumented[] + f:write("\t\t\t},\n\n"); + end + + if (HasConstants) then + f:write("\t\t\tConstants =\n\t\t\t{\n"); + table.sort(cls.UndocumentedConstants); + for j, cn in ipairs(cls.UndocumentedConstants) do + f:write("\t\t\t\t" .. cn .. " = { Notes = \"\" },\n"); + end -- for j, fn - cls.Undocumented[] + f:write("\t\t\t},\n\n"); + end + + if (HasFunctions or HasConstants) then + f:write("\t\t},\n\n"); + end + end -- for i, cls - API[] + f:write("\t},\n"); + + if (#UndocumentedHooks > 0) then + f:write("\n\tHooks =\n\t{\n"); + for i, hook in ipairs(UndocumentedHooks) do + if (i > 1) then + f:write("\n"); + end + f:write("\t\t" .. hook .. " =\n\t\t{\n"); + f:write("\t\t\tCalledWhen = \"\",\n"); + f:write("\t\t\tDefaultFnName = \"On\", -- also used as pagename\n"); + f:write("\t\t\tDesc = [[\n\t\t\t\t\n\t\t\t]],\n"); + f:write("\t\t\tParams =\n\t\t\t{\n"); + f:write("\t\t\t\t{ Name = \"\", Type = \"\", Notes = \"\" },\n"); + f:write("\t\t\t\t{ Name = \"\", Type = \"\", Notes = \"\" },\n"); + f:write("\t\t\t\t{ Name = \"\", Type = \"\", Notes = \"\" },\n"); + f:write("\t\t\t\t{ Name = \"\", Type = \"\", Notes = \"\" },\n"); + f:write("\t\t\t\t{ Name = \"\", Type = \"\", Notes = \"\" },\n"); + f:write("\t\t\t\t{ Name = \"\", Type = \"\", Notes = \"\" },\n"); + f:write("\t\t\t\t{ Name = \"\", Type = \"\", Notes = \"\" },\n"); + f:write("\t\t\t\t{ Name = \"\", Type = \"\", Notes = \"\" },\n"); + f:write("\t\t\t},\n"); + f:write("\t\t\tReturns = [[\n\t\t\t\t\n\t\t\t]],\n"); + f:write("\t\t}, -- " .. hook .. "\n"); + end + end + f:close(); + end + + -- List the unexported documented API objects: + f = io.open("API/unexported-documented.txt", "w"); + if (f ~= nil) then + for clsname, cls in pairs(g_APIDesc.Classes) do + if not(cls.IsExported) then + -- The whole class is not exported + f:write("class\t" .. clsname .. "\n"); + else + if (cls.Functions ~= nil) then + for fnname, fnapi in pairs(cls.Functions) do + if not(fnapi.IsExported) then + f:write("func\t" .. clsname .. "." .. fnname .. "\n"); + end + end -- for j, fn - cls.Functions[] + end + if (cls.Constants ~= nil) then + for cnname, cnapi in pairs(cls.Constants) do + if not(cnapi.IsExported) then + f:write("const\t" .. clsname .. "." .. cnname .. "\n"); + end + end -- for j, fn - cls.Functions[] + end + end + end -- for i, cls - g_APIDesc.Classes[] + f:close(); + end + + LOG("API subfolder written"); +end + + + + + +function ReadDescriptions(a_API) + -- Returns true if the class of the specified name is to be ignored + local function IsClassIgnored(a_ClsName) + if (g_APIDesc.IgnoreClasses == nil) then + return false; + end + for i, name in ipairs(g_APIDesc.IgnoreClasses) do + if (a_ClsName:match(name)) then + return true; + end + end + return false; + end + + -- Returns true if the function (specified by its fully qualified name) is to be ignored + local function IsFunctionIgnored(a_FnName) + if (g_APIDesc.IgnoreFunctions == nil) then + return false; + end + for i, name in ipairs(g_APIDesc.IgnoreFunctions) do + if (a_FnName:match(name)) then + return true; + end + end + return false; + end + + -- Returns true if the constant (specified by its fully qualified name) is to be ignored + local function IsConstantIgnored(a_CnName) + if (g_APIDesc.IgnoreConstants == nil) then + return false; + end; + for i, name in ipairs(g_APIDesc.IgnoreConstants) do + if (a_CnName:match(name)) then + return true; + end + end + return false; + end + + -- Remove ignored classes from a_API: + local APICopy = {}; + for i, cls in ipairs(a_API) do + if not(IsClassIgnored(cls.Name)) then + table.insert(APICopy, cls); + end + end + for i = 1, #a_API do + a_API[i] = APICopy[i]; + end; + + -- Process the documentation for each class: + for i, cls in ipairs(a_API) do + -- Rename special functions: + for j, fn in ipairs(cls.Functions) do + if (fn.Name == ".call") then + fn.DocID = "constructor"; + fn.Name = "() <i>(constructor)</i>"; + elseif (fn.Name == ".add") then + fn.DocID = "operator_plus"; + fn.Name = "<i>operator +</i>"; + elseif (fn.Name == ".div") then + fn.DocID = "operator_div"; + fn.Name = "<i>operator /</i>"; + elseif (fn.Name == ".mul") then + fn.DocID = "operator_mul"; + fn.Name = "<i>operator *</i>"; + elseif (fn.Name == ".sub") then + fn.DocID = "operator_sub"; + fn.Name = "<i>operator -</i>"; + elseif (fn.Name == ".eq") then + fn.DocID = "operator_eq"; + fn.Name = "<i>operator ==</i>"; + end + end + + local APIDesc = g_APIDesc.Classes[cls.Name]; + if (APIDesc ~= nil) then + APIDesc.IsExported = true; + cls.Desc = APIDesc.Desc; + cls.AdditionalInfo = APIDesc.AdditionalInfo; + + -- Process inheritance: + if (APIDesc.Inherits ~= nil) then + for j, icls in ipairs(a_API) do + if (icls.Name == APIDesc.Inherits) then + table.insert(icls.Descendants, cls); + cls.Inherits = icls; + end + end + end + + cls.UndocumentedFunctions = {}; -- This will contain names of all the functions that are not documented + cls.UndocumentedConstants = {}; -- This will contain names of all the constants that are not documented + + local DoxyFunctions = {}; -- This will contain all the API functions together with their documentation + + local function AddFunction(a_Name, a_Params, a_Return, a_Notes) + table.insert(DoxyFunctions, {Name = a_Name, Params = a_Params, Return = a_Return, Notes = a_Notes}); + end + + if (APIDesc.Functions ~= nil) then + -- Assign function descriptions: + for j, func in ipairs(cls.Functions) do + local FnName = func.DocID or func.Name; + local FnDesc = APIDesc.Functions[FnName]; + if (FnDesc == nil) then + -- No description for this API function + AddFunction(func.Name); + if not(IsFunctionIgnored(cls.Name .. "." .. FnName)) then + table.insert(cls.UndocumentedFunctions, FnName); + end + else + -- Description is available + if (FnDesc[1] == nil) then + -- Single function definition + AddFunction(func.Name, FnDesc.Params, FnDesc.Return, FnDesc.Notes); + else + -- Multiple function overloads + for k, desc in ipairs(FnDesc) do + AddFunction(func.Name, desc.Params, desc.Return, desc.Notes); + end -- for k, desc - FnDesc[] + end + FnDesc.IsExported = true; + end + end -- for j, func + + -- Replace functions with their described and overload-expanded versions: + cls.Functions = DoxyFunctions; + end -- if (APIDesc.Functions ~= nil) + + if (APIDesc.Constants ~= nil) then + -- Assign constant descriptions: + for j, cons in ipairs(cls.Constants) do + local CnDesc = APIDesc.Constants[cons.Name]; + if (CnDesc == nil) then + -- Not documented + if not(IsConstantIgnored(cls.Name .. "." .. cons.Name)) then + table.insert(cls.UndocumentedConstants, cons.Name); + end + else + cons.Notes = CnDesc.Notes; + CnDesc.IsExported = true; + end + end -- for j, cons + end -- if (APIDesc.Constants ~= nil) + else + -- Class is not documented at all, add all its members to Undocumented lists: + cls.UndocumentedFunctions = {}; + cls.UndocumentedConstants = {}; + for j, func in ipairs(cls.Functions) do + local FnName = func.DocID or func.Name; + if not(IsFunctionIgnored(cls.Name .. "." .. FnName)) then + table.insert(cls.UndocumentedFunctions, FnName); + end + end -- for j, func - cls.Functions[] + for j, cons in ipairs(cls.Constants) do + if not(IsConstantIgnored(cls.Name .. "." .. cons.Name)) then + table.insert(cls.UndocumentedConstants, cons.Name); + end + end -- for j, cons - cls.Constants[] + end -- else if (APIDesc ~= nil) + + -- Remove ignored functions: + local NewFunctions = {}; + for j, fn in ipairs(cls.Functions) do + if (not(IsFunctionIgnored(cls.Name .. "." .. fn.Name))) then + table.insert(NewFunctions, fn); + end + end -- for j, fn + cls.Functions = NewFunctions; + + -- Sort the functions (they may have been renamed): + table.sort(cls.Functions, + function(f1, f2) + if (f1.Name == f2.Name) then + -- Same name, either comparing the same function to itself, or two overloads, in which case compare the params + if ((f1.Params == nil) or (f2.Params == nil)) then + return 0; + end + return (f1.Params < f2.Params); + end + return (f1.Name < f2.Name); + end + ); + + -- Sort the constants: + table.sort(cls.Constants, + function(c1, c2) + return (c1.Name < c2.Name); + end + ); + end -- for i, cls + + -- Sort the descendants lists: + for i, cls in ipairs(a_API) do + table.sort(cls.Descendants, + function(c1, c2) + return (c1.Name < c2.Name); + end + ); + end -- for i, cls +end + + + + + +function ReadHooks(a_Hooks) + --[[ + a_Hooks = { + { Name = "HOOK_1"}, + { Name = "HOOK_2"}, + ... + }; + We want to add hook descriptions to each hook in this array + --]] + for i, hook in ipairs(a_Hooks) do + local HookDesc = g_APIDesc.Hooks[hook.Name]; + if (HookDesc ~= nil) then + for key, val in pairs(HookDesc) do + hook[key] = val; + end + end + end -- for i, hook - a_Hooks[] +end + + + + + +-- Make a link out of anything with the special linkifying syntax {{link|title}} +function LinkifyString(a_String) + local txt = a_String:gsub("{{([^|}]*)|([^}]*)}}", "<a href=\"%1.html\">%2</a>") -- {{link|title}} + txt = txt:gsub("{{([^|}]*)}}", "<a href=\"%1.html\">%1</a>") -- {{LinkAndTitle}} + return txt; +end + + + + + +function WriteHtmlClass(a_ClassAPI, a_AllAPI) + local cf, err = io.open("API/" .. a_ClassAPI.Name .. ".html", "w"); + if (cf == nil) then + return; + end + + -- Writes a table containing all functions in the specified list, with an optional "inherited from" header when a_InheritedName is valid + local function WriteFunctions(a_Functions, a_InheritedName) + if (#a_Functions == 0) then + return; + end + + if (a_InheritedName ~= nil) then + cf:write("<h2>Functions inherited from " .. a_InheritedName .. "</h2>"); + end + cf:write("<table><tr><th>Name</th><th>Parameters</th><th>Return value</th><th>Notes</th></tr>\n"); + for i, func in ipairs(a_Functions) do + cf:write("<tr><td>" .. func.Name .. "</td>"); + cf:write("<td>" .. LinkifyString(func.Params or "").. "</td>"); + cf:write("<td>" .. LinkifyString(func.Return or "").. "</td>"); + cf:write("<td>" .. LinkifyString(func.Notes or "") .. "</td></tr>\n"); + end + cf:write("</table>\n"); + end + + local function WriteDescendants(a_Descendants) + if (#a_Descendants == 0) then + return; + end + cf:write("<ul>"); + for i, desc in ipairs(a_Descendants) do + cf:write("<li><a href=\"".. desc.Name .. ".html\">" .. desc.Name .. "</a>"); + WriteDescendants(desc.Descendants); + cf:write("</li>\n"); + end + cf:write("</ul>\n"); + end + + -- Build an array of inherited classes chain: + local InheritanceChain = {}; + local CurrInheritance = a_ClassAPI.Inherits; + while (CurrInheritance ~= nil) do + table.insert(InheritanceChain, CurrInheritance); + CurrInheritance = CurrInheritance.Inherits; + end + + cf:write([[<html><head><title>MCServer API - ]] .. a_ClassAPI.Name .. [[ class</title> + <link rel="stylesheet" type="text/css" href="main.css" /> + <script src="https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js"></script> + <script src="http://google-code-prettify.googlecode.com/svn/trunk/src/lang-lua.js"></script> + </head><body> + <h1>Contents</h1> + <ul> + ]]); + + local HasInheritance = ((#a_ClassAPI.Descendants > 0) or (a_ClassAPI.Inherits ~= nil)); + + -- Write the table of contents: + if (HasInheritance) then + cf:write("<li><a href=\"#inherits\">Inheritance</a></li>\n"); + end + cf:write("<li><a href=\"#constants\">Constants</a></li>\n"); + cf:write("<li><a href=\"#functions\">Functions</a></li>\n"); + if (a_ClassAPI.AdditionalInfo ~= nil) then + for i, additional in ipairs(a_ClassAPI.AdditionalInfo) do + cf:write("<li><a href=\"#additionalinfo_" .. i .. "\">" .. additional.Header .. "</a></li>\n"); + end + end + cf:write("</ul>"); + + -- Write the class description: + cf:write("<a name=\"desc\"><h1>" .. a_ClassAPI.Name .. " class</h1></a>\n"); + if (a_ClassAPI.Desc ~= nil) then + cf:write("<p>"); + cf:write(LinkifyString(a_ClassAPI.Desc)); + cf:write("</p>\n"); + end; + + -- Write the inheritance, if available: + if (HasInheritance) then + cf:write("<a name=\"inherits\"><h1>Inheritance</h1></a>\n"); + if (#InheritanceChain > 0) then + cf:write("<p>This class inherits from the following parent classes:<ul>\n"); + for i, cls in ipairs(InheritanceChain) do + cf:write("<li><a href=\"" .. cls.Name .. ".html\">" .. cls.Name .. "</a></li>\n"); + end + cf:write("</ul></p>\n"); + end + if (#a_ClassAPI.Descendants > 0) then + cf:write("<p>This class has the following descendants:\n"); + WriteDescendants(a_ClassAPI.Descendants); + cf:write("</p>\n"); + end + end + + -- Write the constants: + cf:write("<a name=\"constants\"><h1>Constants</h1></a>\n"); + cf:write("<table><tr><th>Name</th><th>Value</th><th>Notes</th></tr>\n"); + for i, cons in ipairs(a_ClassAPI.Constants) do + cf:write("<tr><td>" .. cons.Name .. "</td>"); + cf:write("<td>" .. cons.Value .. "</td>"); + cf:write("<td>" .. LinkifyString(cons.Notes or "") .. "</td></tr>\n"); + end + cf:write("</table>\n"); + + -- Write the functions, including the inherited ones: + cf:write("<a name=\"functions\"><h1>Functions</h1></a>\n"); + WriteFunctions(a_ClassAPI.Functions, nil); + for i, cls in ipairs(InheritanceChain) do + WriteFunctions(cls.Functions, cls.Name); + end + + -- Write the additional infos: + if (a_ClassAPI.AdditionalInfo ~= nil) then + for i, additional in ipairs(a_ClassAPI.AdditionalInfo) do + cf:write("<a name=\"additionalinfo_" .. i .. "\"><h1>" .. additional.Header .. "</h1></a>\n"); + cf:write(LinkifyString(additional.Contents)); + end + end + + cf:write("</body></html>"); + cf:close(); +end + + + + + +function WriteHtmlHook(a_Hook) + local fnam = "API/" .. a_Hook.DefaultFnName .. ".html"; + local f, error = io.open(fnam, "w"); + if (f == nil) then + LOG("Cannot write \"" .. fnam .. "\": \"" .. error .. "\"."); + return; + end + f:write([[<html><head><title>MCServer API - ]] .. a_Hook.DefaultFnName .. [[ hook</title> + <link rel="stylesheet" type="text/css" href="main.css" /> + <script src="https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js"></script> + <script src="http://google-code-prettify.googlecode.com/svn/trunk/src/lang-lua.js"></script> + </head><body> + <h1>]] .. a_Hook.Name .. [[ hook</h1> + <p> + ]]); + f:write(LinkifyString(a_Hook.Desc)); + f:write("</p><h1>Callback function</h1><p>The default name for the callback function is "); + f:write(a_Hook.DefaultFnName .. ". It has the following signature:"); + f:write("<pre class=\"prettyprint lang-lua\">function " .. a_Hook.DefaultFnName .. "("); + if (a_Hook.Params == nil) then + a_Hook.Params = {}; + end + for i, param in ipairs(a_Hook.Params) do + if (i > 1) then + f:write(", "); + end + f:write(param.Name); + end + f:write(")</pre><p>Parameters:\n<table><tr><th>Name</th><th>Type</th><th>Notes</th></tr>\n"); + for i, param in ipairs(a_Hook.Params) do + f:write("<tr><td>" .. param.Name .. "</td><td>" .. LinkifyString(param.Type) .. "</td><td>" .. LinkifyString(param.Notes) .. "</td></tr>\n"); + end + f:write("</table></p>\n<p>" .. (a_Hook.Returns or "") .. "</p>\n"); + f:write([[<h1>Code examples</h1> + <h2>Registering the callback</h2> +<pre class=\"prettyprint lang-lua\"> +cPluginManager.AddHook(cPluginManager.]] .. a_Hook.Name .. ", My" .. a_Hook.DefaultFnName .. [[); +</pre> + ]]); + local Examples = a_Hook.CodeExamples or {}; + for i, example in ipairs(Examples) do + f:write("<h2>" .. example.Title .. "</h2>\n"); + f:write("<p>" .. example.Desc .. "</p>\n"); + f:write("<pre class=\"prettyprint lang-lua\">" .. example.Code .. "</pre>\n"); + end + f:close(); +end + + + + diff --git a/MCServer/Plugins/Core b/MCServer/Plugins/Core -Subproject e3a45f34303331be77aceacf2ba53e503ad7284 +Subproject 839fb9582b74ef55e596b4e71ddb5663e3ed7c7 diff --git a/MCServer/Plugins/MagicCarpet/coremessaging.lua b/MCServer/Plugins/MagicCarpet/coremessaging.lua new file mode 100644 index 000000000..9fb2c0db1 --- /dev/null +++ b/MCServer/Plugins/MagicCarpet/coremessaging.lua @@ -0,0 +1,19 @@ +Core = cPluginManager:Get():GetPlugin("Core") + +function SendMessage(a_Player, a_Message) + if (Core ~= nil) then + Core:Call("SendMessage", a_Player, a_Message) + end +end + +function SendMessageSuccess(a_Player, a_Message) + if (Core ~= nil) then + Core:Call("SendMessageSuccess", a_Player, a_Message) + end +end + +function SendMessageFailure(a_Player, a_Message) + if (Core ~= nil) then + Core:Call("SendMessageFailure", a_Player, a_Message) + end +end diff --git a/MCServer/Plugins/MagicCarpet/plugin.lua b/MCServer/Plugins/MagicCarpet/plugin.lua index 27dcdf45d..81eb02a9c 100644 --- a/MCServer/Plugins/MagicCarpet/plugin.lua +++ b/MCServer/Plugins/MagicCarpet/plugin.lua @@ -1,18 +1,16 @@ -local PLUGIN = {} local Carpets = {} function Initialize( Plugin ) - PLUGIN = Plugin - Plugin:SetName( "MagicCarpet" ) - Plugin:SetVersion( 1 ) + Plugin:SetVersion( 2 ) cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_MOVING, OnPlayerMoving) cPluginManager.AddHook(cPluginManager.HOOK_DISCONNECT, OnDisconnect) + local PluginManager = cPluginManager:Get() PluginManager:BindCommand("/mc", "magiccarpet", HandleCarpetCommand, " - Spawns a magical carpet"); - LOG( "Initialized " .. Plugin:GetName() .. " v." .. Plugin:GetVersion() ) + LOG( "Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion() ) return true end @@ -33,14 +31,15 @@ end function HandleCarpetCommand( Split, Player ) Carpet = Carpets[ Player ] + if( Carpet == nil ) then Carpets[ Player ] = cCarpet:new() - Player:SendMessage(cChatColor.Green .. "[INFO] " .. cChatColor.White .. "You're on a magic carpet!" ) - Player:SendMessage(cChatColor.Yellow .. "[INFO] " .. cChatColor.White .. "Look straight down to descend. Jump to ascend!" ) + SendMessageSuccess(Player, "You're on a magic carpet!") + SendMessage(Player, "Look straight down to descend. Jump to ascend.") else Carpet:remove() Carpets[ Player ] = nil - Player:SendMessage(cChatColor.Green .. "[INFO] " .. cChatColor.White .. "The carpet vanished!" ) + SendMessageSuccess(Player, "The carpet vanished!") end return true diff --git a/MCServer/Plugins/ProtectionAreas b/MCServer/Plugins/ProtectionAreas -Subproject 3019c7b396221b987cd3f89d422276f764834ff +Subproject bef8ff2a883e98db94f842f9db3d256a039b1fc diff --git a/MCServer/Plugins/TransAPI b/MCServer/Plugins/TransAPI -Subproject 52e1de4332a026e58fda843aae98c1f51e57199 +Subproject 678696eeedce502199869577b8e03ff72892646 diff --git a/MCServer/README.txt b/MCServer/README.txt index cd0467ae1..f2611fd04 100644 --- a/MCServer/README.txt +++ b/MCServer/README.txt @@ -1,18 +1,20 @@ /============================\ | Custom Minecraft Server | -| Created by Kevin Bansberg | +| (MCServer) | +| | +| Founder: Kevin Bansberg | | A.K.A. FakeTruth | +| Lead dev: Mattes D | +| A.K.A. _Xoft(o) | +| A.K.A. xoft | | Monsters by Alex Sonek | | A.K.A. Duralex | -| Stuff by Mattes D | -| A.K.A. _Xoft(o) | |============================| -| Info: WWW.MC-SERVER.ORG | -| WWW.AE-C.NET\n") | -| WWW.RBTHINKTANK.COM | +| Info: www.mc-server.org | +| www.ae-c.net | +| www.rbthinktank.com | | Mail: faketruth@gmail.com | \============================/ - -Compatible clients: 1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2 -Compatible protocol versions: 29, 39, 47, 49, 51, 60, 61, 73, 74 +Compatible clients: 1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4 +Compatible protocol versions: 29, 39, 47, 49, 51, 60, 61, 73, 74, 77, 78 diff --git a/MCServer/groups.example.ini b/MCServer/groups.example.ini index f1a8df7d7..2e4b5ebee 100644 --- a/MCServer/groups.example.ini +++ b/MCServer/groups.example.ini @@ -13,5 +13,5 @@ Color=2 Inherits=Default [Default] -Permissions=core.build,core.help,core.playerlist,core.pluginlist,core.spawn,transapi.setlang +Permissions=core.build,core.help,core.playerlist,core.pluginlist,core.spawn,transapi.setlang,core.tell,core.me Color=7 diff --git a/MCServer/monsters.ini b/MCServer/monsters.ini index 8289e0a0a..efd801a62 100644 --- a/MCServer/monsters.ini +++ b/MCServer/monsters.ini @@ -152,3 +152,33 @@ AttackRate=1 AttackDamage=6.0 SightDistance=25.0 MaxHealth=16 + +[Horse] +AttackRange=5.0 +AttackRate=1 +AttackDamage=6.0 +SightDistance=25.0 +MaxHealth=30 + +[EnderDragon] +AttackRange=5.0 +AttackRate=1 +AttackDamage=6.0 +SightDistance=25.0 +MaxHealth=200 + +[Giant] +AttackRange=5.0 +AttackRate=1 +AttackDamage=6.0 +SightDistance=25.0 +MaxHealth=100 + +[IronGolem] +AttackRange=5.0 +AttackRate=1 +AttackDamage=6.0 +SightDistance=25.0 +MaxHealth=100 + + diff --git a/MCServer/vg b/MCServer/vg new file mode 100644 index 000000000..fcc8270d0 --- /dev/null +++ b/MCServer/vg @@ -0,0 +1,7 @@ +#! /bin/bash + +# This script runs MCServer under valgrind +# It expects valgrind to be normally installed and available +# Note that this is for Linux only and debug-only, since it slows down MCS way too much + +valgrind --log-file=valgrind.log --suppressions=vg.supp --tool=memcheck --leak-check=full --leak-resolution=high --show-reachable=yes --track-origins=yes -v ./MCServer diff --git a/MCServer/vg.supp b/MCServer/vg.supp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/MCServer/vg.supp diff --git a/MCServer/webadmin/template.lua b/MCServer/webadmin/template.lua index 1ab1aab88..a066d8b33 100644 --- a/MCServer/webadmin/template.lua +++ b/MCServer/webadmin/template.lua @@ -58,7 +58,7 @@ function ShowPage(WebAdmin, TemplateRequest) SiteContent = {} local BaseURL = WebAdmin:GetBaseURL(TemplateRequest.Request.Path) local Title = "MCServer" - local MemoryUsage = cWebAdmin:GetMemoryUsage() + local MemoryUsageKiB = cRoot:GetPhysicalRAMUsage() local NumChunks = cRoot:Get():GetTotalChunkCount() local PluginPage = WebAdmin:GetPage(TemplateRequest.Request) local PageContent = PluginPage.Content @@ -456,7 +456,7 @@ function ShowPage(WebAdmin, TemplateRequest) </div> <!-- // #containerHolder --> - <p id="footer">MCServer is using: ]] .. MemoryUsage .. [[MB of memory; Current chunk count: ]] .. NumChunks .. [[ </p> + <p id="footer">MCServer is using: ]] .. MemoryUsageKiB / 1024 .. [[ MiB of memory; Current chunk count: ]] .. NumChunks .. [[ </p> </div> <!-- // #wrapper --> </body> diff --git a/MakeLuaAPI.cmd b/MakeLuaAPI.cmd new file mode 100644 index 000000000..80bb206d4 --- /dev/null +++ b/MakeLuaAPI.cmd @@ -0,0 +1,65 @@ +@echo off +:: MakeLuaAPI.cmd +:: This script is run after the nightbuild to produce the Lua API documentation and upload it to a website. +:: It expects at least three environment variables set: ftpsite, ftpuser and ftppass, specifying the FTP site and login to use for the upload + + + + + +:: Check that we got all the environment vars needed for the upload: + +if "a%ftppass%" == "a" ( + echo You need to set FTP password in the ftppass environment variable to upload the files + goto end +) +if "a%ftpuser%" == "a" ( + echo You need to set FTP username in the ftpuser environment variable to upload the files + goto end +) +if "a%ftpsite%" == "a" ( + echo You need to set FTP server in the ftpsite environment variable to upload the files + goto end +) + + + + + +:: Create the API documentation by running the server and stopping it right after it starts: + +cd MCServer +echo stop | MCServer +cd .. + + + + + +:: Upload the API to the web: + +ncftpput -p %ftppass% -u %ftpuser% -T temp_ %ftpsite% /LuaAPI MCServer/API/*.* +if errorlevel 1 goto haderror +echo Upload finished. + +goto end + + + + + +:haderror +echo an error was encountered, check command output above +pause +goto finished + + + + + +:end +if "a%1" == "a" pause + + + +:finished
\ No newline at end of file diff --git a/Tools/BiomeVisualiser/.gitignore b/Tools/BiomeVisualiser/.gitignore new file mode 100644 index 000000000..b4e15dc3c --- /dev/null +++ b/Tools/BiomeVisualiser/.gitignore @@ -0,0 +1,3 @@ +Debug/ +logs/ +Release/ diff --git a/Tools/BiomeVisualiser/BiomeViewWnd.cpp b/Tools/BiomeVisualiser/BiomeViewWnd.cpp index 3dd1bb4e0..7d4db58c0 100644 --- a/Tools/BiomeVisualiser/BiomeViewWnd.cpp +++ b/Tools/BiomeVisualiser/BiomeViewWnd.cpp @@ -45,7 +45,7 @@ bool cBiomeViewWnd::Create(HWND a_ParentWnd, LPCTSTR a_Title) cIniFile IniFile; cBiomeGen * BioGen = new cBioGenMultiStepMap(2); - BioGen->Initialize(IniFile); + BioGen->InitializeBiomeGen(IniFile); m_Renderer.SetSource(new cGeneratorBiomeSource(BioGen)); return true; diff --git a/Tools/BiomeVisualiser/BiomeVisualiser.cpp b/Tools/BiomeVisualiser/BiomeVisualiser.cpp index e1d379f83..a36111d77 100644 --- a/Tools/BiomeVisualiser/BiomeVisualiser.cpp +++ b/Tools/BiomeVisualiser/BiomeVisualiser.cpp @@ -21,8 +21,8 @@ int WINAPI WinMain(HINSTANCE a_Instance, HINSTANCE a_PrevInstance, LPSTR a_CmdLi -cBiomeVisualiser::cBiomeVisualiser(void) - // : m_Logger(Printf("BiomeVisualiser_%08x", time(NULL))) +cBiomeVisualiser::cBiomeVisualiser(void) : + m_Logger(new cMCLogger(Printf("BiomeVisualiser_%08x.log", time(NULL)))) { } diff --git a/Tools/BiomeVisualiser/BiomeVisualiser.h b/Tools/BiomeVisualiser/BiomeVisualiser.h index 3fa90646b..4f8ce7513 100644 --- a/Tools/BiomeVisualiser/BiomeVisualiser.h +++ b/Tools/BiomeVisualiser/BiomeVisualiser.h @@ -23,7 +23,7 @@ public: protected: cBiomeViewWnd m_MainWnd; - cMCLogger m_Logger; + cMCLogger * m_Logger; } ; diff --git a/Tools/BiomeVisualiser/BiomeVisualiser.vcproj b/Tools/BiomeVisualiser/BiomeVisualiser.vcproj index 522606d60..5f840129d 100644 --- a/Tools/BiomeVisualiser/BiomeVisualiser.vcproj +++ b/Tools/BiomeVisualiser/BiomeVisualiser.vcproj @@ -332,6 +332,22 @@ > </File> <File + RelativePath="..\..\source\Enchantments.cpp" + > + </File> + <File + RelativePath="..\..\source\Enchantments.h" + > + </File> + <File + RelativePath="..\..\source\WorldStorage\FastNBT.cpp" + > + </File> + <File + RelativePath="..\..\source\WorldStorage\FastNBT.h" + > + </File> + <File RelativePath="..\..\source\Globals.cpp" > <FileConfiguration @@ -364,6 +380,10 @@ > </File> <File + RelativePath="..\..\source\Item.h" + > + </File> + <File RelativePath="..\..\source\Log.cpp" > </File> @@ -435,14 +455,6 @@ > </File> <File - RelativePath="..\..\source\OSSupport\MakeDir.cpp" - > - </File> - <File - RelativePath="..\..\source\OSSupport\MakeDir.h" - > - </File> - <File RelativePath="..\..\source\OSSupport\Sleep.h" > </File> diff --git a/Tools/ProtoProxy/.gitignore b/Tools/ProtoProxy/.gitignore index 3097f7aab..2a38341e5 100644 --- a/Tools/ProtoProxy/.gitignore +++ b/Tools/ProtoProxy/.gitignore @@ -1,4 +1,5 @@ Debug Release +Logs/ *.log *.nbt diff --git a/Tools/ProtoProxy/Connection.cpp b/Tools/ProtoProxy/Connection.cpp index fc8fceb99..c4776949e 100644 --- a/Tools/ProtoProxy/Connection.cpp +++ b/Tools/ProtoProxy/Connection.cpp @@ -8,11 +8,21 @@ #include "Server.h" #include <iostream> +#ifdef _WIN32 + #include <direct.h> // For _mkdir() +#endif -#ifdef _DEBUG +/// When defined, the following macro causes a sleep after each parsed packet (DEBUG-mode only) +// #define SLEEP_AFTER_PACKET + + + + + +#if defined(_DEBUG) && defined(SLEEP_AFTER_PACKET) #define DebugSleep Sleep #else #define DebugSleep(X) @@ -179,6 +189,7 @@ enum PACKET_BLOCK_CHANGE = 0x35, PACKET_BLOCK_ACTION = 0x36, PACKET_MAP_CHUNK_BULK = 0x38, + PACKET_EXPLOSION = 0x3c, PACKET_SOUND_EFFECT = 0x3d, PACKET_NAMED_SOUND_EFFECT = 0x3e, PACKET_CHANGE_GAME_STATE = 0x46, @@ -261,7 +272,14 @@ cConnection::cConnection(SOCKET a_ClientSocket, cServer & a_Server) : m_ServerBuffer(1024 KiB), m_HasClientPinged(false) { - Printf(m_LogNameBase, "Log_%d", (int)time(NULL)); + // Create the Logs subfolder, if not already created: + #if defined(_WIN32) + _mkdir("Logs"); + #else + mkdir("Logs", 0777); + #endif + + Printf(m_LogNameBase, "Logs/Log_%d", (int)time(NULL)); AString fnam(m_LogNameBase); fnam.append(".log"); m_LogFile = fopen(fnam.c_str(), "w"); @@ -289,7 +307,7 @@ void cConnection::Run(void) Log("Cannot connect to server; aborting"); return; } - + while (true) { fd_set ReadFDs; @@ -684,6 +702,7 @@ bool cConnection::DecodeServersPackets(const char * a_Data, int a_Size) case PACKET_ENTITY_STATUS: HANDLE_SERVER_READ(HandleServerEntityStatus); break; case PACKET_ENTITY_TELEPORT: HANDLE_SERVER_READ(HandleServerEntityTeleport); break; case PACKET_ENTITY_VELOCITY: HANDLE_SERVER_READ(HandleServerEntityVelocity); break; + case PACKET_EXPLOSION: HANDLE_SERVER_READ(HandleServerExplosion); break; case PACKET_INCREMENT_STATISTIC: HANDLE_SERVER_READ(HandleServerIncrementStatistic); break; case PACKET_KEEPALIVE: HANDLE_SERVER_READ(HandleServerKeepAlive); break; case PACKET_KICK: HANDLE_SERVER_READ(HandleServerKick); break; @@ -1576,6 +1595,49 @@ bool cConnection::HandleServerEntityVelocity(void) +bool cConnection::HandleServerExplosion(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosX); + HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosY); + HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosZ); + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Force); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, NumRecords); + struct sCoords + { + int x, y, z; + + sCoords(int a_X, int a_Y, int a_Z) : x(a_X), y(a_Y), z(a_Z) {} + } ; + std::vector<sCoords> Records; + Records.reserve(NumRecords); + int PosXI = (int)PosX, PosYI = (int)PosY, PosZI = (int)PosZ; + for (int i = 0; i < NumRecords; i++) + { + HANDLE_SERVER_PACKET_READ(ReadChar, char, rx); + HANDLE_SERVER_PACKET_READ(ReadChar, char, ry); + HANDLE_SERVER_PACKET_READ(ReadChar, char, rz); + Records.push_back(sCoords(PosXI + rx, PosYI + ry, PosZI + rz)); + } + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PlayerMotionX); + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PlayerMotionY); + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PlayerMotionZ); + Log("Received a PACKET_EXPLOSION from the server:"); + Log(" Pos = {%.02f, %.02f, %.02f}", PosX, PosY, PosZ); + Log(" Force = %.02f", Force); + Log(" NumRecords = %d", NumRecords); + for (int i = 0; i < NumRecords; i++) + { + Log(" Records[%d] = {%d, %d, %d}", i, Records[i].x, Records[i].y, Records[i].z); + } + Log(" Player motion = <%.02f, %.02f, %.02f>", PlayerMotionX, PlayerMotionY, PlayerMotionZ); + COPY_TO_CLIENT(); + return true; +} + + + + + bool cConnection::HandleServerIncrementStatistic(void) { // 0xc8 @@ -1636,7 +1698,8 @@ bool cConnection::HandleServerKick(void) // Split by NULL chars (StringSplit() won't work here): size_t Last = 0; - for (size_t i = 0; i < Reason.size(); i++) + size_t Len = Reason.size(); + for (size_t i = 0; i < Len; i++) { if (Reason[i] == 0) { @@ -1644,14 +1707,40 @@ bool cConnection::HandleServerKick(void) Last = i + 1; } } + if (Last < Len) + { + Split.push_back(Reason.substr(Last)); + } - if (Split.size() == 5) + if (Split.size() == 6) { - Log(" Protocol version: \"%s\"", Split[0].c_str()); - Log(" Server version: \"%s\"", Split[1].c_str()); - Log(" MOTD: \"%s\"", Split[2].c_str()); - Log(" Cur players: \"%s\"", Split[3].c_str()); - Log(" Max players: \"%s\"", Split[4].c_str()); + Log(" Preamble: \"%s\"", Split[0].c_str()); + Log(" Protocol version: \"%s\"", Split[1].c_str()); + Log(" Server version: \"%s\"", Split[2].c_str()); + Log(" MOTD: \"%s\"", Split[3].c_str()); + Log(" Cur players: \"%s\"", Split[4].c_str()); + Log(" Max players: \"%s\"", Split[5].c_str()); + + // Modify the MOTD to show that it's being ProtoProxied: + Reason.assign(Split[0]); + Reason.push_back(0); + Reason.append(Split[1]); + Reason.push_back(0); + Reason.append(Split[2]); + Reason.push_back(0); + Reason.append(Printf("ProtoProxy: %s", Split[3].c_str())); + Reason.push_back(0); + Reason.append(Split[4]); + Reason.push_back(0); + Reason.append(Split[5]); + AString ReasonBE16; + UTF8ToRawBEUTF16(Reason.data(), Reason.size(), ReasonBE16); + AString PacketStart("\xff"); + PacketStart.push_back((ReasonBE16.size() / 2) / 256); + PacketStart.push_back((ReasonBE16.size() / 2) % 256); + CLIENTSEND(PacketStart.data(), PacketStart.size()); + CLIENTSEND(ReasonBE16.data(), ReasonBE16.size()); + return true; } else { diff --git a/Tools/ProtoProxy/Connection.h b/Tools/ProtoProxy/Connection.h index c30a28727..6093408d6 100644 --- a/Tools/ProtoProxy/Connection.h +++ b/Tools/ProtoProxy/Connection.h @@ -158,6 +158,7 @@ protected: bool HandleServerEntityStatus(void); bool HandleServerEntityTeleport(void); bool HandleServerEntityVelocity(void); + bool HandleServerExplosion(void); bool HandleServerIncrementStatistic(void); bool HandleServerKeepAlive(void); bool HandleServerKick(void); diff --git a/VC2008/MCServer.sln b/VC2008/MCServer.sln index 6466fdddd..aeabaf252 100644 --- a/VC2008/MCServer.sln +++ b/VC2008/MCServer.sln @@ -2,7 +2,6 @@ Microsoft Visual Studio Solution File, Format Version 10.00 # Visual C++ Express 2008 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MCServer", "MCServer.vcproj", "{32012054-0C96-4C43-AB27-174FF8E72D66}" ProjectSection(ProjectDependencies) = postProject - {9A476537-42C0-4848-AB40-15CFE83D17A8} = {9A476537-42C0-4848-AB40-15CFE83D17A8} {082E8185-7B3A-4945-8C82-9132341A329D} = {082E8185-7B3A-4945-8C82-9132341A329D} {5FCFAF8D-FF2C-456D-A72C-1D76F913AD96} = {5FCFAF8D-FF2C-456D-A72C-1D76F913AD96} {3423EC9A-52E4-4A4D-9753-EDEBC38785EF} = {3423EC9A-52E4-4A4D-9753-EDEBC38785EF} @@ -19,8 +18,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Lua", "Lua.vcproj", "{082E8 EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ToLua", "ToLua.vcproj", "{EEAB54AD-114C-4AB8-8482-0A52D502BD35}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WebServer", "WebServer.vcproj", "{9A476537-42C0-4848-AB40-15CFE83D17A8}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CryptoPP", "CryptoPP.vcproj", "{3423EC9A-52E4-4A4D-9753-EDEBC38785EF}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "expat", "expat.vcproj", "{5FCFAF8D-FF2C-456D-A72C-1D76F913AD96}" @@ -73,14 +70,6 @@ Global {EEAB54AD-114C-4AB8-8482-0A52D502BD35}.Release profiled|Win32.Build.0 = Release profiled|Win32 {EEAB54AD-114C-4AB8-8482-0A52D502BD35}.Release|Win32.ActiveCfg = Release|Win32 {EEAB54AD-114C-4AB8-8482-0A52D502BD35}.Release|Win32.Build.0 = Release|Win32 - {9A476537-42C0-4848-AB40-15CFE83D17A8}.Debug profiled|Win32.ActiveCfg = Debug profiled|Win32 - {9A476537-42C0-4848-AB40-15CFE83D17A8}.Debug profiled|Win32.Build.0 = Debug profiled|Win32 - {9A476537-42C0-4848-AB40-15CFE83D17A8}.Debug|Win32.ActiveCfg = Debug|Win32 - {9A476537-42C0-4848-AB40-15CFE83D17A8}.Debug|Win32.Build.0 = Debug|Win32 - {9A476537-42C0-4848-AB40-15CFE83D17A8}.Release profiled|Win32.ActiveCfg = Release profiled|Win32 - {9A476537-42C0-4848-AB40-15CFE83D17A8}.Release profiled|Win32.Build.0 = Release profiled|Win32 - {9A476537-42C0-4848-AB40-15CFE83D17A8}.Release|Win32.ActiveCfg = Release|Win32 - {9A476537-42C0-4848-AB40-15CFE83D17A8}.Release|Win32.Build.0 = Release|Win32 {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Debug profiled|Win32.ActiveCfg = Debug|Win32 {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Debug profiled|Win32.Build.0 = Debug|Win32 {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Debug|Win32.ActiveCfg = Debug|Win32 diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj index 2c1624ded..2e5b71ffe 100644 --- a/VC2008/MCServer.vcproj +++ b/VC2008/MCServer.vcproj @@ -487,10 +487,6 @@ > </File> <File - RelativePath="..\source\Doors.h" - > - </File> - <File RelativePath="..\source\Enchantments.cpp" > </File> @@ -968,6 +964,14 @@ > </File> <File + RelativePath="..\source\Mobs\EnderDragon.cpp" + > + </File> + <File + RelativePath="..\source\Mobs\EnderDragon.h" + > + </File> + <File RelativePath="..\source\Mobs\Enderman.cpp" > </File> @@ -984,11 +988,35 @@ > </File> <File - RelativePath="..\source\Mobs\Magmacube.cpp" + RelativePath="..\source\Mobs\Giant.cpp" + > + </File> + <File + RelativePath="..\source\Mobs\Giant.h" + > + </File> + <File + RelativePath="..\source\Mobs\Horse.cpp" + > + </File> + <File + RelativePath="..\source\Mobs\Horse.h" > </File> <File - RelativePath="..\source\Mobs\Magmacube.h" + RelativePath="..\source\Mobs\IronGolem.cpp" + > + </File> + <File + RelativePath="..\source\Mobs\IronGolem.h" + > + </File> + <File + RelativePath="..\source\Mobs\MagmaCube.cpp" + > + </File> + <File + RelativePath="..\source\Mobs\MagmaCube.h" > </File> <File @@ -1064,6 +1092,14 @@ > </File> <File + RelativePath="..\source\Mobs\SnowGolem.cpp" + > + </File> + <File + RelativePath="..\source\Mobs\SnowGolem.h" + > + </File> + <File RelativePath="..\source\Mobs\Spider.cpp" > </File> @@ -1096,6 +1132,18 @@ > </File> <File + RelativePath="..\source\Mobs\Wither.cpp" + > + </File> + <File + RelativePath="..\source\Mobs\Wither.h" + > + </File> + <File + RelativePath="..\source\Mobs\Wolf.cpp" + > + </File> + <File RelativePath="..\source\Mobs\Wolf.h" > </File> @@ -1108,11 +1156,11 @@ > </File> <File - RelativePath="..\source\Mobs\Zombiepigman.cpp" + RelativePath="..\source\Mobs\ZombiePigman.cpp" > </File> <File - RelativePath="..\source\Mobs\Zombiepigman.h" + RelativePath="..\source\Mobs\ZombiePigman.h" > </File> </Filter> @@ -1120,6 +1168,14 @@ Name="Entities" > <File + RelativePath="..\source\Entities\Boat.cpp" + > + </File> + <File + RelativePath="..\source\Entities\Boat.h" + > + </File> + <File RelativePath="..\source\Entities\Entity.cpp" > </File> @@ -1348,14 +1404,6 @@ > </File> <File - RelativePath="..\source\OSSupport\MakeDir.cpp" - > - </File> - <File - RelativePath="..\source\OSSupport\MakeDir.h" - > - </File> - <File RelativePath="..\source\OSSupport\Semaphore.cpp" > </File> @@ -2000,6 +2048,14 @@ > </File> <File + RelativePath="..\source\Blocks\BlockButton.cpp" + > + </File> + <File + RelativePath="..\source\Blocks\BlockButton.h" + > + </File> + <File RelativePath="..\source\blocks\BlockCactus.h" > </File> @@ -2024,6 +2080,14 @@ > </File> <File + RelativePath="..\source\Blocks\BlockComparator.cpp" + > + </File> + <File + RelativePath="..\source\Blocks\BlockComparator.h" + > + </File> + <File RelativePath="..\source\blocks\BlockCrops.h" > </File> @@ -2156,6 +2220,14 @@ > </File> <File + RelativePath="..\source\blocks\BlockPlanks.h" + > + </File> + <File + RelativePath="..\source\Blocks\BlockPumpkin.h" + > + </File> + <File RelativePath="..\source\Blocks\BlockRail.h" > </File> @@ -2248,6 +2320,10 @@ > </File> <File + RelativePath="..\source\Items\ItemBoat.h" + > + </File> + <File RelativePath="..\source\Items\ItemBow.h" > </File> @@ -2268,6 +2344,10 @@ > </File> <File + RelativePath="..\source\Items\ItemComparator.h" + > + </File> + <File RelativePath="..\source\items\ItemDoor.h" > </File> @@ -2340,10 +2420,6 @@ > </File> <File - RelativePath="..\source\items\ItemSlab.h" - > - </File> - <File RelativePath="..\source\Items\ItemSpawnEgg.h" > </File> @@ -2359,10 +2435,6 @@ RelativePath="..\source\Items\ItemThrowable.h" > </File> - <File - RelativePath="..\source\items\ItemWood.h" - > - </File> </Filter> <Filter Name="Protocol" @@ -2640,6 +2712,66 @@ > </File> </Filter> + <Filter + Name="HTTPServer" + > + <File + RelativePath="..\source\HTTPServer\EnvelopeParser.cpp" + > + </File> + <File + RelativePath="..\source\HTTPServer\EnvelopeParser.h" + > + </File> + <File + RelativePath="..\source\HTTPServer\HTTPConnection.cpp" + > + </File> + <File + RelativePath="..\source\HTTPServer\HTTPConnection.h" + > + </File> + <File + RelativePath="..\source\HTTPServer\HTTPFormParser.cpp" + > + </File> + <File + RelativePath="..\source\HTTPServer\HTTPFormParser.h" + > + </File> + <File + RelativePath="..\source\HTTPServer\HTTPMessage.cpp" + > + </File> + <File + RelativePath="..\source\HTTPServer\HTTPMessage.h" + > + </File> + <File + RelativePath="..\source\HTTPServer\HTTPServer.cpp" + > + </File> + <File + RelativePath="..\source\HTTPServer\HTTPServer.h" + > + </File> + <File + RelativePath="..\source\HTTPServer\MultipartParser.cpp" + > + </File> + <File + RelativePath="..\source\HTTPServer\MultipartParser.h" + > + </File> + <File + RelativePath="..\source\HTTPServer\NameValueParser.cpp" + > + </File> + <File + RelativePath="..\source\HTTPServer\NameValueParser.h" + > + </File> + </Filter> </Filter> <Filter Name="Config files" @@ -2868,6 +3000,14 @@ > </File> </Filter> + <Filter + Name="APIDump" + > + <File + RelativePath="..\MCServer\Plugins\APIDump\main.lua" + > + </File> + </Filter> </Filter> </Files> <Globals> diff --git a/VC2008/WebServer.cbp b/VC2008/WebServer.cbp deleted file mode 100644 index 41fb405c2..000000000 --- a/VC2008/WebServer.cbp +++ /dev/null @@ -1,77 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> -<CodeBlocks_project_file> - <FileVersion major="1" minor="6" /> - <Project> - <Option title="WebServer" /> - <Option pch_mode="2" /> - <Option compiler="gcc" /> - <Build> - <Target title="Debug Win32"> - <Option output="lib" prefix_auto="1" extension_auto="1" /> - <Option working_dir="" /> - <Option object_output="Debug Win32/webserver" /> - <Option type="2" /> - <Option compiler="gcc" /> - <Option createDefFile="1" /> - <Compiler> - <Add option="-DWIN32" /> - <Add option="-D_DEBUG" /> - <Add option="-D_LIB" /> - <Add option="-W" /> - <Add option="-g" /> - <Add option="-O0" /> - </Compiler> - </Target> - <Target title="Release Win32"> - <Option output="lib" prefix_auto="1" extension_auto="1" /> - <Option working_dir="" /> - <Option object_output="Release Win32/webserver" /> - <Option type="2" /> - <Option compiler="gcc" /> - <Option createDefFile="1" /> - <Compiler> - <Add option="-DWIN32" /> - <Add option="-DNDEBUG" /> - <Add option="-D_LIB" /> - <Add option="-W" /> - <Add option="-O2" /> - </Compiler> - </Target> - <Target title="Release profiled Win32"> - <Option output="lib" prefix_auto="1" extension_auto="1" /> - <Option working_dir="" /> - <Option object_output="Release profiled Win32/webserver" /> - <Option type="2" /> - <Option compiler="gcc" /> - <Option createDefFile="1" /> - <Compiler> - <Add option="-DWIN32" /> - <Add option="-DNDEBUG" /> - <Add option="-D_LIB" /> - <Add option="-W" /> - <Add option="-O2" /> - </Compiler> - </Target> - </Build> - <Unit filename="../WebServer/Events.cpp" /> - <Unit filename="../WebServer/Events.h" /> - <Unit filename="../WebServer/Globals.cpp" /> - <Unit filename="../WebServer/Globals.h" /> - <Unit filename="../WebServer/Socket.cpp" /> - <Unit filename="../WebServer/Socket.h" /> - <Unit filename="../WebServer/StdHelpers.cpp" /> - <Unit filename="../WebServer/StdHelpers.h" /> - <Unit filename="../WebServer/Tracer.h" /> - <Unit filename="../WebServer/UrlHelper.cpp" /> - <Unit filename="../WebServer/UrlHelper.h" /> - <Unit filename="../WebServer/WebServer.cpp" /> - <Unit filename="../WebServer/WebServer.h" /> - <Unit filename="../WebServer/base64.cpp" /> - <Unit filename="../WebServer/base64.h" /> - <Extensions> - <code_completion /> - <envvars /> - <debugger /> - </Extensions> - </Project> -</CodeBlocks_project_file> diff --git a/VC2008/WebServer.vcproj b/VC2008/WebServer.vcproj deleted file mode 100644 index 601d2f554..000000000 --- a/VC2008/WebServer.vcproj +++ /dev/null @@ -1,374 +0,0 @@ -<?xml version="1.0" encoding="windows-1250"?> -<VisualStudioProject - ProjectType="Visual C++" - Version="9,00" - Name="WebServer" - ProjectGUID="{9A476537-42C0-4848-AB40-15CFE83D17A8}" - RootNamespace="WebServer" - Keyword="Win32Proj" - TargetFrameworkVersion="196613" - > - <Platforms> - <Platform - Name="Win32" - /> - </Platforms> - <ToolFiles> - </ToolFiles> - <Configurations> - <Configuration - Name="Debug|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)\WebServer" - IntermediateDirectory="$(ConfigurationName)\webserver" - ConfigurationType="4" - CharacterSet="1" - > - <Tool - Name="VCPreBuildEventTool" - /> - <Tool - Name="VCCustomBuildTool" - /> - <Tool - Name="VCXMLDataGeneratorTool" - /> - <Tool - Name="VCWebServiceProxyGeneratorTool" - /> - <Tool - Name="VCMIDLTool" - /> - <Tool - Name="VCCLCompilerTool" - Optimization="0" - PreprocessorDefinitions="WIN32;_DEBUG;_LIB" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" - UsePrecompiledHeader="2" - PrecompiledHeaderThrough="Globals.h" - WarningLevel="3" - DebugInformationFormat="4" - /> - <Tool - Name="VCManagedResourceCompilerTool" - /> - <Tool - Name="VCResourceCompilerTool" - /> - <Tool - Name="VCPreLinkEventTool" - /> - <Tool - Name="VCLibrarianTool" - /> - <Tool - Name="VCALinkTool" - /> - <Tool - Name="VCXDCMakeTool" - /> - <Tool - Name="VCBscMakeTool" - /> - <Tool - Name="VCFxCopTool" - /> - <Tool - Name="VCPostBuildEventTool" - /> - </Configuration> - <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)\WebServer" - IntermediateDirectory="$(ConfigurationName)\webserver" - ConfigurationType="4" - CharacterSet="1" - WholeProgramOptimization="1" - > - <Tool - Name="VCPreBuildEventTool" - /> - <Tool - Name="VCCustomBuildTool" - /> - <Tool - Name="VCXMLDataGeneratorTool" - /> - <Tool - Name="VCWebServiceProxyGeneratorTool" - /> - <Tool - Name="VCMIDLTool" - /> - <Tool - Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" - PreprocessorDefinitions="WIN32;NDEBUG;_LIB" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" - UsePrecompiledHeader="2" - PrecompiledHeaderThrough="Globals.h" - WarningLevel="3" - DebugInformationFormat="3" - /> - <Tool - Name="VCManagedResourceCompilerTool" - /> - <Tool - Name="VCResourceCompilerTool" - /> - <Tool - Name="VCPreLinkEventTool" - /> - <Tool - Name="VCLibrarianTool" - /> - <Tool - Name="VCALinkTool" - /> - <Tool - Name="VCXDCMakeTool" - /> - <Tool - Name="VCBscMakeTool" - /> - <Tool - Name="VCFxCopTool" - /> - <Tool - Name="VCPostBuildEventTool" - /> - </Configuration> - <Configuration - Name="Release profiled|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)\webserver" - IntermediateDirectory="$(ConfigurationName)\webserver" - ConfigurationType="4" - CharacterSet="1" - WholeProgramOptimization="1" - > - <Tool - Name="VCPreBuildEventTool" - /> - <Tool - Name="VCCustomBuildTool" - /> - <Tool - Name="VCXMLDataGeneratorTool" - /> - <Tool - Name="VCWebServiceProxyGeneratorTool" - /> - <Tool - Name="VCMIDLTool" - /> - <Tool - Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" - PreprocessorDefinitions="WIN32;NDEBUG;_LIB" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" - UsePrecompiledHeader="2" - PrecompiledHeaderThrough="Globals.h" - WarningLevel="3" - DebugInformationFormat="3" - /> - <Tool - Name="VCManagedResourceCompilerTool" - /> - <Tool - Name="VCResourceCompilerTool" - /> - <Tool - Name="VCPreLinkEventTool" - /> - <Tool - Name="VCLibrarianTool" - /> - <Tool - Name="VCALinkTool" - /> - <Tool - Name="VCXDCMakeTool" - /> - <Tool - Name="VCBscMakeTool" - /> - <Tool - Name="VCFxCopTool" - /> - <Tool - Name="VCPostBuildEventTool" - /> - </Configuration> - <Configuration - Name="Debug profiled|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)\WebServer" - IntermediateDirectory="$(ConfigurationName)\webserver" - ConfigurationType="4" - CharacterSet="1" - > - <Tool - Name="VCPreBuildEventTool" - /> - <Tool - Name="VCCustomBuildTool" - /> - <Tool - Name="VCXMLDataGeneratorTool" - /> - <Tool - Name="VCWebServiceProxyGeneratorTool" - /> - <Tool - Name="VCMIDLTool" - /> - <Tool - Name="VCCLCompilerTool" - Optimization="0" - PreprocessorDefinitions="WIN32;_DEBUG;_LIB" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" - UsePrecompiledHeader="2" - PrecompiledHeaderThrough="Globals.h" - WarningLevel="3" - DebugInformationFormat="4" - /> - <Tool - Name="VCManagedResourceCompilerTool" - /> - <Tool - Name="VCResourceCompilerTool" - /> - <Tool - Name="VCPreLinkEventTool" - /> - <Tool - Name="VCLibrarianTool" - /> - <Tool - Name="VCALinkTool" - /> - <Tool - Name="VCXDCMakeTool" - /> - <Tool - Name="VCBscMakeTool" - /> - <Tool - Name="VCFxCopTool" - /> - <Tool - Name="VCPostBuildEventTool" - /> - </Configuration> - </Configurations> - <References> - </References> - <Files> - <Filter - Name="Source Files" - Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" - UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" - > - <File - RelativePath="..\WebServer\base64.cpp" - > - </File> - <File - RelativePath="..\WebServer\base64.h" - > - </File> - <File - RelativePath="..\WebServer\Events.cpp" - > - </File> - <File - RelativePath="..\WebServer\Events.h" - > - </File> - <File - RelativePath="..\WebServer\Globals.cpp" - > - <FileConfiguration - Name="Debug|Win32" - > - <Tool - Name="VCCLCompilerTool" - UsePrecompiledHeader="1" - /> - </FileConfiguration> - <FileConfiguration - Name="Release|Win32" - > - <Tool - Name="VCCLCompilerTool" - UsePrecompiledHeader="1" - /> - </FileConfiguration> - <FileConfiguration - Name="Release profiled|Win32" - > - <Tool - Name="VCCLCompilerTool" - UsePrecompiledHeader="1" - /> - </FileConfiguration> - <FileConfiguration - Name="Debug profiled|Win32" - > - <Tool - Name="VCCLCompilerTool" - UsePrecompiledHeader="1" - /> - </FileConfiguration> - </File> - <File - RelativePath="..\WebServer\Globals.h" - > - </File> - <File - RelativePath="..\WebServer\Socket.cpp" - > - </File> - <File - RelativePath="..\WebServer\Socket.h" - > - </File> - <File - RelativePath="..\WebServer\StdHelpers.cpp" - > - </File> - <File - RelativePath="..\WebServer\StdHelpers.h" - > - </File> - <File - RelativePath="..\WebServer\Tracer.h" - > - </File> - <File - RelativePath="..\WebServer\UrlHelper.cpp" - > - </File> - <File - RelativePath="..\WebServer\UrlHelper.h" - > - </File> - <File - RelativePath="..\WebServer\WebServer.cpp" - > - </File> - <File - RelativePath="..\WebServer\WebServer.h" - > - </File> - </Filter> - </Files> - <Globals> - </Globals> -</VisualStudioProject> diff --git a/WebServer/Events.cpp b/WebServer/Events.cpp deleted file mode 100644 index 30eb731f9..000000000 --- a/WebServer/Events.cpp +++ /dev/null @@ -1,125 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Events.h" - - - - - -cEvents::cEvents( unsigned int a_NumEvents /* = 1 */ ) - : m_NumEvents( a_NumEvents ) -#ifndef _WIN32 - , m_bNamed( false ) -#endif -{ - if( m_NumEvents < 1 ) m_NumEvents = 1; - -#ifdef _WIN32 - m_Handle = new HANDLE[ m_NumEvents ]; - for( unsigned int i = 0; i < m_NumEvents; i++) - { - ((HANDLE*)m_Handle)[i] = CreateEvent( 0, FALSE, FALSE, 0 ); - } -#else - m_Handle = new sem_t*[ m_NumEvents ]; - for( unsigned int i = 0; i < m_NumEvents; i++) - { - - sem_t* & HandlePtr = ((sem_t**)m_Handle)[i]; - HandlePtr = new sem_t; - - if( sem_init( HandlePtr, 0, 0 ) ) - { - LOG("WARNING cEvents: Could not create unnamed semaphore, fallback to named."); - m_bNamed = true; - delete HandlePtr; // named semaphores return their own address - - char c_Str[32]; - sprintf( c_Str, "cEvents%p", &HandlePtr ); - HandlePtr = sem_open( c_Str, O_CREAT, 777, 0 ); - if( HandlePtr == SEM_FAILED ) - LOG("ERROR: Could not create Event. (%i)", errno); - else - if( sem_unlink( c_Str ) != 0 ) - LOG("ERROR: Could not unlink cEvents. (%i)", errno); - } - } -#endif -} - - - - - -cEvents::~cEvents() -{ -#ifdef _WIN32 - for( unsigned int i = 0; i < m_NumEvents; i++ ) - { - CloseHandle( ((HANDLE*)m_Handle)[i] ); - } - delete [] (HANDLE*)m_Handle; -#else - for( unsigned int i = 0; i < m_NumEvents; i++ ) - { - if( m_bNamed ) - { - sem_t* & HandlePtr = ((sem_t**)m_Handle)[i]; - char c_Str[32]; - sprintf( c_Str, "cEvents%p", &HandlePtr ); - // LOG("Closing event: %s", c_Str ); - // LOG("Sem ptr: %p", HandlePtr ); - if( sem_close( HandlePtr ) != 0 ) - { - LOG("ERROR: Could not close cEvents. (%i)", errno); - } - } - else - { - sem_destroy( ((sem_t**)m_Handle)[i] ); - delete ((sem_t**)m_Handle)[i]; - } - } - delete [] (sem_t**)m_Handle; m_Handle = 0; -#endif -} - - - - - -void cEvents::Wait() -{ -#ifdef _WIN32 - WaitForMultipleObjects( m_NumEvents, (HANDLE*)m_Handle, true, INFINITE ); -#else - for(unsigned int i = 0; i < m_NumEvents; i++) - { - if( sem_wait( ((sem_t**)m_Handle)[i] ) != 0 ) - { - LOG("ERROR: Could not wait for cEvents. (%i)", errno); - } - } -#endif -} - - - - - -void cEvents::Set(unsigned int a_EventNum /* = 0 */) -{ -#ifdef _WIN32 - SetEvent( ((HANDLE*)m_Handle)[a_EventNum] ); -#else - if( sem_post( ((sem_t**)m_Handle)[a_EventNum] ) != 0 ) - { - LOG("ERROR: Could not set cEvents. (%i)", errno); - } -#endif -} - - - - diff --git a/WebServer/Events.h b/WebServer/Events.h deleted file mode 100644 index 352dc4056..000000000 --- a/WebServer/Events.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -class cEvents -{ -public: - cEvents( unsigned int a_NumEvents = 1 ); - ~cEvents(); - - void Wait(); - void Set(unsigned int a_EventNum = 0); -private: - unsigned int m_NumEvents; - void* m_Handle; // HANDLE[] pointer - -#ifndef _WIN32 - bool m_bNamed; -#endif -}; diff --git a/WebServer/Globals.cpp b/WebServer/Globals.cpp deleted file mode 100644 index 13c6ae709..000000000 --- a/WebServer/Globals.cpp +++ /dev/null @@ -1,10 +0,0 @@ - -// Globals.cpp - -// This file is used for precompiled header generation in MSVC environments - -#include "Globals.h" - - - - diff --git a/WebServer/Globals.h b/WebServer/Globals.h deleted file mode 100644 index d60f34720..000000000 --- a/WebServer/Globals.h +++ /dev/null @@ -1,15 +0,0 @@ - -// Globals.h - -// This file gets included from every module in the project, so that global symbols may be introduced easily -// Also used for precompiled header generation in MSVC environments - - - - - -#include "../source/Globals.h" - - - - diff --git a/WebServer/Socket.cpp b/WebServer/Socket.cpp deleted file mode 100644 index 82c76a8f1..000000000 --- a/WebServer/Socket.cpp +++ /dev/null @@ -1,376 +0,0 @@ -/* - Socket.cpp - - Copyright (C) 2002-2004 René Nyffenegger - - This source code is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this source code must not be misrepresented; you must not - claim that you wrote the original source code. If you use this source code - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original source code. - - 3. This notice may not be removed or altered from any source distribution. - - René Nyffenegger rene.nyffenegger@adp-gmbh.ch -*/ - -/* - Note on point 2: - THIS IS NOT THE ORIGINAL SOURCE1!!1!!!~!!~`1ONE!!`1 -*/ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Socket.h" -#include <iostream> - -#ifndef _WIN32 - #include <cstring> - #include <sys/time.h> - #include <unistd.h> - #define SD_SEND 0x01 -#else - #define MSG_NOSIGNAL (0) -#endif - -#ifdef __MAC_NA - #define MSG_NOSIGNAL (0) -#endif - -#ifndef MSG_NOSIGNAL - #define MSG_NOSIGNAL 0 -#endif // MSG_NOSIGNAL - -using namespace std; - -int Socket::nofSockets_= 0; - - - - - -void Socket::Start() -{ - #ifdef _WIN32 - if (!nofSockets_) - { - WSADATA info; - if (WSAStartup(MAKEWORD(2,0), &info)) - { - throw "Could not start WSA"; - } - } - #endif - ++nofSockets_; -} - - - - - -void Socket::End() -{ -#ifdef _WIN32 - WSACleanup(); -#endif -} - - - - - -Socket::Socket() : - s_(INVALID_SOCKET) -{ - Start(); - // UDP: use SOCK_DGRAM instead of SOCK_STREAM - s_ = socket(AF_INET,SOCK_STREAM,0); - - if (!IsValid()) - { -#if !defined(ANDROID_NDK) - throw "INVALID_SOCKET"; -#endif - } - - refCounter_ = new int(1); -} - - - - - -Socket::Socket(SOCKET s) : s_(s) -{ - Start(); - refCounter_ = new int(1); -}; - - - - - -Socket::~Socket() -{ - if (! --(*refCounter_)) - { - Close(); - delete refCounter_; - } - - --nofSockets_; - if (!nofSockets_) - { - End(); - } -} - - - - - -Socket::Socket(const Socket& o) -{ - refCounter_=o.refCounter_; - (*refCounter_)++; - s_ =o.s_; - - nofSockets_++; -} - - - - - -Socket& Socket::operator=(Socket& o) -{ - (*o.refCounter_)++; - - refCounter_ = o.refCounter_; - s_ = o.s_; - - nofSockets_++; - - return *this; -} - - - - - -bool Socket::IsValid(void) const -{ - #ifdef _WIN32 - return (s_ != INVALID_SOCKET); - #else - return (s_ >= 0); - #endif -} - - - - - -void Socket::Close( bool a_WaitSend /* = false */ ) -{ - if (IsValid()) - { - if (a_WaitSend) - { - assert( shutdown(s_, SD_SEND ) == 0 ); - char c; - while( recv(s_, &c, 1, 0 ) > 0 ) - {} - } - - #ifndef _WIN32 - // Linux needs to shut the socket down first, otherwise the socket keeps blocking accept() and select() calls! - // Ref.: http://forum.mc-server.org/showthread.php?tid=344 - if (shutdown(s_, SHUT_RDWR) != 0) - { - LOGWARN("Error on shutting down socket %d", s_); - } - #endif // _WIN32 - - closesocket(s_); - - s_ = INVALID_SOCKET; - } -} - - - - - -std::string Socket::ReceiveLine() -{ - std::string ret; - while (1) - { - char r; - - if (recv(s_, &r, 1, 0) <= 0 ) - { - return ""; - } - ret += r; - if (r == '\n') return ret; - } -} - - - - - -std::string Socket::ReceiveBytes( unsigned int a_Length ) -{ - std::string ret; - while( ret.size() < a_Length ) - { - char r; - - if (recv(s_, &r, 1, 0) <= 0 ) - { - return ""; - } - ret += r; - } - return ret; -} - - - - - -void Socket::SendLine(std::string s) -{ - s += '\n'; - if( send(s_,s.c_str(),s.length(),MSG_NOSIGNAL) <= 0 ) - { - //printf("SendLine Socket error!! \n" ); - Close(); - } -} - - - - - -void Socket::SendBytes(const std::string& s) -{ - if( send(s_,s.c_str(),s.length(), MSG_NOSIGNAL) <= 0 ) - { - //printf("SendBytes Socket error!! \n" ); - Close(); - } -} - - - - - -SocketServer::SocketServer(int port, int connections, TypeSocket type) -{ - sockaddr_in sa; - - memset(&sa, 0, sizeof(sa)); - - sa.sin_family = PF_INET; - sa.sin_port = htons(port); - s_ = socket(AF_INET, SOCK_STREAM, 0); - - if (!IsValid()) - { - LOG("WebServer: INVALID_SOCKET"); - } - - if(type==NonBlockingSocket) - { - return; - } - - #ifdef _WIN32 - char yes = 1; - #else - int yes = 1; - #endif - - if (setsockopt(s_,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) - { - LOG("WebServer: setsockopt == -1"); - return; - } - - /* bind the socket to the internet address */ - if (bind(s_, (sockaddr *)&sa, sizeof(sockaddr_in)) == SOCKET_ERROR) - { - closesocket(s_); - LOG("WebServer: INVALID_SOCKET"); - } - - listen(s_, connections); -} - - - - - -Socket* SocketServer::Accept() -{ - Socket * r = new Socket(accept(s_, 0, 0)); - if (!r->IsValid()) - { - delete r; - r = NULL; - } - - return r; -} - - - - - -SocketSelect::SocketSelect(Socket const * const s1, Socket const * const s2, TypeSocket type) -{ - FD_ZERO(&fds_); - SOCKET Highest = s1->s_; - FD_SET(const_cast<Socket*>(s1)->s_,&fds_); - if(s2) - { - FD_SET(const_cast<Socket*>(s2)->s_,&fds_); - if (s2->s_ > Highest) - { - Highest = s2->s_; - } - } - if (select(Highest + 1, &fds_, NULL, NULL, NULL) == SOCKET_ERROR) - { -#if !defined(ANDROID_NDK) - throw "Error in select"; -#endif - } -} - - - - - -bool SocketSelect::Readable(Socket const* const s) -{ - return (FD_ISSET(s->s_,&fds_) != 0); -} - - - - diff --git a/WebServer/Socket.h b/WebServer/Socket.h deleted file mode 100644 index b144659c7..000000000 --- a/WebServer/Socket.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - Socket.h - - Copyright (C) 2002-2004 René Nyffenegger - - This source code is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this source code must not be misrepresented; you must not - claim that you wrote the original source code. If you use this source code - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original source code. - - 3. This notice may not be removed or altered from any source distribution. - - René Nyffenegger rene.nyffenegger@adp-gmbh.ch -*/ - -/* - Note on point 2: - THIS IS NOT THE ORIGINAL SOURCE1!!1!!!~!!~`1ONE!!`1 -*/ - -#ifndef SOCKET_H -#define SOCKET_H - - - - - -#ifndef _WIN32 - typedef int SOCKET; - #define SOCKET_ERROR (-1) - #define INVALID_SOCKET (-1) - #define closesocket close -#endif // !_WIN32 - - - - - -enum TypeSocket {BlockingSocket, NonBlockingSocket}; - - - - - -class Socket -{ -public: - - virtual ~Socket(); - Socket(const Socket&); - Socket& operator=(Socket&); - - std::string ReceiveLine(); - std::string ReceiveBytes( unsigned int a_Length ); - - bool IsValid(void) const; - - void Close( bool a_WaitSend = false ); - - // The parameter of SendLine is not a const reference - // because SendLine modifes the std::string passed. - void SendLine (std::string); - - // The parameter of SendBytes is a const reference - // because SendBytes does not modify the std::string passed - // (in contrast to SendLine). - void SendBytes(const std::string&); - -protected: - friend class SocketServer; - friend class SocketSelect; - - Socket(SOCKET s); - Socket(); - - - SOCKET s_; - - int* refCounter_; - -private: - static void Start(); - static void End(); - static int nofSockets_; -}; - - - - - -class SocketServer : - public Socket -{ -public: - SocketServer(int port, int connections, TypeSocket type=BlockingSocket); - - Socket* Accept(); -}; - - - - - -// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/wsapiref_2tiq.asp -class SocketSelect -{ -public: - SocketSelect(Socket const * const s1, Socket const * const s2=NULL, TypeSocket type=BlockingSocket); - - bool Readable(Socket const * const s); - -private: - fd_set fds_; -}; - -#endif diff --git a/WebServer/StdHelpers.cpp b/WebServer/StdHelpers.cpp deleted file mode 100644 index 63e48a7d2..000000000 --- a/WebServer/StdHelpers.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - stdHelpers.cpp - - Copyright (C) 2002-2004 René Nyffenegger - - This source code is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this source code must not be misrepresented; you must not - claim that you wrote the original source code. If you use this source code - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original source code. - - 3. This notice may not be removed or altered from any source distribution. - - René Nyffenegger rene.nyffenegger@adp-gmbh.ch -*/ - -/* - Note on point 2: - THIS IS NOT THE ORIGINAL SOURCE1!!1!!!~!!~`1ONE!!`1 -*/ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "StdHelpers.h" -#include <cctype> - -std::string ReplaceInStr(const std::string& in, const std::string& search_for, const std::string& replace_with) { - std::string ret = in; - - std::string::size_type pos = ret.find(search_for); - - while (pos != std::string::npos) { - ret = ret.replace(pos, search_for.size(), replace_with); - pos = pos - search_for.size() + replace_with.size() + 1; - pos = ret.find(search_for, pos); - } - - return ret; -} - -// std:toupper and std::tolower are overloaded. Well... -// http://gcc.gnu.org/ml/libstdc++/2002-11/msg00180.html -char toLower_ (char c) { return std::tolower(c); } -char toUpper_ (char c) { return std::toupper(c); } - -void ToUpper(std::string& s) { - std::transform(s.begin(), s.end(), s.begin(),toUpper_); -} - -void ToLower(std::string& s) { - std::transform(s.begin(), s.end(), s.begin(),toLower_); -} diff --git a/WebServer/StdHelpers.h b/WebServer/StdHelpers.h deleted file mode 100644 index 1011881f8..000000000 --- a/WebServer/StdHelpers.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - stdHelpers.h - - Copyright (C) 2002-2005 René Nyffenegger - - This source code is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this source code must not be misrepresented; you must not - claim that you wrote the original source code. If you use this source code - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original source code. - - 3. This notice may not be removed or altered from any source distribution. - - René Nyffenegger rene.nyffenegger@adp-gmbh.ch -*/ - -/* - Note on point 2: - THIS IS NOT THE ORIGINAL SOURCE1!!1!!!~!!~`1ONE!!`1 -*/ - -#ifndef STDHELPERS_H__ -#define STDHELPERS_H__ - -#include <string> -#include <sstream> - -std::string ReplaceInStr(const std::string& in, const std::string& search_for, const std::string& replace_with); - -void ToUpper(std::string& s); -void ToLower(std::string& s); - -template <class T> -T To(std::string const& s) { - T ret; - - std::stringstream stream; - stream << s; - stream >> ret; - - return ret; -} - -template<class T> -std::string StringFrom(T const& t) { - std::string ret; - - std::stringstream stream; - stream << t; - stream >> ret; - - return ret; -} - -#endif
\ No newline at end of file diff --git a/WebServer/Tracer.h b/WebServer/Tracer.h deleted file mode 100644 index c13d5128f..000000000 --- a/WebServer/Tracer.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - Tracer.h - - Copyright (C) 2002-2004 René Nyffenegger - - This source code is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this source code must not be misrepresented; you must not - claim that you wrote the original source code. If you use this source code - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original source code. - - 3. This notice may not be removed or altered from any source distribution. - - The most current version of Tracer.h can be found at - http://www.adp-gmbh.ch/cpp/common/Tracer.html - - René Nyffenegger rene.nyffenegger@adp-gmbh.ch -*/ - -/* - Note on point 2: - THIS IS NOT THE ORIGINAL SOURCE1!!1!!!~!!~`1ONE!!`1 -*/ - -#ifndef TRACER_H__ -#define TRACER_H__ - -#ifdef DO_TRACE - -#include <string> -#include <sstream> - -#include <windows.h> - -#define StartTrace(x) TraceFunc_::StartTrace_(x) -#define Trace(x) dummy_____for_trace_func______.Trace_(x) -#define Trace2(x,y) dummy_____for_trace_func______.Trace_(x,y) -#define TraceFunc(x) TraceFunc_ dummy_____for_trace_func______(x) -#define TraceFunc2(x,y) TraceFunc_ dummy_____for_trace_func______(x,y) - -class TraceFunc_ { - std::string func_name_; - public: - /* - Calling TraceFunc_ indents the output until the enclosing scope ( {...} ) - is left. - */ - TraceFunc_(std::string const&); - TraceFunc_(std::string const&, std::string const& something); - ~TraceFunc_(); - - /* - Creates the trace output file named by filename. - Must be called before any other tracing function. - Use the StartTrace macro for it. - */ - static void StartTrace_(std::string const& filename); - - template <class T> - void Trace_(T const& t) { - DWORD d; - std::string indent_s; - std::stringstream s; - - s << t; - - for (int i=0; i< indent; i++) indent_s += " "; - - ::WriteFile(trace_file_,indent_s.c_str(), indent_s.size(), &d, 0); - ::WriteFile(trace_file_, s.str().c_str(), s.str().size() ,&d, 0); - ::WriteFile(trace_file_,"\x0a",1,&d,0); - } - - template <class T, class U> - void Trace_(T const& t, U const& u) { - std::string indent_s; - std::stringstream s; - - s << t; - s << u; - Trace_(s.str()); - } - - static int indent; - /* trace_file_ is a HANDLE for the file in which the traced output goes. - The file is opened (that is, created) by calling StartTrace_. - Better yet, use the StartTrace macro - to create the file. - */ - static HANDLE trace_file_; -}; - -#else - -#define StartTrace(x) -#define Trace(x) -#define Trace2(x,y) -#define TraceFunc(x) -#define TraceFunc2(x,y) - -#endif - -#endif // TRACER_H__ diff --git a/WebServer/UrlHelper.cpp b/WebServer/UrlHelper.cpp deleted file mode 100644 index 3ed5660b0..000000000 --- a/WebServer/UrlHelper.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - UrlHelper.cpp - - Copyright (C) 2002-2004 René Nyffenegger - - This source code is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this source code must not be misrepresented; you must not - claim that you wrote the original source code. If you use this source code - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original source code. - - 3. This notice may not be removed or altered from any source distribution. - - René Nyffenegger rene.nyffenegger@adp-gmbh.ch -*/ - -/* - Note on point 2: - THIS IS NOT THE ORIGINAL SOURCE1!!1!!!~!!~`1ONE!!`1 -*/ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "UrlHelper.h" -#include "Tracer.h" -#include "StdHelpers.h" - -#include <sstream> -#include <iostream> - -bool RemoveProtocolFromUrl(std::string const& url, std::string& protocol, std::string& rest) { - TraceFunc("RemoveProtocolFromUrl"); - Trace(std::string("url='")+url+"'"); - std::string::size_type pos_colon = url.find(":"); - - if (pos_colon == std::string::npos) { - rest = url; - return false; - } - - if (url.size() < pos_colon + 2) { - rest = url; - return false; - } - - if (url[pos_colon+1] != '/' || - url[pos_colon+2] != '/') { - rest = url; - return false; - } - - protocol = url.substr(0,pos_colon); - rest = url.substr(3+pos_colon); // Skipping three characters ( '://' ) - - return true; -} - -void SplitGetReq(std::string get_req, std::string& path, std::map<std::string, std::string>& params) { - TraceFunc("SplitGetReq"); - - // Remove trailing newlines - if (get_req[get_req.size()-1] == '\x0d' || - get_req[get_req.size()-1] == '\x0a') - get_req=get_req.substr(0, get_req.size()-1); - - if (get_req[get_req.size()-1] == '\x0d' || - get_req[get_req.size()-1] == '\x0a') - get_req=get_req.substr(0, get_req.size()-1); - - // Remove potential Trailing HTTP/1.x - if (get_req.size() > 7) { - if (get_req.substr(get_req.size()-8, 7) == "HTTP/1.") { - get_req=get_req.substr(0, get_req.size()-9); - } - } - - std::string::size_type qm = get_req.find("?"); - if (qm != std::string::npos) { - std::string url_params = get_req.substr(qm+1); - - path = get_req.substr(0, qm); - - // Appending a '&' so that there are as many '&' as name-value pairs. - // It makes it easier to split the url for name value pairs, he he he - url_params += "&"; - - std::string::size_type next_amp = url_params.find("&"); - - while (next_amp != std::string::npos) { - std::string name_value = url_params.substr(0,next_amp); - url_params = url_params.substr(next_amp+1); - next_amp = url_params.find("&"); - - std::string::size_type pos_equal = name_value.find("="); - - std::string nam = name_value.substr(0,pos_equal); - std::string val = name_value.substr(pos_equal+1); - - std::string::size_type pos_plus; - while ( (pos_plus = val.find("+")) != std::string::npos ) { - val.replace(pos_plus, 1, " "); - } - - while ( (pos_plus = val.find("%20")) != std::string::npos ) { - val.replace(pos_plus, 3, " "); - } - - // Replacing %xy notation - std::string::size_type pos_hex = 0; - while ( (pos_hex = val.find("%", pos_hex)) != std::string::npos ) { - std::stringstream h; - h << val.substr(pos_hex+1, 2); - h << std::hex; - - int i; - h>>i; - - std::stringstream f; - f << static_cast<char>(i); - std::string s; - f >> s; - - val.replace(pos_hex, 3, s); - pos_hex ++; - } - - params.insert(std::map<std::string,std::string>::value_type(nam, val)); - } - } - else { - path = get_req; - } -} - -void SplitUrl(std::string const& url, std::string& protocol, std::string& server, std::string& path) { - TraceFunc("SplitUrl"); - RemoveProtocolFromUrl(url, protocol, server); - - if (protocol == "http") { - std::string::size_type pos_slash = server.find("/"); - - if (pos_slash != std::string::npos) { - Trace("slash found"); - path = server.substr(pos_slash); - server = server.substr(0, pos_slash); - } - else { - Trace("slash not found"); - path = "/"; - } - } - else if (protocol == "file") { - path = ReplaceInStr(server, "\\", "/"); - server = ""; - } - else { - std::cerr << "unknown protocol in SplitUrl: '" << protocol << "'" << std::endl; - } -} diff --git a/WebServer/UrlHelper.h b/WebServer/UrlHelper.h deleted file mode 100644 index 12f12c6fe..000000000 --- a/WebServer/UrlHelper.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - UrlHelper.h - - Copyright (C) 2002-2004 René Nyffenegger - - This source code is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this source code must not be misrepresented; you must not - claim that you wrote the original source code. If you use this source code - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original source code. - - 3. This notice may not be removed or altered from any source distribution. - - René Nyffenegger rene.nyffenegger@adp-gmbh.ch -*/ - -/* - Note on point 2: - THIS IS NOT THE ORIGINAL SOURCE1!!1!!!~!!~`1ONE!!`1 -*/ -#ifndef __URL_HELPER_H__ -#define __URL_HELPER_H__ - -#include <string> -#include <map> - -void SplitUrl (std::string const& url, std::string& protocol, std::string& server, std::string& path); -bool RemoveProtocolFromUrl(std::string const& url, std::string& protocol, std::string& rest); - -void SplitGetReq (std::string et_req, std::string& path, std::map<std::string, std::string>& params); - -#endif diff --git a/WebServer/WebServer.cpp b/WebServer/WebServer.cpp deleted file mode 100644 index eb9a09049..000000000 --- a/WebServer/WebServer.cpp +++ /dev/null @@ -1,498 +0,0 @@ -/* - WebServer.cpp - - Copyright (C) 2003-2007 René Nyffenegger - - This source code is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this source code must not be misrepresented; you must not - claim that you wrote the original source code. If you use this source code - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original source code. - - 3. This notice may not be removed or altered from any source distribution. - - René Nyffenegger rene.nyffenegger@adp-gmbh.ch - - Thanks to Tom Lynn who pointed out an error in this source code. -*/ - -/* - Note on point 2: - THIS IS NOT THE ORIGINAL SOURCE1!!1!!!~!!~`1ONE!!`1 -*/ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include <ctime> -#ifdef _WIN32 - #include <process.h> -#endif -#include <iostream> -#include <sstream> - - -#include "WebServer.h" -#include "Events.h" -#include "Socket.h" -#include "UrlHelper.h" -#include "base64.h" - - - - - -webserver::request_func webserver::request_func_ = NULL; - - - - - -static std::string EatLine( std::string& a_String ) -{ - std::string RetVal = ""; - unsigned int StringSize = a_String.size(); - const char* c = a_String.c_str(); - - for( unsigned int i = 0; i < StringSize; ++i, ++c) - { - if( *c == '\n' ) - { - RetVal += *c; -// ++i; ++c; -// if( i < StringSize ) -// { -// if( *c == '\r' ) -// { -// RetVal += *c; -// } -// } - break; - } - RetVal += *c; - } - a_String = a_String.substr( RetVal.size() ); - return RetVal; -} - - - - - -// Turns -// "blabla my string with \"quotes\"!" -// into -// blabla my string with "quotes"! -static std::string GetQuotedString( const std::string& a_String ) -{ - std::string RetVal; - - bool bGotFirstQuote = false; - bool bIgnoreNext = false; - unsigned int StrLen = a_String.size(); - for( unsigned int i = 0; i < StrLen; ++i ) - { - if( bIgnoreNext == false ) - { - if( a_String[i] == '\"' ) - { - if( bGotFirstQuote == false ) - { - bGotFirstQuote = true; - } - else - { - break; - } - continue; - } - else if( a_String[i] == '\\' ) // Escape character - { - bIgnoreNext = true; - continue; - } - } - RetVal.push_back( a_String[i] ); - bIgnoreNext = false; - } - - return RetVal; -} - - - - - -void ParseMultipartFormData( webserver::http_request& req, Socket* s) -{ - static const std::string multipart_form_data = "multipart/form-data"; - if(req.content_type_.substr(0, multipart_form_data.size()) == multipart_form_data) // Difficult data... :( - { - AStringVector ContentTypeData = StringSplit( req.content_type_, "; " ); - - std::string boundary; - // Find boundary - for( unsigned int i = 0; i < ContentTypeData.size(); ++i ) - { - static const std::string boundary_ = "boundary="; - if( ContentTypeData[i].substr(0, boundary_.size()) == boundary_ ) // Found boundary - { - boundary = ContentTypeData[i].substr( boundary_.size() ); - } - } - - //LOGINFO("Boundary: %s", boundary.c_str() ); - std::string boundary_start = "--" + boundary; - std::string boundary_end = boundary_start + "--"; - - std::string Content = s->ReceiveBytes( req.content_length_ ); - - //printf("Total content: \n%s\n", Content.c_str() ); - - // Should start with boundary! - std::string line = EatLine( Content ); - if( line.substr(0, boundary_start.size() ) != boundary_start ) - { - // Something was wrong! :( - Content.clear(); - } - - while( !Content.empty() ) - { - webserver::formdata FormData; - - static const std::string content_disposition = "Content-Disposition: "; - static const std::string content_type = "Content-Type: "; - - std::string f_disposition; - - while( 1 ) - { - std::string line = EatLine( Content ); - if( line.empty() ) - break; - - unsigned int pos_cr_lf = line.find_first_of("\x0a\x0d"); - if (pos_cr_lf == 0) break; // Empty line, indicates end of mime thingy - - if( line.substr(0, content_disposition.size() ) == content_disposition ) - { - f_disposition = line.substr(content_disposition.size()); - LOGINFO("Disposition: %s", f_disposition.c_str() ); - } - else if( line.substr(0, content_type.size() ) == content_type ) - { - FormData.content_type_ = line.substr(content_type.size()); - } - - //LOGINFO("Got line: '%s'", line.c_str() ); - } - - // Check if we got the proper headers - if( !f_disposition.empty() ) - { - static const std::string disp_name = "name="; - static const std::string disp_filename = "filename="; - - // Parse the disposition - AStringVector DispositionData = StringSplit( f_disposition, "; " ); - for( unsigned int i = 0; i < DispositionData.size(); ++i ) - { - if( DispositionData[i].substr(0, disp_name.size()) == disp_name ) - { - FormData.name_ = GetQuotedString( DispositionData[i].substr(disp_name.size()) ); - } - else if( DispositionData[i].substr(0, disp_filename.size()) == disp_filename ) - { - FormData.filename_ = GetQuotedString( DispositionData[i].substr(disp_filename.size()) ); - } - } - - std::string ContentValue; - // Parse until boundary_end is found - while( 1 ) - { - std::string line = EatLine( Content ); - if( line.empty() ) - break; - - if( line.substr(0, boundary_end.size() ) == boundary_end ) - { - break; - } - else if( line.substr(0, boundary_start.size() ) == boundary_start ) - { - break; - } - ContentValue.append( line.c_str(), line.size() ); - } - - - FormData.value_ = ContentValue; - } - - req.multipart_formdata_.push_back( FormData ); - } - } -} - - - - - -#ifdef _WIN32 -unsigned webserver::Request(void* ptr_s) -#else -void* webserver::Request(void* ptr_s) -#endif -{ - Socket* s = (reinterpret_cast<Socket*>(ptr_s)); - - std::string line = s->ReceiveLine(); - if (line.empty()) - { - s->Close(); - delete s; - return 0; - } - - http_request req; - std::string path; - std::map<std::string, std::string> params; - size_t posStartPath = 0; - - if (line.find("GET") == 0) - { - req.method_="GET"; - posStartPath = line.find_first_not_of(" ",3); - } - else if (line.find("POST") == 0) - { - req.method_="POST"; - posStartPath = line.find_first_not_of(" ",4); - } - - SplitGetReq(line.substr(posStartPath), path, params); - - req.status_ = "202 OK"; - req.s_ = s; - req.path_ = path; - req.params_ = params; - - static const char authorization[] = "Authorization: Basic "; - static const char accept[] = "Accept: "; - static const char accept_language[] = "Accept-Language: "; - static const char accept_encoding[] = "Accept-Encoding: "; - static const char user_agent[] = "User-Agent: "; - static const char content_length[] = "Content-Length: "; - static const char content_type[] = "Content-Type: "; - - while(1) - { - line=s->ReceiveLine(); - if (line.empty()) - { - break; - } - - unsigned int pos_cr_lf = line.find_first_of("\x0a\x0d"); - if (pos_cr_lf == 0) break; - - line = line.substr(0,pos_cr_lf); - - if (line.compare(0, sizeof(authorization) - 1, authorization) == 0) - { - req.authentication_given_ = true; - std::string encoded = line.substr(sizeof(authorization) - 1); - std::string decoded = base64_decode(encoded); - - unsigned int pos_colon = decoded.find(":"); - - req.username_ = decoded.substr(0, pos_colon); - req.password_ = decoded.substr(pos_colon + 1); - } - else if (line.compare(0, sizeof(accept) - 1, accept) == 0) - { - req.accept_ = line.substr(sizeof(accept) - 1); - } - else if (line.compare(0, sizeof(accept_language) - 1, accept_language) == 0) - { - req.accept_language_ = line.substr(sizeof(accept_language) - 1); - } - else if (line.compare(0, sizeof(accept_encoding) - 1, accept_encoding) == 0) - { - req.accept_encoding_ = line.substr(sizeof(accept_encoding) - 1); - } - else if (line.compare(0, sizeof(user_agent) - 1, user_agent) == 0) - { - req.user_agent_ = line.substr(sizeof(user_agent) - 1); - } - else if (line.compare(0, sizeof(content_length) - 1, content_length) == 0) - { - req.content_length_ = atoi(line.substr(sizeof(content_length) - 1).c_str() ); - } - else if (line.compare(0, sizeof(content_type) - 1, content_type) == 0) - { - req.content_type_ = line.substr(sizeof(content_type) - 1); - } - } - - if( (req.method_.compare("POST") == 0) && (req.content_length_ > 0) ) - { - const char FormUrlEncoded[] = "application/x-www-form-urlencoded"; - // The only content type we can parse at the moment, the default HTML post data - if( req.content_type_.substr(0, strlen(FormUrlEncoded)).compare( FormUrlEncoded ) == 0 ) - { - std::string Content = s->ReceiveBytes( req.content_length_ ); - Content.insert( 0, "/ ?" ); // Little hack, inserts dummy URL so that SplitGetReq() can work with this content - - std::string dummy; - std::map<std::string, std::string> post_params; - SplitGetReq(Content, dummy, post_params); - - req.params_post_ = post_params; - } - else - { - ParseMultipartFormData( req, s ); - } - } - - request_func_(&req); - - std::stringstream str_str; - str_str << req.answer_.size(); - - time_t ltime; - time(<ime); - tm* gmt= gmtime(<ime); - -#ifdef _WIN32 - static const char serverName[] = "MCServerWebAdmin (Windows)"; -#elif __APPLE__ - static const char serverName[] = "MCServerWebAdmin (MacOSX)"; -#else - static const char serverName[] = "MCServerWebAdmin (Linux)"; -#endif - - - char* asctime_remove_nl = std::asctime(gmt); - asctime_remove_nl[24] = 0; - - s->SendBytes("HTTP/1.1 "); - - if (! req.auth_realm_.empty() ) - { - s->SendLine("401 Unauthorized"); - s->SendBytes("WWW-Authenticate: Basic Realm=\""); - s->SendBytes(req.auth_realm_); - s->SendLine("\""); - } - else - { - s->SendLine(req.status_); - } - s->SendLine(std::string("Date: ") + asctime_remove_nl + " GMT"); - s->SendLine(std::string("Server: ") + serverName); - s->SendLine("Connection: close"); - s->SendLine("Content-Type: text/html; charset=ISO-8859-1"); - s->SendLine("Content-Length: " + str_str.str()); - s->SendLine(""); - s->SendLine(req.answer_); - - s->Close( true ); // true = wait for all data to be sent before closing - delete s; - - - return 0; -} - - - - - -void webserver::Stop() -{ - m_bStop = true; - m_Socket->Close(); - m_Events->Wait(); -} - - - - - -bool webserver::Begin() -{ - if (!m_Socket->IsValid()) - { - LOGINFO("WebAdmin: The server socket is invalid. Terminating WebAdmin."); - return false; - } - m_bStop = false; - while ( !m_bStop ) - { - Socket * ptr_s = m_Socket->Accept(); - if (m_bStop) - { - if (ptr_s != 0) - { - ptr_s->Close(); - delete ptr_s; - } - break; - } - if (ptr_s == NULL) - { - LOGINFO("WebAdmin: Accepted socket is NULL. Terminating WebAdmin to avoid busywait."); - return false; - } - - #ifdef _WIN32 - unsigned ret; - HANDLE hHandle = reinterpret_cast<HANDLE>(_beginthreadex(NULL, 0, Request, (void *)ptr_s, 0, &ret)); - CloseHandle(hHandle); - #else - // Mattes: TODO: this handle probably leaks! - pthread_t * hHandle = new pthread_t; - pthread_create( hHandle, NULL, Request, ptr_s); - #endif - } - m_Events->Set(); - return true; -} - - - - - -webserver::webserver(unsigned int port_to_listen, request_func r) -{ - m_Socket = new SocketServer(port_to_listen, 1); - - request_func_ = r; - m_Events = new cEvents(); -} - - - - - -webserver::~webserver() -{ - delete m_Socket; - delete m_Events; -} - - - - diff --git a/WebServer/WebServer.h b/WebServer/WebServer.h deleted file mode 100644 index 882624db5..000000000 --- a/WebServer/WebServer.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - WebServer.h - - Copyright (C) 2003-2004 René Nyffenegger - - This source code is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this source code must not be misrepresented; you must not - claim that you wrote the original source code. If you use this source code - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original source code. - - 3. This notice may not be removed or altered from any source distribution. - - René Nyffenegger rene.nyffenegger@adp-gmbh.ch - -*/ - -/* - Note on point 2: - THIS IS NOT THE ORIGINAL SOURCE1!!1!!!~!!~`1ONE!!`1 -*/ - - -class cEvents; -class Socket; -class SocketServer; -class webserver { -public: - struct formdata - { - std::string name_; - std::string filename_; - std::string content_type_; - std::string value_; - }; - - struct http_request { - - http_request() - : s_( 0 ) - , content_length_( 0 ) - , authentication_given_(false) - {} - - Socket* s_; - std::string method_; - std::string path_; - std::map<std::string, std::string> params_; - std::map<std::string, std::string> params_post_; - - std::string accept_; - std::string accept_language_; - std::string accept_encoding_; - std::string user_agent_; - int content_length_; - std::string content_type_; - std::vector< formdata > multipart_formdata_; - - /* status_: used to transmit server's error status, such as - o 202 OK - o 404 Not Found - and so on */ - std::string status_; - - /* auth_realm_: allows to set the basic realm for an authentication, - no need to additionally set status_ if set */ - std::string auth_realm_; - - std::string answer_; - - /* authentication_given_ is true when the user has entered a username and password. - These can then be read from username_ and password_ */ - bool authentication_given_; - std::string username_; - std::string password_; - }; - - typedef void (*request_func) (http_request*); - webserver(unsigned int port_to_listen, request_func); - ~webserver(); - - bool Begin(); - void Stop(); - -private: - bool m_bStop; - - #ifdef _WIN32 - static unsigned __stdcall Request(void*); - #else - static void* Request(void*); - #endif - - static request_func request_func_; - - cEvents * m_Events; - SocketServer* m_Socket; -}; diff --git a/WebServer/base64.cpp b/WebServer/base64.cpp deleted file mode 100644 index f505ea6c3..000000000 --- a/WebServer/base64.cpp +++ /dev/null @@ -1,102 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "base64.h" -#include <iostream> -#if defined(ANDROID_NDK) -#include <ctype.h> -#endif - -static const std::string base64_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - - -static inline bool is_base64(unsigned char c) { - return (isalnum(c) || (c == '+') || (c == '/')); -} - -std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { - std::string ret; - int i = 0; - int j = 0; - unsigned char char_array_3[3]; - unsigned char char_array_4[4]; - - while (in_len--) { - char_array_3[i++] = *(bytes_to_encode++); - if (i == 3) { - char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; - char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); - char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); - char_array_4[3] = char_array_3[2] & 0x3f; - - for(i = 0; (i <4) ; i++) - ret += base64_chars[char_array_4[i]]; - i = 0; - } - } - - if (i) - { - for(j = i; j < 3; j++) - char_array_3[j] = '\0'; - - char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; - char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); - char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); - char_array_4[3] = char_array_3[2] & 0x3f; - - for (j = 0; (j < i + 1); j++) - ret += base64_chars[char_array_4[j]]; - - while((i++ < 3)) - ret += '='; - - } - - return ret; - -} - -std::string base64_decode(std::string const& encoded_string) { - int in_len = encoded_string.size(); - int i = 0; - int j = 0; - int in_ = 0; - unsigned char char_array_4[4], char_array_3[3]; - std::string ret; - - while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { - char_array_4[i++] = encoded_string[in_]; in_++; - if (i ==4) { - for (i = 0; i <4; i++) - char_array_4[i] = base64_chars.find(char_array_4[i]); - - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - - for (i = 0; (i < 3); i++) - ret += char_array_3[i]; - i = 0; - } - } - - if (i) { - for (j = i; j <4; j++) - char_array_4[j] = 0; - - for (j = 0; j <4; j++) - char_array_4[j] = base64_chars.find(char_array_4[j]); - - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - - for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; - } - - return ret; -} diff --git a/WebServer/base64.h b/WebServer/base64.h deleted file mode 100644 index 65d5db8b2..000000000 --- a/WebServer/base64.h +++ /dev/null @@ -1,4 +0,0 @@ -#include <string> - -std::string base64_encode(unsigned char const* , unsigned int len); -std::string base64_decode(std::string const& s); diff --git a/source/AllToLua.pkg b/source/AllToLua.pkg index b423c43a5..00257e460 100644 --- a/source/AllToLua.pkg +++ b/source/AllToLua.pkg @@ -17,6 +17,8 @@ $cfile "ChunkDef.h" $cfile "../iniFile/iniFile.h" +$cfile "OSSupport/File.h" + $cfile "BlockID.h" $cfile "StringUtils.h" $cfile "Defines.h" diff --git a/source/Authenticator.cpp b/source/Authenticator.cpp index dcc63299e..a45617f93 100644 --- a/source/Authenticator.cpp +++ b/source/Authenticator.cpp @@ -100,6 +100,16 @@ void cAuthenticator::Authenticate(int a_ClientID, const AString & a_UserName, co +void cAuthenticator::Start(void) +{ + m_ShouldTerminate = false; + super::Start(); +} + + + + + void cAuthenticator::Stop(void) { m_ShouldTerminate = true; diff --git a/source/Authenticator.h b/source/Authenticator.h index c9e647329..868476d80 100644 --- a/source/Authenticator.h +++ b/source/Authenticator.h @@ -42,7 +42,10 @@ public: /// Queues a request for authenticating a user. If the auth fails, the user is kicked void Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash); - // Stops the authenticator thread + /// Starts the authenticator thread. The thread may be started and stopped repeatedly + void Start(void); + + /// Stops the authenticator thread. The thread may be started and stopped repeatedly void Stop(void); private: diff --git a/source/Bindings.cpp b/source/Bindings.cpp index 35b32d5cb..c241bad75 100644 --- a/source/Bindings.cpp +++ b/source/Bindings.cpp @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 09/07/13 22:05:18. +** Generated automatically by tolua++-1.0.92 on 10/13/13 18:01:21. */ #ifndef __cplusplus @@ -17,6 +17,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S); #include "tolua_base.h" #include "ChunkDef.h" #include "../iniFile/iniFile.h" +#include "OSSupport/File.h" #include "BlockID.h" #include "StringUtils.h" #include "Defines.h" @@ -240,31 +241,33 @@ static void tolua_reg_types (lua_State* tolua_S) tolua_usertype(tolua_S,"cCraftingRecipe"); tolua_usertype(tolua_S,"cPlugin"); tolua_usertype(tolua_S,"cItemGrid"); - tolua_usertype(tolua_S,"cBlockArea"); + tolua_usertype(tolua_S,"cHTTPServer::cCallbacks"); tolua_usertype(tolua_S,"cLuaWindow"); tolua_usertype(tolua_S,"cInventory"); tolua_usertype(tolua_S,"cBoundingBox"); tolua_usertype(tolua_S,"cBlockEntityWithItems"); + tolua_usertype(tolua_S,"cWindow"); + tolua_usertype(tolua_S,"cGroup"); tolua_usertype(tolua_S,"HTTPFormData"); - tolua_usertype(tolua_S,"cTracer"); + tolua_usertype(tolua_S,"cCraftingGrid"); tolua_usertype(tolua_S,"cArrowEntity"); tolua_usertype(tolua_S,"cDropSpenserEntity"); - tolua_usertype(tolua_S,"cWindow"); - tolua_usertype(tolua_S,"Vector3i"); - tolua_usertype(tolua_S,"cCraftingGrid"); - tolua_usertype(tolua_S,"cGroup"); + tolua_usertype(tolua_S,"cBlockArea"); + tolua_usertype(tolua_S,"cTracer"); tolua_usertype(tolua_S,"cStringMap"); + tolua_usertype(tolua_S,"cServer"); + tolua_usertype(tolua_S,"Vector3i"); tolua_usertype(tolua_S,"cBlockEntity"); tolua_usertype(tolua_S,"cCriticalSection"); tolua_usertype(tolua_S,"HTTPTemplateRequest"); - tolua_usertype(tolua_S,"cServer"); + tolua_usertype(tolua_S,"cFile"); tolua_usertype(tolua_S,"std::vector<std::string>"); tolua_usertype(tolua_S,"cClientHandle"); tolua_usertype(tolua_S,"cChatColor"); - tolua_usertype(tolua_S,"sWebAdminPage"); tolua_usertype(tolua_S,"cWebPlugin"); - tolua_usertype(tolua_S,"cIniFile"); tolua_usertype(tolua_S,"cWebAdmin"); + tolua_usertype(tolua_S,"cIniFile"); + tolua_usertype(tolua_S,"sWebAdminPage"); tolua_usertype(tolua_S,"cItem"); tolua_usertype(tolua_S,"cPawn"); tolua_usertype(tolua_S,"cPlayer"); @@ -2460,6 +2463,260 @@ tolua_lerror: } #endif //#ifndef TOLUA_DISABLE +/* method: Exists of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Exists00 +static int tolua_AllToLua_cFile_Exists00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::Exists(a_FileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Exists'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Delete of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Delete00 +static int tolua_AllToLua_cFile_Delete00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::Delete(a_FileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Delete'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Rename of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Rename00 +static int tolua_AllToLua_cFile_Rename00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_OrigPath = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString a_NewPath = ((const AString) tolua_tocppstring(tolua_S,3,0)); + { + bool tolua_ret = (bool) cFile::Rename(a_OrigPath,a_NewPath); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_OrigPath); + tolua_pushcppstring(tolua_S,(const char*)a_NewPath); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Rename'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Copy of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Copy00 +static int tolua_AllToLua_cFile_Copy00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_SrcFileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString a_DstFileName = ((const AString) tolua_tocppstring(tolua_S,3,0)); + { + bool tolua_ret = (bool) cFile::Copy(a_SrcFileName,a_DstFileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_SrcFileName); + tolua_pushcppstring(tolua_S,(const char*)a_DstFileName); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Copy'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsFolder of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_IsFolder00 +static int tolua_AllToLua_cFile_IsFolder00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_Path = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::IsFolder(a_Path); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Path); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsFolder'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsFile of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_IsFile00 +static int tolua_AllToLua_cFile_IsFile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_Path = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::IsFile(a_Path); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Path); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsFile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSize of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_GetSize00 +static int tolua_AllToLua_cFile_GetSize00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + int tolua_ret = (int) cFile::GetSize(a_FileName); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSize'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CreateFolder of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_CreateFolder00 +static int tolua_AllToLua_cFile_CreateFolder00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_FolderPath = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::CreateFolder(a_FolderPath); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FolderPath); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CreateFolder'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* function: BlockStringToType */ #ifndef TOLUA_DISABLE_tolua_AllToLua_BlockStringToType00 static int tolua_AllToLua_BlockStringToType00(lua_State* tolua_S) @@ -2758,6 +3015,51 @@ static int tolua_AllToLua_StringToDamageType00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* function: GetIniItemSet */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_GetIniItemSet00 +static int tolua_AllToLua_GetIniItemSet00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + (tolua_isvaluenil(tolua_S,1,&tolua_err) || !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err)) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isstring(tolua_S,3,0,&tolua_err) || + !tolua_isstring(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* a_IniFile = ((cIniFile*) tolua_tousertype(tolua_S,1,0)); + const char* a_Section = ((const char*) tolua_tostring(tolua_S,2,0)); + const char* a_Key = ((const char*) tolua_tostring(tolua_S,3,0)); + const char* a_Default = ((const char*) tolua_tostring(tolua_S,4,0)); + { + cItem tolua_ret = (cItem) GetIniItemSet(*a_IniFile,a_Section,a_Key,a_Default); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetIniItemSet'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* function: TrimString */ #ifndef TOLUA_DISABLE_tolua_AllToLua_TrimString00 static int tolua_AllToLua_TrimString00(lua_State* tolua_S) @@ -4696,6 +4998,38 @@ static int tolua_AllToLua_cEntity_IsMob00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* method: IsFallingBlock of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsFallingBlock00 +static int tolua_AllToLua_cEntity_IsFallingBlock00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsFallingBlock'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsFallingBlock(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsFallingBlock'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* method: IsMinecart of class cEntity */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsMinecart00 static int tolua_AllToLua_cEntity_IsMinecart00(lua_State* tolua_S) @@ -4728,6 +5062,38 @@ static int tolua_AllToLua_cEntity_IsMinecart00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* method: IsBoat of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsBoat00 +static int tolua_AllToLua_cEntity_IsBoat00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsBoat'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsBoat(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsBoat'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* method: IsTNT of class cEntity */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsTNT00 static int tolua_AllToLua_cEntity_IsTNT00(lua_State* tolua_S) @@ -4760,6 +5126,38 @@ static int tolua_AllToLua_cEntity_IsTNT00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* method: IsProjectile of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsProjectile00 +static int tolua_AllToLua_cEntity_IsProjectile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsProjectile'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsProjectile(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsProjectile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* method: IsA of class cEntity */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsA00 static int tolua_AllToLua_cEntity_IsA00(lua_State* tolua_S) @@ -7624,6 +8022,38 @@ static int tolua_AllToLua_cEntity_IsRclking00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* method: IsInvisible of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsInvisible00 +static int tolua_AllToLua_cEntity_IsInvisible00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInvisible'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInvisible(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsInvisible'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* method: GetEyeHeight of class cPlayer */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEyeHeight00 static int tolua_AllToLua_cPlayer_GetEyeHeight00(lua_State* tolua_S) @@ -9644,15 +10074,15 @@ static int tolua_AllToLua_cPickup_new00(lua_State* tolua_S) else #endif { - int a_MicroPosX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_MicroPosY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_MicroPosZ = ((int) tolua_tonumber(tolua_S,4,0)); + double a_X = ((double) tolua_tonumber(tolua_S,2,0)); + double a_Y = ((double) tolua_tonumber(tolua_S,3,0)); + double a_Z = ((double) tolua_tonumber(tolua_S,4,0)); const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,5,0)); float a_SpeedX = ((float) tolua_tonumber(tolua_S,6,0.f)); float a_SpeedY = ((float) tolua_tonumber(tolua_S,7,0.f)); float a_SpeedZ = ((float) tolua_tonumber(tolua_S,8,0.f)); { - cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_MicroPosX,a_MicroPosY,a_MicroPosZ,*a_Item,a_SpeedX,a_SpeedY,a_SpeedZ)); + cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_X,a_Y,a_Z,*a_Item,a_SpeedX,a_SpeedY,a_SpeedZ)); tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPickup"); } } @@ -9686,15 +10116,15 @@ static int tolua_AllToLua_cPickup_new00_local(lua_State* tolua_S) else #endif { - int a_MicroPosX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_MicroPosY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_MicroPosZ = ((int) tolua_tonumber(tolua_S,4,0)); + double a_X = ((double) tolua_tonumber(tolua_S,2,0)); + double a_Y = ((double) tolua_tonumber(tolua_S,3,0)); + double a_Z = ((double) tolua_tonumber(tolua_S,4,0)); const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,5,0)); float a_SpeedX = ((float) tolua_tonumber(tolua_S,6,0.f)); float a_SpeedY = ((float) tolua_tonumber(tolua_S,7,0.f)); float a_SpeedZ = ((float) tolua_tonumber(tolua_S,8,0.f)); { - cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_MicroPosX,a_MicroPosY,a_MicroPosZ,*a_Item,a_SpeedX,a_SpeedY,a_SpeedZ)); + cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_X,a_Y,a_Z,*a_Item,a_SpeedX,a_SpeedY,a_SpeedZ)); tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPickup"); tolua_register_gc(tolua_S,lua_gettop(tolua_S)); } @@ -10796,6 +11226,38 @@ static int tolua_AllToLua_cPlugin_GetLocalDirectory00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* method: GetLocalFolder of class cPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetLocalFolder00 +static int tolua_AllToLua_cPlugin_GetLocalFolder00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLocalFolder'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetLocalFolder(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetLocalFolder'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* get function: __cWebPlugin__ of class cPluginLua */ #ifndef TOLUA_DISABLE_tolua_get_cPluginLua___cWebPlugin__ static int tolua_get_cPluginLua___cWebPlugin__(lua_State* tolua_S) @@ -10974,62 +11436,6 @@ static int tolua_AllToLua_cServer_GetServerID00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE -/* method: GetClassStatic of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetClassStatic00 -static int tolua_AllToLua_cWorld_GetClassStatic00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - const char* tolua_ret = (const char*) cWorld::GetClassStatic(); - tolua_pushstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetClassStatic'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetTime of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetTime00 -static int tolua_AllToLua_cWorld_GetTime00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - float tolua_ret = (float) cWorld::GetTime(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetTime'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - /* method: GetTicksUntilWeatherChange of class cWorld */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetTicksUntilWeatherChange00 static int tolua_AllToLua_cWorld_GetTicksUntilWeatherChange00(lua_State* tolua_S) @@ -11192,39 +11598,6 @@ static int tolua_AllToLua_cWorld_SetTimeOfDay00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE -/* method: SetWorldTime of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetWorldTime00 -static int tolua_AllToLua_cWorld_SetWorldTime00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - long long a_TimeOfDay = (( long long) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetWorldTime'", NULL); -#endif - { - self->SetWorldTime(a_TimeOfDay); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetWorldTime'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - /* method: GetGameMode of class cWorld */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetGameMode00 static int tolua_AllToLua_cWorld_GetGameMode00(lua_State* tolua_S) @@ -12027,100 +12400,6 @@ static int tolua_AllToLua_cWorld_GetBlockBlockLight00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE -/* method: GetBlockTypeMeta of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockTypeMeta00 -static int tolua_AllToLua_cWorld_GetBlockTypeMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockTypeMeta'", NULL); -#endif - { - bool tolua_ret = (bool) self->GetBlockTypeMeta(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushnumber(tolua_S,(lua_Number)a_BlockType); - tolua_pushnumber(tolua_S,(lua_Number)a_BlockMeta); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBlockTypeMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBlockInfo of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockInfo00 -static int tolua_AllToLua_cWorld_GetBlockInfo00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnumber(tolua_S,8,0,&tolua_err) || - !tolua_isnoobj(tolua_S,9,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); - unsigned char a_Meta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); - unsigned char a_SkyLight = (( unsigned char) tolua_tonumber(tolua_S,7,0)); - unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,8,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockInfo'", NULL); -#endif - { - bool tolua_ret = (bool) self->GetBlockInfo(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_Meta,a_SkyLight,a_BlockLight); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushnumber(tolua_S,(lua_Number)a_BlockType); - tolua_pushnumber(tolua_S,(lua_Number)a_Meta); - tolua_pushnumber(tolua_S,(lua_Number)a_SkyLight); - tolua_pushnumber(tolua_S,(lua_Number)a_BlockLight); - } - } - return 5; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBlockInfo'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - /* method: FastSetBlock of class cWorld */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_FastSetBlock01 static int tolua_AllToLua_cWorld_FastSetBlock01(lua_State* tolua_S) @@ -12661,51 +12940,40 @@ static int tolua_AllToLua_cWorld_DoExplosionAt00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE -/* method: GetSignLines of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetSignLines00 -static int tolua_AllToLua_cWorld_GetSignLines00(lua_State* tolua_S) +/* method: UseBlockEntity of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_UseBlockEntity00 +static int tolua_AllToLua_cWorld_UseBlockEntity00(lua_State* tolua_S) { #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err) || !tolua_isnumber(tolua_S,3,0,&tolua_err) || !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_iscppstring(tolua_S,5,0,&tolua_err) || - !tolua_iscppstring(tolua_S,6,0,&tolua_err) || - !tolua_iscppstring(tolua_S,7,0,&tolua_err) || - !tolua_iscppstring(tolua_S,8,0,&tolua_err) || - !tolua_isnoobj(tolua_S,9,&tolua_err) + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) ) goto tolua_lerror; else #endif { cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - AString a_Line1 = ((AString) tolua_tocppstring(tolua_S,5,0)); - AString a_Line2 = ((AString) tolua_tocppstring(tolua_S,6,0)); - AString a_Line3 = ((AString) tolua_tocppstring(tolua_S,7,0)); - AString a_Line4 = ((AString) tolua_tocppstring(tolua_S,8,0)); + cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); + int a_BlockX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,5,0)); #ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSignLines'", NULL); + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'UseBlockEntity'", NULL); #endif { - bool tolua_ret = (bool) self->GetSignLines(a_BlockX,a_BlockY,a_BlockZ,a_Line1,a_Line2,a_Line3,a_Line4); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_Line1); - tolua_pushcppstring(tolua_S,(const char*)a_Line2); - tolua_pushcppstring(tolua_S,(const char*)a_Line3); - tolua_pushcppstring(tolua_S,(const char*)a_Line4); + self->UseBlockEntity(a_Player,a_BlockX,a_BlockY,a_BlockZ); } } - return 5; + return 0; #ifndef TOLUA_RELEASE tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSignLines'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'UseBlockEntity'.",&tolua_err); return 0; #endif } @@ -12770,7 +13038,7 @@ static int tolua_AllToLua_cWorld_GrowTreeFromSapling00(lua_State* tolua_S) int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - char a_SaplingMeta = ((char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_SaplingMeta = (( unsigned char) tolua_tonumber(tolua_S,5,0)); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowTreeFromSapling'", NULL); #endif @@ -12925,7 +13193,7 @@ static int tolua_AllToLua_cWorld_GrowMelonPumpkin00(lua_State* tolua_S) int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - char a_BlockType = ((char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowMelonPumpkin'", NULL); #endif @@ -13081,37 +13349,6 @@ static int tolua_AllToLua_cWorld_GetIniFileName00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE -/* method: SaveAllChunks of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SaveAllChunks00 -static int tolua_AllToLua_cWorld_SaveAllChunks00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SaveAllChunks'", NULL); -#endif - { - self->SaveAllChunks(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SaveAllChunks'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - /* method: QueueSaveAllChunks of class cWorld */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_QueueSaveAllChunks00 static int tolua_AllToLua_cWorld_QueueSaveAllChunks00(lua_State* tolua_S) @@ -13325,12 +13562,12 @@ static int tolua_AllToLua_cWorld_QueueBlockForTick00(lua_State* tolua_S) int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - float a_TimeToWait = ((float) tolua_tonumber(tolua_S,5,0)); + int a_TicksToWait = ((int) tolua_tonumber(tolua_S,5,0)); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueBlockForTick'", NULL); #endif { - self->QueueBlockForTick(a_BlockX,a_BlockY,a_BlockZ,a_TimeToWait); + self->QueueBlockForTick(a_BlockX,a_BlockY,a_BlockZ,a_TicksToWait); } } return 0; @@ -13475,6 +13712,134 @@ static int tolua_AllToLua_cWorld_GetWeather00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* method: IsWeatherSunny of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherSunny00 +static int tolua_AllToLua_cWorld_IsWeatherSunny00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherSunny'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsWeatherSunny(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsWeatherSunny'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsWeatherRain of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherRain00 +static int tolua_AllToLua_cWorld_IsWeatherRain00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherRain'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsWeatherRain(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsWeatherRain'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsWeatherStorm of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherStorm00 +static int tolua_AllToLua_cWorld_IsWeatherStorm00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherStorm'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsWeatherStorm(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsWeatherStorm'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsWeatherWet of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherWet00 +static int tolua_AllToLua_cWorld_IsWeatherWet00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherWet'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsWeatherWet(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsWeatherWet'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* method: SetNextBlockTick of class cWorld */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetNextBlockTick00 static int tolua_AllToLua_cWorld_SetNextBlockTick00(lua_State* tolua_S) @@ -18814,8 +19179,8 @@ static int tolua_AllToLua_cWebAdmin_GetMemoryUsage00(lua_State* tolua_S) #endif { { - AString tolua_ret = (AString) cWebAdmin::GetMemoryUsage(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + int tolua_ret = (int) cWebAdmin::GetMemoryUsage(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); } } return 1; @@ -18827,77 +19192,77 @@ static int tolua_AllToLua_cWebAdmin_GetMemoryUsage00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE -/* method: GetPort of class cWebAdmin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetPort00 -static int tolua_AllToLua_cWebAdmin_GetPort00(lua_State* tolua_S) +/* method: GetPage of class cWebAdmin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetPage00 +static int tolua_AllToLua_cWebAdmin_GetPage00(lua_State* tolua_S) { #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const HTTPRequest",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) ) goto tolua_lerror; else #endif { cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0); + const HTTPRequest* a_Request = ((const HTTPRequest*) tolua_tousertype(tolua_S,2,0)); #ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPort'", NULL); + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPage'", NULL); #endif { - int tolua_ret = (int) self->GetPort(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + sWebAdminPage tolua_ret = (sWebAdminPage) self->GetPage(*a_Request); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((sWebAdminPage)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(sWebAdminPage)); + tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } } } return 1; #ifndef TOLUA_RELEASE tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPort'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'GetPage'.",&tolua_err); return 0; #endif } #endif //#ifndef TOLUA_DISABLE -/* method: GetPage of class cWebAdmin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetPage00 -static int tolua_AllToLua_cWebAdmin_GetPage00(lua_State* tolua_S) +/* method: GetDefaultPage of class cWebAdmin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetDefaultPage00 +static int tolua_AllToLua_cWebAdmin_GetDefaultPage00(lua_State* tolua_S) { #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const HTTPRequest",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) + !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0); - const HTTPRequest* a_Request = ((const HTTPRequest*) tolua_tousertype(tolua_S,2,0)); #ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPage'", NULL); + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDefaultPage'", NULL); #endif { - sWebAdminPage tolua_ret = (sWebAdminPage) self->GetPage(*a_Request); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((sWebAdminPage)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(sWebAdminPage)); - tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } + AString tolua_ret = (AString) self->GetDefaultPage(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); } } return 1; #ifndef TOLUA_RELEASE tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPage'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'GetDefaultPage'.",&tolua_err); return 0; #endif } @@ -19578,6 +19943,62 @@ static int tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00(lua_State* tolua } #endif //#ifndef TOLUA_DISABLE +/* method: GetVirtualRAMUsage of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetVirtualRAMUsage00 +static int tolua_AllToLua_cRoot_GetVirtualRAMUsage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + int tolua_ret = (int) cRoot::GetVirtualRAMUsage(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetVirtualRAMUsage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPhysicalRAMUsage of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetPhysicalRAMUsage00 +static int tolua_AllToLua_cRoot_GetPhysicalRAMUsage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + int tolua_ret = (int) cRoot::GetPhysicalRAMUsage(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPhysicalRAMUsage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* method: new of class Vector3f */ #ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new00 static int tolua_AllToLua_Vector3f_new00(lua_State* tolua_S) @@ -26324,41 +26745,6 @@ static int tolua_AllToLua_cChunkDesc_GetChunkZ00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE -/* method: SetChunkCoords of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetChunkCoords00 -static int tolua_AllToLua_cChunkDesc_SetChunkCoords00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - int a_ChunkX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_ChunkZ = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetChunkCoords'", NULL); -#endif - { - self->SetChunkCoords(a_ChunkX,a_ChunkZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetChunkCoords'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - /* method: FillBlocks of class cChunkDesc */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FillBlocks00 static int tolua_AllToLua_cChunkDesc_FillBlocks00(lua_State* tolua_S) @@ -28776,6 +29162,38 @@ static int tolua_get_cLuaWindow___cItemGrid__cListener__(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* method: GetMobType of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetMobType00 +static int tolua_AllToLua_cMonster_GetMobType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cMonster",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cMonster* self = (cMonster*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMobType'", NULL); +#endif + { + int tolua_ret = (int) self->GetMobType(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMobType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* Open function */ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) { @@ -28809,8 +29227,47 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_constant(tolua_S,"biExtremeHillsEdge",biExtremeHillsEdge); tolua_constant(tolua_S,"biJungle",biJungle); tolua_constant(tolua_S,"biJungleHills",biJungleHills); + tolua_constant(tolua_S,"biJungleEdge",biJungleEdge); + tolua_constant(tolua_S,"biDeepOcean",biDeepOcean); + tolua_constant(tolua_S,"biStoneBeach",biStoneBeach); + tolua_constant(tolua_S,"biColdBeach",biColdBeach); + tolua_constant(tolua_S,"biBirchForest",biBirchForest); + tolua_constant(tolua_S,"biBirchForestHills",biBirchForestHills); + tolua_constant(tolua_S,"biRoofedForest",biRoofedForest); + tolua_constant(tolua_S,"biColdTaiga",biColdTaiga); + tolua_constant(tolua_S,"biColdTaigaHills",biColdTaigaHills); + tolua_constant(tolua_S,"biMegaTaiga",biMegaTaiga); + tolua_constant(tolua_S,"biMegaTaigaHills",biMegaTaigaHills); + tolua_constant(tolua_S,"biExtremeHillsPlus",biExtremeHillsPlus); + tolua_constant(tolua_S,"biSavanna",biSavanna); + tolua_constant(tolua_S,"biSavannaPlateau",biSavannaPlateau); + tolua_constant(tolua_S,"biMesa",biMesa); + tolua_constant(tolua_S,"biMesaPlateauF",biMesaPlateauF); + tolua_constant(tolua_S,"biMesaPlateau",biMesaPlateau); tolua_constant(tolua_S,"biNumBiomes",biNumBiomes); tolua_constant(tolua_S,"biMaxBiome",biMaxBiome); + tolua_constant(tolua_S,"biVariant",biVariant); + tolua_constant(tolua_S,"biSunflowerPlains",biSunflowerPlains); + tolua_constant(tolua_S,"biDesertM",biDesertM); + tolua_constant(tolua_S,"biExtremeHillsM",biExtremeHillsM); + tolua_constant(tolua_S,"biFlowerForest",biFlowerForest); + tolua_constant(tolua_S,"biTaigaM",biTaigaM); + tolua_constant(tolua_S,"biSwamplandM",biSwamplandM); + tolua_constant(tolua_S,"biIcePlainsSpikes",biIcePlainsSpikes); + tolua_constant(tolua_S,"biJungleM",biJungleM); + tolua_constant(tolua_S,"biJungleEdgeM",biJungleEdgeM); + tolua_constant(tolua_S,"biBirchForestM",biBirchForestM); + tolua_constant(tolua_S,"biBirchForestHillsM",biBirchForestHillsM); + tolua_constant(tolua_S,"biRoofedForestM",biRoofedForestM); + tolua_constant(tolua_S,"biColdTaigaM",biColdTaigaM); + tolua_constant(tolua_S,"biMegaSpruceTaiga",biMegaSpruceTaiga); + tolua_constant(tolua_S,"biMegaSpruceTaigaHills",biMegaSpruceTaigaHills); + tolua_constant(tolua_S,"biExtremeHillsPlusM",biExtremeHillsPlusM); + tolua_constant(tolua_S,"biSavannaM",biSavannaM); + tolua_constant(tolua_S,"biSavannaPlateauM",biSavannaPlateauM); + tolua_constant(tolua_S,"biMesaBryce",biMesaBryce); + tolua_constant(tolua_S,"biMesaPlateauFM",biMesaPlateauFM); + tolua_constant(tolua_S,"biMesaPlateauM",biMesaPlateauM); #ifdef __cplusplus tolua_cclass(tolua_S,"cIniFile","cIniFile","",tolua_collect_cIniFile); #else @@ -28885,6 +29342,17 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"DeleteKeyComments",tolua_AllToLua_cIniFile_DeleteKeyComments00); tolua_function(tolua_S,"DeleteKeyComments",tolua_AllToLua_cIniFile_DeleteKeyComments01); tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cFile","cFile","",NULL); + tolua_beginmodule(tolua_S,"cFile"); + tolua_function(tolua_S,"Exists",tolua_AllToLua_cFile_Exists00); + tolua_function(tolua_S,"Delete",tolua_AllToLua_cFile_Delete00); + tolua_function(tolua_S,"Rename",tolua_AllToLua_cFile_Rename00); + tolua_function(tolua_S,"Copy",tolua_AllToLua_cFile_Copy00); + tolua_function(tolua_S,"IsFolder",tolua_AllToLua_cFile_IsFolder00); + tolua_function(tolua_S,"IsFile",tolua_AllToLua_cFile_IsFile00); + tolua_function(tolua_S,"GetSize",tolua_AllToLua_cFile_GetSize00); + tolua_function(tolua_S,"CreateFolder",tolua_AllToLua_cFile_CreateFolder00); + tolua_endmodule(tolua_S); tolua_constant(tolua_S,"E_BLOCK_AIR",E_BLOCK_AIR); tolua_constant(tolua_S,"E_BLOCK_STONE",E_BLOCK_STONE); tolua_constant(tolua_S,"E_BLOCK_GRASS",E_BLOCK_GRASS); @@ -29045,7 +29513,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_constant(tolua_S,"E_BLOCK_NETHER_QUARTZ_ORE",E_BLOCK_NETHER_QUARTZ_ORE); tolua_constant(tolua_S,"E_BLOCK_HOPPER",E_BLOCK_HOPPER); tolua_constant(tolua_S,"E_BLOCK_QUARTZ_BLOCK",E_BLOCK_QUARTZ_BLOCK); - tolua_constant(tolua_S,"E_BLOCK_QUARTZ_STAIR",E_BLOCK_QUARTZ_STAIR); + tolua_constant(tolua_S,"E_BLOCK_QUARTZ_STAIRS",E_BLOCK_QUARTZ_STAIRS); tolua_constant(tolua_S,"E_BLOCK_ACTIVATOR_RAIL",E_BLOCK_ACTIVATOR_RAIL); tolua_constant(tolua_S,"E_BLOCK_DROPPER",E_BLOCK_DROPPER); tolua_constant(tolua_S,"E_BLOCK_STAINED_CLAY",E_BLOCK_STAINED_CLAY); @@ -29191,14 +29659,17 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_constant(tolua_S,"E_ITEM_BOOK_AND_QUILL",E_ITEM_BOOK_AND_QUILL); tolua_constant(tolua_S,"E_ITEM_WRITTEN_BOOK",E_ITEM_WRITTEN_BOOK); tolua_constant(tolua_S,"E_ITEM_EMERALD",E_ITEM_EMERALD); + tolua_constant(tolua_S,"E_ITEM_ITEM_FRAME",E_ITEM_ITEM_FRAME); tolua_constant(tolua_S,"E_ITEM_FLOWER_POT",E_ITEM_FLOWER_POT); tolua_constant(tolua_S,"E_ITEM_CARROT",E_ITEM_CARROT); tolua_constant(tolua_S,"E_ITEM_POTATO",E_ITEM_POTATO); tolua_constant(tolua_S,"E_ITEM_BAKED_POTATO",E_ITEM_BAKED_POTATO); tolua_constant(tolua_S,"E_ITEM_POISONOUS_POTATO",E_ITEM_POISONOUS_POTATO); + tolua_constant(tolua_S,"E_ITEM_EMPTY_MAP",E_ITEM_EMPTY_MAP); tolua_constant(tolua_S,"E_ITEM_GOLDEN_CARROT",E_ITEM_GOLDEN_CARROT); tolua_constant(tolua_S,"E_ITEM_HEAD",E_ITEM_HEAD); tolua_constant(tolua_S,"E_ITEM_CARROT_ON_STICK",E_ITEM_CARROT_ON_STICK); + tolua_constant(tolua_S,"E_ITEM_NETHER_STAR",E_ITEM_NETHER_STAR); tolua_constant(tolua_S,"E_ITEM_PUMPKIN_PIE",E_ITEM_PUMPKIN_PIE); tolua_constant(tolua_S,"E_ITEM_FIREWORK_ROCKET",E_ITEM_FIREWORK_ROCKET); tolua_constant(tolua_S,"E_ITEM_FIREWORK_STAR",E_ITEM_FIREWORK_STAR); @@ -29392,11 +29863,35 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_constant(tolua_S,"E_META_GOLDEN_APPLE_ENCHANTED",E_META_GOLDEN_APPLE_ENCHANTED); tolua_constant(tolua_S,"E_META_TRACKS_X",E_META_TRACKS_X); tolua_constant(tolua_S,"E_META_TRACKS_Z",E_META_TRACKS_Z); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_PICKUP",E_META_SPAWN_EGG_PICKUP); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_EXPERIENCE_ORB",E_META_SPAWN_EGG_EXPERIENCE_ORB); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_LEASH_KNOT",E_META_SPAWN_EGG_LEASH_KNOT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_PAINTING",E_META_SPAWN_EGG_PAINTING); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ARROW",E_META_SPAWN_EGG_ARROW); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SNOWBALL",E_META_SPAWN_EGG_SNOWBALL); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_FIREBALL",E_META_SPAWN_EGG_FIREBALL); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SMALL_FIREBALL",E_META_SPAWN_EGG_SMALL_FIREBALL); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDER_PEARL",E_META_SPAWN_EGG_ENDER_PEARL); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_EYE_OF_ENDER",E_META_SPAWN_EGG_EYE_OF_ENDER); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SPLASH_POTION",E_META_SPAWN_EGG_SPLASH_POTION); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_EXP_BOTTLE",E_META_SPAWN_EGG_EXP_BOTTLE); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ITEM_FRAME",E_META_SPAWN_EGG_ITEM_FRAME); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_WITHER_SKULL",E_META_SPAWN_EGG_WITHER_SKULL); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_PRIMED_TNT",E_META_SPAWN_EGG_PRIMED_TNT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_FALLING_BLOCK",E_META_SPAWN_EGG_FALLING_BLOCK); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_FIREWORK",E_META_SPAWN_EGG_FIREWORK); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_BOAT",E_META_SPAWN_EGG_BOAT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART",E_META_SPAWN_EGG_MINECART); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_CHEST",E_META_SPAWN_EGG_MINECART_CHEST); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_FURNACE",E_META_SPAWN_EGG_MINECART_FURNACE); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_TNT",E_META_SPAWN_EGG_MINECART_TNT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_HOPPER",E_META_SPAWN_EGG_MINECART_HOPPER); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_SPAWNER",E_META_SPAWN_EGG_MINECART_SPAWNER); tolua_constant(tolua_S,"E_META_SPAWN_EGG_CREEPER",E_META_SPAWN_EGG_CREEPER); tolua_constant(tolua_S,"E_META_SPAWN_EGG_SKELETON",E_META_SPAWN_EGG_SKELETON); tolua_constant(tolua_S,"E_META_SPAWN_EGG_SPIDER",E_META_SPAWN_EGG_SPIDER); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_ZOMBIE",E_META_SPAWN_EGG_ZOMBIE); tolua_constant(tolua_S,"E_META_SPAWN_EGG_GIANT",E_META_SPAWN_EGG_GIANT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ZOMBIE",E_META_SPAWN_EGG_ZOMBIE); tolua_constant(tolua_S,"E_META_SPAWN_EGG_SLIME",E_META_SPAWN_EGG_SLIME); tolua_constant(tolua_S,"E_META_SPAWN_EGG_GHAST",E_META_SPAWN_EGG_GHAST); tolua_constant(tolua_S,"E_META_SPAWN_EGG_ZOMBIE_PIGMAN",E_META_SPAWN_EGG_ZOMBIE_PIGMAN); @@ -29421,6 +29916,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_constant(tolua_S,"E_META_SPAWN_EGG_IRON_GOLEM",E_META_SPAWN_EGG_IRON_GOLEM); tolua_constant(tolua_S,"E_META_SPAWN_EGG_HORSE",E_META_SPAWN_EGG_HORSE); tolua_constant(tolua_S,"E_META_SPAWN_EGG_VILLAGER",E_META_SPAWN_EGG_VILLAGER); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDER_CRYSTAL",E_META_SPAWN_EGG_ENDER_CRYSTAL); tolua_constant(tolua_S,"dimNether",dimNether); tolua_constant(tolua_S,"dimOverworld",dimOverworld); tolua_constant(tolua_S,"dimEnd",dimEnd); @@ -29480,6 +29976,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"StringToDimension",tolua_AllToLua_StringToDimension00); tolua_function(tolua_S,"DamageTypeToString",tolua_AllToLua_DamageTypeToString00); tolua_function(tolua_S,"StringToDamageType",tolua_AllToLua_StringToDamageType00); + tolua_function(tolua_S,"GetIniItemSet",tolua_AllToLua_GetIniItemSet00); tolua_function(tolua_S,"TrimString",tolua_AllToLua_TrimString00); tolua_function(tolua_S,"NoCaseCompare",tolua_AllToLua_NoCaseCompare00); tolua_function(tolua_S,"ReplaceString",tolua_AllToLua_ReplaceString00); @@ -29632,39 +30129,25 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cEntity","cEntity","",NULL); tolua_beginmodule(tolua_S,"cEntity"); - tolua_constant(tolua_S,"ENTITY_STATUS_HURT",cEntity::ENTITY_STATUS_HURT); - tolua_constant(tolua_S,"ENTITY_STATUS_DEAD",cEntity::ENTITY_STATUS_DEAD); - tolua_constant(tolua_S,"ENTITY_STATUS_WOLF_TAMING",cEntity::ENTITY_STATUS_WOLF_TAMING); - tolua_constant(tolua_S,"ENTITY_STATUS_WOLF_TAMED",cEntity::ENTITY_STATUS_WOLF_TAMED); - tolua_constant(tolua_S,"ENTITY_STATUS_WOLF_SHAKING",cEntity::ENTITY_STATUS_WOLF_SHAKING); - tolua_constant(tolua_S,"ENTITY_STATUS_EATING_ACCEPTED",cEntity::ENTITY_STATUS_EATING_ACCEPTED); - tolua_constant(tolua_S,"ENTITY_STATUS_SHEEP_EATING",cEntity::ENTITY_STATUS_SHEEP_EATING); - tolua_constant(tolua_S,"FIRE_TICKS_PER_DAMAGE",cEntity::FIRE_TICKS_PER_DAMAGE); - tolua_constant(tolua_S,"FIRE_DAMAGE",cEntity::FIRE_DAMAGE); - tolua_constant(tolua_S,"LAVA_TICKS_PER_DAMAGE",cEntity::LAVA_TICKS_PER_DAMAGE); - tolua_constant(tolua_S,"LAVA_DAMAGE",cEntity::LAVA_DAMAGE); - tolua_constant(tolua_S,"BURN_TICKS_PER_DAMAGE",cEntity::BURN_TICKS_PER_DAMAGE); - tolua_constant(tolua_S,"BURN_DAMAGE",cEntity::BURN_DAMAGE); - tolua_constant(tolua_S,"BURN_TICKS",cEntity::BURN_TICKS); tolua_constant(tolua_S,"etEntity",cEntity::etEntity); tolua_constant(tolua_S,"etPlayer",cEntity::etPlayer); tolua_constant(tolua_S,"etPickup",cEntity::etPickup); tolua_constant(tolua_S,"etMonster",cEntity::etMonster); tolua_constant(tolua_S,"etFallingBlock",cEntity::etFallingBlock); tolua_constant(tolua_S,"etMinecart",cEntity::etMinecart); + tolua_constant(tolua_S,"etBoat",cEntity::etBoat); tolua_constant(tolua_S,"etTNT",cEntity::etTNT); tolua_constant(tolua_S,"etProjectile",cEntity::etProjectile); tolua_constant(tolua_S,"etMob",cEntity::etMob); - tolua_constant(tolua_S,"eEntityType_Entity",cEntity::eEntityType_Entity); - tolua_constant(tolua_S,"eEntityType_Player",cEntity::eEntityType_Player); - tolua_constant(tolua_S,"eEntityType_Pickup",cEntity::eEntityType_Pickup); - tolua_constant(tolua_S,"eEntityType_Mob",cEntity::eEntityType_Mob); tolua_function(tolua_S,"GetEntityType",tolua_AllToLua_cEntity_GetEntityType00); tolua_function(tolua_S,"IsPlayer",tolua_AllToLua_cEntity_IsPlayer00); tolua_function(tolua_S,"IsPickup",tolua_AllToLua_cEntity_IsPickup00); tolua_function(tolua_S,"IsMob",tolua_AllToLua_cEntity_IsMob00); + tolua_function(tolua_S,"IsFallingBlock",tolua_AllToLua_cEntity_IsFallingBlock00); tolua_function(tolua_S,"IsMinecart",tolua_AllToLua_cEntity_IsMinecart00); + tolua_function(tolua_S,"IsBoat",tolua_AllToLua_cEntity_IsBoat00); tolua_function(tolua_S,"IsTNT",tolua_AllToLua_cEntity_IsTNT00); + tolua_function(tolua_S,"IsProjectile",tolua_AllToLua_cEntity_IsProjectile00); tolua_function(tolua_S,"IsA",tolua_AllToLua_cEntity_IsA00); tolua_function(tolua_S,"GetClass",tolua_AllToLua_cEntity_GetClass00); tolua_function(tolua_S,"GetClassStatic",tolua_AllToLua_cEntity_GetClassStatic00); @@ -29751,6 +30234,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"IsRiding",tolua_AllToLua_cEntity_IsRiding00); tolua_function(tolua_S,"IsSprinting",tolua_AllToLua_cEntity_IsSprinting00); tolua_function(tolua_S,"IsRclking",tolua_AllToLua_cEntity_IsRclking00); + tolua_function(tolua_S,"IsInvisible",tolua_AllToLua_cEntity_IsInvisible00); tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cPawn","cPawn","cEntity",NULL); tolua_beginmodule(tolua_S,"cPawn"); @@ -29956,6 +30440,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"SetVersion",tolua_AllToLua_cPlugin_SetVersion00); tolua_function(tolua_S,"GetDirectory",tolua_AllToLua_cPlugin_GetDirectory00); tolua_function(tolua_S,"GetLocalDirectory",tolua_AllToLua_cPlugin_GetLocalDirectory00); + tolua_function(tolua_S,"GetLocalFolder",tolua_AllToLua_cPlugin_GetLocalFolder00); tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cPluginLua","cPluginLua","cPlugin",NULL); tolua_beginmodule(tolua_S,"cPluginLua"); @@ -29971,14 +30456,11 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cWorld","cWorld","",NULL); tolua_beginmodule(tolua_S,"cWorld"); - tolua_function(tolua_S,"GetClassStatic",tolua_AllToLua_cWorld_GetClassStatic00); - tolua_function(tolua_S,"GetTime",tolua_AllToLua_cWorld_GetTime00); tolua_function(tolua_S,"GetTicksUntilWeatherChange",tolua_AllToLua_cWorld_GetTicksUntilWeatherChange00); tolua_function(tolua_S,"GetWorldAge",tolua_AllToLua_cWorld_GetWorldAge00); tolua_function(tolua_S,"GetTimeOfDay",tolua_AllToLua_cWorld_GetTimeOfDay00); tolua_function(tolua_S,"SetTicksUntilWeatherChange",tolua_AllToLua_cWorld_SetTicksUntilWeatherChange00); tolua_function(tolua_S,"SetTimeOfDay",tolua_AllToLua_cWorld_SetTimeOfDay00); - tolua_function(tolua_S,"SetWorldTime",tolua_AllToLua_cWorld_SetWorldTime00); tolua_function(tolua_S,"GetGameMode",tolua_AllToLua_cWorld_GetGameMode00); tolua_function(tolua_S,"IsGameModeCreative",tolua_AllToLua_cWorld_IsGameModeCreative00); tolua_function(tolua_S,"IsGameModeSurvival",tolua_AllToLua_cWorld_IsGameModeSurvival00); @@ -30001,8 +30483,6 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cWorld_SetBlockMeta00); tolua_function(tolua_S,"GetBlockSkyLight",tolua_AllToLua_cWorld_GetBlockSkyLight00); tolua_function(tolua_S,"GetBlockBlockLight",tolua_AllToLua_cWorld_GetBlockBlockLight00); - tolua_function(tolua_S,"GetBlockTypeMeta",tolua_AllToLua_cWorld_GetBlockTypeMeta00); - tolua_function(tolua_S,"GetBlockInfo",tolua_AllToLua_cWorld_GetBlockInfo00); tolua_function(tolua_S,"FastSetBlock",tolua_AllToLua_cWorld_FastSetBlock01); tolua_function(tolua_S,"GetBlock",tolua_AllToLua_cWorld_GetBlock01); tolua_function(tolua_S,"GetBlockMeta",tolua_AllToLua_cWorld_GetBlockMeta01); @@ -30018,7 +30498,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"WakeUpSimulators",tolua_AllToLua_cWorld_WakeUpSimulators00); tolua_function(tolua_S,"WakeUpSimulatorsInArea",tolua_AllToLua_cWorld_WakeUpSimulatorsInArea00); tolua_function(tolua_S,"DoExplosionAt",tolua_AllToLua_cWorld_DoExplosionAt00); - tolua_function(tolua_S,"GetSignLines",tolua_AllToLua_cWorld_GetSignLines00); + tolua_function(tolua_S,"UseBlockEntity",tolua_AllToLua_cWorld_UseBlockEntity00); tolua_function(tolua_S,"GrowTree",tolua_AllToLua_cWorld_GrowTree00); tolua_function(tolua_S,"GrowTreeFromSapling",tolua_AllToLua_cWorld_GrowTreeFromSapling00); tolua_function(tolua_S,"GrowTreeByBiome",tolua_AllToLua_cWorld_GrowTreeByBiome00); @@ -30029,7 +30509,6 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"GetBiomeAt",tolua_AllToLua_cWorld_GetBiomeAt00); tolua_function(tolua_S,"GetName",tolua_AllToLua_cWorld_GetName00); tolua_function(tolua_S,"GetIniFileName",tolua_AllToLua_cWorld_GetIniFileName00); - tolua_function(tolua_S,"SaveAllChunks",tolua_AllToLua_cWorld_SaveAllChunks00); tolua_function(tolua_S,"QueueSaveAllChunks",tolua_AllToLua_cWorld_QueueSaveAllChunks00); tolua_function(tolua_S,"GetNumChunks",tolua_AllToLua_cWorld_GetNumChunks00); tolua_function(tolua_S,"GetGeneratorQueueLength",tolua_AllToLua_cWorld_GetGeneratorQueueLength00); @@ -30041,6 +30520,10 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"SetWeather",tolua_AllToLua_cWorld_SetWeather00); tolua_function(tolua_S,"ChangeWeather",tolua_AllToLua_cWorld_ChangeWeather00); tolua_function(tolua_S,"GetWeather",tolua_AllToLua_cWorld_GetWeather00); + tolua_function(tolua_S,"IsWeatherSunny",tolua_AllToLua_cWorld_IsWeatherSunny00); + tolua_function(tolua_S,"IsWeatherRain",tolua_AllToLua_cWorld_IsWeatherRain00); + tolua_function(tolua_S,"IsWeatherStorm",tolua_AllToLua_cWorld_IsWeatherStorm00); + tolua_function(tolua_S,"IsWeatherWet",tolua_AllToLua_cWorld_IsWeatherWet00); tolua_function(tolua_S,"SetNextBlockTick",tolua_AllToLua_cWorld_SetNextBlockTick00); tolua_function(tolua_S,"GetMaxSugarcaneHeight",tolua_AllToLua_cWorld_GetMaxSugarcaneHeight00); tolua_function(tolua_S,"GetMaxCactusHeight",tolua_AllToLua_cWorld_GetMaxCactusHeight00); @@ -30337,11 +30820,11 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_variable(tolua_S,"PluginName",tolua_get_sWebAdminPage_PluginName,tolua_set_sWebAdminPage_PluginName); tolua_variable(tolua_S,"TabName",tolua_get_sWebAdminPage_TabName,tolua_set_sWebAdminPage_TabName); tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cWebAdmin","cWebAdmin","",NULL); + tolua_cclass(tolua_S,"cWebAdmin","cWebAdmin","cHTTPServer::cCallbacks",NULL); tolua_beginmodule(tolua_S,"cWebAdmin"); tolua_function(tolua_S,"GetMemoryUsage",tolua_AllToLua_cWebAdmin_GetMemoryUsage00); - tolua_function(tolua_S,"GetPort",tolua_AllToLua_cWebAdmin_GetPort00); tolua_function(tolua_S,"GetPage",tolua_AllToLua_cWebAdmin_GetPage00); + tolua_function(tolua_S,"GetDefaultPage",tolua_AllToLua_cWebAdmin_GetDefaultPage00); tolua_function(tolua_S,"GetBaseURL",tolua_AllToLua_cWebAdmin_GetBaseURL00); tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cWebPlugin","cWebPlugin","",NULL); @@ -30369,6 +30852,8 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"SaveAllChunks",tolua_AllToLua_cRoot_SaveAllChunks00); tolua_function(tolua_S,"BroadcastChat",tolua_AllToLua_cRoot_BroadcastChat00); tolua_function(tolua_S,"GetProtocolVersionTextFromInt",tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00); + tolua_function(tolua_S,"GetVirtualRAMUsage",tolua_AllToLua_cRoot_GetVirtualRAMUsage00); + tolua_function(tolua_S,"GetPhysicalRAMUsage",tolua_AllToLua_cRoot_GetPhysicalRAMUsage00); tolua_endmodule(tolua_S); #ifdef __cplusplus tolua_cclass(tolua_S,"Vector3f","Vector3f","",tolua_collect_Vector3f); @@ -30658,7 +31143,6 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_beginmodule(tolua_S,"cChunkDesc"); tolua_function(tolua_S,"GetChunkX",tolua_AllToLua_cChunkDesc_GetChunkX00); tolua_function(tolua_S,"GetChunkZ",tolua_AllToLua_cChunkDesc_GetChunkZ00); - tolua_function(tolua_S,"SetChunkCoords",tolua_AllToLua_cChunkDesc_SetChunkCoords00); tolua_function(tolua_S,"FillBlocks",tolua_AllToLua_cChunkDesc_FillBlocks00); tolua_function(tolua_S,"SetBlockTypeMeta",tolua_AllToLua_cChunkDesc_SetBlockTypeMeta00); tolua_function(tolua_S,"GetBlockTypeMeta",tolua_AllToLua_cChunkDesc_GetBlockTypeMeta00); @@ -30764,34 +31248,36 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cMonster","cMonster","cPawn",NULL); tolua_beginmodule(tolua_S,"cMonster"); + tolua_constant(tolua_S,"mtBat",cMonster::mtBat); + tolua_constant(tolua_S,"mtBlaze",cMonster::mtBlaze); + tolua_constant(tolua_S,"mtCaveSpider",cMonster::mtCaveSpider); + tolua_constant(tolua_S,"mtChicken",cMonster::mtChicken); + tolua_constant(tolua_S,"mtCow",cMonster::mtCow); tolua_constant(tolua_S,"mtCreeper",cMonster::mtCreeper); - tolua_constant(tolua_S,"mtSkeleton",cMonster::mtSkeleton); - tolua_constant(tolua_S,"mtSpider",cMonster::mtSpider); - tolua_constant(tolua_S,"mtGiant",cMonster::mtGiant); - tolua_constant(tolua_S,"mtZombie",cMonster::mtZombie); - tolua_constant(tolua_S,"mtSlime",cMonster::mtSlime); - tolua_constant(tolua_S,"mtGhast",cMonster::mtGhast); - tolua_constant(tolua_S,"mtZombiePigman",cMonster::mtZombiePigman); + tolua_constant(tolua_S,"mtEnderDragon",cMonster::mtEnderDragon); tolua_constant(tolua_S,"mtEnderman",cMonster::mtEnderman); - tolua_constant(tolua_S,"mtCaveSpider",cMonster::mtCaveSpider); - tolua_constant(tolua_S,"mtSilverfish",cMonster::mtSilverfish); - tolua_constant(tolua_S,"mtBlaze",cMonster::mtBlaze); + tolua_constant(tolua_S,"mtGhast",cMonster::mtGhast); + tolua_constant(tolua_S,"mtGiant",cMonster::mtGiant); + tolua_constant(tolua_S,"mtHorse",cMonster::mtHorse); + tolua_constant(tolua_S,"mtIronGolem",cMonster::mtIronGolem); tolua_constant(tolua_S,"mtMagmaCube",cMonster::mtMagmaCube); - tolua_constant(tolua_S,"mtEnderDragon",cMonster::mtEnderDragon); - tolua_constant(tolua_S,"mtWither",cMonster::mtWither); - tolua_constant(tolua_S,"mtBat",cMonster::mtBat); - tolua_constant(tolua_S,"mtWitch",cMonster::mtWitch); + tolua_constant(tolua_S,"mtMooshroom",cMonster::mtMooshroom); + tolua_constant(tolua_S,"mtOcelot",cMonster::mtOcelot); tolua_constant(tolua_S,"mtPig",cMonster::mtPig); tolua_constant(tolua_S,"mtSheep",cMonster::mtSheep); - tolua_constant(tolua_S,"mtCow",cMonster::mtCow); - tolua_constant(tolua_S,"mtChicken",cMonster::mtChicken); - tolua_constant(tolua_S,"mtSquid",cMonster::mtSquid); - tolua_constant(tolua_S,"mtWolf",cMonster::mtWolf); - tolua_constant(tolua_S,"mtMooshroom",cMonster::mtMooshroom); + tolua_constant(tolua_S,"mtSilverfish",cMonster::mtSilverfish); + tolua_constant(tolua_S,"mtSkeleton",cMonster::mtSkeleton); + tolua_constant(tolua_S,"mtSlime",cMonster::mtSlime); tolua_constant(tolua_S,"mtSnowGolem",cMonster::mtSnowGolem); - tolua_constant(tolua_S,"mtOcelot",cMonster::mtOcelot); - tolua_constant(tolua_S,"mtIronGolem",cMonster::mtIronGolem); + tolua_constant(tolua_S,"mtSpider",cMonster::mtSpider); + tolua_constant(tolua_S,"mtSquid",cMonster::mtSquid); tolua_constant(tolua_S,"mtVillager",cMonster::mtVillager); + tolua_constant(tolua_S,"mtWitch",cMonster::mtWitch); + tolua_constant(tolua_S,"mtWither",cMonster::mtWither); + tolua_constant(tolua_S,"mtWolf",cMonster::mtWolf); + tolua_constant(tolua_S,"mtZombie",cMonster::mtZombie); + tolua_constant(tolua_S,"mtZombiePigman",cMonster::mtZombiePigman); + tolua_function(tolua_S,"GetMobType",tolua_AllToLua_cMonster_GetMobType00); tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cLineBlockTracer","cLineBlockTracer","",NULL); tolua_beginmodule(tolua_S,"cLineBlockTracer"); diff --git a/source/Bindings.h b/source/Bindings.h index 95935fb90..1d567520c 100644 --- a/source/Bindings.h +++ b/source/Bindings.h @@ -1,6 +1,6 @@ /*
** Lua binding: AllToLua
-** Generated automatically by tolua++-1.0.92 on 09/07/13 22:05:19.
+** Generated automatically by tolua++-1.0.92 on 10/13/13 18:01:22.
*/
/* Exported function */
diff --git a/source/BlockID.cpp b/source/BlockID.cpp index 687f27aa8..82bff9234 100644 --- a/source/BlockID.cpp +++ b/source/BlockID.cpp @@ -21,6 +21,7 @@ bool g_BlockPistonBreakable[256]; bool g_BlockIsSnowable[256]; bool g_BlockRequiresSpecialTool[256]; bool g_BlockIsSolid[256]; +bool g_BlockIsTorchPlaceable[256]; @@ -306,6 +307,48 @@ EMCSBiome StringToBiome(const AString & a_BiomeString) {biExtremeHillsEdge, "ExtremeHillsEdge"}, {biJungle, "Jungle"}, {biJungleHills, "JungleHills"}, + + // Release 1.7 biomes: + {biJungleEdge, "JungleEdge"}, + {biDeepOcean, "DeepOcean"}, + {biStoneBeach, "StoneBeach"}, + {biColdBeach, "ColdBeach"}, + {biBirchForest, "BirchForest"}, + {biBirchForestHills, "BirchForestHills"}, + {biRoofedForest, "RoofedForest"}, + {biColdTaiga, "ColdTaiga"}, + {biColdTaigaHills, "ColdTaigaHills"}, + {biMegaTaiga, "MegaTaiga"}, + {biMegaTaigaHills, "MegaTaigaHills"}, + {biExtremeHillsPlus, "ExtremeHillsPlus"}, + {biSavanna, "Savanna"}, + {biSavannaPlateau, "SavannaPlateau"}, + {biMesa, "Mesa"}, + {biMesaPlateauF, "MesaPlateauF"}, + {biMesaPlateau, "MesaPlateau"}, + + // Release 1.7 variants: + {biSunflowerPlains, "SunflowerPlains"}, + {biDesertM, "DesertM"}, + {biExtremeHillsM, "ExtremeHillsM"}, + {biFlowerForest, "FlowerForest"}, + {biTaigaM, "TaigaM"}, + {biSwamplandM, "SwamplandM"}, + {biIcePlainsSpikes, "IcePlainsSpikes"}, + {biJungleM, "JungleM"}, + {biJungleEdgeM, "JungleEdgeM"}, + {biBirchForestM, "BirchForestM"}, + {biBirchForestHillsM, "BirchForestHillsM"}, + {biRoofedForestM, "RoofedForestM"}, + {biColdTaigaM, "ColdTaigaM"}, + {biMegaSpruceTaiga, "MegaSpruceTaiga"}, + {biMegaSpruceTaigaHills, "MegaSpruceTaigaHills"}, + {biExtremeHillsPlusM, "ExtremeHillsPlusM"}, + {biSavannaM, "SavannaM"}, + {biSavannaPlateauM, "SavannaPlateauM"}, + {biMesaBryce, "MesaBryce"}, + {biMesaPlateauFM, "MesaPlateauFM"}, + {biMesaPlateauM, "MesaPlateauM"}, } ; for (int i = 0; i < ARRAYCOUNT(BiomeMap); i++) @@ -517,6 +560,21 @@ eDamageType StringToDamageType(const AString & a_DamageTypeString) +cItem GetIniItemSet(cIniFile & a_IniFile, const char * a_Section, const char * a_Key, const char * a_Default) +{ + AString ItemStr = a_IniFile.GetValueSet(a_Section, a_Key, a_Default); + cItem res; + if (!StringToItem(ItemStr, res)) + { + res.Empty(); + } + return res; +} + + + + + // This is actually just some code that needs to run at program startup, so it is wrapped into a global var's constructor: class cBlockPropertiesInitializer { @@ -528,6 +586,7 @@ public: memset(g_BlockTransparent, 0x00, sizeof(g_BlockTransparent)); memset(g_BlockOneHitDig, 0x00, sizeof(g_BlockOneHitDig)); memset(g_BlockPistonBreakable, 0x00, sizeof(g_BlockPistonBreakable)); + memset(g_BlockIsTorchPlaceable, 0x00, sizeof(g_BlockIsTorchPlaceable)); // Setting bools to true must be done manually, see http://forum.mc-server.org/showthread.php?tid=629&pid=5415#pid5415 for (int i = 0; i < ARRAYCOUNT(g_BlockIsSnowable); i++) @@ -601,6 +660,7 @@ public: g_BlockTransparent[E_BLOCK_FIRE] = true; g_BlockTransparent[E_BLOCK_FLOWER_POT] = true; g_BlockTransparent[E_BLOCK_GLASS] = true; + g_BlockTransparent[E_BLOCK_GLASS_PANE] = true; g_BlockTransparent[E_BLOCK_ICE] = true; g_BlockTransparent[E_BLOCK_IRON_DOOR] = true; g_BlockTransparent[E_BLOCK_LAVA] = true; @@ -632,11 +692,13 @@ public: // TODO: Any other transparent blocks? // One hit break blocks + g_BlockOneHitDig[E_BLOCK_ACTIVE_COMPARATOR] = true; g_BlockOneHitDig[E_BLOCK_BROWN_MUSHROOM] = true; g_BlockOneHitDig[E_BLOCK_CARROTS] = true; g_BlockOneHitDig[E_BLOCK_CROPS] = true; g_BlockOneHitDig[E_BLOCK_FIRE] = true; g_BlockOneHitDig[E_BLOCK_FLOWER_POT] = true; + g_BlockOneHitDig[E_BLOCK_INACTIVE_COMPARATOR] = true; g_BlockOneHitDig[E_BLOCK_LOCKED_CHEST] = true; g_BlockOneHitDig[E_BLOCK_MELON_STEM] = true; g_BlockOneHitDig[E_BLOCK_POTATOES] = true; @@ -646,7 +708,6 @@ public: g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_OFF] = true; g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_ON] = true; g_BlockOneHitDig[E_BLOCK_REDSTONE_WIRE] = true; - g_BlockOneHitDig[E_BLOCK_REDSTONE_WIRE] = true; g_BlockOneHitDig[E_BLOCK_RED_MUSHROOM] = true; g_BlockOneHitDig[E_BLOCK_RED_ROSE] = true; g_BlockOneHitDig[E_BLOCK_REEDS] = true; @@ -657,6 +718,7 @@ public: g_BlockOneHitDig[E_BLOCK_YELLOW_FLOWER] = true; // Blocks that breaks when pushed by piston + g_BlockPistonBreakable[E_BLOCK_ACTIVE_COMPARATOR] = true; g_BlockPistonBreakable[E_BLOCK_AIR] = true; g_BlockPistonBreakable[E_BLOCK_BED] = true; g_BlockPistonBreakable[E_BLOCK_BROWN_MUSHROOM] = true; @@ -664,6 +726,7 @@ public: g_BlockPistonBreakable[E_BLOCK_CROPS] = true; g_BlockPistonBreakable[E_BLOCK_DEAD_BUSH] = true; g_BlockPistonBreakable[E_BLOCK_FIRE] = true; + g_BlockPistonBreakable[E_BLOCK_INACTIVE_COMPARATOR] = true; g_BlockPistonBreakable[E_BLOCK_IRON_DOOR] = true; g_BlockPistonBreakable[E_BLOCK_JACK_O_LANTERN] = true; g_BlockPistonBreakable[E_BLOCK_LADDER] = true; @@ -696,6 +759,7 @@ public: // Blocks that can be snowed over: + g_BlockIsSnowable[E_BLOCK_ACTIVE_COMPARATOR] = false; g_BlockIsSnowable[E_BLOCK_AIR] = false; g_BlockIsSnowable[E_BLOCK_BROWN_MUSHROOM] = false; g_BlockIsSnowable[E_BLOCK_CACTUS] = false; @@ -704,7 +768,9 @@ public: g_BlockIsSnowable[E_BLOCK_FIRE] = false; g_BlockIsSnowable[E_BLOCK_GLASS] = false; g_BlockIsSnowable[E_BLOCK_ICE] = false; + g_BlockIsSnowable[E_BLOCK_INACTIVE_COMPARATOR] = false; g_BlockIsSnowable[E_BLOCK_LAVA] = false; + g_BlockIsSnowable[E_BLOCK_LILY_PAD] = false; g_BlockIsSnowable[E_BLOCK_LOCKED_CHEST] = false; g_BlockIsSnowable[E_BLOCK_REDSTONE_REPEATER_OFF] = false; g_BlockIsSnowable[E_BLOCK_REDSTONE_REPEATER_ON] = false; @@ -726,8 +792,9 @@ public: g_BlockIsSnowable[E_BLOCK_WALLSIGN] = false; g_BlockIsSnowable[E_BLOCK_WATER] = false; g_BlockIsSnowable[E_BLOCK_YELLOW_FLOWER] = false; + - // Blocks that don´t drop without a special tool + // Blocks that don't drop without a special tool g_BlockRequiresSpecialTool[E_BLOCK_BRICK] = true; g_BlockRequiresSpecialTool[E_BLOCK_CAULDRON] = true; g_BlockRequiresSpecialTool[E_BLOCK_COAL_ORE] = true; @@ -763,35 +830,24 @@ public: g_BlockRequiresSpecialTool[E_BLOCK_VINES] = true; // Nonsolid Blocks: + g_BlockIsSolid[E_BLOCK_ACTIVATOR_RAIL] = false; g_BlockIsSolid[E_BLOCK_AIR] = false; - g_BlockIsSolid[E_BLOCK_BED] = false; - g_BlockIsSolid[E_BLOCK_BIRCH_WOOD_STAIRS] = false; - g_BlockIsSolid[E_BLOCK_BRICK_STAIRS] = false; g_BlockIsSolid[E_BLOCK_BROWN_MUSHROOM] = false; - g_BlockIsSolid[E_BLOCK_CACTUS] = false; - g_BlockIsSolid[E_BLOCK_CAKE] = false; - g_BlockIsSolid[E_BLOCK_CHEST] = false; - g_BlockIsSolid[E_BLOCK_COBBLESTONE_STAIRS] = false; + g_BlockIsSolid[E_BLOCK_CARROTS] = false; + g_BlockIsSolid[E_BLOCK_COBWEB] = false; g_BlockIsSolid[E_BLOCK_CROPS] = false; - g_BlockIsSolid[E_BLOCK_ENCHANTMENT_TABLE] = false; + g_BlockIsSolid[E_BLOCK_DETECTOR_RAIL] = false; g_BlockIsSolid[E_BLOCK_END_PORTAL] = false; - g_BlockIsSolid[E_BLOCK_END_PORTAL_FRAME] = false; - g_BlockIsSolid[E_BLOCK_FARMLAND] = false; - g_BlockIsSolid[E_BLOCK_FENCE] = false; g_BlockIsSolid[E_BLOCK_FIRE] = false; - g_BlockIsSolid[E_BLOCK_GLASS] = false; - g_BlockIsSolid[E_BLOCK_IRON_DOOR] = false; - g_BlockIsSolid[E_BLOCK_JUNGLE_WOOD_STAIRS] = false; - g_BlockIsSolid[E_BLOCK_LADDER] = false; + g_BlockIsSolid[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE] = false; g_BlockIsSolid[E_BLOCK_LAVA] = false; - g_BlockIsSolid[E_BLOCK_LEAVES] = false; g_BlockIsSolid[E_BLOCK_LEVER] = false; - g_BlockIsSolid[E_BLOCK_LOCKED_CHEST] = false; - g_BlockIsSolid[E_BLOCK_NETHER_BRICK_STAIRS] = false; + g_BlockIsSolid[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE] = false; + g_BlockIsSolid[E_BLOCK_MELON_STEM] = false; g_BlockIsSolid[E_BLOCK_NETHER_PORTAL] = false; g_BlockIsSolid[E_BLOCK_PISTON] = false; g_BlockIsSolid[E_BLOCK_PISTON_EXTENSION] = false; - g_BlockIsSolid[E_BLOCK_RAIL] = true; + g_BlockIsSolid[E_BLOCK_RAIL] = false; g_BlockIsSolid[E_BLOCK_REDSTONE_REPEATER_OFF] = false; g_BlockIsSolid[E_BLOCK_REDSTONE_REPEATER_ON] = false; g_BlockIsSolid[E_BLOCK_REDSTONE_TORCH_OFF] = false; @@ -800,29 +856,84 @@ public: g_BlockIsSolid[E_BLOCK_RED_MUSHROOM] = false; g_BlockIsSolid[E_BLOCK_RED_ROSE] = false; g_BlockIsSolid[E_BLOCK_REEDS] = false; - g_BlockIsSolid[E_BLOCK_SANDSTONE_STAIRS] = false; g_BlockIsSolid[E_BLOCK_SAPLING] = false; g_BlockIsSolid[E_BLOCK_SIGN_POST] = false; g_BlockIsSolid[E_BLOCK_SNOW] = false; - g_BlockIsSolid[E_BLOCK_SPRUCE_WOOD_STAIRS] = false; g_BlockIsSolid[E_BLOCK_STATIONARY_LAVA] = false; g_BlockIsSolid[E_BLOCK_STATIONARY_WATER] = false; - g_BlockIsSolid[E_BLOCK_STONE_BRICK_STAIRS] = false; g_BlockIsSolid[E_BLOCK_STONE_BUTTON] = false; g_BlockIsSolid[E_BLOCK_STONE_PRESSURE_PLATE] = false; - g_BlockIsSolid[E_BLOCK_STONE_SLAB] = false; g_BlockIsSolid[E_BLOCK_TALL_GRASS] = false; - g_BlockIsSolid[E_BLOCK_TNT] = false; g_BlockIsSolid[E_BLOCK_TORCH] = false; - g_BlockIsSolid[E_BLOCK_TRAPDOOR] = false; + g_BlockIsSolid[E_BLOCK_TRIPWIRE] = false; g_BlockIsSolid[E_BLOCK_VINES] = false; g_BlockIsSolid[E_BLOCK_WALLSIGN] = false; g_BlockIsSolid[E_BLOCK_WATER] = false; g_BlockIsSolid[E_BLOCK_WOODEN_BUTTON] = false; - g_BlockIsSolid[E_BLOCK_WOODEN_DOOR] = false; g_BlockIsSolid[E_BLOCK_WOODEN_PRESSURE_PLATE] = false; g_BlockIsSolid[E_BLOCK_WOODEN_SLAB] = false; g_BlockIsSolid[E_BLOCK_YELLOW_FLOWER] = false; + + // Torch placeable + g_BlockIsTorchPlaceable[E_BLOCK_BEDROCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_BLOCK_OF_COAL] = true; + g_BlockIsTorchPlaceable[E_BLOCK_BLOCK_OF_REDSTONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_BOOKCASE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_BRICK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_CLAY] = true; + g_BlockIsTorchPlaceable[E_BLOCK_COAL_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_COBBLESTONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_COMMAND_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_CRAFTING_TABLE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DIAMOND_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DIAMOND_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DIRT] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DISPENSER] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DOUBLE_STONE_SLAB] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DOUBLE_WOODEN_SLAB] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DROPPER] = true; + g_BlockIsTorchPlaceable[E_BLOCK_EMERALD_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_EMERALD_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_END_STONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_FURNACE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_GLOWSTONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_GOLD_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_GOLD_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_GRASS] = true; + g_BlockIsTorchPlaceable[E_BLOCK_GRAVEL] = true; + g_BlockIsTorchPlaceable[E_BLOCK_HARDENED_CLAY] = true; + g_BlockIsTorchPlaceable[E_BLOCK_HAY_BALE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_HUGE_BROWN_MUSHROOM] = true; + g_BlockIsTorchPlaceable[E_BLOCK_HUGE_RED_MUSHROOM] = true; + g_BlockIsTorchPlaceable[E_BLOCK_IRON_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_IRON_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_JACK_O_LANTERN] = true; + g_BlockIsTorchPlaceable[E_BLOCK_JUKEBOX] = true; + g_BlockIsTorchPlaceable[E_BLOCK_LAPIS_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_LAPIS_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_LOG] = true; + g_BlockIsTorchPlaceable[E_BLOCK_MELON] = true; + g_BlockIsTorchPlaceable[E_BLOCK_MOSSY_COBBLESTONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_MYCELIUM] = true; + g_BlockIsTorchPlaceable[E_BLOCK_NETHERRACK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_NETHER_BRICK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_NETHER_QUARTZ_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_NOTE_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_OBSIDIAN] = true; + g_BlockIsTorchPlaceable[E_BLOCK_PLANKS] = true; + g_BlockIsTorchPlaceable[E_BLOCK_PUMPKIN] = true; + g_BlockIsTorchPlaceable[E_BLOCK_QUARTZ_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_LAMP_OFF] = true; + g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_LAMP_ON] = true; + g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_ORE_GLOWING] = true; + g_BlockIsTorchPlaceable[E_BLOCK_SANDSTONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_SAND] = true; + g_BlockIsTorchPlaceable[E_BLOCK_SILVERFISH_EGG] = true; + g_BlockIsTorchPlaceable[E_BLOCK_SPONGE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_STAINED_CLAY] = true; + g_BlockIsTorchPlaceable[E_BLOCK_WOOL] = true; + g_BlockIsTorchPlaceable[E_BLOCK_STONE] = true; } } BlockPropertiesInitializer; diff --git a/source/BlockID.h b/source/BlockID.h index cd3bd78c4..28725406d 100644 --- a/source/BlockID.h +++ b/source/BlockID.h @@ -165,7 +165,7 @@ enum ENUM_BLOCK_ID E_BLOCK_NETHER_QUARTZ_ORE = 153, E_BLOCK_HOPPER = 154, E_BLOCK_QUARTZ_BLOCK = 155, - E_BLOCK_QUARTZ_STAIR = 156, + E_BLOCK_QUARTZ_STAIRS = 156, E_BLOCK_ACTIVATOR_RAIL = 157, E_BLOCK_DROPPER = 158, @@ -323,17 +323,17 @@ enum ENUM_ITEM_ID E_ITEM_BOOK_AND_QUILL = 386, E_ITEM_WRITTEN_BOOK = 387, E_ITEM_EMERALD = 388, - // TODO: missing an item: item frame + E_ITEM_ITEM_FRAME = 389, E_ITEM_FLOWER_POT = 390, E_ITEM_CARROT = 391, E_ITEM_POTATO = 392, E_ITEM_BAKED_POTATO = 393, E_ITEM_POISONOUS_POTATO = 394, - // TODO: missing an item: empty map + E_ITEM_EMPTY_MAP = 395, E_ITEM_GOLDEN_CARROT = 396, E_ITEM_HEAD = 397, E_ITEM_CARROT_ON_STICK = 398, - // TODO: missing an item: nether star + E_ITEM_NETHER_STAR = 399, E_ITEM_PUMPKIN_PIE = 400, E_ITEM_FIREWORK_ROCKET = 401, E_ITEM_FIREWORK_STAR = 402, @@ -607,35 +607,60 @@ enum // E_ITEM_SPAWN_EGG metas: // See also cMonster::eType, since monster type and spawn egg meta are the same - E_META_SPAWN_EGG_CREEPER = 50, - E_META_SPAWN_EGG_SKELETON = 51, - E_META_SPAWN_EGG_SPIDER = 52, - E_META_SPAWN_EGG_ZOMBIE = 54, - E_META_SPAWN_EGG_GIANT = 53, - E_META_SPAWN_EGG_SLIME = 55, - E_META_SPAWN_EGG_GHAST = 56, - E_META_SPAWN_EGG_ZOMBIE_PIGMAN = 57, - E_META_SPAWN_EGG_ENDERMAN = 58, - E_META_SPAWN_EGG_CAVE_SPIDER = 59, - E_META_SPAWN_EGG_SILVERFISH = 60, - E_META_SPAWN_EGG_BLAZE = 61, - E_META_SPAWN_EGG_MAGMA_CUBE = 62, - E_META_SPAWN_EGG_ENDER_DRAGON = 63, - E_META_SPAWN_EGG_WITHER = 64, - E_META_SPAWN_EGG_BAT = 65, - E_META_SPAWN_EGG_WITCH = 66, - E_META_SPAWN_EGG_PIG = 90, - E_META_SPAWN_EGG_SHEEP = 91, - E_META_SPAWN_EGG_COW = 92, - E_META_SPAWN_EGG_CHICKEN = 93, - E_META_SPAWN_EGG_SQUID = 94, - E_META_SPAWN_EGG_WOLF = 95, - E_META_SPAWN_EGG_MOOSHROOM = 96, - E_META_SPAWN_EGG_SNOW_GOLEM = 97, - E_META_SPAWN_EGG_OCELOT = 98, - E_META_SPAWN_EGG_IRON_GOLEM = 99, - E_META_SPAWN_EGG_HORSE = 100, - E_META_SPAWN_EGG_VILLAGER = 120, + E_META_SPAWN_EGG_PICKUP = 1, + E_META_SPAWN_EGG_EXPERIENCE_ORB = 2, + E_META_SPAWN_EGG_LEASH_KNOT = 8, + E_META_SPAWN_EGG_PAINTING = 9, + E_META_SPAWN_EGG_ARROW = 10, + E_META_SPAWN_EGG_SNOWBALL = 11, + E_META_SPAWN_EGG_FIREBALL = 12, + E_META_SPAWN_EGG_SMALL_FIREBALL = 13, + E_META_SPAWN_EGG_ENDER_PEARL = 14, + E_META_SPAWN_EGG_EYE_OF_ENDER = 15, + E_META_SPAWN_EGG_SPLASH_POTION = 16, + E_META_SPAWN_EGG_EXP_BOTTLE = 17, + E_META_SPAWN_EGG_ITEM_FRAME = 18, + E_META_SPAWN_EGG_WITHER_SKULL = 19, + E_META_SPAWN_EGG_PRIMED_TNT = 20, + E_META_SPAWN_EGG_FALLING_BLOCK = 21, + E_META_SPAWN_EGG_FIREWORK = 22, + E_META_SPAWN_EGG_BOAT = 41, + E_META_SPAWN_EGG_MINECART = 42, + E_META_SPAWN_EGG_MINECART_CHEST = 43, + E_META_SPAWN_EGG_MINECART_FURNACE = 44, + E_META_SPAWN_EGG_MINECART_TNT = 45, + E_META_SPAWN_EGG_MINECART_HOPPER = 46, + E_META_SPAWN_EGG_MINECART_SPAWNER = 47, + E_META_SPAWN_EGG_CREEPER = 50, + E_META_SPAWN_EGG_SKELETON = 51, + E_META_SPAWN_EGG_SPIDER = 52, + E_META_SPAWN_EGG_GIANT = 53, + E_META_SPAWN_EGG_ZOMBIE = 54, + E_META_SPAWN_EGG_SLIME = 55, + E_META_SPAWN_EGG_GHAST = 56, + E_META_SPAWN_EGG_ZOMBIE_PIGMAN = 57, + E_META_SPAWN_EGG_ENDERMAN = 58, + E_META_SPAWN_EGG_CAVE_SPIDER = 59, + E_META_SPAWN_EGG_SILVERFISH = 60, + E_META_SPAWN_EGG_BLAZE = 61, + E_META_SPAWN_EGG_MAGMA_CUBE = 62, + E_META_SPAWN_EGG_ENDER_DRAGON = 63, + E_META_SPAWN_EGG_WITHER = 64, + E_META_SPAWN_EGG_BAT = 65, + E_META_SPAWN_EGG_WITCH = 66, + E_META_SPAWN_EGG_PIG = 90, + E_META_SPAWN_EGG_SHEEP = 91, + E_META_SPAWN_EGG_COW = 92, + E_META_SPAWN_EGG_CHICKEN = 93, + E_META_SPAWN_EGG_SQUID = 94, + E_META_SPAWN_EGG_WOLF = 95, + E_META_SPAWN_EGG_MOOSHROOM = 96, + E_META_SPAWN_EGG_SNOW_GOLEM = 97, + E_META_SPAWN_EGG_OCELOT = 98, + E_META_SPAWN_EGG_IRON_GOLEM = 99, + E_META_SPAWN_EGG_HORSE = 100, + E_META_SPAWN_EGG_VILLAGER = 120, + E_META_SPAWN_EGG_ENDER_CRYSTAL = 200, } ; @@ -721,8 +746,9 @@ enum eExplosionSource -// fwd: cItem.h: +// fwd: class cItem; +class cIniFile; @@ -760,6 +786,9 @@ extern AString DamageTypeToString(eDamageType a_DamageType); /// Translates a damage type string to damage type. Takes either a number or a damage type alias (built-in). Returns -1 on failure extern eDamageType StringToDamageType(const AString & a_DamageString); +/// Returns a cItem representing the item described in an IniFile's value; if the value doesn't exist, creates it with the provided default. +extern cItem GetIniItemSet(cIniFile & a_IniFile, const char * a_Section, const char * a_Key, const char * a_Default); + // tolua_end @@ -775,7 +804,7 @@ extern bool g_BlockPistonBreakable[256]; extern bool g_BlockIsSnowable[256]; extern bool g_BlockRequiresSpecialTool[256]; extern bool g_BlockIsSolid[256]; - +extern bool g_BlockIsTorchPlaceable[256]; diff --git a/source/Blocks/BlockBed.h b/source/Blocks/BlockBed.h index 0bf1cfc0f..8a289b22c 100644 --- a/source/Blocks/BlockBed.h +++ b/source/Blocks/BlockBed.h @@ -37,12 +37,6 @@ public: } - virtual bool DoesAllowBlockOnTop() override - { - return false; - } - - // Bed specific helper functions static NIBBLETYPE RotationToMetaData(double a_Rotation) { diff --git a/source/Blocks/BlockButton.cpp b/source/Blocks/BlockButton.cpp new file mode 100644 index 000000000..1011f9351 --- /dev/null +++ b/source/Blocks/BlockButton.cpp @@ -0,0 +1,39 @@ + +#include "Globals.h" +#include "BlockButton.h" + + + + + +cBlockButtonHandler::cBlockButtonHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockButtonHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + // Flip the ON bit on/off. Using XOR bitwise operation to turn it on/off. + NIBBLETYPE Meta = ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f); + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); + + if (Meta & 0x08) + { + a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); + } + else + { + a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.5f); + } + + // Queue a button reset (unpress), with a GetBlock to prevent duplication of buttons (press, break, wait for reset) + a_World->QueueSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ), ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f), m_BlockType == E_BLOCK_STONE_BUTTON ? 20 : 25); +} + + + + diff --git a/source/Blocks/BlockButton.h b/source/Blocks/BlockButton.h new file mode 100644 index 000000000..e3f655bfa --- /dev/null +++ b/source/Blocks/BlockButton.h @@ -0,0 +1,69 @@ +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockButtonHandler : + public cBlockHandler +{ +public: + cBlockButtonHandler(BLOCKTYPE a_BlockType); + + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(m_BlockType == E_BLOCK_WOODEN_BUTTON ? E_BLOCK_WOODEN_BUTTON : E_BLOCK_STONE_BUTTON, 1, 0)); + } + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = BlockFaceToMetaData(a_BlockFace); + return true; + } + + + virtual const char * GetStepSound(void) override + { + return m_BlockType == E_BLOCK_WOODEN_BUTTON ? "step.wood" : "step.stone"; + } + + + inline static NIBBLETYPE BlockFaceToMetaData(char a_BlockFace) + { + switch (a_BlockFace) + { + case BLOCK_FACE_ZP: { return 0x4; } + case BLOCK_FACE_ZM: { return 0x3; } + case BLOCK_FACE_XP: { return 0x2; } + case BLOCK_FACE_XM: { return 0x1; } + default: + { + ASSERT(!"Unhandled block face!"); + return 0x0; // No idea, give a special meta (button in centre of block) + } + } + } +} ; + + + + diff --git a/source/Blocks/BlockCactus.h b/source/Blocks/BlockCactus.h index 1d123bc0a..4147ad473 100644 --- a/source/Blocks/BlockCactus.h +++ b/source/Blocks/BlockCactus.h @@ -63,12 +63,6 @@ public: return true; } - - - virtual bool CanBePlacedOnSide(void) override - { - return false; - } void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override diff --git a/source/Blocks/BlockComparator.cpp b/source/Blocks/BlockComparator.cpp new file mode 100644 index 000000000..b4e5a55d0 --- /dev/null +++ b/source/Blocks/BlockComparator.cpp @@ -0,0 +1,53 @@ + +#include "Globals.h" +#include "BlockComparator.h" +#include "../Simulator/RedstoneSimulator.h" +#include "../Entities/Player.h" + + + + + +cBlockComparatorHandler::cBlockComparatorHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockComparatorHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Nothing needed yet +} + + + + + +void cBlockComparatorHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + Meta ^= 0x04; // Toggle 3rd (addition/subtraction) bit with XOR + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); +} + + + + +bool cBlockComparatorHandler::GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta +) +{ + a_BlockType = m_BlockType; + a_BlockMeta = cRedstoneSimulator::RepeaterRotationToMetaData(a_Player->GetRotation()); + return true; +} + + + + diff --git a/source/Blocks/BlockComparator.h b/source/Blocks/BlockComparator.h new file mode 100644 index 000000000..cb2941d3c --- /dev/null +++ b/source/Blocks/BlockComparator.h @@ -0,0 +1,55 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockComparatorHandler : + public cBlockHandler +{ +public: + cBlockComparatorHandler(BLOCKTYPE a_BlockType); + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(E_ITEM_COMPARATOR, 1, 0)); + } + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override; + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/source/Blocks/BlockCrops.h b/source/Blocks/BlockCrops.h index 4bc76fd50..e7b320eac 100644 --- a/source/Blocks/BlockCrops.h +++ b/source/Blocks/BlockCrops.h @@ -20,12 +20,6 @@ public: } - virtual bool DoesAllowBlockOnTop() override - { - return false; - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override { MTRand rand; @@ -84,10 +78,16 @@ public: void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override { NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - if (Meta < 7) + NIBBLETYPE Light = a_World->GetBlockBlockLight(a_BlockX, a_BlockY, a_BlockZ); + + if ((Meta < 7) && (Light > 8)) { a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_CROPS, ++Meta); } + else if (Light < 9) + { + a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); + } } diff --git a/source/Blocks/BlockDeadBush.h b/source/Blocks/BlockDeadBush.h index 379e8e5df..14617d006 100644 --- a/source/Blocks/BlockDeadBush.h +++ b/source/Blocks/BlockDeadBush.h @@ -28,18 +28,6 @@ public: { return (a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_SAND); } - - - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } - - - virtual bool CanBePlacedOnSide() override - { - return false; - } } ; diff --git a/source/Blocks/BlockDoor.cpp b/source/Blocks/BlockDoor.cpp index 02cbd28e2..e71ccd368 100644 --- a/source/Blocks/BlockDoor.cpp +++ b/source/Blocks/BlockDoor.cpp @@ -3,7 +3,6 @@ #include "BlockDoor.h" #include "../Item.h" #include "../World.h" -#include "../Doors.h" #include "../Entities/Player.h" @@ -26,7 +25,7 @@ void cBlockDoorHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY if (OldMeta & 8) { // Was upper part of door - if (cDoors::IsDoor(a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) + if (IsDoor(a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) { a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); } @@ -34,7 +33,7 @@ void cBlockDoorHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY else { // Was lower part - if (cDoors::IsDoor(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))) + if (IsDoor(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))) { a_World->FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_AIR, 0); } @@ -49,7 +48,7 @@ void cBlockDoorHandler::OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX { if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_WOODEN_DOOR) { - cDoors::ChangeDoor(a_World, a_BlockX, a_BlockY, a_BlockZ); + ChangeDoor(a_World, a_BlockX, a_BlockY, a_BlockZ); } } diff --git a/source/Blocks/BlockDoor.h b/source/Blocks/BlockDoor.h index 4978fee38..03a79d47d 100644 --- a/source/Blocks/BlockDoor.h +++ b/source/Blocks/BlockDoor.h @@ -3,7 +3,6 @@ #include "BlockHandler.h" #include "../World.h" -#include "../Doors.h" #include "../Entities/Player.h" @@ -43,7 +42,7 @@ public: } a_BlockType = m_BlockType; - a_BlockMeta = cDoors::RotationToMetaData(a_Player->GetRotation()); + a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation()); return true; } @@ -68,12 +67,6 @@ public: } - virtual bool CanBePlacedOnSide(void) override - { - return false; - } - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override { return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); @@ -98,6 +91,83 @@ public: } return false; } + + + /// Converts the player's yaw to placed door's blockmeta + inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw) + { + ASSERT((a_Yaw >= -180) && (a_Yaw < 180)); + + a_Yaw += 90 + 45; + if (a_Yaw > 360) + { + a_Yaw -= 360; + } + if ((a_Yaw >= 0) && (a_Yaw < 90)) + { + return 0x0; + } + else if ((a_Yaw >= 180) && (a_Yaw < 270)) + { + return 0x2; + } + else if ((a_Yaw >= 90) && (a_Yaw < 180)) + { + return 0x1; + } + else + { + return 0x3; + } + } + + + /// Returns true if the specified blocktype is any kind of door + inline static bool IsDoor(BLOCKTYPE a_Block) + { + return (a_Block == E_BLOCK_WOODEN_DOOR) || (a_Block == E_BLOCK_IRON_DOOR); + } + + + /// Returns the metadata for the opposite door state (open vs closed) + static NIBBLETYPE ChangeStateMetaData(NIBBLETYPE a_MetaData) + { + return a_MetaData ^ 4; + } + + + /// Changes the door at the specified coords from open to close or vice versa + static void ChangeDoor(cWorld * a_World, int a_X, int a_Y, int a_Z) + { + NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_X, a_Y, a_Z); + + a_World->SetBlockMeta(a_X, a_Y, a_Z, ChangeStateMetaData(OldMetaData)); + + if (OldMetaData & 8) + { + // Current block is top of the door + BLOCKTYPE BottomBlock = a_World->GetBlock(a_X, a_Y - 1, a_Z); + NIBBLETYPE BottomMeta = a_World->GetBlockMeta(a_X, a_Y - 1, a_Z); + + if (IsDoor(BottomBlock) && !(BottomMeta & 8)) + { + a_World->SetBlockMeta(a_X, a_Y - 1, a_Z, ChangeStateMetaData(BottomMeta)); + } + } + else + { + // Current block is bottom of the door + BLOCKTYPE TopBlock = a_World->GetBlock(a_X, a_Y + 1, a_Z); + NIBBLETYPE TopMeta = a_World->GetBlockMeta(a_X, a_Y + 1, a_Z); + + if (IsDoor(TopBlock) && (TopMeta & 8)) + { + a_World->SetBlockMeta(a_X, a_Y + 1, a_Z, ChangeStateMetaData(TopMeta)); + } + } + } + + } ; diff --git a/source/Blocks/BlockDropSpenser.h b/source/Blocks/BlockDropSpenser.h index e5572da8a..b7f20825d 100644 --- a/source/Blocks/BlockDropSpenser.h +++ b/source/Blocks/BlockDropSpenser.h @@ -31,7 +31,7 @@ public: a_BlockType = m_BlockType; // FIXME: Do not use cPiston class for dispenser placement! - a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), 0); + a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), a_Player->GetPitch()); return true; } } ; diff --git a/source/Blocks/BlockFarmland.h b/source/Blocks/BlockFarmland.h index 6cab1fa38..7bc71f7f3 100644 --- a/source/Blocks/BlockFarmland.h +++ b/source/Blocks/BlockFarmland.h @@ -30,30 +30,38 @@ public: virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override { - // TODO: Rain hydrates farmland, too. Check world weather, don't search for water if raining. - // NOTE: The desert biomes do not get precipitation, so another check needs to be made. + bool Found = false; - // Search for water in a close proximity: - // Ref.: http://www.minecraftwiki.net/wiki/Farmland#Hydrated_Farmland_Tiles - cBlockArea Area; - if (!Area.Read(a_World, a_BlockX - 4, a_BlockX + 4, a_BlockY, a_BlockY + 1, a_BlockZ - 4, a_BlockZ + 4)) + int Biome = a_World->GetBiomeAt(a_BlockX, a_BlockZ); + if (a_World->IsWeatherWet() && (Biome != biDesert) && (Biome != biDesertHills)) { - // Too close to the world edge, cannot check surroudnings; don't tick at all - return; + // Rain hydrates farmland, too, except in Desert biomes. + Found = true; } - bool Found = false; - int NumBlocks = Area.GetBlockCount(); - BLOCKTYPE * BlockTypes = Area.GetBlockTypes(); - for (int i = 0; i < NumBlocks; i++) + else { - if ( - (BlockTypes[i] == E_BLOCK_WATER) || - (BlockTypes[i] == E_BLOCK_STATIONARY_WATER) - ) + // Search for water in a close proximity: + // Ref.: http://www.minecraftwiki.net/wiki/Farmland#Hydrated_Farmland_Tiles + cBlockArea Area; + if (!Area.Read(a_World, a_BlockX - 4, a_BlockX + 4, a_BlockY, a_BlockY + 1, a_BlockZ - 4, a_BlockZ + 4)) { - Found = true; - break; + // Too close to the world edge, cannot check surroudnings; don't tick at all + return; } + + int NumBlocks = Area.GetBlockCount(); + BLOCKTYPE * BlockTypes = Area.GetBlockTypes(); + for (int i = 0; i < NumBlocks; i++) + { + if ( + (BlockTypes[i] == E_BLOCK_WATER) || + (BlockTypes[i] == E_BLOCK_STATIONARY_WATER) + ) + { + Found = true; + break; + } + } // for i - BlockTypes[] } NIBBLETYPE BlockMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); diff --git a/source/Blocks/BlockFenceGate.h b/source/Blocks/BlockFenceGate.h index d6f8aa85f..6423a7cb0 100644 --- a/source/Blocks/BlockFenceGate.h +++ b/source/Blocks/BlockFenceGate.h @@ -2,7 +2,6 @@ #pragma once #include "BlockHandler.h" -#include "../Doors.h" @@ -26,7 +25,7 @@ public: ) override { a_BlockType = m_BlockType; - a_BlockMeta = cDoors::RotationToMetaData(a_Player->GetRotation() + 270); + a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation()); return true; } @@ -34,7 +33,7 @@ public: virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override { NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - NIBBLETYPE NewMetaData = cDoors::RotationToMetaData(a_Player->GetRotation() + 270); + NIBBLETYPE NewMetaData = PlayerYawToMetaData(a_Player->GetRotation()); OldMetaData ^= 4; // Toggle the gate if ((OldMetaData & 1) == (NewMetaData & 1)) { @@ -53,6 +52,35 @@ public: { return true; } + + + /// Converts the player's yaw to placed gate's blockmeta + inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw) + { + ASSERT((a_Yaw >= -180) && (a_Yaw < 180)); + + a_Yaw += 360 + 45; + if (a_Yaw > 360) + { + a_Yaw -= 360; + } + if ((a_Yaw >= 0) && (a_Yaw < 90)) + { + return 0x0; + } + else if ((a_Yaw >= 180) && (a_Yaw < 270)) + { + return 0x2; + } + else if ((a_Yaw >= 90) && (a_Yaw < 180)) + { + return 0x1; + } + else + { + return 0x3; + } + } } ; diff --git a/source/Blocks/BlockFlower.h b/source/Blocks/BlockFlower.h index 952901ba5..421e2d5d8 100644 --- a/source/Blocks/BlockFlower.h +++ b/source/Blocks/BlockFlower.h @@ -30,18 +30,6 @@ public: } - virtual bool DoesAllowBlockOnTop(void) override - { - return true; - } - - - virtual bool CanBePlacedOnSide(void) override - { - return false; - } - - virtual const char * GetStepSound(void) override { return "step.grass"; diff --git a/source/Blocks/BlockHandler.cpp b/source/Blocks/BlockHandler.cpp index 5134c1103..e59fee8ee 100644 --- a/source/Blocks/BlockHandler.cpp +++ b/source/Blocks/BlockHandler.cpp @@ -7,12 +7,14 @@ #include "../PluginManager.h" #include "BlockBed.h" #include "BlockBrewingStand.h" +#include "BlockButton.h" #include "BlockCactus.h" #include "BlockCarpet.h" #include "BlockCauldron.h" #include "BlockChest.h" #include "BlockCloth.h" #include "BlockCobWeb.h" +#include "BlockComparator.h" #include "BlockCrops.h" #include "BlockDeadBush.h" #include "BlockDirt.h" @@ -41,6 +43,8 @@ #include "BlockNote.h" #include "BlockOre.h" #include "BlockPiston.h" +#include "BlockPlanks.h" +#include "BlockPumpkin.h" #include "BlockRail.h" #include "BlockRedstone.h" #include "BlockRedstoneRepeater.h" @@ -108,6 +112,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_CAULDRON: return new cBlockCauldronHandler (a_BlockType); case E_BLOCK_CHEST: return new cBlockChestHandler (a_BlockType); case E_BLOCK_COAL_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_ACTIVE_COMPARATOR: return new cBlockComparatorHandler (a_BlockType); case E_BLOCK_COBBLESTONE: return new cBlockStoneHandler (a_BlockType); case E_BLOCK_COBBLESTONE_STAIRS: return new cBlockStairsHandler (a_BlockType); case E_BLOCK_COBWEB: return new cBlockCobWebHandler (a_BlockType); @@ -122,7 +127,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_DROPPER: return new cBlockDropSpenserHandler (a_BlockType); case E_BLOCK_EMERALD_ORE: return new cBlockOreHandler (a_BlockType); case E_BLOCK_ENDER_CHEST: return new cBlockEnderchestHandler (a_BlockType); - case E_BLOCK_FARMLAND: return new cBlockFarmlandHandler; + case E_BLOCK_FARMLAND: return new cBlockFarmlandHandler ( ); case E_BLOCK_FENCE_GATE: return new cBlockFenceGateHandler (a_BlockType); case E_BLOCK_FIRE: return new cBlockFireHandler (a_BlockType); case E_BLOCK_FLOWER_POT: return new cBlockFlowerPotHandler (a_BlockType); @@ -134,6 +139,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_GRAVEL: return new cBlockGravelHandler (a_BlockType); case E_BLOCK_HOPPER: return new cBlockHopperHandler (a_BlockType); case E_BLOCK_ICE: return new cBlockIceHandler (a_BlockType); + case E_BLOCK_INACTIVE_COMPARATOR: return new cBlockComparatorHandler (a_BlockType); case E_BLOCK_IRON_DOOR: return new cBlockDoorHandler (a_BlockType); case E_BLOCK_IRON_ORE: return new cBlockOreHandler (a_BlockType); case E_BLOCK_JUKEBOX: return new cBlockEntityHandler (a_BlockType); @@ -151,10 +157,12 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_NETHER_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); case E_BLOCK_NOTE_BLOCK: return new cBlockNoteHandler (a_BlockType); case E_BLOCK_PISTON: return new cBlockPistonHandler (a_BlockType); - case E_BLOCK_PISTON_EXTENSION: return new cBlockPistonHeadHandler (); - case E_BLOCK_PLANKS: return new cBlockWoodHandler (a_BlockType); + case E_BLOCK_PISTON_EXTENSION: return new cBlockPistonHeadHandler ( ); + case E_BLOCK_PLANKS: return new cBlockPlanksHandler (a_BlockType); + case E_BLOCK_PUMPKIN: return new cBlockPumpkinHandler (a_BlockType); + case E_BLOCK_JACK_O_LANTERN: return new cBlockPumpkinHandler (a_BlockType); case E_BLOCK_PUMPKIN_STEM: return new cBlockStemsHandler (a_BlockType); - case E_BLOCK_QUARTZ_STAIR: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_QUARTZ_STAIRS: return new cBlockStairsHandler (a_BlockType); case E_BLOCK_RAIL: return new cBlockRailHandler (a_BlockType); case E_BLOCK_POTATOES: return new cBlockCropsHandler (a_BlockType); case E_BLOCK_POWERED_RAIL: return new cBlockRailHandler (a_BlockType); @@ -178,6 +186,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_STICKY_PISTON: return new cBlockPistonHandler (a_BlockType); case E_BLOCK_STONE: return new cBlockStoneHandler (a_BlockType); case E_BLOCK_STONE_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_STONE_BUTTON: return new cBlockButtonHandler (a_BlockType); case E_BLOCK_STONE_SLAB: return new cBlockSlabHandler (a_BlockType); case E_BLOCK_SUGARCANE: return new cBlockSugarcaneHandler (a_BlockType); case E_BLOCK_TALL_GRASS: return new cBlockTallGrassHandler (a_BlockType); @@ -185,6 +194,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_VINES: return new cBlockVineHandler (a_BlockType); case E_BLOCK_WALLSIGN: return new cBlockSignHandler (a_BlockType); case E_BLOCK_WATER: return new cBlockFluidHandler (a_BlockType); + case E_BLOCK_WOODEN_BUTTON: return new cBlockButtonHandler (a_BlockType); case E_BLOCK_WOODEN_DOOR: return new cBlockDoorHandler (a_BlockType); case E_BLOCK_WOODEN_SLAB: return new cBlockSlabHandler (a_BlockType); case E_BLOCK_WOODEN_STAIRS: return new cBlockStairsHandler (a_BlockType); @@ -351,7 +361,20 @@ void cBlockHandler::DropBlock(cWorld * a_World, cEntity * a_Digger, int a_BlockX if (!Pickups.empty()) { - a_World->SpawnItemPickups(Pickups, a_BlockX, a_BlockY, a_BlockZ); + MTRand r1; + + // Mid-block position first + double MicroX, MicroY, MicroZ; + MicroX = a_BlockX + 0.5; + MicroY = a_BlockY + 0.5; + MicroZ = a_BlockZ + 0.5; + + // Add random offset second (this causes pickups to spawn inside blocks most times, it's a little buggy) + //MicroX += (int)(r1.randInt(16) + r1.randInt(16) - 16); + //MicroY += (int)(r1.randInt(16) + r1.randInt(16) - 16); + //MicroZ += (int)(r1.randInt(16) + r1.randInt(16) - 16); + + a_World->SpawnItemPickups(Pickups, MicroX, MicroY, MicroZ); } } @@ -404,24 +427,6 @@ bool cBlockHandler::DoesIgnoreBuildCollision(void) -bool cBlockHandler::DoesAllowBlockOnTop(void) -{ - return true; -} - - - - - -bool cBlockHandler::CanBePlacedOnSide(void) -{ - return true; -} - - - - - bool cBlockHandler::DoesDropOnUnsuitable(void) { return true; diff --git a/source/Blocks/BlockHandler.h b/source/Blocks/BlockHandler.h index 228ce174b..0487505ee 100644 --- a/source/Blocks/BlockHandler.h +++ b/source/Blocks/BlockHandler.h @@ -83,10 +83,7 @@ public: NOTE: This call doesn't actually place the block */ // virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); - - /// Called when the player tries to place a block on top of this block (Only if he aims directly on this block); return false to disallow - virtual bool DoesAllowBlockOnTop(void); - + /// Called to check whether this block supports a rclk action. If it returns true, OnUse() is called virtual bool IsUseable(void); @@ -99,9 +96,6 @@ public: For example blocks placed "on" snow will be placed at the same position. So: Snow ignores Build collision */ virtual bool DoesIgnoreBuildCollision(void); - - /// Indicates this block can be placed on the side of other blocks. Default: true - virtual bool CanBePlacedOnSide(void); /// Does this block drop if it gets destroyed by an unsuitable situation? Default: true virtual bool DoesDropOnUnsuitable(void); diff --git a/source/Blocks/BlockLever.cpp b/source/Blocks/BlockLever.cpp index f2ca1805a..a9bd6c990 100644 --- a/source/Blocks/BlockLever.cpp +++ b/source/Blocks/BlockLever.cpp @@ -1,8 +1,6 @@ #include "Globals.h" #include "BlockLever.h" -#include "../Item.h" -#include "../World.h" #include "../Entities/Player.h" #include "../Simulator/RedstoneSimulator.h" @@ -23,7 +21,8 @@ void cBlockLeverHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, { // Flip the ON bit on/off. Using XOR bitwise operation to turn it on/off. NIBBLETYPE Meta = ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f); - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta); + + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); if (Meta & 0x08) { a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); @@ -37,12 +36,3 @@ void cBlockLeverHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, - -void cBlockLeverHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - OnUse(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NONE, 8, 8, 8); -} - - - - diff --git a/source/Blocks/BlockLever.h b/source/Blocks/BlockLever.h index 362cf563e..5553170e2 100644 --- a/source/Blocks/BlockLever.h +++ b/source/Blocks/BlockLever.h @@ -1,7 +1,6 @@ #pragma once #include "BlockHandler.h" -#include "../World.h" #include "../Simulator/RedstoneSimulator.h" @@ -14,7 +13,6 @@ class cBlockLeverHandler : public: cBlockLeverHandler(BLOCKTYPE a_BlockType); - virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override; virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; @@ -42,12 +40,6 @@ public: a_BlockMeta = cRedstoneSimulator::LeverDirectionToMetaData(a_BlockFace); return true; } - - - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } virtual const char * GetStepSound(void) override diff --git a/source/Blocks/BlockMushroom.h b/source/Blocks/BlockMushroom.h index b3b23e2ba..2846a6317 100644 --- a/source/Blocks/BlockMushroom.h +++ b/source/Blocks/BlockMushroom.h @@ -46,18 +46,6 @@ public: } return true; } - - - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } - - - virtual bool CanBePlacedOnSide(void) override - { - return false; - } virtual const char * GetStepSound(void) override diff --git a/source/Blocks/BlockMycelium.h b/source/Blocks/BlockMycelium.h index 0ed7162ac..7f897c72a 100644 --- a/source/Blocks/BlockMycelium.h +++ b/source/Blocks/BlockMycelium.h @@ -20,6 +20,11 @@ public: { a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0)); } + + virtual const char * GetStepSound(void) override + { + return "step.gravel"; + } } ; diff --git a/source/Blocks/BlockPlanks.h b/source/Blocks/BlockPlanks.h new file mode 100644 index 000000000..f3b8dbfb6 --- /dev/null +++ b/source/Blocks/BlockPlanks.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockPlanksHandler : public cBlockHandler +{ +public: + cBlockPlanksHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage); + a_BlockMeta = Meta; + return true; + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/source/Blocks/BlockPumpkin.h b/source/Blocks/BlockPumpkin.h new file mode 100644 index 000000000..76abc6818 --- /dev/null +++ b/source/Blocks/BlockPumpkin.h @@ -0,0 +1,60 @@ +#pragma once + +#include "BlockHandler.h" + + + + +class cBlockPumpkinHandler : + public cBlockHandler +{ +public: + cBlockPumpkinHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation()); + return true; + } + + inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw) + { + ASSERT((a_Yaw >= -180) && (a_Yaw < 180)); + + a_Yaw += 180 + 45; + if (a_Yaw > 360) + { + a_Yaw -= 360; + } + if ((a_Yaw >= 0) && (a_Yaw < 90)) + { + return 0x0; + } + else if ((a_Yaw >= 180) && (a_Yaw < 270)) + { + return 0x2; + } + else if ((a_Yaw >= 90) && (a_Yaw < 180)) + { + return 0x1; + } + else + { + return 0x3; + } + } + +} ; + + + + diff --git a/source/Blocks/BlockRedstone.h b/source/Blocks/BlockRedstone.h index ae0466937..f28f3f2d6 100644 --- a/source/Blocks/BlockRedstone.h +++ b/source/Blocks/BlockRedstone.h @@ -16,11 +16,6 @@ public: virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override { @@ -33,12 +28,6 @@ public: // Reset meta to 0 a_Pickups.push_back(cItem(E_ITEM_REDSTONE_DUST, 1)); } - - - virtual bool CanBePlacedOnSide(void) override - { - return false; - } } ; diff --git a/source/Blocks/BlockRedstoneRepeater.cpp b/source/Blocks/BlockRedstoneRepeater.cpp index 3bc879435..72ea21012 100644 --- a/source/Blocks/BlockRedstoneRepeater.cpp +++ b/source/Blocks/BlockRedstoneRepeater.cpp @@ -1,9 +1,8 @@ #include "Globals.h" #include "BlockRedstoneRepeater.h" -#include "../Item.h" -#include "../World.h" #include "../Simulator/RedstoneSimulator.h" +#include "../Entities/Player.h" @@ -29,16 +28,22 @@ void cBlockRedstoneRepeaterHandler::OnDestroyed(cWorld *a_World, int a_BlockX, i void cBlockRedstoneRepeaterHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) + 0x04) & 0x0f)); + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) + 0x04) & 0x0f)); } - -void cBlockRedstoneRepeaterHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) +bool cBlockRedstoneRepeaterHandler::GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta +) { - OnUse(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NONE, 8, 8, 8); + a_BlockType = m_BlockType; + a_BlockMeta = cRedstoneSimulator::RepeaterRotationToMetaData(a_Player->GetRotation()); + return true; } diff --git a/source/Blocks/BlockRedstoneRepeater.h b/source/Blocks/BlockRedstoneRepeater.h index f3e250963..958841a34 100644 --- a/source/Blocks/BlockRedstoneRepeater.h +++ b/source/Blocks/BlockRedstoneRepeater.h @@ -2,7 +2,6 @@ #pragma once #include "BlockHandler.h" -#include "../World.h" @@ -15,7 +14,6 @@ public: cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockType); virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; - virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override; virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; @@ -32,22 +30,19 @@ public: } - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override { return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); } - - virtual bool CanBePlacedOnSide(void) override - { - return false; - } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override; + virtual const char * GetStepSound(void) override { diff --git a/source/Blocks/BlockSapling.h b/source/Blocks/BlockSapling.h index 17ef4984f..fff2fa88b 100644 --- a/source/Blocks/BlockSapling.h +++ b/source/Blocks/BlockSapling.h @@ -29,12 +29,6 @@ public: { return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)); } - - - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override @@ -50,12 +44,6 @@ public: a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x08); } } - - - virtual bool CanBePlacedOnSide() override - { - return false; - } virtual const char * GetStepSound(void) override diff --git a/source/Blocks/BlockSign.h b/source/Blocks/BlockSign.h index e6426180f..7fbe61893 100644 --- a/source/Blocks/BlockSign.h +++ b/source/Blocks/BlockSign.h @@ -23,12 +23,6 @@ public: { a_Pickups.push_back(cItem(E_ITEM_SIGN, 1, 0)); } - - - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } virtual const char * GetStepSound(void) override diff --git a/source/Blocks/BlockSnow.h b/source/Blocks/BlockSnow.h index bdd9f0b87..b8d48362c 100644 --- a/source/Blocks/BlockSnow.h +++ b/source/Blocks/BlockSnow.h @@ -15,8 +15,28 @@ public: : cBlockHandler(a_BlockType) { } - - + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + NIBBLETYPE Meta = a_World->GetBlockMeta(Vector3i(a_BlockX, a_BlockY, a_BlockZ)); + + if ((Meta < 7) && (Meta != 0)) // Is height at maximum (7) or at mininum (0)? Don't do anything if so + { + Meta++; + } + + a_BlockMeta = Meta; + return true; + } + + virtual bool DoesIgnoreBuildCollision(void) override { return true; diff --git a/source/Blocks/BlockStairs.h b/source/Blocks/BlockStairs.h index 485ebda1a..8d259eee3 100644 --- a/source/Blocks/BlockStairs.h +++ b/source/Blocks/BlockStairs.h @@ -53,7 +53,6 @@ public: static NIBBLETYPE RotationToMetaData(double a_Rotation) { a_Rotation += 90 + 45; // So its not aligned with axis - NIBBLETYPE result = 0x0; if (a_Rotation > 360) { a_Rotation -= 360; diff --git a/source/Blocks/BlockSugarcane.h b/source/Blocks/BlockSugarcane.h index 9d66d6be6..28a60df80 100644 --- a/source/Blocks/BlockSugarcane.h +++ b/source/Blocks/BlockSugarcane.h @@ -77,12 +77,6 @@ public: { a_World->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, 1); } - - - virtual bool CanBePlacedOnSide() override - { - return false; - } virtual const char * GetStepSound(void) override diff --git a/source/Blocks/BlockTorch.h b/source/Blocks/BlockTorch.h index 3a50cab77..a52b373cb 100644 --- a/source/Blocks/BlockTorch.h +++ b/source/Blocks/BlockTorch.h @@ -24,16 +24,31 @@ public: BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta ) override { - // Find proper placement. Use the player-supplied one as the default, but fix if not okay: - if (!TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) + // Find proper placement of torch + + if ((a_BlockFace == BLOCK_FACE_TOP) || (a_BlockFace == BLOCK_FACE_BOTTOM)) { - a_BlockFace = FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ); - - if (a_BlockFace == BLOCK_FACE_BOTTOM) + a_BlockFace = FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ); // Top or bottom faces clicked, find a suitable face + if (a_BlockFace == BLOCK_FACE_NONE) { + // Client wouldn't have sent anything anyway, but whatever return false; } } + else + { + // Not top or bottom faces, try to preserve whatever face was clicked + if (!TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) + { + // Torch couldn't be placed on whatever face was clicked, last ditch resort - find another face + a_BlockFace = FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ); + if (a_BlockFace == BLOCK_FACE_NONE) + { + return false; + } + } + } + a_BlockType = m_BlockType; a_BlockMeta = DirectionToMetaData(a_BlockFace); return true; @@ -100,51 +115,54 @@ public: } - virtual bool DoesAllowBlockOnTop(void) override - { - return true; - } - - - static bool CanBePlacedOn(BLOCKTYPE a_BlockType, char a_Direction) + static bool CanBePlacedOn(BLOCKTYPE a_BlockType, char a_BlockFace) { - if ( g_BlockIsSolid[a_BlockType] ) { - return (a_Direction == 0x1); // allow only direction "standing on floor" + if ( !g_BlockIsTorchPlaceable[a_BlockType] ) + { + return (a_BlockFace == BLOCK_FACE_TOP); // Allow placement only when torch upright } - else { - return g_BlockIsSolid[a_BlockType]; + else + { + return true; } } static bool TorchCanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) { - // TODO: If placing a torch from below, check all 4 XZ neighbors, place it on that neighbor instead - // How to propagate that change up? - // Simon: The easiest way is to calculate the position two times, shouldn�t cost much cpu power :) - - if (a_BlockFace == BLOCK_FACE_BOTTOM) - { - return false; - } - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); - return CanBePlacedOn(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ), a_BlockFace); } - /// Finds a suitable Face for the Torch. Returns BLOCK_FACE_BOTTOM on failure + /// Finds a suitable face to place the torch, returning BLOCK_FACE_NONE on failure static char FindSuitableFace(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) { - for (int i = 1; i <= 5; i++) + for (int i = 0; i <= 5; i++) { - if (TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, i)) + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, i, true); + BLOCKTYPE BlockInQuestion = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + + if ( + ((BlockInQuestion == E_BLOCK_GLASS) || + (BlockInQuestion == E_BLOCK_FENCE) || + (BlockInQuestion == E_BLOCK_NETHER_BRICK_FENCE) || + (BlockInQuestion == E_BLOCK_COBBLESTONE_WALL)) && + (i == BLOCK_FACE_TOP) + ) + { + return i; + } + else if ((g_BlockIsTorchPlaceable[BlockInQuestion]) && (i != BLOCK_FACE_BOTTOM)) { return i; } + else + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, i, false); + } } - return BLOCK_FACE_BOTTOM; + return BLOCK_FACE_NONE; } @@ -163,11 +181,33 @@ public: virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override { - // TODO: Use AdjustCoordsByMeta(), then cChunk::UnboundedRelGetBlock() and finally some comparison char Face = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; - return TorchCanBePlacedAt(a_Chunk.GetWorld(), BlockX, a_RelY, BlockZ, Face); + + AddFaceDirection(a_RelX, a_RelY, a_RelZ, Face, true); + BLOCKTYPE BlockInQuestion; + a_Chunk.UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, BlockInQuestion); + + if ( + (BlockInQuestion == E_BLOCK_GLASS) || + (BlockInQuestion == E_BLOCK_FENCE) || + (BlockInQuestion == E_BLOCK_NETHER_BRICK_FENCE) || + (BlockInQuestion == E_BLOCK_COBBLESTONE_WALL) + ) + { + // Torches can be placed on tops of glass and fences, despite them being 'untorcheable' + // No need to check for upright orientation, it was done when the torch was placed + return true; + } + else if ( !g_BlockIsTorchPlaceable[BlockInQuestion] ) + { + return false; + } + else + { + return true; + } } diff --git a/source/Blocks/BlockVine.h b/source/Blocks/BlockVine.h index 37d9f1a45..2c9f67cab 100644 --- a/source/Blocks/BlockVine.h +++ b/source/Blocks/BlockVine.h @@ -151,12 +151,6 @@ public: } - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } - - virtual const char * GetStepSound(void) override { return "step.grass"; diff --git a/source/Blocks/BlockWood.h b/source/Blocks/BlockWood.h index 4e2246506..cb5ee995a 100644 --- a/source/Blocks/BlockWood.h +++ b/source/Blocks/BlockWood.h @@ -15,6 +15,51 @@ public: { } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage); + a_BlockMeta = BlockFaceToMetaData(a_BlockFace, Meta); + return true; + } + + + inline static NIBBLETYPE BlockFaceToMetaData(char a_BlockFace, NIBBLETYPE a_WoodMeta) + { + switch (a_BlockFace) + { + case BLOCK_FACE_YM: + case BLOCK_FACE_YP: + { + return a_WoodMeta; // Top or bottom, just return original + } + + case BLOCK_FACE_ZP: + case BLOCK_FACE_ZM: + { + return a_WoodMeta | 0x8; // North or south + } + + case BLOCK_FACE_XP: + case BLOCK_FACE_XM: + { + return a_WoodMeta | 0x4; // East or west + } + + default: + { + ASSERT(!"Unhandled block face!"); + return a_WoodMeta | 0xC; // No idea, give a special meta (all sides bark) + } + } + } + virtual const char * GetStepSound(void) override { diff --git a/source/ChunkDef.h b/source/ChunkDef.h index 4cc2d15b0..9db88f293 100644 --- a/source/ChunkDef.h +++ b/source/ChunkDef.h @@ -93,9 +93,54 @@ enum EMCSBiome biJungle = 21, biJungleHills = 22, - // Automatically capture the maximum biome value into biMaxBiome: + // Release 1.7 biomes: + biJungleEdge = 23, + biDeepOcean = 24, + biStoneBeach = 25, + biColdBeach = 26, + biBirchForest = 27, + biBirchForestHills = 28, + biRoofedForest = 29, + biColdTaiga = 30, + biColdTaigaHills = 31, + biMegaTaiga = 32, + biMegaTaigaHills = 33, + biExtremeHillsPlus = 34, + biSavanna = 35, + biSavannaPlateau = 36, + biMesa = 37, + biMesaPlateauF = 38, + biMesaPlateau = 39, + + // Automatically capture the maximum consecutive biome value into biMaxBiome: biNumBiomes, // True number of biomes, since they are zero-based - biMaxBiome = biNumBiomes - 1 // The maximum biome value + biMaxBiome = biNumBiomes - 1, // The maximum biome value + + // Add this number to the biomes to get the variant + biVariant = 128, + + // Release 1.7 biome variants: + biSunflowerPlains = 129, + biDesertM = 130, + biExtremeHillsM = 131, + biFlowerForest = 132, + biTaigaM = 133, + biSwamplandM = 134, + biIcePlainsSpikes = 140, + biJungleM = 149, + biJungleEdgeM = 151, + biBirchForestM = 155, + biBirchForestHillsM = 156, + biRoofedForestM = 157, + biColdTaigaM = 158, + biMegaSpruceTaiga = 160, + biMegaSpruceTaigaHills = 161, + biExtremeHillsPlusM = 162, + biSavannaM = 163, + biSavannaPlateauM = 164, + biMesaBryce = 165, + biMesaPlateauFM = 166, + biMesaPlateauM = 167, } ; // tolua_end diff --git a/source/ClientHandle.cpp b/source/ClientHandle.cpp index 3d819ee18..f67a546fd 100644 --- a/source/ClientHandle.cpp +++ b/source/ClientHandle.cpp @@ -11,7 +11,6 @@ #include "BlockEntities/SignEntity.h" #include "UI/Window.h" #include "Item.h" -#include "Doors.h" #include "Piston.h" #include "Mobs/Monster.h" #include "ChatColor.h" @@ -98,6 +97,7 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) , m_HasStartedDigging(false) , m_CurrentExplosionTick(0) , m_RunningSumExplosions(0) + , m_HasSentPlayerChunk(false) { m_Protocol = new cProtocolRecognizer(this); @@ -489,8 +489,14 @@ void cClientHandle::HandleCreativeInventory(short a_SlotNum, const cItem & a_Hel void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround) { + if ((m_Player == NULL) || (m_State != csPlaying)) + { + // The client hasn't been spawned yet and sends nonsense, we know better + return; + } + /* - // TODO: Invalid stance check + // TODO: Invalid stance check if ((a_PosY >= a_Stance) || (a_Stance > a_PosY + 1.65)) { LOGD("Invalid stance"); @@ -499,7 +505,7 @@ void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, } */ - // LOGD("recv player pos: {%0.2f %0.2f %0.2f}, ground: %d", a_PosX, a_PosY, a_PosZ, a_IsOnGround ? 1 : 0); + // If the player has moved too far, "repair" them: Vector3d Pos(a_PosX, a_PosY, a_PosZ); if ((m_Player->GetPosition() - Pos).SqrLength() > 100 * 100) { @@ -513,7 +519,7 @@ void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, { // we only add this exhaustion if the player is not swimming - otherwise we end up with both jump + swim exhaustion - if(! m_Player->IsSwimming() ) + if (!m_Player->IsSwimming()) { m_Player->AddFoodExhaustion(m_Player->IsSprinting() ? 0.8 : 0.2); } @@ -729,6 +735,7 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo cWorld * World = m_Player->GetWorld(); ItemHandler->OnBlockDestroyed(World, m_Player, m_Player->GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ); + // The ItemHandler is also responsible for spawning the pickups BlockHandler(a_OldBlock)->OnDestroyedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); World->BroadcastSoundParticleEffect(2001, a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, a_OldBlock, this); @@ -908,15 +915,6 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, c } else { - // Check for Blocks not allowing placement on top - if ((a_BlockFace == BLOCK_FACE_TOP) && !Handler->DoesAllowBlockOnTop()) - { - // Resend the old block - // Sometimes the client still places the block O.o - World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - return; - } - if (!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision()) { // Tried to place a block *into* another? @@ -938,13 +936,6 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, c cBlockHandler * NewBlock = BlockHandler(BlockType); - if ((a_BlockFace != BLOCK_FACE_TOP) && !NewBlock->CanBePlacedOnSide()) - { - // Cannot be placed on the side of an other block - World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - return; - } - if (cRoot::Get()->GetPluginManager()->CallHookPlayerPlacingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) { // A plugin doesn't agree with placing the block, revert the block on the client: @@ -997,11 +988,15 @@ void cClientHandle::HandleChat(const AString & a_Message) void cClientHandle::HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsOnGround) { + if ((m_Player == NULL) || (m_State != csPlaying)) + { + return; + } + m_Player->SetRotation (a_Rotation); m_Player->SetHeadYaw (a_Rotation); m_Player->SetPitch (a_Pitch); m_Player->SetTouchGround(a_IsOnGround); - m_Player->WrapRotation(); } @@ -1010,6 +1005,12 @@ void cClientHandle::HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsO void cClientHandle::HandlePlayerMoveLook(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround) { + if ((m_Player == NULL) || (m_State != csPlaying)) + { + // The client hasn't been spawned yet and sends nonsense, we know better + return; + } + /* // TODO: Invalid stance check if ((a_PosY >= a_Stance) || (a_Stance > a_PosY + 1.65)) @@ -1019,46 +1020,13 @@ void cClientHandle::HandlePlayerMoveLook(double a_PosX, double a_PosY, double a_ return; } */ - switch (m_State) - { - case csPlaying: - { - m_Player->MoveTo(Vector3d(a_PosX, a_PosY, a_PosZ)); - m_Player->SetStance (a_Stance); - m_Player->SetTouchGround(a_IsOnGround); - m_Player->SetHeadYaw (a_Rotation); - m_Player->SetRotation (a_Rotation); - m_Player->SetPitch (a_Pitch); - m_Player->WrapRotation(); - break; - } - - case csDownloadingWorld: - { - Vector3d ReceivedPosition = Vector3d(a_PosX, a_PosY, a_PosZ); - // LOGD("Received MoveLook confirmation: {%0.2f %0.2f %0.2f}", a_PosX, a_PosY, a_PosZ); - - // Test the distance between points with a small/large enough value instead of comparing directly. Floating point inaccuracies might screw stuff up - double Dist = (ReceivedPosition - m_ConfirmPosition).SqrLength(); - if (Dist < 1.0) - { - if (ReceivedPosition.Equals(m_ConfirmPosition)) - { - LOGINFO("Exact position confirmed by client!"); - } - m_State = csPlaying; - } - else - { - LOGWARNING("Player \"%s\" sent a weird position confirmation %.2f blocks away, retrying", m_Username.c_str(), sqrt(Dist)); - LOGD(" Expected pos: {%0.2f, %0.2f, %0.2f}", m_ConfirmPosition.x, m_ConfirmPosition.y, m_ConfirmPosition.z); - LOGD(" Received pos: {%0.2f, %0.2f, %0.2f}", a_PosX, a_PosY, a_PosZ); - m_ConfirmPosition = m_Player->GetPosition(); - SendPlayerMoveLook(); - } - break; - } - } + + m_Player->MoveTo(Vector3d(a_PosX, a_PosY, a_PosZ)); + m_Player->SetStance (a_Stance); + m_Player->SetTouchGround(a_IsOnGround); + m_Player->SetHeadYaw (a_Rotation); + m_Player->SetRotation (a_Rotation); + m_Player->SetPitch (a_Pitch); } @@ -1208,7 +1176,7 @@ void cClientHandle::HandleUseEntity(int a_TargetEntityID, bool a_IsLeftClick) void cClientHandle::HandleRespawn(void) { - if( m_Player == NULL ) + if (m_Player == NULL) { Destroy(); return; @@ -1422,6 +1390,7 @@ void cClientHandle::MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket) m_State = csAuthenticated; m_LastStreamedChunkX = 0x7fffffff; m_LastStreamedChunkZ = 0x7fffffff; + m_HasSentPlayerChunk = false; } @@ -1495,17 +1464,23 @@ void cClientHandle::Tick(float a_Dt) Destroy(); } - if ((m_State == csDownloadingWorld) && m_ShouldCheckDownloaded) - { - CheckIfWorldDownloaded(); - m_ShouldCheckDownloaded = false; - } - if (m_Player == NULL) { return; } + // If the chunk the player's in was just sent, spawn the player: + if (m_HasSentPlayerChunk && (m_State != csPlaying)) + { + if (!cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*m_Player)) + { + // Broadcast that this player has joined the game! Yay~ + m_Player->GetWorld()->BroadcastChat(m_Username + " joined the game!", this); + } + m_Protocol->SendPlayerMoveLook(); + m_State = csPlaying; + } + // Send a ping packet: cTimer t1; if ((m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime())) @@ -1601,14 +1576,6 @@ void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializ { ASSERT(m_Player != NULL); - if ((m_State == csAuthenticated) || (m_State == csDownloadingWorld)) - { - if ((a_ChunkX == m_Player->GetChunkX()) && (a_ChunkZ == m_Player->GetChunkZ())) - { - m_Protocol->SendPlayerMoveLook(); - } - } - // Check chunks being sent, erase them from m_ChunksToSend: bool Found = false; { @@ -1618,11 +1585,6 @@ void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializ if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ)) { m_ChunksToSend.erase(itr); - - // Make the tick thread check if all the needed chunks have been downloaded - // -- needed to offload this from here due to a deadlock possibility - m_ShouldCheckDownloaded = true; - Found = true; break; } @@ -1637,6 +1599,15 @@ void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializ } m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer); + + // If it is the chunk the player's in, make them spawn (in the tick thread): + if ((m_State == csAuthenticated) || (m_State == csDownloadingWorld)) + { + if ((a_ChunkX == m_Player->GetChunkX()) && (a_ChunkZ == m_Player->GetChunkZ())) + { + m_HasSentPlayerChunk = true; + } + } } @@ -1947,7 +1918,7 @@ void cClientHandle::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, -void cClientHandle::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) // VehicleTypeType is specific to Minecarts +void cClientHandle::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) // VehicleSubType is specific to Minecarts { m_Protocol->SendSpawnVehicle(a_Vehicle, a_VehicleType, a_VehicleSubType); } @@ -2078,50 +2049,6 @@ void cClientHandle::SendWindowProperty(const cWindow & a_Window, int a_Property, -void cClientHandle::CheckIfWorldDownloaded(void) -{ - if (m_State != csDownloadingWorld) - { - return; - } - - bool ShouldSendConfirm = false; - { - cCSLock Lock(m_CSChunkLists); - ShouldSendConfirm = m_ChunksToSend.empty(); - } - - if (ShouldSendConfirm) - { - SendConfirmPosition(); - } -} - - - - - -void cClientHandle::SendConfirmPosition(void) -{ - LOG("Spawning player \"%s\" at {%.2f, %.2f, %.2f}", - m_Username.c_str(), m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ() - ); - - m_State = csConfirmingPos; - - if (!cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*m_Player)) - { - // Broadcast that this player has joined the game! Yay~ - m_Player->GetWorld()->BroadcastChat(m_Username + " joined the game!", this); - } - - SendPlayerMoveLook(); -} - - - - - const AString & cClientHandle::GetUsername(void) const { return m_Username; diff --git a/source/ClientHandle.h b/source/ClientHandle.h index 9a2092361..ef6dbd124 100644 --- a/source/ClientHandle.h +++ b/source/ClientHandle.h @@ -53,10 +53,10 @@ public: #if defined(ANDROID_NDK) static const int DEFAULT_VIEW_DISTANCE = 4; // The default ViewDistance (used when no value is set in Settings.ini) #else - static const int DEFAULT_VIEW_DISTANCE = 9; + static const int DEFAULT_VIEW_DISTANCE = 10; #endif - static const int MAX_VIEW_DISTANCE = 10; - static const int MIN_VIEW_DISTANCE = 4; + static const int MAX_VIEW_DISTANCE = 15; + static const int MIN_VIEW_DISTANCE = 3; /// How many ticks should be checked for a running average of explosions, for limiting purposes static const int NUM_CHECK_EXPLOSIONS_TICKS = 20; @@ -125,7 +125,7 @@ public: void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock); void SendSpawnMob (const cMonster & a_Mob); void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch); - void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType); + void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType = 0); void SendTabCompletionResults(const AStringVector & a_Results); void SendTeleportEntity (const cEntity & a_Entity); void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ); @@ -299,17 +299,14 @@ private: static int s_ClientCount; int m_UniqueID; + /// Set to true when the chunk where the player is is sent to the client. Used for spawning the player + bool m_HasSentPlayerChunk; + /// Returns true if the rate block interactions is within a reasonable limit (bot protection) bool CheckBlockInteractionsRate(void); - /// Checks whether all loaded chunks have been sent to the client; if so, sends the position to confirm - void CheckIfWorldDownloaded(void); - - /// Sends the PlayerMoveLook packet that the client needs to reply to for the game to start - void SendConfirmPosition(void); - /// Adds a single chunk to be streamed to the client; used by StreamChunks() void StreamChunk(int a_ChunkX, int a_ChunkZ); diff --git a/source/CraftingRecipes.cpp b/source/CraftingRecipes.cpp index 13a8ac1e0..9dc471781 100644 --- a/source/CraftingRecipes.cpp +++ b/source/CraftingRecipes.cpp @@ -310,7 +310,7 @@ void cCraftingRecipes::GetRecipe(const cPlayer * a_Player, const cCraftingGrid & void cCraftingRecipes::LoadRecipes(void) { - LOG("-- Loading crafting recipes from crafting.txt --"); + LOGD("Loading crafting recipes from crafting.txt..."); ClearRecipes(); // Load the crafting.txt file: @@ -338,7 +338,7 @@ void cCraftingRecipes::LoadRecipes(void) } AddRecipeLine(LineNum, Recipe); } // for itr - Split[] - LOG("-- %d crafting recipes loaded from crafting.txt --", m_Recipes.size()); + LOG("Loaded %d crafting recipes", m_Recipes.size()); } diff --git a/source/Doors.h b/source/Doors.h deleted file mode 100644 index 69784a3d7..000000000 --- a/source/Doors.h +++ /dev/null @@ -1,87 +0,0 @@ - -#pragma once - - - - - -// tolua_begin -class cDoors -{ -public: - static char RotationToMetaData(double a_Rotation) - { - a_Rotation += 90 + 45; // So its not aligned with axis - if (a_Rotation > 360) - { - a_Rotation -= 360; - } - if (a_Rotation >= 0.f && a_Rotation < 90) - { - return 0x0; - } - else if ((a_Rotation >= 180) && (a_Rotation < 270)) - { - return 0x2; - } - else if ((a_Rotation >= 90) && (a_Rotation < 180)) - { - return 0x1; - } - else - { - return 0x3; - } - } - - - static NIBBLETYPE ChangeStateMetaData(NIBBLETYPE a_MetaData) - { - - a_MetaData ^= 4; //XOR bit 2 aka 3. bit (Door open state) - - return a_MetaData; - } - - - static void ChangeDoor(cWorld * a_World, int a_X, int a_Y, int a_Z) - { - NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_X, a_Y, a_Z); - - a_World->SetBlockMeta(a_X, a_Y, a_Z, ChangeStateMetaData(OldMetaData)); - - if (OldMetaData & 8) - { - // Current block is top of the door - BLOCKTYPE BottomBlock = a_World->GetBlock(a_X, a_Y - 1, a_Z); - NIBBLETYPE BottomMeta = a_World->GetBlockMeta(a_X, a_Y - 1, a_Z); - - if (IsDoor(BottomBlock) && !(BottomMeta & 8)) - { - a_World->SetBlockMeta(a_X, a_Y - 1, a_Z, ChangeStateMetaData(BottomMeta)); - } - } - else - { - // Current block is bottom of the door - BLOCKTYPE TopBlock = a_World->GetBlock(a_X, a_Y + 1, a_Z); - NIBBLETYPE TopMeta = a_World->GetBlockMeta(a_X, a_Y + 1, a_Z); - - if (IsDoor(TopBlock) && (TopMeta & 8)) - { - a_World->SetBlockMeta(a_X, a_Y + 1, a_Z, ChangeStateMetaData(TopMeta)); - } - } - } - - - inline static bool IsDoor(BLOCKTYPE a_Block) - { - return (a_Block == E_BLOCK_WOODEN_DOOR) || (a_Block == E_BLOCK_IRON_DOOR); - } -} ; -// tolua_end - - - - diff --git a/source/Entities/Boat.cpp b/source/Entities/Boat.cpp new file mode 100644 index 000000000..56e766dd4 --- /dev/null +++ b/source/Entities/Boat.cpp @@ -0,0 +1,87 @@ + +// Boat.cpp + +// Implements the cBoat class representing a boat in the world + +#include "Globals.h" +#include "Boat.h" +#include "../World.h" +#include "../ClientHandle.h" +#include "Player.h" + + + + + +cBoat::cBoat(double a_X, double a_Y, double a_Z) : + super(etBoat, a_X, a_Y, a_Z, 0.98, 0.7) +{ + SetMass(20.f); + SetMaxHealth(6); + SetHealth(6); +} + + + + +void cBoat::SpawnOn(cClientHandle & a_ClientHandle) +{ + a_ClientHandle.SendSpawnVehicle(*this, 1); +} + + + + + +void cBoat::DoTakeDamage(TakeDamageInfo & TDI) +{ + super::DoTakeDamage(TDI); + + if (GetHealth() == 0) + { + Destroy(true); + } +} + + + + + +void cBoat::OnRightClicked(cPlayer & a_Player) +{ + if (m_Attachee != NULL) + { + if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) + { + // This player is already sitting in, they want out. + a_Player.Detach(); + return; + } + + if (m_Attachee->IsPlayer()) + { + // Another player is already sitting in here, cannot attach + return; + } + + // Detach whatever is sitting in this boat now: + m_Attachee->Detach(); + } + + // Attach the player to this boat + a_Player.AttachTo(this); +} + + + + + +void cBoat::HandlePhysics(float a_Dt, cChunk & a_Chunk) +{ + super::HandlePhysics(a_Dt, a_Chunk); + BroadcastMovementUpdate(); +} + + + + diff --git a/source/Entities/Boat.h b/source/Entities/Boat.h new file mode 100644 index 000000000..8c51ab86c --- /dev/null +++ b/source/Entities/Boat.h @@ -0,0 +1,37 @@ + +// Boat.h + +// Declares the cBoat class representing a boat in the world + + + + + +#pragma once + +#include "Entity.h" + + + + + +class cBoat : + public cEntity +{ + typedef cEntity super; + +public: + CLASS_PROTODEF(cBoat); + + // cEntity overrides: + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + virtual void DoTakeDamage(TakeDamageInfo & TDI) override; + virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; + + cBoat(double a_X, double a_Y, double a_Z); +} ; + + + + diff --git a/source/Entities/Entity.cpp b/source/Entities/Entity.cpp index 1a593b3d1..d465c75bd 100644 --- a/source/Entities/Entity.cpp +++ b/source/Entities/Entity.cpp @@ -1,4 +1,3 @@ - #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Entity.h" @@ -13,6 +12,7 @@ #include "../Simulator/FluidSimulator.h" #include "../PluginManager.h" #include "../Tracer.h" +#include "Minecart.h" @@ -55,6 +55,7 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d , m_TicksSinceLastBurnDamage(0) , m_TicksSinceLastLavaDamage(0) , m_TicksSinceLastFireDamage(0) + , m_TicksSinceLastVoidDamage(0) , m_TicksLeftBurning(0) , m_WaterSpeed(0, 0, 0) , m_Width(a_Width) @@ -505,6 +506,11 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk) { TickBurning(a_Chunk); } + if ((a_Chunk.IsValid()) && (GetPosY() < -46)) + { + TickInVoid(a_Chunk); + } + else { m_TicksSinceLastVoidDamage = 0; } } @@ -524,8 +530,15 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) if ((BlockY >= cChunkDef::Height) || (BlockY < 0)) { // Outside of the world - // TODO: Current speed should still be added to the entity position - // Otherwise TNT explosions in the void will still effect the bottommost layers of the world + + cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ); + // See if we can commit our changes. If not, we will discard them. + if (NextChunk != NULL) + { + SetSpeed(NextSpeed); + NextPos += (NextSpeed * a_Dt); + SetPosition(NextPos); + } return; } @@ -536,12 +549,11 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width); int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width); BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ ); - BLOCKTYPE BlockBelow = NextChunk->GetBlock( RelBlockX, BlockY - 1, RelBlockZ ); + BLOCKTYPE BlockBelow = (BlockY > 0) ? NextChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR; if (!g_BlockIsSolid[BlockIn]) // Making sure we are not inside a solid block { if (m_bOnGround) // check if it's still on the ground { - BLOCKTYPE BlockBelow = NextChunk->GetBlock( RelBlockX, BlockY - 1, RelBlockZ ); if (!g_BlockIsSolid[BlockBelow]) // Check if block below is air or water. { m_bOnGround = false; @@ -551,8 +563,43 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) else { // Push out entity. + BLOCKTYPE GotBlock; + + static const struct + { + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + } ; + + bool IsNoAirSurrounding = true; + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + { + if (!NextChunk->UnboundedRelGetBlockType(RelBlockX + gCrossCoords[i].x, BlockY, RelBlockZ + gCrossCoords[i].z, GotBlock)) + { + // The pickup is too close to an unloaded chunk, bail out of any physics handling + return; + } + if (!g_BlockIsSolid[GotBlock]) + { + NextPos.x += gCrossCoords[i].x; + NextPos.z += gCrossCoords[i].z; + IsNoAirSurrounding = false; + break; + } + } // for i - gCrossCoords[] + + if (IsNoAirSurrounding) + { + NextPos.y += 0.5; + } + m_bOnGround = true; - NextPos.y += 0.2; + LOGD("Entity #%d (%s) is inside a block at {%d, %d, %d}", m_UniqueID, GetClass(), BlockX, BlockY, BlockZ ); @@ -565,6 +612,11 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) { fallspeed = m_Gravity * a_Dt / 3; // Fall 3x slower in water. } + else if (IsBlockRail(BlockBelow) && IsMinecart()) // Rails aren't solid, except for Minecarts + { + fallspeed = 0; + m_bOnGround = true; + } else if (BlockIn == E_BLOCK_COBWEB) { NextSpeed.y *= 0.05; // Reduce overall falling speed @@ -579,24 +631,37 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) } else { - // TODO: This condition belongs to minecarts, without it, they derails too much. - // But it shouldn't be here for other entities. We need a complete minecart physics overhaul. - if ( - (BlockBelow != E_BLOCK_RAIL) && - (BlockBelow != E_BLOCK_DETECTOR_RAIL) && - (BlockBelow != E_BLOCK_POWERED_RAIL) && - (BlockBelow != E_BLOCK_ACTIVATOR_RAIL) - ) + if (IsMinecart()) { - // Friction + if (!IsBlockRail(BlockBelow)) + { + // Friction if minecart is off track, otherwise, Minecart.cpp handles this + if (NextSpeed.SqrLength() > 0.0004f) + { + NextSpeed.x *= 0.7f / (1 + a_Dt); + if (fabs(NextSpeed.x) < 0.05) + { + NextSpeed.x = 0; + } + NextSpeed.z *= 0.7f / (1 + a_Dt); + if (fabs(NextSpeed.z) < 0.05) + { + NextSpeed.z = 0; + } + } + } + } + else + { + // Friction for non-minecarts if (NextSpeed.SqrLength() > 0.0004f) { - NextSpeed.x *= 0.6666; + NextSpeed.x *= 0.7f / (1 + a_Dt); if (fabs(NextSpeed.x) < 0.05) { NextSpeed.x = 0; } - NextSpeed.z *= 0.6666; + NextSpeed.z *= 0.7f / (1 + a_Dt); if (fabs(NextSpeed.z) < 0.05) { NextSpeed.z = 0; @@ -621,19 +686,19 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) switch(WaterDir) { case X_PLUS: - m_WaterSpeed.x = 1.f; + m_WaterSpeed.x = 0.2f; m_bOnGround = false; break; case X_MINUS: - m_WaterSpeed.x = -1.f; + m_WaterSpeed.x = -0.2f; m_bOnGround = false; break; case Z_PLUS: - m_WaterSpeed.z = 1.f; + m_WaterSpeed.z = 0.2f; m_bOnGround = false; break; case Z_MINUS: - m_WaterSpeed.z = -1.f; + m_WaterSpeed.z = -0.2f; m_bOnGround = false; break; @@ -664,7 +729,6 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) { if( Ret == 1 ) { - if( Tracer.HitNormal.x != 0.f ) NextSpeed.x = 0.f; if( Tracer.HitNormal.y != 0.f ) NextSpeed.y = 0.f; if( Tracer.HitNormal.z != 0.f ) NextSpeed.z = 0.f; @@ -675,11 +739,14 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) } } NextPos.Set(Tracer.RealHit.x,Tracer.RealHit.y,Tracer.RealHit.z); - NextPos.x += Tracer.HitNormal.x * 0.5f; - NextPos.z += Tracer.HitNormal.z * 0.5f; + NextPos.x += Tracer.HitNormal.x * 0.3f; + NextPos.y += Tracer.HitNormal.y * 0.05f; // Any larger produces entity vibration-upon-the-spot + NextPos.z += Tracer.HitNormal.z * 0.3f; } else + { NextPos += (NextSpeed * a_Dt); + } } else { @@ -829,6 +896,23 @@ void cEntity::TickBurning(cChunk & a_Chunk) +void cEntity::TickInVoid(cChunk & a_Chunk) +{ + if (m_TicksSinceLastVoidDamage == 20) + { + TakeDamage(dtInVoid, NULL, 2, 0); + m_TicksSinceLastVoidDamage = 0; + } + else + { + m_TicksSinceLastVoidDamage++; + } +} + + + + + /// Called when the entity starts burning void cEntity::OnStartedBurning(void) { diff --git a/source/Entities/Entity.h b/source/Entities/Entity.h index b063838eb..c6b70a7fc 100644 --- a/source/Entities/Entity.h +++ b/source/Entities/Entity.h @@ -61,7 +61,26 @@ struct TakeDamageInfo // tolua_begin class cEntity { -public: +public: + + enum eEntityType + { + etEntity, // For all other types + etPlayer, + etPickup, + etMonster, + etFallingBlock, + etMinecart, + etBoat, + etTNT, + etProjectile, + + // Common variations + etMob = etMonster, // DEPRECATED, use etMonster instead! + } ; + + // tolua_end + enum { ENTITY_STATUS_HURT = 2, @@ -71,6 +90,13 @@ public: ENTITY_STATUS_WOLF_SHAKING = 8, ENTITY_STATUS_EATING_ACCEPTED = 9, ENTITY_STATUS_SHEEP_EATING = 10, + ENTITY_STATUS_GOLEM_ROSING = 11, + ENTITY_STATUS_VILLAGER_HEARTS = 12, + ENTITY_STATUS_VILLAGER_ANGRY = 13, + ENTITY_STATUS_VILLAGER_HAPPY = 14, + ENTITY_STATUS_WITCH_MAGICKING = 15, + // It seems 16 (zombie conversion) is now done with metadata + ENTITY_STATUS_FIREWORK_EXPLODE= 17, } ; enum @@ -84,27 +110,6 @@ public: BURN_TICKS = 200, ///< How long to keep an entity burning after it has stood in lava / fire } ; - enum eEntityType - { - etEntity, // For all other types - etPlayer, - etPickup, - etMonster, - etFallingBlock, - etMinecart, - etTNT, - etProjectile, - - // DEPRECATED older constants, left over for compatibility reasons (plugins) - etMob = etMonster, // DEPRECATED, use etMonster instead! - eEntityType_Entity = etEntity, - eEntityType_Player = etPlayer, - eEntityType_Pickup = etPickup, - eEntityType_Mob = etMob, - } ; - - // tolua_end - cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height); virtual ~cEntity(); @@ -115,11 +120,14 @@ public: eEntityType GetEntityType(void) const { return m_EntityType; } - bool IsPlayer (void) const { return (m_EntityType == etPlayer); } - bool IsPickup (void) const { return (m_EntityType == etPickup); } - bool IsMob (void) const { return (m_EntityType == etMob); } - bool IsMinecart(void) const { return (m_EntityType == etMinecart); } - bool IsTNT (void) const { return (m_EntityType == etTNT); } + bool IsPlayer (void) const { return (m_EntityType == etPlayer); } + bool IsPickup (void) const { return (m_EntityType == etPickup); } + bool IsMob (void) const { return (m_EntityType == etMonster); } + bool IsFallingBlock(void) const { return (m_EntityType == etFallingBlock); } + bool IsMinecart (void) const { return (m_EntityType == etMinecart); } + bool IsBoat (void) const { return (m_EntityType == etBoat); } + bool IsTNT (void) const { return (m_EntityType == etTNT); } + bool IsProjectile (void) const { return (m_EntityType == etProjectile); } /// Returns true if the entity is of the specified class or a subclass (cPawn's IsA("cEntity") returns true) virtual bool IsA(const char * a_ClassName) const; @@ -232,16 +240,16 @@ public: /// Returns the curently equipped weapon; empty item if none virtual cItem GetEquippedWeapon(void) const { return cItem(); } - /// Returns the currently equipped helmet; empty item if nonte + /// Returns the currently equipped helmet; empty item if none virtual cItem GetEquippedHelmet(void) const { return cItem(); } - /// Returns the currently equipped chestplate; empty item if nonte + /// Returns the currently equipped chestplate; empty item if none virtual cItem GetEquippedChestplate(void) const { return cItem(); } - /// Returns the currently equipped leggings; empty item if nonte + /// Returns the currently equipped leggings; empty item if none virtual cItem GetEquippedLeggings(void) const { return cItem(); } - /// Returns the currently equipped boots; empty item if nonte + /// Returns the currently equipped boots; empty item if none virtual cItem GetEquippedBoots(void) const { return cItem(); } /// Called when the health drops below zero. a_Killer may be NULL (environmental damage) @@ -265,6 +273,9 @@ public: /// Updates the state related to this entity being on fire virtual void TickBurning(cChunk & a_Chunk); + + /// Handles when the entity is in the void + virtual void TickInVoid(cChunk & a_Chunk); /// Called when the entity starts burning virtual void OnStartedBurning(void); @@ -322,12 +333,13 @@ public: // tolua_begin - // Metadata flags; descendants may override the defaults: + // COMMON metadata flags; descendants may override the defaults: virtual bool IsOnFire (void) const {return (m_TicksLeftBurning > 0); } virtual bool IsCrouched (void) const {return false; } virtual bool IsRiding (void) const {return false; } virtual bool IsSprinting(void) const {return false; } virtual bool IsRclking (void) const {return false; } + virtual bool IsInvisible(void) const {return false; } // tolua_end @@ -387,6 +399,9 @@ protected: /// Time, in ticks, until the entity extinguishes its fire int m_TicksLeftBurning; + + /// Time, in ticks, since the last damage dealt by the void. Reset to zero when moving out of the void. + int m_TicksSinceLastVoidDamage; virtual void Destroyed(void) {} // Called after the entity has been destroyed diff --git a/source/Entities/Minecart.cpp b/source/Entities/Minecart.cpp index 0c0b7b58a..f75e23d8b 100644 --- a/source/Entities/Minecart.cpp +++ b/source/Entities/Minecart.cpp @@ -8,6 +8,7 @@ #include "Minecart.h" #include "../World.h" #include "../ClientHandle.h" +#include "../Chunk.h" #include "Player.h" @@ -16,7 +17,8 @@ cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) : super(etMinecart, a_X, a_Y, a_Z, 0.98, 0.7), - m_Payload(a_Payload) + m_Payload(a_Payload), + m_LastDamage(0) { SetMass(20.f); SetMaxHealth(6); @@ -51,30 +53,43 @@ void cMinecart::SpawnOn(cClientHandle & a_ClientHandle) void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk) { - if ((GetPosY() > 0) && (GetPosY() < cChunkDef::Height)) + int PosY = (int)floor(GetPosY()); + if ((PosY <= 0) || (PosY >= cChunkDef::Height)) { - BLOCKTYPE BelowType = GetWorld()->GetBlock(floor(GetPosX()), floor(GetPosY() -1 ), floor(GetPosZ())); - - if ( - (BelowType == E_BLOCK_RAIL) || - (BelowType == E_BLOCK_POWERED_RAIL) || - (BelowType == E_BLOCK_DETECTOR_RAIL) || - (BelowType == E_BLOCK_ACTIVATOR_RAIL) - ) + // Outside the world, just process normal falling physics + super::HandlePhysics(a_Dt, a_Chunk); + BroadcastMovementUpdate(); + return; + } + + int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width; + int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width; + cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ); + if (Chunk == NULL) + { + // Inside an unloaded chunk, bail out all processing + return; + } + BLOCKTYPE BelowType = Chunk->GetBlock(RelPosX, PosY - 1, RelPosZ); + BLOCKTYPE InsideType = Chunk->GetBlock(RelPosX, PosY, RelPosZ); + + if (IsBlockRail(BelowType)) + { + HandleRailPhysics(a_Dt, *Chunk); + } + else + { + if (IsBlockRail(InsideType)) { - HandleRailPhysics(a_Dt, a_Chunk); + SetPosY(PosY + 1); + HandleRailPhysics(a_Dt, *Chunk); } else { - super::HandlePhysics(a_Dt, a_Chunk); + super::HandlePhysics(a_Dt, *Chunk); BroadcastMovementUpdate(); } } - else - { - super::HandlePhysics(a_Dt, a_Chunk); - BroadcastMovementUpdate(); - } } @@ -83,6 +98,7 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk) static const double MAX_SPEED = 8; static const double MAX_SPEED_NEGATIVE = (0 - MAX_SPEED); + void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk) { @@ -94,7 +110,9 @@ void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk) */ // Get block meta below the cart - NIBBLETYPE BelowMeta = GetWorld()->GetBlockMeta(floor(GetPosX()), floor(GetPosY() -1 ), floor(GetPosZ())); + int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width; + int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width; + NIBBLETYPE BelowMeta = a_Chunk.GetMeta(RelPosX, (int)floor(GetPosY() - 1), RelPosZ); double SpeedX = GetSpeedX(), SpeedY = GetSpeedY(), SpeedZ = GetSpeedZ(); // Get current speed switch (BelowMeta) @@ -105,9 +123,6 @@ void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk) SpeedY = 0; // Don't move vertically as on ground SpeedX = 0; // Correct diagonal movement from curved rails - // Set Y as current Y rounded up to bypass friction - SetPosY(floor(GetPosY())); - if (SpeedZ != 0) // Don't do anything if cart is stationary { if (SpeedZ > 0) @@ -130,8 +145,6 @@ void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk) SpeedY = 0; SpeedZ = 0; - SetPosY(floor(GetPosY())); - if (SpeedX != 0) { if (SpeedX > 0) @@ -345,11 +358,51 @@ void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk) void cMinecart::DoTakeDamage(TakeDamageInfo & TDI) { + m_LastDamage = TDI.FinalDamage; super::DoTakeDamage(TDI); - if (GetHealth() == 0) + m_World->BroadcastEntityMetadata(*this); + + if (GetHealth() <= 0) { Destroy(true); + + cItems Drops; + switch (m_Payload) + { + case mpNone: + { + Drops.push_back(cItem(E_ITEM_MINECART, 1, 0)); + break; + } + case mpChest: + { + Drops.push_back(cItem(E_ITEM_CHEST_MINECART, 1, 0)); + break; + } + case mpFurnace: + { + Drops.push_back(cItem(E_ITEM_FURNACE_MINECART, 1, 0)); + break; + } + case mpTNT: + { + Drops.push_back(cItem(E_ITEM_MINECART_WITH_TNT, 1, 0)); + break; + } + case mpHopper: + { + Drops.push_back(cItem(E_ITEM_MINECART_WITH_HOPPER, 1, 0)); + break; + } + default: + { + ASSERT(!"Unhandled minecart type when spawning pickup!"); + return; + } + } + + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ()); } } @@ -435,7 +488,8 @@ void cMinecartWithChest::OnRightClicked(cPlayer & a_Player) // cMinecartWithFurnace: cMinecartWithFurnace::cMinecartWithFurnace(double a_X, double a_Y, double a_Z) : - super(mpFurnace, a_X, a_Y, a_Z) + super(mpFurnace, a_X, a_Y, a_Z), + m_IsFueled(false) { } @@ -445,8 +499,16 @@ cMinecartWithFurnace::cMinecartWithFurnace(double a_X, double a_Y, double a_Z) : void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player) { - // Try to power the furnace with whatever the player is holding - // TODO + if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_COAL) + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + + m_IsFueled = true; + m_World->BroadcastEntityMetadata(*this); + } } diff --git a/source/Entities/Minecart.h b/source/Entities/Minecart.h index f98b02bb5..b1b48be4e 100644 --- a/source/Entities/Minecart.h +++ b/source/Entities/Minecart.h @@ -10,7 +10,20 @@ #pragma once #include "Entity.h" -#include "../Item.h" + + + + + +inline bool IsBlockRail(BLOCKTYPE a_BlockType) + { + return ( + (a_BlockType == E_BLOCK_RAIL) || + (a_BlockType == E_BLOCK_ACTIVATOR_RAIL) || + (a_BlockType == E_BLOCK_DETECTOR_RAIL) || + (a_BlockType == E_BLOCK_POWERED_RAIL) + ) ; + } @@ -37,16 +50,19 @@ public: // cEntity overrides: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; - void HandleRailPhysics(float a_Dt, cChunk & a_Chunk); virtual void DoTakeDamage(TakeDamageInfo & TDI) override; - + int LastDamage(void) const { return m_LastDamage; } + void HandleRailPhysics(float a_Dt, cChunk & a_Chunk); ePayload GetPayload(void) const { return m_Payload; } protected: ePayload m_Payload; cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z); + + int m_LastDamage; + } ; @@ -114,6 +130,12 @@ public: // cEntity overrides: virtual void OnRightClicked(cPlayer & a_Player) override; + bool IsFueled (void) const { return m_IsFueled; } + +private: + + bool m_IsFueled; + } ; diff --git a/source/Entities/Pickup.cpp b/source/Entities/Pickup.cpp index 9b388366a..075f93449 100644 --- a/source/Entities/Pickup.cpp +++ b/source/Entities/Pickup.cpp @@ -24,8 +24,8 @@ -cPickup::cPickup(int a_MicroPosX, int a_MicroPosY, int a_MicroPosZ, const cItem & a_Item, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */) - : cEntity(etPickup, ((double)(a_MicroPosX)) / 32, ((double)(a_MicroPosY)) / 32, ((double)(a_MicroPosZ)) / 32, 0.2, 0.2) +cPickup::cPickup(double a_X, double a_Y, double a_Z, const cItem & a_Item, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */) + : cEntity(etPickup, a_X, a_Y, a_Z, 0.2, 0.2) , m_Timer( 0.f ) , m_Item(a_Item) , m_bCollected( false ) @@ -33,7 +33,6 @@ cPickup::cPickup(int a_MicroPosX, int a_MicroPosY, int a_MicroPosZ, const cItem m_MaxHealth = 5; m_Health = 5; SetSpeed(a_SpeedX, a_SpeedY, a_SpeedZ); - m_Gravity = -3.0; } diff --git a/source/Entities/Pickup.h b/source/Entities/Pickup.h index af6eaf3bb..488f91fb2 100644 --- a/source/Entities/Pickup.h +++ b/source/Entities/Pickup.h @@ -24,7 +24,7 @@ class cPickup : public: CLASS_PROTODEF(cPickup); - cPickup(int a_MicroPosX, int a_MicroPosY, int a_MicroPosZ, const cItem & a_Item, float a_SpeedX = 0.f, float a_SpeedY = 0.f, float a_SpeedZ = 0.f); // tolua_export + cPickup(double a_X, double a_Y, double a_Z, const cItem & a_Item, float a_SpeedX = 0.f, float a_SpeedY = 0.f, float a_SpeedZ = 0.f); // tolua_export cItem & GetItem(void) {return m_Item; } // tolua_export const cItem & GetItem(void) const {return m_Item; } diff --git a/source/Entities/Player.cpp b/source/Entities/Player.cpp index 0943f61ff..d93b45614 100644 --- a/source/Entities/Player.cpp +++ b/source/Entities/Player.cpp @@ -16,7 +16,6 @@ #include "../Item.h" #include "../Tracer.h" #include "../Root.h" -#include "../OSSupport/MakeDir.h" #include "../OSSupport/Timer.h" #include "../MersenneTwister.h" #include "../Chunk.h" @@ -220,7 +219,6 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk) if (m_IsChargingBow) { m_BowCharge += 1; - LOGD("Player \"%s\" charging bow: %d", m_PlayerName.c_str(), m_BowCharge); } if (m_bDirtyPosition) @@ -611,10 +609,13 @@ void cPlayer::SetSprint(bool a_IsSprinting) void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) { - if (m_GameMode == eGameMode_Creative) + if (a_TDI.DamageType != dtInVoid) { - // No damage / health in creative mode - return; + if (IsGameModeCreative()) + { + // No damage / health in creative mode + return; + } } super::DoTakeDamage(a_TDI); @@ -1182,7 +1183,7 @@ void cPlayer::TossItem( double vX = 0, vY = 0, vZ = 0; EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY); vY = -vY * 2 + 1.f; - m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 2, vY * 2, vZ * 2); + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 3, vY * 3, vZ * 3); } @@ -1338,7 +1339,7 @@ bool cPlayer::LoadFromDisk() bool cPlayer::SaveToDisk() { - cMakeDir::MakeDir("players"); + cFile::CreateFolder(FILE_IO_PREFIX + AString("players")); // create the JSON data Json::Value JSON_PlayerPosition; @@ -1446,7 +1447,17 @@ void cPlayer::SetSwimState(cChunk & a_Chunk) // Check if the player is swimming: // Use Unbounded, because we're being called *after* processing super::Tick(), which could have changed our chunk - VERIFY(a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockIn)); + if (!a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockIn)) + { + // This sometimes happens on Linux machines + // Ref.: http://forum.mc-server.org/showthread.php?tid=1244 + LOGD("SetSwimState failure: RelX = %d, RelZ = %d, LastPos = {%.02f, %.02f}, Pos = %.02f, %.02f}", + RelX, RelY, m_LastPosX, m_LastPosZ, GetPosX(), GetPosZ() + ); + m_IsSwimming = false; + m_IsSubmerged = false; + return; + } m_IsSwimming = IsBlockWater(BlockIn); // Check if the player is submerged: diff --git a/source/FurnaceRecipe.cpp b/source/FurnaceRecipe.cpp index 8b1ee09a2..2e2276981 100644 --- a/source/FurnaceRecipe.cpp +++ b/source/FurnaceRecipe.cpp @@ -51,7 +51,7 @@ cFurnaceRecipe::~cFurnaceRecipe() void cFurnaceRecipe::ReloadRecipes(void) { ClearRecipes(); - LOG("-- Loading furnace recipes --"); + LOGD("Loading furnace recipes..."); std::ifstream f; char a_File[] = "furnace.txt"; @@ -175,7 +175,7 @@ void cFurnaceRecipe::ReloadRecipes(void) { LOGERROR("ERROR: FurnaceRecipe, syntax error" ); } - LOG("Got %u furnace recipes, and %u fuels.", m_pState->Recipes.size(), m_pState->Fuel.size()); + LOG("Loaded %u furnace recipes and %u fuels", m_pState->Recipes.size(), m_pState->Fuel.size()); } diff --git a/source/Generating/BioGen.cpp b/source/Generating/BioGen.cpp index 4345852c6..926120afc 100644 --- a/source/Generating/BioGen.cpp +++ b/source/Generating/BioGen.cpp @@ -27,7 +27,7 @@ void cBioGenConstant::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap -void cBioGenConstant::Initialize(cIniFile & a_IniFile) +void cBioGenConstant::InitializeBiomeGen(cIniFile & a_IniFile) { AString Biome = a_IniFile.GetValueSet("Generator", "ConstantBiome", "Plains"); m_Biome = StringToBiome(Biome); @@ -131,10 +131,10 @@ void cBioGenCache::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a -void cBioGenCache::Initialize(cIniFile & a_IniFile) +void cBioGenCache::InitializeBiomeGen(cIniFile & a_IniFile) { - super::Initialize(a_IniFile); - m_BioGenToCache->Initialize(a_IniFile); + super::InitializeBiomeGen(a_IniFile); + m_BioGenToCache->InitializeBiomeGen(a_IniFile); } @@ -242,9 +242,9 @@ void cBioGenCheckerboard::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::Biome -void cBioGenCheckerboard::Initialize(cIniFile & a_IniFile) +void cBioGenCheckerboard::InitializeBiomeGen(cIniFile & a_IniFile) { - super::Initialize(a_IniFile); + super::InitializeBiomeGen(a_IniFile); AString Biomes = a_IniFile.GetValueSet ("Generator", "CheckerBoardBiomes", ""); m_BiomeSize = a_IniFile.GetValueSetI("Generator", "CheckerboardBiomeSize", 64); m_BiomeSize = (m_BiomeSize < 8) ? 8 : m_BiomeSize; @@ -276,9 +276,9 @@ void cBioGenVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & -void cBioGenVoronoi::Initialize(cIniFile & a_IniFile) +void cBioGenVoronoi::InitializeBiomeGen(cIniFile & a_IniFile) { - super::Initialize(a_IniFile); + super::InitializeBiomeGen(a_IniFile); m_CellSize = a_IniFile.GetValueSetI("Generator", "VoronoiCellSize", 64); AString Biomes = a_IniFile.GetValueSet ("Generator", "VoronoiBiomes", ""); InitializeBiomes(Biomes); @@ -358,9 +358,9 @@ void cBioGenDistortedVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::B -void cBioGenDistortedVoronoi::Initialize(cIniFile & a_IniFile) +void cBioGenDistortedVoronoi::InitializeBiomeGen(cIniFile & a_IniFile) { - // Do NOT call super::Initialize(), as it would try to read Voronoi params instead of DistortedVoronoi params + // Do NOT call super::InitializeBiomeGen(), as it would try to read Voronoi params instead of DistortedVoronoi params m_CellSize = a_IniFile.GetValueSetI("Generator", "DistortedVoronoiCellSize", 96); AString Biomes = a_IniFile.GetValueSet ("Generator", "DistortedVoronoiBiomes", ""); InitializeBiomes(Biomes); @@ -409,7 +409,7 @@ cBioGenMultiStepMap::cBioGenMultiStepMap(int a_Seed) : -void cBioGenMultiStepMap::Initialize(cIniFile & a_IniFile) +void cBioGenMultiStepMap::InitializeBiomeGen(cIniFile & a_IniFile) { m_OceanCellSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapOceanCellSize", m_OceanCellSize); m_MushroomIslandSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapMushroomIslandSize", m_MushroomIslandSize); diff --git a/source/Generating/BioGen.h b/source/Generating/BioGen.h index f2afc3e8c..bc70bfab2 100644 --- a/source/Generating/BioGen.h +++ b/source/Generating/BioGen.h @@ -33,7 +33,7 @@ protected: // cBiomeGen overrides: virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void Initialize(cIniFile & a_IniFile) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; } ; @@ -72,7 +72,7 @@ protected: int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits) virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void Initialize(cIniFile & a_IniFile) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; } ; @@ -109,7 +109,7 @@ protected: // cBiomeGen overrides: virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void Initialize(cIniFile & a_IniFile) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; } ; @@ -134,7 +134,7 @@ protected: // cBiomeGen overrides: virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void Initialize(cIniFile & a_IniFile) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; EMCSBiome VoronoiBiome(int a_BlockX, int a_BlockZ); } ; @@ -156,7 +156,7 @@ public: protected: // cBiomeGen overrides: virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void Initialize(cIniFile & a_IniFile) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; /// Distorts the coords using a Perlin-like noise void Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ); @@ -195,7 +195,7 @@ protected: // cBiomeGen overrides: virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void Initialize(cIniFile & a_IniFile) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; /** Step 1: Decides between ocean, land and mushroom, using a DistVoronoi with special conditions and post-processing for mushroom islands Sets biomes to biOcean, -1 (i.e. land), biMushroomIsland or biMushroomShore diff --git a/source/Generating/ChunkDesc.h b/source/Generating/ChunkDesc.h index 41b85a814..067d8494a 100644 --- a/source/Generating/ChunkDesc.h +++ b/source/Generating/ChunkDesc.h @@ -36,13 +36,13 @@ public: cChunkDesc(int a_ChunkX, int a_ChunkZ); ~cChunkDesc(); + void SetChunkCoords(int a_ChunkX, int a_ChunkZ); + // tolua_begin int GetChunkX(void) const { return m_ChunkX; } int GetChunkZ(void) const { return m_ChunkZ; } - void SetChunkCoords(int a_ChunkX, int a_ChunkZ); - void FillBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); void SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); @@ -74,7 +74,7 @@ public: /// Writes the block area into the chunk, with its origin set at the specified relative coords. Area's data overwrite everything in the chunk. void WriteBlockArea(const cBlockArea & a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy = cBlockArea::msOverwrite); - /// Reads an area from the chunk into a cBlockArea + /// Reads an area from the chunk into a cBlockArea, blocktypes and blockmetas void ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ); /// Returns the maximum height value in the heightmap diff --git a/source/Generating/CompoGen.cpp b/source/Generating/CompoGen.cpp index b99919e0d..cc2a203af 100644 --- a/source/Generating/CompoGen.cpp +++ b/source/Generating/CompoGen.cpp @@ -10,7 +10,9 @@ #include "Globals.h" #include "CompoGen.h" #include "../BlockID.h" +#include "../Item.h" #include "../LinearUpscale.h" +#include "../../iniFile/iniFile.h" @@ -48,6 +50,16 @@ void cCompoGenSameBlock::ComposeTerrain(cChunkDesc & a_ChunkDesc) +void cCompoGenSameBlock::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_BlockType = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "SameBlockType", "stone").m_ItemType); + m_IsBedrocked = (a_IniFile.GetValueSetI("Generator", "SameBlockBedrocked", 1) != 0); +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cCompoGenDebugBiomes: @@ -102,20 +114,16 @@ void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cCompoGenClassic: -cCompoGenClassic::cCompoGenClassic( - int a_SeaLevel, int a_BeachHeight, int a_BeachDepth, - BLOCKTYPE a_BlockTop, BLOCKTYPE a_BlockMiddle, BLOCKTYPE a_BlockBottom, - BLOCKTYPE a_BlockBeach, BLOCKTYPE a_BlockBeachBottom, BLOCKTYPE a_BlockSea -) : - m_SeaLevel(a_SeaLevel), - m_BeachHeight(a_BeachHeight), - m_BeachDepth(a_BeachDepth), - m_BlockTop(a_BlockTop), - m_BlockMiddle(a_BlockMiddle), - m_BlockBottom(a_BlockBottom), - m_BlockBeach(a_BlockBeach), - m_BlockBeachBottom(a_BlockBeachBottom), - m_BlockSea(a_BlockSea) +cCompoGenClassic::cCompoGenClassic(void) : + m_SeaLevel(60), + m_BeachHeight(2), + m_BeachDepth(4), + m_BlockTop(E_BLOCK_GRASS), + m_BlockMiddle(E_BLOCK_DIRT), + m_BlockBottom(E_BLOCK_STONE), + m_BlockBeach(E_BLOCK_SAND), + m_BlockBeachBottom(E_BLOCK_SANDSTONE), + m_BlockSea(E_BLOCK_STATIONARY_WATER) { } @@ -184,6 +192,23 @@ void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc) +void cCompoGenClassic::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "ClassicSeaLevel", m_SeaLevel); + m_BeachHeight = a_IniFile.GetValueSetI("Generator", "ClassicBeachHeight", m_BeachHeight); + m_BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", m_BeachDepth); + m_BlockTop = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockTop", "grass").m_ItemType); + m_BlockMiddle = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockMiddle", "dirt").m_ItemType); + m_BlockBottom = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBottom", "stone").m_ItemType); + m_BlockBeach = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBeach", "sand").m_ItemType); + m_BlockBeachBottom = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBeachBottom", "sandstone").m_ItemType); + m_BlockSea = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockSea", "stationarywater").m_ItemType); +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cCompoGenBiomal: @@ -286,6 +311,15 @@ void cCompoGenBiomal::ComposeTerrain(cChunkDesc & a_ChunkDesc) +void cCompoGenBiomal::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalSeaLevel", m_SeaLevel) - 1; +} + + + + + void cCompoGenBiomal::FillColumnGrass(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes) { BLOCKTYPE Pattern[] = @@ -485,10 +519,19 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc) +void cCompoGenNether::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_Threshold = a_IniFile.GetValueSetI("Generator", "NetherThreshold", m_Threshold); +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cCompoGenCache: -cCompoGenCache::cCompoGenCache(cTerrainCompositionGen * a_Underlying, int a_CacheSize) : +cCompoGenCache::cCompoGenCache(cTerrainCompositionGen & a_Underlying, int a_CacheSize) : m_Underlying(a_Underlying), m_CacheSize(a_CacheSize), m_CacheOrder(new int[a_CacheSize]), @@ -521,13 +564,13 @@ cCompoGenCache::~cCompoGenCache() void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc) { - //* + #ifdef _DEBUG if (((m_NumHits + m_NumMisses) % 1024) == 10) { LOGD("CompoGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses)); LOGD("CompoGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits); } - //*/ + #endif // _DEBUG int ChunkX = a_ChunkDesc.GetChunkX(); int ChunkZ = a_ChunkDesc.GetChunkZ(); @@ -562,7 +605,7 @@ void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc) // Not in the cache: m_NumMisses++; - m_Underlying->ComposeTerrain(a_ChunkDesc); + m_Underlying.ComposeTerrain(a_ChunkDesc); // Insert it as the first item in the MRU order: int Idx = m_CacheOrder[m_CacheSize - 1]; @@ -580,3 +623,12 @@ void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc) + +void cCompoGenCache::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_Underlying.InitializeCompoGen(a_IniFile); +} + + + + diff --git a/source/Generating/CompoGen.h b/source/Generating/CompoGen.h index 8391de66e..2ee286b06 100644 --- a/source/Generating/CompoGen.h +++ b/source/Generating/CompoGen.h @@ -27,9 +27,9 @@ class cCompoGenSameBlock : public cTerrainCompositionGen { public: - cCompoGenSameBlock(BLOCKTYPE a_BlockType, bool a_IsBedrocked) : - m_BlockType(a_BlockType), - m_IsBedrocked(a_IsBedrocked) + cCompoGenSameBlock(void) : + m_BlockType(E_BLOCK_STONE), + m_IsBedrocked(true) {} protected: @@ -39,6 +39,7 @@ protected: // cTerrainCompositionGen overrides: virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; } ; @@ -65,11 +66,7 @@ class cCompoGenClassic : public cTerrainCompositionGen { public: - cCompoGenClassic( - int a_SeaLevel, int a_BeachHeight, int a_BeachDepth, - BLOCKTYPE a_BlockTop, BLOCKTYPE a_BlockMiddle, BLOCKTYPE a_BlockBottom, - BLOCKTYPE a_BlockBeach, BLOCKTYPE a_BlockBeachBottom, BLOCKTYPE a_BlockSea - ); + cCompoGenClassic(void); protected: @@ -85,6 +82,7 @@ protected: // cTerrainCompositionGen overrides: virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; } ; @@ -95,9 +93,9 @@ class cCompoGenBiomal : public cTerrainCompositionGen { public: - cCompoGenBiomal(int a_Seed, int a_SeaLevel) : + cCompoGenBiomal(int a_Seed) : m_Noise(a_Seed + 1000), - m_SeaLevel(a_SeaLevel - 1) // we do an adjustment later in filling the terrain with water + m_SeaLevel(62) { } @@ -108,6 +106,7 @@ protected: // cTerrainCompositionGen overrides: virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; void FillColumnGrass (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); void FillColumnSand (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); @@ -136,6 +135,7 @@ protected: // cTerrainCompositionGen overrides: virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; } ; @@ -147,15 +147,16 @@ class cCompoGenCache : public cTerrainCompositionGen { public: - cCompoGenCache(cTerrainCompositionGen * a_Underlying, int a_CacheSize); // Doesn't take ownership of a_Underlying + cCompoGenCache(cTerrainCompositionGen & a_Underlying, int a_CacheSize); // Doesn't take ownership of a_Underlying ~cCompoGenCache(); // cTerrainCompositionGen override: virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; protected: - cTerrainCompositionGen * m_Underlying; + cTerrainCompositionGen & m_Underlying; struct sCacheData { diff --git a/source/Generating/ComposableGenerator.cpp b/source/Generating/ComposableGenerator.cpp index 0852f559e..2637b64e7 100644 --- a/source/Generating/ComposableGenerator.cpp +++ b/source/Generating/ComposableGenerator.cpp @@ -211,7 +211,7 @@ void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile) m_UnderlyingBiomeGen = m_BiomeGen; m_BiomeGen = new cBioGenCache(m_UnderlyingBiomeGen, CacheSize); } - m_BiomeGen->Initialize(a_IniFile); + m_BiomeGen->InitializeBiomeGen(a_IniFile); } @@ -231,35 +231,24 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile) bool CacheOffByDefault = false; if (NoCaseCompare(HeightGenName, "flat") == 0) { - int Height = a_IniFile.GetValueSetI("Generator", "FlatHeight", 5); - m_HeightGen = new cHeiGenFlat(Height); + m_HeightGen = new cHeiGenFlat; CacheOffByDefault = true; // We're generating faster than a cache would retrieve data } else if (NoCaseCompare(HeightGenName, "classic") == 0) { - // These used to be in terrain.ini, but now they are in world.ini (so that multiple worlds can have different values): - float HeightFreq1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq1", 0.1); - float HeightFreq2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq2", 1.0); - float HeightFreq3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq3", 2.0); - float HeightAmp1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp1", 1.0); - float HeightAmp2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp2", 0.5); - float HeightAmp3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp3", 0.5); - m_HeightGen = new cHeiGenClassic(Seed, HeightFreq1, HeightAmp1, HeightFreq2, HeightAmp2, HeightFreq3, HeightAmp3); + m_HeightGen = new cHeiGenClassic(Seed); } else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0) { m_HeightGen = new cDistortedHeightmap(Seed, *m_BiomeGen); - ((cDistortedHeightmap *)m_HeightGen)->Initialize(a_IniFile); } else if (NoCaseCompare(HeightGenName, "End") == 0) { m_HeightGen = new cEndGen(Seed); - ((cEndGen *)m_HeightGen)->Initialize(a_IniFile); } else if (NoCaseCompare(HeightGenName, "Noise3D") == 0) { m_HeightGen = new cNoise3DComposable(Seed); - ((cNoise3DComposable *)m_HeightGen)->Initialize(a_IniFile); } else // "biomal" or <not found> { @@ -283,6 +272,9 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile) //*/ } + // Read the settings: + m_HeightGen->InitializeHeightGen(a_IniFile); + // Add a cache, if requested: int CacheSize = a_IniFile.GetValueSetI("Generator", "HeightGenCacheSize", CacheOffByDefault ? 0 : 64); if (CacheSize > 0) @@ -294,9 +286,9 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile) ); CacheSize = 4; } - LOGINFO("Using a cache for Heightgen of size %d.", CacheSize); + LOGD("Using a cache for Heightgen of size %d.", CacheSize); m_UnderlyingHeightGen = m_HeightGen; - m_HeightGen = new cHeiGenCache(m_UnderlyingHeightGen, CacheSize); + m_HeightGen = new cHeiGenCache(*m_UnderlyingHeightGen, CacheSize); } } @@ -306,6 +298,7 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile) void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile) { + int Seed = m_ChunkGenerator.GetSeed(); AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", ""); if (CompoGenName.empty()) { @@ -314,9 +307,7 @@ void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile) } if (NoCaseCompare(CompoGenName, "sameblock") == 0) { - int Block = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "SameBlockType", "stone"); - bool Bedrocked = (a_IniFile.GetValueSetI("Generator", "SameBlockBedrocked", 1) != 0); - m_CompositionGen = new cCompoGenSameBlock((BLOCKTYPE)Block, Bedrocked); + m_CompositionGen = new cCompoGenSameBlock; } else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0) { @@ -324,38 +315,23 @@ void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile) } else if (NoCaseCompare(CompoGenName, "classic") == 0) { - int SeaLevel = a_IniFile.GetValueSetI("Generator", "ClassicSeaLevel", 60); - int BeachHeight = a_IniFile.GetValueSetI("Generator", "ClassicBeachHeight", 2); - int BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", 4); - BLOCKTYPE BlockTop = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockTop", "grass"); - BLOCKTYPE BlockMiddle = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockMiddle", "dirt"); - BLOCKTYPE BlockBottom = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBottom", "stone"); - BLOCKTYPE BlockBeach = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBeach", "sand"); - BLOCKTYPE BlockBeachBottom = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBeachBottom", "sandstone"); - BLOCKTYPE BlockSea = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockSea", "stationarywater"); - m_CompositionGen = new cCompoGenClassic( - SeaLevel, BeachHeight, BeachDepth, BlockTop, BlockMiddle, BlockBottom, BlockBeach, - BlockBeachBottom, BlockSea - ); + m_CompositionGen = new cCompoGenClassic; } else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0) { - m_CompositionGen = new cDistortedHeightmap(m_ChunkGenerator.GetSeed(), *m_BiomeGen); - ((cDistortedHeightmap *)m_CompositionGen)->Initialize(a_IniFile); + m_CompositionGen = new cDistortedHeightmap(Seed, *m_BiomeGen); } else if (NoCaseCompare(CompoGenName, "end") == 0) { - m_CompositionGen = new cEndGen(m_ChunkGenerator.GetSeed()); - ((cEndGen *)m_CompositionGen)->Initialize(a_IniFile); + m_CompositionGen = new cEndGen(Seed); } else if (NoCaseCompare(CompoGenName, "nether") == 0) { - m_CompositionGen = new cCompoGenNether(m_ChunkGenerator.GetSeed()); + m_CompositionGen = new cCompoGenNether(Seed); } else if (NoCaseCompare(CompoGenName, "Noise3D") == 0) { m_CompositionGen = new cNoise3DComposable(m_ChunkGenerator.GetSeed()); - ((cNoise3DComposable *)m_CompositionGen)->Initialize(a_IniFile); } else { @@ -363,9 +339,7 @@ void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile) { LOGWARN("Unknown CompositionGen \"%s\", using \"biomal\" instead.", CompoGenName.c_str()); } - int SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalSeaLevel", 62); - int Seed = m_ChunkGenerator.GetSeed(); - m_CompositionGen = new cCompoGenBiomal(Seed, SeaLevel); + m_CompositionGen = new cCompoGenBiomal(Seed); /* // Performance-testing: @@ -383,11 +357,14 @@ void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile) //*/ } + // Read the settings from the ini file: + m_CompositionGen->InitializeCompoGen(a_IniFile); + int CompoGenCacheSize = a_IniFile.GetValueSetI("Generator", "CompositionGenCacheSize", 64); if (CompoGenCacheSize > 1) { m_UnderlyingCompositionGen = m_CompositionGen; - m_CompositionGen = new cCompoGenCache(m_UnderlyingCompositionGen, 32); + m_CompositionGen = new cCompoGenCache(*m_UnderlyingCompositionGen, 32); } } diff --git a/source/Generating/ComposableGenerator.h b/source/Generating/ComposableGenerator.h index 1d5c98e5d..d5e33a439 100644 --- a/source/Generating/ComposableGenerator.h +++ b/source/Generating/ComposableGenerator.h @@ -47,7 +47,7 @@ public: virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0; /// Reads parameters from the ini file, prepares generator for use. - virtual void Initialize(cIniFile & a_IniFile) {} + virtual void InitializeBiomeGen(cIniFile & a_IniFile) {} } ; @@ -67,6 +67,9 @@ public: /// Generates heightmap for the given chunk virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0; + + /// Reads parameters from the ini file, prepares generator for use. + virtual void InitializeHeightGen(cIniFile & a_IniFile) {} } ; @@ -84,6 +87,9 @@ public: virtual ~cTerrainCompositionGen() {} // Force a virtual destructor in descendants virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) = 0; + + /// Reads parameters from the ini file, prepares generator for use. + virtual void InitializeCompoGen(cIniFile & a_IniFile) {} } ; diff --git a/source/Generating/DistortedHeightmap.cpp b/source/Generating/DistortedHeightmap.cpp index 6ac4d61d5..98eab31b5 100644 --- a/source/Generating/DistortedHeightmap.cpp +++ b/source/Generating/DistortedHeightmap.cpp @@ -60,7 +60,7 @@ cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen) : m_OceanFloorSelect(a_Seed + 3000), m_BiomeGen(a_BiomeGen), m_UnderlyingHeiGen(a_Seed, a_BiomeGen), - m_HeightGen(&m_UnderlyingHeiGen, 64) + m_HeightGen(m_UnderlyingHeiGen, 64) { m_NoiseDistortX.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5); m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1); @@ -77,11 +77,18 @@ cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen) : void cDistortedHeightmap::Initialize(cIniFile & a_IniFile) { + if (m_IsInitialized) + { + return; + } + // Read the params from the INI file: m_SeaLevel = a_IniFile.GetValueSetI("Generator", "DistortedHeightmapSeaLevel", 62); m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyX", 10); m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyY", 10); m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyZ", 10); + + m_IsInitialized = true; } @@ -184,6 +191,15 @@ void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::He +void cDistortedHeightmap::InitializeHeightGen(cIniFile & a_IniFile) +{ + Initialize(a_IniFile); +} + + + + + void cDistortedHeightmap::ComposeTerrain(cChunkDesc & a_ChunkDesc) { // Frequencies for the ocean floor selecting noise: @@ -311,6 +327,15 @@ void cDistortedHeightmap::ComposeTerrain(cChunkDesc & a_ChunkDesc) +void cDistortedHeightmap::InitializeCompoGen(cIniFile & a_IniFile) +{ + Initialize(a_IniFile); +} + + + + + int cDistortedHeightmap::GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z) { int ChunkX = (int)floor(a_X / (NOISE_DATATYPE)16); diff --git a/source/Generating/DistortedHeightmap.h b/source/Generating/DistortedHeightmap.h index b2b235e61..6d7007375 100644 --- a/source/Generating/DistortedHeightmap.h +++ b/source/Generating/DistortedHeightmap.h @@ -30,8 +30,6 @@ class cDistortedHeightmap : public: cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen); - void Initialize(cIniFile & a_IniFile); - protected: typedef cChunkDef::BiomeMap BiomeNeighbors[3][3]; @@ -77,6 +75,9 @@ protected: NOISE_DATATYPE m_DistortAmpX[DIM_X * DIM_Z]; NOISE_DATATYPE m_DistortAmpZ[DIM_X * DIM_Z]; + /// True if Initialize() has been called. Used to initialize-once even with multiple init entrypoints (HeiGen / CompoGen) + bool m_IsInitialized; + /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap) void PrepareState(int a_ChunkX, int a_ChunkZ); @@ -93,10 +94,15 @@ protected: /// Calculates the X and Z distortion amplitudes based on the neighbors' biomes void GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ); + /// Reads the settings from the ini file. Skips reading if already initialized + void Initialize(cIniFile & a_IniFile); + // cTerrainHeightGen overrides: virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; // cTerrainCompositionGen overrides: virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; } ; diff --git a/source/Generating/HeiGen.cpp b/source/Generating/HeiGen.cpp index 2aa942c73..5dee181b7 100644 --- a/source/Generating/HeiGen.cpp +++ b/source/Generating/HeiGen.cpp @@ -6,6 +6,8 @@ #include "Globals.h" #include "HeiGen.h" #include "../LinearUpscale.h" +#include "../../iniFile/iniFile.h" + @@ -26,10 +28,19 @@ void cHeiGenFlat::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap +void cHeiGenFlat::InitializeHeightGen(cIniFile & a_IniFile) +{ + m_Height = a_IniFile.GetValueSetI("Generator", "FlatHeight", m_Height); +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cHeiGenCache: -cHeiGenCache::cHeiGenCache(cTerrainHeightGen * a_HeiGenToCache, int a_CacheSize) : +cHeiGenCache::cHeiGenCache(cTerrainHeightGen & a_HeiGenToCache, int a_CacheSize) : m_HeiGenToCache(a_HeiGenToCache), m_CacheSize(a_CacheSize), m_CacheOrder(new int[a_CacheSize]), @@ -99,7 +110,7 @@ void cHeiGenCache::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap // Not in the cache: m_NumMisses++; - m_HeiGenToCache->GenHeightMap(a_ChunkX, a_ChunkZ, a_HeightMap); + m_HeiGenToCache.GenHeightMap(a_ChunkX, a_ChunkZ, a_HeightMap); // Insert it as the first item in the MRU order: int Idx = m_CacheOrder[m_CacheSize - 1]; @@ -117,6 +128,15 @@ void cHeiGenCache::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap +void cHeiGenCache::InitializeHeightGen(cIniFile & a_IniFile) +{ + m_HeiGenToCache.InitializeHeightGen(a_IniFile); +} + + + + + bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height) { for (int i = 0; i < m_CacheSize; i++) @@ -137,17 +157,10 @@ bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_Rel /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cHeiGenClassic: -cHeiGenClassic::cHeiGenClassic(int a_Seed, float a_HeightFreq1, float a_HeightAmp1, float a_HeightFreq2, float a_HeightAmp2, float a_HeightFreq3, float a_HeightAmp3) : +cHeiGenClassic::cHeiGenClassic(int a_Seed) : m_Seed(a_Seed), - m_Noise(a_Seed), - m_HeightFreq1(a_HeightFreq1), - m_HeightAmp1 (a_HeightAmp1), - m_HeightFreq2(a_HeightFreq2), - m_HeightAmp2 (a_HeightAmp2), - m_HeightFreq3(a_HeightFreq3), - m_HeightAmp3 (a_HeightAmp3) + m_Noise(a_Seed) { - // Nothing needed yet } @@ -199,6 +212,20 @@ void cHeiGenClassic::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightM +void cHeiGenClassic::InitializeHeightGen(cIniFile & a_IniFile) +{ + m_HeightFreq1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq1", 0.1); + m_HeightFreq2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq2", 1.0); + m_HeightFreq3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq3", 2.0); + m_HeightAmp1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp1", 1.0); + m_HeightAmp2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp2", 0.5); + m_HeightAmp3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp3", 0.5); +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cHeiGenBiomal: @@ -294,6 +321,15 @@ void cHeiGenBiomal::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMa +void cHeiGenBiomal::InitializeHeightGen(cIniFile & a_IniFile) +{ + // No user-settable params +} + + + + + NOISE_DATATYPE cHeiGenBiomal::GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const cHeiGenBiomal::BiomeNeighbors & a_BiomeNeighbors) { // Sum up how many biomes of each type there are in the neighborhood: diff --git a/source/Generating/HeiGen.h b/source/Generating/HeiGen.h index 437b5f104..1b246c70a 100644 --- a/source/Generating/HeiGen.h +++ b/source/Generating/HeiGen.h @@ -25,14 +25,15 @@ class cHeiGenFlat : public cTerrainHeightGen { public: - cHeiGenFlat(int a_Height) : m_Height(a_Height) {} + cHeiGenFlat(void) : m_Height(5) {} protected: int m_Height; - // cTerrainHeightGen override: + // cTerrainHeightGen overrides: virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; } ; @@ -44,18 +45,19 @@ class cHeiGenCache : public cTerrainHeightGen { public: - cHeiGenCache(cTerrainHeightGen * a_HeiGenToCache, int a_CacheSize); // Doesn't take ownership of a_HeiGenToCache + cHeiGenCache(cTerrainHeightGen & a_HeiGenToCache, int a_CacheSize); // Doesn't take ownership of a_HeiGenToCache ~cHeiGenCache(); - // cTerrainHeightGen override: + // cTerrainHeightGen overrides: virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; /// Retrieves height at the specified point in the cache, returns true if found, false if not found bool GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height); protected: - cTerrainHeightGen * m_HeiGenToCache; + cTerrainHeightGen & m_HeiGenToCache; struct sCacheData { @@ -83,7 +85,7 @@ class cHeiGenClassic : public cTerrainHeightGen { public: - cHeiGenClassic(int a_Seed, float a_HeightFreq1, float a_HeightAmp1, float a_HeightFreq2, float a_HeightAmp2, float a_HeightFreq3, float a_HeightAmp3); + cHeiGenClassic(int a_Seed); protected: @@ -95,8 +97,9 @@ protected: float GetNoise(float x, float y); - // cTerrainHeightGen override: + // cTerrainHeightGen overrides: virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; } ; @@ -130,8 +133,9 @@ protected: } ; static const sGenParam m_GenParam[biNumBiomes]; - // cTerrainHeightGen override: + // cTerrainHeightGen overrides: virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; NOISE_DATATYPE GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const BiomeNeighbors & a_BiomeNeighbors); } ; diff --git a/source/Globals.h b/source/Globals.h index 150051de0..1e531f7f3 100644 --- a/source/Globals.h +++ b/source/Globals.h @@ -109,7 +109,6 @@ typedef unsigned short UInt16; #endif // GetFreeSpace #else #include <sys/types.h> - #include <sys/stat.h> // for mkdir #include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> @@ -142,6 +141,7 @@ typedef unsigned short UInt16; // CRT stuff: +#include <sys/stat.h> #include <assert.h> #include <stdio.h> #include <math.h> diff --git a/source/GroupManager.cpp b/source/GroupManager.cpp index cef32dd58..b79fde9dc 100644 --- a/source/GroupManager.cpp +++ b/source/GroupManager.cpp @@ -43,7 +43,7 @@ cGroupManager::~cGroupManager() cGroupManager::cGroupManager() : m_pState( new sGroupManagerState ) { - LOG("-- Loading Groups --"); + LOGD("-- Loading Groups --"); cIniFile IniFile("groups.ini"); if (!IniFile.ReadFile()) { @@ -57,7 +57,7 @@ cGroupManager::cGroupManager() std::string KeyName = IniFile.GetKeyName( i ); cGroup* Group = GetGroup( KeyName.c_str() ); - LOG("Loading group: %s", KeyName.c_str() ); + LOGD("Loading group: %s", KeyName.c_str() ); Group->SetName( KeyName ); char Color = IniFile.GetValue( KeyName, "Color", "-" )[0]; @@ -73,7 +73,6 @@ cGroupManager::cGroupManager() for( unsigned int i = 0; i < Split.size(); i++) { Group->AddCommand( Split[i] ); - //LOG("%s", Split[i].c_str() ); } } @@ -84,7 +83,6 @@ cGroupManager::cGroupManager() for( unsigned int i = 0; i < Split.size(); i++) { Group->AddPermission( Split[i] ); - //LOGINFO("Permission: %s", Split[i].c_str() ); } } @@ -98,7 +96,7 @@ cGroupManager::cGroupManager() } } } - LOG("-- Groups Successfully Loaded --"); + LOGD("-- Groups Successfully Loaded --"); } diff --git a/source/HTTPServer/EnvelopeParser.cpp b/source/HTTPServer/EnvelopeParser.cpp new file mode 100644 index 000000000..8dbe05f14 --- /dev/null +++ b/source/HTTPServer/EnvelopeParser.cpp @@ -0,0 +1,132 @@ + +// EnvelopeParser.cpp + +// Implements the cEnvelopeParser class representing a parser for RFC-822 envelope headers, used both in HTTP and in MIME + +#include "Globals.h" +#include "EnvelopeParser.h" + + + + + +cEnvelopeParser::cEnvelopeParser(cCallbacks & a_Callbacks) : + m_Callbacks(a_Callbacks), + m_IsInHeaders(true) +{ +} + + + + + +int cEnvelopeParser::Parse(const char * a_Data, int a_Size) +{ + if (!m_IsInHeaders) + { + return 0; + } + + // Start searching 1 char from the end of the already received data, if available: + size_t SearchStart = m_IncomingData.size(); + SearchStart = (SearchStart > 1) ? SearchStart - 1 : 0; + + m_IncomingData.append(a_Data, a_Size); + + size_t idxCRLF = m_IncomingData.find("\r\n", SearchStart); + if (idxCRLF == AString::npos) + { + // Not a complete line yet, all input consumed: + return a_Size; + } + + // Parse as many lines as found: + size_t Last = 0; + do + { + if (idxCRLF == Last) + { + // This was the last line of the data. Finish whatever value has been cached and return: + NotifyLast(); + m_IsInHeaders = false; + return a_Size - (m_IncomingData.size() - idxCRLF) + 2; + } + if (!ParseLine(m_IncomingData.c_str() + Last, idxCRLF - Last)) + { + // An error has occurred + m_IsInHeaders = false; + return -1; + } + Last = idxCRLF + 2; + idxCRLF = m_IncomingData.find("\r\n", idxCRLF + 2); + } while (idxCRLF != AString::npos); + m_IncomingData.erase(0, Last); + + // Parsed all lines and still expecting more + return a_Size; +} + + + + + +void cEnvelopeParser::Reset(void) +{ + m_IsInHeaders = true; + m_IncomingData.clear(); + m_LastKey.clear(); + m_LastValue.clear(); +} + + + + + +void cEnvelopeParser::NotifyLast(void) +{ + if (!m_LastKey.empty()) + { + m_Callbacks.OnHeaderLine(m_LastKey, m_LastValue); + m_LastKey.clear(); + } + m_LastValue.clear(); +} + + + + + +bool cEnvelopeParser::ParseLine(const char * a_Data, size_t a_Size) +{ + ASSERT(a_Size > 0); + if (a_Data[0] <= ' ') + { + // This line is a continuation for the previous line + if (m_LastKey.empty()) + { + return false; + } + // Append, including the whitespace in a_Data[0] + m_LastValue.append(a_Data, a_Size); + return true; + } + + // This is a line with a new key: + NotifyLast(); + for (size_t i = 0; i < a_Size; i++) + { + if (a_Data[i] == ':') + { + m_LastKey.assign(a_Data, i); + m_LastValue.assign(a_Data + i + 2, a_Size - i - 2); + return true; + } + } // for i - a_Data[] + + // No colon was found, key-less header?? + return false; +} + + + + diff --git a/source/HTTPServer/EnvelopeParser.h b/source/HTTPServer/EnvelopeParser.h new file mode 100644 index 000000000..6430fbebf --- /dev/null +++ b/source/HTTPServer/EnvelopeParser.h @@ -0,0 +1,69 @@ + +// EnvelopeParser.h + +// Declares the cEnvelopeParser class representing a parser for RFC-822 envelope headers, used both in HTTP and in MIME + + + + + +#pragma once + + + + + +class cEnvelopeParser +{ +public: + class cCallbacks + { + public: + /// Called when a full header line is parsed + virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) = 0; + } ; + + + cEnvelopeParser(cCallbacks & a_Callbacks); + + /** Parses the incoming data. + Returns the number of bytes consumed from the input. The bytes not consumed are not part of the envelope header + */ + int Parse(const char * a_Data, int a_Size); + + /// Makes the parser forget everything parsed so far, so that it can be reused for parsing another datastream + void Reset(void); + + /// Returns true if more input is expected for the envelope header + bool IsInHeaders(void) const { return m_IsInHeaders; } + + /// Sets the IsInHeaders flag; used by cMultipartParser to simplify the parser initial conditions + void SetIsInHeaders(bool a_IsInHeaders) { m_IsInHeaders = a_IsInHeaders; } + +public: + /// Callbacks to call for the various events + cCallbacks & m_Callbacks; + + /// Set to true while the parser is still parsing the envelope headers. Once set to true, the parser will not consume any more data. + bool m_IsInHeaders; + + /// Buffer for the incoming data until it is parsed + AString m_IncomingData; + + /// Holds the last parsed key; used for line-wrapped values + AString m_LastKey; + + /// Holds the last parsed value; used for line-wrapped values + AString m_LastValue; + + + /// Notifies the callback of the key/value stored in m_LastKey/m_LastValue, then erases them + void NotifyLast(void); + + /// Parses one line of header data. Returns true if successful + bool ParseLine(const char * a_Data, size_t a_Size); +} ; + + + + diff --git a/source/HTTPServer/HTTPConnection.cpp b/source/HTTPServer/HTTPConnection.cpp new file mode 100644 index 000000000..68afdfc11 --- /dev/null +++ b/source/HTTPServer/HTTPConnection.cpp @@ -0,0 +1,247 @@ + +// HTTPConnection.cpp + +// Implements the cHTTPConnection class representing a single persistent connection in the HTTP server. + +#include "Globals.h" +#include "HTTPConnection.h" +#include "HTTPMessage.h" +#include "HTTPServer.h" + + + + + +cHTTPConnection::cHTTPConnection(cHTTPServer & a_HTTPServer) : + m_HTTPServer(a_HTTPServer), + m_State(wcsRecvHeaders), + m_CurrentRequest(NULL) +{ + // LOGD("HTTP: New connection at %p", this); +} + + + + + +cHTTPConnection::~cHTTPConnection() +{ + // LOGD("HTTP: Del connection at %p", this); +} + + + + + +void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Response) +{ + AppendPrintf(m_OutgoingData, "%d %s\r\nContent-Length: 0\r\n\r\n", a_StatusCode, a_Response.c_str()); + m_HTTPServer.NotifyConnectionWrite(*this); + m_State = wcsRecvHeaders; +} + + + + + +void cHTTPConnection::SendNeedAuth(const AString & a_Realm) +{ + AppendPrintf(m_OutgoingData, "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"%s\"\r\nContent-Length: 0\r\n\r\n", a_Realm.c_str()); + m_HTTPServer.NotifyConnectionWrite(*this); + m_State = wcsRecvHeaders; +} + + + + + +void cHTTPConnection::Send(const cHTTPResponse & a_Response) +{ + ASSERT(m_State = wcsRecvIdle); + a_Response.AppendToData(m_OutgoingData); + m_State = wcsSendingResp; + m_HTTPServer.NotifyConnectionWrite(*this); +} + + + + + +void cHTTPConnection::Send(const void * a_Data, int a_Size) +{ + ASSERT(m_State == wcsSendingResp); + AppendPrintf(m_OutgoingData, "%x\r\n", a_Size); + m_OutgoingData.append((const char *)a_Data, a_Size); + m_OutgoingData.append("\r\n"); + m_HTTPServer.NotifyConnectionWrite(*this); +} + + + + + +void cHTTPConnection::FinishResponse(void) +{ + ASSERT(m_State == wcsSendingResp); + m_OutgoingData.append("0\r\n\r\n"); + m_State = wcsRecvHeaders; + m_HTTPServer.NotifyConnectionWrite(*this); +} + + + + + +void cHTTPConnection::AwaitNextRequest(void) +{ + switch (m_State) + { + case wcsRecvHeaders: + { + // Nothing has been received yet, or a special response was given (SendStatusAndReason() or SendNeedAuth() ) + break; + } + + case wcsRecvIdle: + { + // The client is waiting for a response, send an "Internal server error": + m_OutgoingData.append("HTTP/1.1 500 Internal Server Error\r\n\r\n"); + m_HTTPServer.NotifyConnectionWrite(*this); + m_State = wcsRecvHeaders; + break; + } + + case wcsSendingResp: + { + // The response headers have been sent, we need to terminate the response body: + m_OutgoingData.append("0\r\n\r\n"); + m_State = wcsRecvHeaders; + break; + } + + default: + { + ASSERT(!"Unhandled state recovery"); + break; + } + } +} + + + + + +void cHTTPConnection::Terminate(void) +{ + if (m_CurrentRequest != NULL) + { + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + } + m_HTTPServer.CloseConnection(*this); +} + + + + + +void cHTTPConnection::DataReceived(const char * a_Data, int a_Size) +{ + switch (m_State) + { + case wcsRecvHeaders: + { + if (m_CurrentRequest == NULL) + { + m_CurrentRequest = new cHTTPRequest; + } + + int BytesConsumed = m_CurrentRequest->ParseHeaders(a_Data, a_Size); + if (BytesConsumed < 0) + { + delete m_CurrentRequest; + m_CurrentRequest = NULL; + m_State = wcsInvalid; + m_HTTPServer.CloseConnection(*this); + return; + } + if (m_CurrentRequest->IsInHeaders()) + { + // The request headers are not yet complete + return; + } + + // The request has finished parsing its headers successfully, notify of it: + m_State = wcsRecvBody; + m_HTTPServer.NewRequest(*this, *m_CurrentRequest); + m_CurrentRequestBodyRemaining = m_CurrentRequest->GetContentLength(); + if (m_CurrentRequestBodyRemaining < 0) + { + // The body length was not specified in the request, assume zero + m_CurrentRequestBodyRemaining = 0; + } + + // Process the rest of the incoming data into the request body: + if (a_Size > BytesConsumed) + { + DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed); + } + else + { + DataReceived("", 0); // If the request has zero body length, let it be processed right-away + } + break; + } + + case wcsRecvBody: + { + ASSERT(m_CurrentRequest != NULL); + if (m_CurrentRequestBodyRemaining > 0) + { + int BytesToConsume = std::min(m_CurrentRequestBodyRemaining, a_Size); + m_HTTPServer.RequestBody(*this, *m_CurrentRequest, a_Data, BytesToConsume); + m_CurrentRequestBodyRemaining -= BytesToConsume; + } + if (m_CurrentRequestBodyRemaining == 0) + { + m_State = wcsRecvIdle; + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + delete m_CurrentRequest; + m_CurrentRequest = NULL; + } + break; + } + + default: + { + // TODO: Should we be receiving data in this state? + break; + } + } +} + + + + + +void cHTTPConnection::GetOutgoingData(AString & a_Data) +{ + std::swap(a_Data, m_OutgoingData); +} + + + + + +void cHTTPConnection::SocketClosed(void) +{ + if (m_CurrentRequest != NULL) + { + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + } + m_HTTPServer.CloseConnection(*this); +} + + + + + diff --git a/source/HTTPServer/HTTPConnection.h b/source/HTTPServer/HTTPConnection.h new file mode 100644 index 000000000..14603bb70 --- /dev/null +++ b/source/HTTPServer/HTTPConnection.h @@ -0,0 +1,101 @@ + +// HTTPConnection.h + +// Declares the cHTTPConnection class representing a single persistent connection in the HTTP server. + + + + + +#pragma once + +#include "../OSSupport/SocketThreads.h" + + + + + +// fwd: +class cHTTPServer; +class cHTTPResponse; +class cHTTPRequest; + + + + + +class cHTTPConnection : + public cSocketThreads::cCallback +{ +public: + + enum eState + { + wcsRecvHeaders, ///< Receiving request headers (m_CurrentRequest is created if NULL) + wcsRecvBody, ///< Receiving request body (m_CurrentRequest is valid) + wcsRecvIdle, ///< Has received the entire body, waiting to send the response (m_CurrentRequest == NULL) + wcsSendingResp, ///< Sending response body (m_CurrentRequest == NULL) + wcsInvalid, ///< The request was malformed, the connection is closing + } ; + + cHTTPConnection(cHTTPServer & a_HTTPServer); + ~cHTTPConnection(); + + /// Sends HTTP status code together with a_Reason (used for HTTP errors) + void SendStatusAndReason(int a_StatusCode, const AString & a_Reason); + + /// Sends the "401 unauthorized" reply together with instructions on authorizing, using the specified realm + void SendNeedAuth(const AString & a_Realm); + + /// Sends the headers contained in a_Response + void Send(const cHTTPResponse & a_Response); + + /// Sends the data as the response (may be called multiple times) + void Send(const void * a_Data, int a_Size); + + /// Sends the data as the response (may be called multiple times) + void Send(const AString & a_Data) { Send(a_Data.data(), a_Data.size()); } + + /// Indicates that the current response is finished, gets ready for receiving another request (HTTP 1.1 keepalive) + void FinishResponse(void); + + /// Resets the connection for a new request. Depending on the state, this will send an "InternalServerError" status or a "ResponseEnd" + void AwaitNextRequest(void); + + /// Terminates the connection; finishes any request being currently processed + void Terminate(void); + +protected: + typedef std::map<AString, AString> cNameValueMap; + + /// The parent webserver that is to be notified of events on this connection + cHTTPServer & m_HTTPServer; + + /// All the incoming data until the entire request header is parsed + AString m_IncomingHeaderData; + + /// Status in which the request currently is + eState m_State; + + /// Data that is queued for sending, once the socket becomes writable + AString m_OutgoingData; + + /// The request being currently received (valid only between having parsed the headers and finishing receiving the body) + cHTTPRequest * m_CurrentRequest; + + /// Number of bytes that remain to read for the complete body of the message to be received. Valid only in wcsRecvBody + int m_CurrentRequestBodyRemaining; + + + // cSocketThreads::cCallback overrides: + virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client + virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client + virtual void SocketClosed (void) override; // The socket has been closed for any reason +} ; + +typedef std::vector<cHTTPConnection *> cHTTPConnections; + + + + + diff --git a/source/HTTPServer/HTTPFormParser.cpp b/source/HTTPServer/HTTPFormParser.cpp new file mode 100644 index 000000000..596db424e --- /dev/null +++ b/source/HTTPServer/HTTPFormParser.cpp @@ -0,0 +1,290 @@ + +// HTTPFormParser.cpp + +// Implements the cHTTPFormParser class representing a parser for forms sent over HTTP + +#include "Globals.h" +#include "HTTPFormParser.h" +#include "HTTPMessage.h" +#include "MultipartParser.h" +#include "NameValueParser.h" + + + + + +cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks) : + m_Callbacks(a_Callbacks), + m_IsValid(true) +{ + if (a_Request.GetMethod() == "GET") + { + m_Kind = fpkURL; + + // Directly parse the URL in the request: + const AString & URL = a_Request.GetURL(); + size_t idxQM = URL.find('?'); + if (idxQM != AString::npos) + { + Parse(URL.c_str() + idxQM + 1, URL.size() - idxQM - 1); + } + return; + } + if ((a_Request.GetMethod() == "POST") || (a_Request.GetMethod() == "PUT")) + { + if (strncmp(a_Request.GetContentType().c_str(), "application/x-www-form-urlencoded", 33) == 0) + { + m_Kind = fpkFormUrlEncoded; + return; + } + if (strncmp(a_Request.GetContentType().c_str(), "multipart/form-data", 19) == 0) + { + m_Kind = fpkMultipart; + BeginMultipart(a_Request); + return; + } + } + // Invalid method / content type combination, this is not a HTTP form + m_IsValid = false; +} + + + + + +cHTTPFormParser::cHTTPFormParser(eKind a_Kind, const char * a_Data, int a_Size, cCallbacks & a_Callbacks) : + m_Callbacks(a_Callbacks), + m_Kind(a_Kind), + m_IsValid(true) +{ + Parse(a_Data, a_Size); +} + + + + + +void cHTTPFormParser::Parse(const char * a_Data, int a_Size) +{ + if (!m_IsValid) + { + return; + } + + switch (m_Kind) + { + case fpkURL: + case fpkFormUrlEncoded: + { + // This format is used for smaller forms (not file uploads), so we can delay parsing it until Finish() + m_IncomingData.append(a_Data, a_Size); + break; + } + case fpkMultipart: + { + ASSERT(m_MultipartParser.get() != NULL); + m_MultipartParser->Parse(a_Data, a_Size); + break; + } + default: + { + ASSERT(!"Unhandled form kind"); + break; + } + } +} + + + + + +bool cHTTPFormParser::Finish(void) +{ + switch (m_Kind) + { + case fpkURL: + case fpkFormUrlEncoded: + { + // m_IncomingData has all the form data, parse it now: + ParseFormUrlEncoded(); + break; + } + } + return (m_IsValid && m_IncomingData.empty()); +} + + + + + +bool cHTTPFormParser::HasFormData(const cHTTPRequest & a_Request) +{ + const AString & ContentType = a_Request.GetContentType(); + return ( + (ContentType == "application/x-www-form-urlencoded") || + (strncmp(ContentType.c_str(), "multipart/form-data", 19) == 0) || + ( + (a_Request.GetMethod() == "GET") && + (a_Request.GetURL().find('?') != AString::npos) + ) + ); + return false; +} + + + + + +void cHTTPFormParser::BeginMultipart(const cHTTPRequest & a_Request) +{ + ASSERT(m_MultipartParser.get() == NULL); + m_MultipartParser.reset(new cMultipartParser(a_Request.GetContentType(), *this)); +} + + + + + +void cHTTPFormParser::ParseFormUrlEncoded(void) +{ + // Parse m_IncomingData for all the variables; no more data is incoming, since this is called from Finish() + // This may not be the most performant version, but we don't care, the form data is small enough and we're not a full-fledged web server anyway + AStringVector Lines = StringSplit(m_IncomingData, "&"); + for (AStringVector::iterator itr = Lines.begin(), end = Lines.end(); itr != end; ++itr) + { + AStringVector Components = StringSplit(*itr, "="); + switch (Components.size()) + { + default: + { + // Neither name nor value, or too many "="s, mark this as invalid form: + m_IsValid = false; + return; + } + case 1: + { + // Only name present + (*this)[URLDecode(ReplaceAllCharOccurrences(Components[0], '+', ' '))] = ""; + break; + } + case 2: + { + // name=value format: + (*this)[URLDecode(ReplaceAllCharOccurrences(Components[0], '+', ' '))] = URLDecode(ReplaceAllCharOccurrences(Components[1], '+', ' ')); + break; + } + } + } // for itr - Lines[] + m_IncomingData.clear(); +} + + + + + +void cHTTPFormParser::OnPartStart(void) +{ + m_CurrentPartFileName.clear(); + m_CurrentPartName.clear(); + m_IsCurrentPartFile = false; + m_FileHasBeenAnnounced = false; +} + + + + + +void cHTTPFormParser::OnPartHeader(const AString & a_Key, const AString & a_Value) +{ + if (NoCaseCompare(a_Key, "Content-Disposition") == 0) + { + size_t len = a_Value.size(); + size_t ParamsStart = AString::npos; + for (size_t i = 0; i < len; ++i) + { + if (a_Value[i] > ' ') + { + if (strncmp(a_Value.c_str() + i, "form-data", 9) != 0) + { + // Content disposition is not "form-data", mark the whole form invalid + m_IsValid = false; + return; + } + ParamsStart = a_Value.find(';', i + 9); + break; + } + } + if (ParamsStart == AString::npos) + { + // There is data missing in the Content-Disposition field, mark the whole form invalid: + m_IsValid = false; + return; + } + + // Parse the field name and optional filename from this header: + cNameValueParser Parser(a_Value.data() + ParamsStart, a_Value.size() - ParamsStart); + Parser.Finish(); + m_CurrentPartName = Parser["name"]; + if (!Parser.IsValid() || m_CurrentPartName.empty()) + { + // The required parameter "name" is missing, mark the whole form invalid: + m_IsValid = false; + return; + } + m_CurrentPartFileName = Parser["filename"]; + } +} + + + + + +void cHTTPFormParser::OnPartData(const char * a_Data, int a_Size) +{ + if (m_CurrentPartName.empty()) + { + // Prologue, epilogue or invalid part + return; + } + if (m_CurrentPartFileName.empty()) + { + // This is a variable, store it in the map + iterator itr = find(m_CurrentPartName); + if (itr == end()) + { + (*this)[m_CurrentPartName] = AString(a_Data, a_Size); + } + else + { + itr->second.append(a_Data, a_Size); + } + } + else + { + // This is a file, pass it on through the callbacks + if (!m_FileHasBeenAnnounced) + { + m_Callbacks.OnFileStart(*this, m_CurrentPartFileName); + m_FileHasBeenAnnounced = true; + } + m_Callbacks.OnFileData(*this, a_Data, a_Size); + } +} + + + + + +void cHTTPFormParser::OnPartEnd(void) +{ + if (m_FileHasBeenAnnounced) + { + m_Callbacks.OnFileEnd(*this); + } + m_CurrentPartName.clear(); + m_CurrentPartFileName.clear(); +} + + + + diff --git a/source/HTTPServer/HTTPFormParser.h b/source/HTTPServer/HTTPFormParser.h new file mode 100644 index 000000000..a554ca5a4 --- /dev/null +++ b/source/HTTPServer/HTTPFormParser.h @@ -0,0 +1,112 @@ + +// HTTPFormParser.h + +// Declares the cHTTPFormParser class representing a parser for forms sent over HTTP + + + + +#pragma once + +#include "MultipartParser.h" + + + + + +// fwd: +class cHTTPRequest; + + + + + +class cHTTPFormParser : + public std::map<AString, AString>, + public cMultipartParser::cCallbacks +{ +public: + enum eKind + { + fpkURL, ///< The form has been transmitted as parameters to a GET request + fpkFormUrlEncoded, ///< The form has been POSTed or PUT, with Content-Type of "application/x-www-form-urlencoded" + fpkMultipart, ///< The form has been POSTed or PUT, with Content-Type of "multipart/form-data" + } ; + + class cCallbacks + { + public: + /// Called when a new file part is encountered in the form data + virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) = 0; + + /// Called when more file data has come for the current file in the form data + virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) = 0; + + /// Called when the current file part has ended in the form data + virtual void OnFileEnd(cHTTPFormParser & a_Parser) = 0; + } ; + + + /// Creates a parser that is tied to a request and notifies of various events using a callback mechanism + cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks); + + /// Creates a parser with the specified content type that reads data from a string + cHTTPFormParser(eKind a_Kind, const char * a_Data, int a_Size, cCallbacks & a_Callbacks); + + /// Adds more data into the parser, as the request body is received + void Parse(const char * a_Data, int a_Size); + + /** Notifies that there's no more data incoming and the parser should finish its parsing. + Returns true if parsing successful + */ + bool Finish(void); + + /// Returns true if the headers suggest the request has form data parseable by this class + static bool HasFormData(const cHTTPRequest & a_Request); + +protected: + + /// The callbacks to call for incoming file data + cCallbacks & m_Callbacks; + + /// The kind of the parser (decided in the constructor, used in Parse() + eKind m_Kind; + + /// Buffer for the incoming data until it's parsed + AString m_IncomingData; + + /// True if the information received so far is a valid form; set to false on first problem. Further parsing is skipped when false. + bool m_IsValid; + + /// The parser for the multipart data, if used + std::auto_ptr<cMultipartParser> m_MultipartParser; + + /// Name of the currently parsed part in multipart data + AString m_CurrentPartName; + + /// True if the currently parsed part in multipart data is a file + bool m_IsCurrentPartFile; + + /// Filename of the current parsed part in multipart data (for file uploads) + AString m_CurrentPartFileName; + + /// Set to true after m_Callbacks.OnFileStart() has been called, reset to false on PartEnd + bool m_FileHasBeenAnnounced; + + + /// Sets up the object for parsing a fpkMultipart request + void BeginMultipart(const cHTTPRequest & a_Request); + + /// Parses m_IncomingData as form-urlencoded data (fpkURL or fpkFormUrlEncoded kinds) + void ParseFormUrlEncoded(void); + + // cMultipartParser::cCallbacks overrides: + virtual void OnPartStart (void) override; + virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) override; + virtual void OnPartData (const char * a_Data, int a_Size) override; + virtual void OnPartEnd (void) override; +} ; + + + + diff --git a/source/HTTPServer/HTTPMessage.cpp b/source/HTTPServer/HTTPMessage.cpp new file mode 100644 index 000000000..ab23866e6 --- /dev/null +++ b/source/HTTPServer/HTTPMessage.cpp @@ -0,0 +1,279 @@ + +// HTTPMessage.cpp + +// Declares the cHTTPMessage class representing the common ancestor for HTTP request and response classes + +#include "Globals.h" +#include "HTTPMessage.h" + + + + + +// Disable MSVC warnings: +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4355) // 'this' : used in base member initializer list +#endif + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHTTPMessage: + +cHTTPMessage::cHTTPMessage(eKind a_Kind) : + m_Kind(a_Kind), + m_ContentLength(-1) +{ +} + + + + + +void cHTTPMessage::AddHeader(const AString & a_Key, const AString & a_Value) +{ + AString Key = a_Key; + StrToLower(Key); + cNameValueMap::iterator itr = m_Headers.find(Key); + if (itr == m_Headers.end()) + { + m_Headers[Key] = a_Value; + } + else + { + // The header-field key is specified multiple times, combine into comma-separated list (RFC 2616 @ 4.2) + itr->second.append(", "); + itr->second.append(a_Value); + } + + // Special processing for well-known headers: + if (Key == "content-type") + { + m_ContentType = m_Headers[Key]; + } + else if (Key == "content-length") + { + m_ContentLength = atoi(m_Headers[Key].c_str()); + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHTTPRequest: + +cHTTPRequest::cHTTPRequest(void) : + super(mkRequest), + m_EnvelopeParser(*this), + m_IsValid(true), + m_UserData(NULL), + m_HasAuth(false) +{ +} + + + + + +int cHTTPRequest::ParseHeaders(const char * a_Data, int a_Size) +{ + if (!m_IsValid) + { + return -1; + } + + if (m_Method.empty()) + { + // The first line hasn't been processed yet + int res = ParseRequestLine(a_Data, a_Size); + if ((res < 0) || (res == a_Size)) + { + return res; + } + int res2 = m_EnvelopeParser.Parse(a_Data + res, a_Size - res); + if (res2 < 0) + { + m_IsValid = false; + return res2; + } + return res2 + res; + } + + if (m_EnvelopeParser.IsInHeaders()) + { + int res = m_EnvelopeParser.Parse(a_Data, a_Size); + if (res < 0) + { + m_IsValid = false; + } + return res; + } + return 0; +} + + + + + +AString cHTTPRequest::GetBareURL(void) const +{ + size_t idxQM = m_URL.find('?'); + if (idxQM != AString::npos) + { + return m_URL.substr(0, idxQM); + } + else + { + return m_URL; + } +} + + + + + +int cHTTPRequest::ParseRequestLine(const char * a_Data, int a_Size) +{ + m_IncomingHeaderData.append(a_Data, a_Size); + size_t IdxEnd = m_IncomingHeaderData.size(); + + // Ignore the initial CRLFs (HTTP spec's "should") + size_t LineStart = 0; + while ( + (LineStart < IdxEnd) && + ( + (m_IncomingHeaderData[LineStart] == '\r') || + (m_IncomingHeaderData[LineStart] == '\n') + ) + ) + { + LineStart++; + } + if (LineStart >= IdxEnd) + { + m_IsValid = false; + return -1; + } + + int NumSpaces = 0; + size_t MethodEnd = 0; + size_t URLEnd = 0; + for (size_t i = LineStart; i < IdxEnd; i++) + { + switch (m_IncomingHeaderData[i]) + { + case ' ': + { + switch (NumSpaces) + { + case 0: + { + MethodEnd = i; + break; + } + case 1: + { + URLEnd = i; + break; + } + default: + { + // Too many spaces in the request + m_IsValid = false; + return -1; + } + } + NumSpaces += 1; + break; + } + case '\n': + { + if ((i == 0) || (m_IncomingHeaderData[i - 1] != '\r') || (NumSpaces != 2) || (i < URLEnd + 7)) + { + // LF too early, without a CR, without two preceeding spaces or too soon after the second space + m_IsValid = false; + return -1; + } + // Check that there's HTTP/version at the end + if (strncmp(a_Data + URLEnd + 1, "HTTP/1.", 7) != 0) + { + m_IsValid = false; + return -1; + } + m_Method = m_IncomingHeaderData.substr(LineStart, MethodEnd - LineStart); + m_URL = m_IncomingHeaderData.substr(MethodEnd + 1, URLEnd - MethodEnd - 1); + return i + 1; + } + } // switch (m_IncomingHeaderData[i]) + } // for i - m_IncomingHeaderData[] + + // CRLF hasn't been encountered yet, consider all data consumed + return a_Size; +} + + + + + +void cHTTPRequest::OnHeaderLine(const AString & a_Key, const AString & a_Value) +{ + if ( + (NoCaseCompare(a_Key, "Authorization") == 0) && + (strncmp(a_Value.c_str(), "Basic ", 6) == 0) + ) + { + AString UserPass = Base64Decode(a_Value.substr(6)); + size_t idxCol = UserPass.find(':'); + if (idxCol != AString::npos) + { + m_AuthUsername = UserPass.substr(0, idxCol); + m_AuthPassword = UserPass.substr(idxCol + 1); + m_HasAuth = true; + } + } + AddHeader(a_Key, a_Value); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHTTPResponse: + +cHTTPResponse::cHTTPResponse(void) : + super(mkResponse) +{ +} + + + + + +void cHTTPResponse::AppendToData(AString & a_DataStream) const +{ + a_DataStream.append("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: "); + a_DataStream.append(m_ContentType); + a_DataStream.append("\r\n"); + for (cNameValueMap::const_iterator itr = m_Headers.begin(), end = m_Headers.end(); itr != end; ++itr) + { + if ((itr->first == "Content-Type") || (itr->first == "Content-Length")) + { + continue; + } + a_DataStream.append(itr->first); + a_DataStream.append(": "); + a_DataStream.append(itr->second); + a_DataStream.append("\r\n"); + } // for itr - m_Headers[] + a_DataStream.append("\r\n"); +} + + + + diff --git a/source/HTTPServer/HTTPMessage.h b/source/HTTPServer/HTTPMessage.h new file mode 100644 index 000000000..f5284c535 --- /dev/null +++ b/source/HTTPServer/HTTPMessage.h @@ -0,0 +1,164 @@ + +// HTTPMessage.h + +// Declares the cHTTPMessage class representing the common ancestor for HTTP request and response classes + + + + + +#pragma once + +#include "EnvelopeParser.h" + + + + + +class cHTTPMessage +{ +public: + enum + { + HTTP_OK = 200, + HTTP_BAD_REQUEST = 400, + } ; + + enum eKind + { + mkRequest, + mkResponse, + } ; + + cHTTPMessage(eKind a_Kind); + + /// Adds a header into the internal map of headers. Recognizes special headers: Content-Type and Content-Length + void AddHeader(const AString & a_Key, const AString & a_Value); + + void SetContentType (const AString & a_ContentType) { m_ContentType = a_ContentType; } + void SetContentLength(int a_ContentLength) { m_ContentLength = a_ContentLength; } + + const AString & GetContentType (void) const { return m_ContentType; } + int GetContentLength(void) const { return m_ContentLength; } + +protected: + typedef std::map<AString, AString> cNameValueMap; + + eKind m_Kind; + + cNameValueMap m_Headers; + + /// Type of the content; parsed by AddHeader(), set directly by SetContentLength() + AString m_ContentType; + + /// Length of the content that is to be received. -1 when the object is created, parsed by AddHeader() or set directly by SetContentLength() + int m_ContentLength; +} ; + + + + + +class cHTTPRequest : + public cHTTPMessage, + protected cEnvelopeParser::cCallbacks +{ + typedef cHTTPMessage super; + +public: + cHTTPRequest(void); + + /** Parses the request line and then headers from the received data. + Returns the number of bytes consumed or a negative number for error + */ + int ParseHeaders(const char * a_Data, int a_Size); + + /// Returns true if the request did contain a Content-Length header + bool HasReceivedContentLength(void) const { return (m_ContentLength >= 0); } + + /// Returns the method used in the request + const AString & GetMethod(void) const { return m_Method; } + + /// Returns the URL used in the request + const AString & GetURL(void) const { return m_URL; } + + /// Returns the URL used in the request, without any parameters + AString GetBareURL(void) const; + + /// Sets the UserData pointer that is stored within this request. The request doesn't touch this data (doesn't delete it)! + void SetUserData(void * a_UserData) { m_UserData = a_UserData; } + + /// Retrieves the UserData pointer that has been stored within this request. + void * GetUserData(void) const { return m_UserData; } + + /// Returns true if more data is expected for the request headers + bool IsInHeaders(void) const { return m_EnvelopeParser.IsInHeaders(); } + + /// Returns true if the request did present auth data that was understood by the parser + bool HasAuth(void) const { return m_HasAuth; } + + /// Returns the username that the request presented. Only valid if HasAuth() is true + const AString & GetAuthUsername(void) const { return m_AuthUsername; } + + /// Returns the password that the request presented. Only valid if HasAuth() is true + const AString & GetAuthPassword(void) const { return m_AuthPassword; } + +protected: + /// Parser for the envelope data + cEnvelopeParser m_EnvelopeParser; + + /// True if the data received so far is parsed successfully. When false, all further parsing is skipped + bool m_IsValid; + + /// Bufferred incoming data, while parsing for the request line + AString m_IncomingHeaderData; + + /// Method of the request (GET / PUT / POST / ...) + AString m_Method; + + /// Full URL of the request + AString m_URL; + + /// Data that the HTTPServer callbacks are allowed to store. + void * m_UserData; + + /// Set to true if the request contains auth data that was understood by the parser + bool m_HasAuth; + + /// The username used for auth + AString m_AuthUsername; + + /// The password used for auth + AString m_AuthPassword; + + + /** Parses the incoming data for the first line (RequestLine) + Returns the number of bytes consumed, or -1 for an error + */ + int ParseRequestLine(const char * a_Data, int a_Size); + + // cEnvelopeParser::cCallbacks overrides: + virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override; +} ; + + + + + +class cHTTPResponse : + public cHTTPMessage +{ + typedef cHTTPMessage super; + +public: + cHTTPResponse(void); + + /** Appends the response to the specified datastream - response line and headers. + The body will be sent later directly through cConnection::Send() + */ + void AppendToData(AString & a_DataStream) const; +} ; + + + + diff --git a/source/HTTPServer/HTTPServer.cpp b/source/HTTPServer/HTTPServer.cpp new file mode 100644 index 000000000..f6f5b0f8b --- /dev/null +++ b/source/HTTPServer/HTTPServer.cpp @@ -0,0 +1,258 @@ + +// HTTPServer.cpp + +// Implements the cHTTPServer class representing a HTTP webserver that uses cListenThread and cSocketThreads for processing + +#include "Globals.h" +#include "HTTPServer.h" +#include "HTTPMessage.h" +#include "HTTPConnection.h" +#include "HTTPFormParser.h" + + + + + +// Disable MSVC warnings: +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4355) // 'this' : used in base member initializer list +#endif + + + + + +class cDebugCallbacks : + public cHTTPServer::cCallbacks, + protected cHTTPFormParser::cCallbacks +{ + virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override + { + if (cHTTPFormParser::HasFormData(a_Request)) + { + a_Request.SetUserData(new cHTTPFormParser(a_Request, *this)); + } + } + + + virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) override + { + cHTTPFormParser * FormParser = (cHTTPFormParser *)(a_Request.GetUserData()); + if (FormParser != NULL) + { + FormParser->Parse(a_Data, a_Size); + } + } + + + virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override + { + cHTTPFormParser * FormParser = (cHTTPFormParser *)(a_Request.GetUserData()); + if (FormParser != NULL) + { + if (FormParser->Finish()) + { + cHTTPResponse Resp; + Resp.SetContentType("text/html"); + a_Connection.Send(Resp); + a_Connection.Send("<html><body><table border=1 cellspacing=0><tr><th>Name</th><th>Value</th></tr>\r\n"); + for (cHTTPFormParser::iterator itr = FormParser->begin(), end = FormParser->end(); itr != end; ++itr) + { + a_Connection.Send(Printf("<tr><td valign=\"top\"><pre>%s</pre></td><td valign=\"top\"><pre>%s</pre></td></tr>\r\n", itr->first.c_str(), itr->second.c_str())); + } // for itr - FormParser[] + a_Connection.Send("</table></body></html>"); + return; + } + + // Parsing failed: + cHTTPResponse Resp; + Resp.SetContentType("text/plain"); + a_Connection.Send(Resp); + a_Connection.Send("Form parsing failed"); + return; + } + + // Test the auth failure and success: + if (a_Request.GetURL() == "/auth") + { + if (!a_Request.HasAuth() || (a_Request.GetAuthUsername() != "a") || (a_Request.GetAuthPassword() != "b")) + { + a_Connection.SendNeedAuth("MCServer WebAdmin"); + return; + } + } + + cHTTPResponse Resp; + Resp.SetContentType("text/plain"); + a_Connection.Send(Resp); + a_Connection.Send("Hello, world"); + } + + + virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) override + { + // TODO + } + + + virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) override + { + // TODO + } + + + virtual void OnFileEnd(cHTTPFormParser & a_Parser) override + { + // TODO + } + +} g_DebugCallbacks; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHTTPServer: + +cHTTPServer::cHTTPServer(void) : + m_ListenThreadIPv4(*this, cSocket::IPv4, "WebServer IPv4"), + m_ListenThreadIPv6(*this, cSocket::IPv6, "WebServer IPv6"), + m_Callbacks(NULL) +{ +} + + + + + +cHTTPServer::~cHTTPServer() +{ + Stop(); +} + + + + + +bool cHTTPServer::Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6) +{ + bool HasAnyPort; + HasAnyPort = m_ListenThreadIPv4.Initialize(a_PortsIPv4); + HasAnyPort = m_ListenThreadIPv6.Initialize(a_PortsIPv6) || HasAnyPort; + if (!HasAnyPort) + { + return false; + } + + return true; +} + + + + + +bool cHTTPServer::Start(cCallbacks & a_Callbacks) +{ + m_Callbacks = &a_Callbacks; + if (!m_ListenThreadIPv4.Start()) + { + return false; + } + if (!m_ListenThreadIPv6.Start()) + { + m_ListenThreadIPv4.Stop(); + return false; + } + return true; +} + + + + + +void cHTTPServer::Stop(void) +{ + m_ListenThreadIPv4.Stop(); + m_ListenThreadIPv6.Stop(); + + // Drop all current connections: + cCSLock Lock(m_CSConnections); + while (!m_Connections.empty()) + { + m_Connections.front()->Terminate(); + } // for itr - m_Connections[] +} + + + + + +void cHTTPServer::OnConnectionAccepted(cSocket & a_Socket) +{ + cHTTPConnection * Connection = new cHTTPConnection(*this); + m_SocketThreads.AddClient(a_Socket, Connection); + cCSLock Lock(m_CSConnections); + m_Connections.push_back(Connection); +} + + + + + +void cHTTPServer::CloseConnection(cHTTPConnection & a_Connection) +{ + m_SocketThreads.RemoveClient(&a_Connection); + cCSLock Lock(m_CSConnections); + for (cHTTPConnections::iterator itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) + { + if (*itr == &a_Connection) + { + m_Connections.erase(itr); + break; + } + } + delete &a_Connection; +} + + + + + +void cHTTPServer::NotifyConnectionWrite(cHTTPConnection & a_Connection) +{ + m_SocketThreads.NotifyWrite(&a_Connection); +} + + + + + +void cHTTPServer::NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +{ + m_Callbacks->OnRequestBegun(a_Connection, a_Request); +} + + + + + +void cHTTPServer::RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) +{ + m_Callbacks->OnRequestBody(a_Connection, a_Request, a_Data, a_Size); +} + + + + + +void cHTTPServer::RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +{ + m_Callbacks->OnRequestFinished(a_Connection, a_Request); + a_Connection.AwaitNextRequest(); +} + + + + diff --git a/source/HTTPServer/HTTPServer.h b/source/HTTPServer/HTTPServer.h new file mode 100644 index 000000000..fea2a9029 --- /dev/null +++ b/source/HTTPServer/HTTPServer.h @@ -0,0 +1,101 @@ + +// HTTPServer.h + +// Declares the cHTTPServer class representing a HTTP webserver that uses cListenThread and cSocketThreads for processing + + + + + +#pragma once + +#include "../OSSupport/ListenThread.h" +#include "../OSSupport/SocketThreads.h" +#include "../../iniFile/iniFile.h" + + + + + +// fwd: +class cHTTPMessage; +class cHTTPRequest; +class cHTTPResponse; +class cHTTPConnection; + +typedef std::vector<cHTTPConnection *> cHTTPConnections; + + + + + + +class cHTTPServer : + public cListenThread::cCallback +{ +public: + class cCallbacks + { + public: + /** Called when a new request arrives over a connection and its headers have been parsed. + The request body needn't have arrived yet. + */ + virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0; + + /// Called when another part of request body has arrived. + virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) = 0; + + /// Called when the request body has been fully received in previous calls to OnRequestBody() + virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0; + } ; + + cHTTPServer(void); + ~cHTTPServer(); + + /// Initializes the server on the specified ports + bool Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6); + + /// Starts the server and assigns the callbacks to use for incoming requests + bool Start(cCallbacks & a_Callbacks); + + /// Stops the server, drops all current connections + void Stop(void); + +protected: + friend class cHTTPConnection; + + cListenThread m_ListenThreadIPv4; + cListenThread m_ListenThreadIPv6; + + cSocketThreads m_SocketThreads; + + cCriticalSection m_CSConnections; + cHTTPConnections m_Connections; ///< All the connections that are currently being serviced + + /// The callbacks to call for various events + cCallbacks * m_Callbacks; + + + // cListenThread::cCallback overrides: + virtual void OnConnectionAccepted(cSocket & a_Socket) override; + + /// Called by cHTTPConnection to close the connection (presumably due to an error) + void CloseConnection(cHTTPConnection & a_Connection); + + /// Called by cHTTPConnection to notify SocketThreads that there's data to be sent for the connection + void NotifyConnectionWrite(cHTTPConnection & a_Connection); + + /// Called by cHTTPConnection when it finishes parsing the request header + void NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); + + /// Called by cHTTPConenction when it receives more data for the request body + void RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size); + + /// Called by cHTTPConnection when it detects that the request has finished (all of its body has been received) + void RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); +} ; + + + + + diff --git a/source/HTTPServer/MultipartParser.cpp b/source/HTTPServer/MultipartParser.cpp new file mode 100644 index 000000000..b49f6ec07 --- /dev/null +++ b/source/HTTPServer/MultipartParser.cpp @@ -0,0 +1,256 @@ + +// MultipartParser.cpp + +// Implements the cMultipartParser class that parses messages in "multipart/*" encoding into the separate parts + +#include "Globals.h" +#include "MultipartParser.h" +#include "NameValueParser.h" + + + + + +// Disable MSVC warnings: +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4355) // 'this' : used in base member initializer list +#endif + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// self-test: + +#if 0 + +class cMultipartParserTest : + public cMultipartParser::cCallbacks +{ +public: + cMultipartParserTest(void) + { + cMultipartParser Parser("multipart/mixed; boundary=\"MyBoundaryString\"; foo=bar", *this); + const char Data[] = +"ThisIsIgnoredPrologue\r\n\ +--MyBoundaryString\r\n\ +\r\n\ +Body with confusing strings\r\n\ +--NotABoundary\r\n\ +--MyBoundaryStringWithPostfix\r\n\ +--\r\n\ +--MyBoundaryString\r\n\ +content-disposition: inline\r\n\ +\r\n\ +This is body\r\n\ +--MyBoundaryString\r\n\ +\r\n\ +Headerless body with trailing CRLF\r\n\ +\r\n\ +--MyBoundaryString--\r\n\ +ThisIsIgnoredEpilogue"; + printf("Multipart parsing test commencing.\n"); + Parser.Parse(Data, sizeof(Data) - 1); + // DEBUG: Check if the onscreen output corresponds with the data above + printf("Multipart parsing test finished\n"); + } + + virtual void OnPartStart(void) override + { + printf("Starting a new part\n"); + } + + + virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) override + { + printf(" Hdr: \"%s\"=\"%s\"\n", a_Key.c_str(), a_Value.c_str()); + } + + + virtual void OnPartData(const char * a_Data, int a_Size) override + { + printf(" Data: %d bytes, \"%.*s\"\n", a_Size, a_Size, a_Data); + } + + + virtual void OnPartEnd(void) override + { + printf("Part end\n"); + } +} g_Test; + +#endif + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMultipartParser: + + +cMultipartParser::cMultipartParser(const AString & a_ContentType, cCallbacks & a_Callbacks) : + m_Callbacks(a_Callbacks), + m_IsValid(true), + m_EnvelopeParser(*this), + m_HasHadData(false) +{ + static AString s_Multipart = "multipart/"; + + // Check that the content type is multipart: + AString ContentType(a_ContentType); + if (strncmp(ContentType.c_str(), "multipart/", 10) != 0) + { + m_IsValid = false; + return; + } + size_t idxSC = ContentType.find(';', 10); + if (idxSC == AString::npos) + { + m_IsValid = false; + return; + } + + // Find the multipart boundary: + ContentType.erase(0, idxSC + 1); + cNameValueParser CTParser(ContentType.c_str(), ContentType.size()); + CTParser.Finish(); + if (!CTParser.IsValid()) + { + m_IsValid = false; + return; + } + m_Boundary = CTParser["boundary"]; + m_IsValid = !m_Boundary.empty(); + if (!m_IsValid) + { + return; + } + + // Set the envelope parser for parsing the body, so that our Parse() function parses the ignored prefix data as a body + m_EnvelopeParser.SetIsInHeaders(false); + + // Append an initial CRLF to the incoming data, so that a body starting with the boundary line will get caught + m_IncomingData.assign("\r\n"); + + /* + m_Boundary = AString("\r\n--") + m_Boundary + m_BoundaryEnd = m_Boundary + "--\r\n"; + m_Boundary = m_Boundary + "\r\n"; + */ +} + + + + + +void cMultipartParser::Parse(const char * a_Data, int a_Size) +{ + // Skip parsing if invalid + if (!m_IsValid) + { + return; + } + + // Append to buffer, then parse it: + m_IncomingData.append(a_Data, a_Size); + while (true) + { + if (m_EnvelopeParser.IsInHeaders()) + { + int BytesConsumed = m_EnvelopeParser.Parse(m_IncomingData.data(), m_IncomingData.size()); + if (BytesConsumed < 0) + { + m_IsValid = false; + return; + } + if ((BytesConsumed == a_Size) && m_EnvelopeParser.IsInHeaders()) + { + // All the incoming data has been consumed and still waiting for more + return; + } + m_IncomingData.erase(0, BytesConsumed); + } + + // Search for boundary / boundary end: + size_t idxBoundary = m_IncomingData.find("\r\n--"); + if (idxBoundary == AString::npos) + { + // Boundary string start not present, present as much data to the part callback as possible + if (m_IncomingData.size() > m_Boundary.size() + 8) + { + size_t BytesToReport = m_IncomingData.size() - m_Boundary.size() - 8; + m_Callbacks.OnPartData(m_IncomingData.data(), BytesToReport); + m_IncomingData.erase(0, BytesToReport); + } + return; + } + if (idxBoundary > 0) + { + m_Callbacks.OnPartData(m_IncomingData.data(), idxBoundary); + m_IncomingData.erase(0, idxBoundary); + } + idxBoundary = 4; + size_t LineEnd = m_IncomingData.find("\r\n", idxBoundary); + if (LineEnd == AString::npos) + { + // Not a complete line yet, present as much data to the part callback as possible + if (m_IncomingData.size() > m_Boundary.size() + 8) + { + size_t BytesToReport = m_IncomingData.size() - m_Boundary.size() - 8; + m_Callbacks.OnPartData(m_IncomingData.data(), BytesToReport); + m_IncomingData.erase(0, BytesToReport); + } + return; + } + if ( + (LineEnd - idxBoundary != m_Boundary.size()) && // Line length not equal to boundary + (LineEnd - idxBoundary != m_Boundary.size() + 2) // Line length not equal to boundary end + ) + { + // Got a line, but it's not a boundary, report it as data: + m_Callbacks.OnPartData(m_IncomingData.data(), LineEnd); + m_IncomingData.erase(0, LineEnd); + continue; + } + + if (strncmp(m_IncomingData.c_str() + idxBoundary, m_Boundary.c_str(), m_Boundary.size()) == 0) + { + // Boundary or BoundaryEnd found: + m_Callbacks.OnPartEnd(); + size_t idxSlash = idxBoundary + m_Boundary.size(); + if ((m_IncomingData[idxSlash] == '-') && (m_IncomingData[idxSlash + 1] == '-')) + { + // This was the last part + m_Callbacks.OnPartData(m_IncomingData.data() + idxSlash + 4, m_IncomingData.size() - idxSlash - 4); + m_IncomingData.clear(); + return; + } + m_Callbacks.OnPartStart(); + m_IncomingData.erase(0, LineEnd + 2); + + // Keep parsing for the headers that may have come with this data: + m_EnvelopeParser.Reset(); + continue; + } + + // It's a line, but not a boundary. It can be fully sent to the data receiver, since a boundary cannot cross lines + m_Callbacks.OnPartData(m_IncomingData.c_str(), LineEnd); + m_IncomingData.erase(0, LineEnd); + } // while (true) +} + + + + + +void cMultipartParser::OnHeaderLine(const AString & a_Key, const AString & a_Value) +{ + m_Callbacks.OnPartHeader(a_Key, a_Value); +} + + + + diff --git a/source/HTTPServer/MultipartParser.h b/source/HTTPServer/MultipartParser.h new file mode 100644 index 000000000..d853929ed --- /dev/null +++ b/source/HTTPServer/MultipartParser.h @@ -0,0 +1,76 @@ + +// MultipartParser.h + +// Declares the cMultipartParser class that parses messages in "multipart/*" encoding into the separate parts + + + + + +#pragma once + +#include "EnvelopeParser.h" + + + + + +class cMultipartParser : + protected cEnvelopeParser::cCallbacks +{ +public: + class cCallbacks + { + public: + /// Called when a new part starts + virtual void OnPartStart(void) = 0; + + /// Called when a complete header line is received for a part + virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) = 0; + + /// Called when body for a part is received + virtual void OnPartData(const char * a_Data, int a_Size) = 0; + + /// Called when the current part ends + virtual void OnPartEnd(void) = 0; + } ; + + /// Creates the parser, expects to find the boundary in a_ContentType + cMultipartParser(const AString & a_ContentType, cCallbacks & a_Callbacks); + + /// Parses more incoming data + void Parse(const char * a_Data, int a_Size); + +protected: + /// The callbacks to call for various parsing events + cCallbacks & m_Callbacks; + + /// True if the data parsed so far is valid; if false, further parsing is skipped + bool m_IsValid; + + /// Parser for each part's envelope + cEnvelopeParser m_EnvelopeParser; + + /// Buffer for the incoming data until it is parsed + AString m_IncomingData; + + /// The boundary, excluding both the initial "--" and the terminating CRLF + AString m_Boundary; + + /// Set to true if some data for the current part has already been signalized to m_Callbacks. Used for proper CRLF inserting. + bool m_HasHadData; + + + /// Parse one line of incoming data. The CRLF has already been stripped from a_Data / a_Size + void ParseLine(const char * a_Data, int a_Size); + + /// Parse one line of incoming data in the headers section of a part. The CRLF has already been stripped from a_Data / a_Size + void ParseHeaderLine(const char * a_Data, int a_Size); + + // cEnvelopeParser overrides: + virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override; +} ; + + + + diff --git a/source/HTTPServer/NameValueParser.cpp b/source/HTTPServer/NameValueParser.cpp new file mode 100644 index 000000000..a27f07d19 --- /dev/null +++ b/source/HTTPServer/NameValueParser.cpp @@ -0,0 +1,412 @@ + +// NameValueParser.cpp + +// Implements the cNameValueParser class that parses strings in the "name=value;name2=value2" format into a stringmap + +#include "Globals.h" +#include "NameValueParser.h" + + + + + + +// DEBUG: Self-test + +#if 0 + +class cNameValueParserTest +{ +public: + cNameValueParserTest(void) + { + const char Data[] = " Name1=Value1;Name2 = Value 2; Name3 =\"Value 3\"; Name4 =\'Value 4\'; Name5=\"Confusing; isn\'t it?\""; + + // Now try parsing char-by-char, to debug transitions across datachunk boundaries: + cNameValueParser Parser2; + for (int i = 0; i < sizeof(Data) - 1; i++) + { + Parser2.Parse(Data + i, 1); + } + Parser2.Finish(); + + // Parse as a single chunk of data: + cNameValueParser Parser(Data, sizeof(Data) - 1); + + // Use the debugger to inspect the Parser variable + + // Check that the two parsers have the same content: + for (cNameValueParser::const_iterator itr = Parser.begin(), end = Parser.end(); itr != end; ++itr) + { + ASSERT(Parser2[itr->first] == itr->second); + } // for itr - Parser[] + + // Try parsing in 2-char chunks: + cNameValueParser Parser3; + for (int i = 0; i < sizeof(Data) - 2; i += 2) + { + Parser3.Parse(Data + i, 2); + } + if ((sizeof(Data) % 2) == 0) // There are even number of chars, including the NUL, so the data has an odd length. Parse one more char + { + Parser3.Parse(Data + sizeof(Data) - 2, 1); + } + Parser3.Finish(); + + // Check that the third parser has the same content: + for (cNameValueParser::const_iterator itr = Parser.begin(), end = Parser.end(); itr != end; ++itr) + { + ASSERT(Parser3[itr->first] == itr->second); + } // for itr - Parser[] + + printf("cNameValueParserTest done"); + } +} g_Test; + +#endif + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cNameValueParser: + +cNameValueParser::cNameValueParser(bool a_AllowsKeyOnly) : + m_State(psKeySpace), + m_AllowsKeyOnly(a_AllowsKeyOnly) +{ +} + + + + + +cNameValueParser::cNameValueParser(const char * a_Data, int a_Size, bool a_AllowsKeyOnly) : + m_State(psKeySpace), + m_AllowsKeyOnly(a_AllowsKeyOnly) +{ + Parse(a_Data, a_Size); +} + + + + + +void cNameValueParser::Parse(const char * a_Data, int a_Size) +{ + ASSERT(m_State != psFinished); // Calling Parse() after Finish() is wrong! + + if ((m_State == psInvalid) || (m_State == psFinished)) + { + return; + } + int Last = 0; + for (int i = 0; i < a_Size;) + { + switch (m_State) + { + case psKeySpace: + { + // Skip whitespace until a non-whitespace is found, then start the key: + while ((i < a_Size) && (a_Data[i] <= ' ')) + { + i++; + } + if ((i < a_Size) && (a_Data[i] > ' ')) + { + m_State = psKey; + Last = i; + } + break; + } + + case psKey: + { + // Read the key until whitespace or an equal sign: + while (i < a_Size) + { + if (a_Data[i] == '=') + { + m_CurrentKey.append(a_Data + Last, i - Last); + i++; + Last = i; + m_State = psEqual; + break; + } + else if (a_Data[i] <= ' ') + { + m_CurrentKey.append(a_Data + Last, i - Last); + i++; + Last = i; + m_State = psEqualSpace; + break; + } + else if (a_Data[i] == ';') + { + if (!m_AllowsKeyOnly) + { + m_State = psInvalid; + return; + } + m_CurrentKey.append(a_Data + Last, i - Last); + i++; + Last = i; + (*this)[m_CurrentKey] = ""; + m_CurrentKey.clear(); + m_State = psKeySpace; + break; + } + else if ((a_Data[i] == '\"') || (a_Data[i] == '\'')) + { + m_State = psInvalid; + return; + } + i++; + } // while (i < a_Size) + if (i == a_Size) + { + // Still the key, ran out of data to parse, store the part of the key parsed so far: + m_CurrentKey.append(a_Data + Last, a_Size - Last); + return; + } + break; + } + + case psEqualSpace: + { + // The space before the expected equal sign; the current key is already assigned + while (i < a_Size) + { + if (a_Data[i] == '=') + { + m_State = psEqual; + i++; + Last = i; + break; + } + else if (a_Data[i] == ';') + { + // Key-only + if (!m_AllowsKeyOnly) + { + m_State = psInvalid; + return; + } + i++; + Last = i; + (*this)[m_CurrentKey] = ""; + m_CurrentKey.clear(); + m_State = psKeySpace; + break; + } + else if (a_Data[i] > ' ') + { + m_State = psInvalid; + return; + } + i++; + } // while (i < a_Size) + break; + } // case psEqualSpace + + case psEqual: + { + // just parsed the equal-sign + while (i < a_Size) + { + if (a_Data[i] == ';') + { + if (!m_AllowsKeyOnly) + { + m_State = psInvalid; + return; + } + i++; + Last = i; + (*this)[m_CurrentKey] = ""; + m_CurrentKey.clear(); + m_State = psKeySpace; + break; + } + else if (a_Data[i] == '\"') + { + i++; + Last = i; + m_State = psValueInDQuotes; + break; + } + else if (a_Data[i] == '\'') + { + i++; + Last = i; + m_State = psValueInSQuotes; + break; + } + else + { + m_CurrentValue.push_back(a_Data[i]); + i++; + Last = i; + m_State = psValueRaw; + break; + } + i++; + } // while (i < a_Size) + break; + } // case psEqual + + case psValueInDQuotes: + { + while (i < a_Size) + { + if (a_Data[i] == '\"') + { + m_CurrentValue.append(a_Data + Last, i - Last); + (*this)[m_CurrentKey] = m_CurrentValue; + m_CurrentKey.clear(); + m_CurrentValue.clear(); + m_State = psAfterValue; + i++; + Last = i; + break; + } + i++; + } // while (i < a_Size) + if (i == a_Size) + { + m_CurrentValue.append(a_Data + Last, a_Size - Last); + } + break; + } // case psValueInDQuotes + + case psValueInSQuotes: + { + while (i < a_Size) + { + if (a_Data[i] == '\'') + { + m_CurrentValue.append(a_Data + Last, i - Last); + (*this)[m_CurrentKey] = m_CurrentValue; + m_CurrentKey.clear(); + m_CurrentValue.clear(); + m_State = psAfterValue; + i++; + Last = i; + break; + } + i++; + } // while (i < a_Size) + if (i == a_Size) + { + m_CurrentValue.append(a_Data + Last, a_Size - Last); + } + break; + } // case psValueInSQuotes + + case psValueRaw: + { + while (i < a_Size) + { + if (a_Data[i] == ';') + { + m_CurrentValue.append(a_Data + Last, i - Last); + (*this)[m_CurrentKey] = m_CurrentValue; + m_CurrentKey.clear(); + m_CurrentValue.clear(); + m_State = psKeySpace; + i++; + Last = i; + break; + } + i++; + } + if (i == a_Size) + { + m_CurrentValue.append(a_Data + Last, a_Size - Last); + } + break; + } // case psValueRaw + + case psAfterValue: + { + // Between the closing DQuote or SQuote and the terminating semicolon + while (i < a_Size) + { + if (a_Data[i] == ';') + { + m_State = psKeySpace; + i++; + Last = i; + break; + } + else if (a_Data[i] < ' ') + { + i++; + continue; + } + m_State = psInvalid; + return; + } // while (i < a_Size) + break; + } + } // switch (m_State) + } // for i - a_Data[] +} + + + + + +bool cNameValueParser::Finish(void) +{ + switch (m_State) + { + case psInvalid: + { + return false; + } + case psFinished: + { + return true; + } + case psKey: + case psEqualSpace: + case psEqual: + { + if ((m_AllowsKeyOnly) && !m_CurrentKey.empty()) + { + (*this)[m_CurrentKey] = ""; + m_State = psFinished; + return true; + } + m_State = psInvalid; + return false; + } + case psValueRaw: + { + (*this)[m_CurrentKey] = m_CurrentValue; + m_State = psFinished; + return true; + } + case psValueInDQuotes: + case psValueInSQuotes: + { + // Missing the terminating quotes, this is an error + m_State = psInvalid; + return false; + } + case psKeySpace: + case psAfterValue: + { + m_State = psFinished; + return true; + } + } + ASSERT(!"Unhandled parser state!"); + return false; +} + + + + diff --git a/source/HTTPServer/NameValueParser.h b/source/HTTPServer/NameValueParser.h new file mode 100644 index 000000000..07dc0b942 --- /dev/null +++ b/source/HTTPServer/NameValueParser.h @@ -0,0 +1,70 @@ + +// NameValueParser.h + +// Declares the cNameValueParser class that parses strings in the "name=value;name2=value2" format into a stringmap + + + + + +#pragma once + + + + + +class cNameValueParser : + public std::map<AString, AString> +{ +public: + /// Creates an empty parser + cNameValueParser(bool a_AllowsKeyOnly = true); + + /// Creates an empty parser, then parses the data given. Doesn't call Finish(), so more data can be parsed later + cNameValueParser(const char * a_Data, int a_Size, bool a_AllowsKeyOnly = true); + + /// Parses the data given + void Parse(const char * a_Data, int a_Size); + + /// Notifies the parser that no more data will be coming. Returns true if the parser state is valid + bool Finish(void); + + /// Returns true if the data parsed so far was valid + bool IsValid(void) const { return (m_State != psInvalid); } + + /// Returns true if the parser expects no more data + bool IsFinished(void) const { return ((m_State == psInvalid) || (m_State == psFinished)); } + +protected: + enum eState + { + psKeySpace, ///< Parsing the space in front of the next key + psKey, ///< Currently adding more chars to the key in m_CurrentKey + psEqualSpace, ///< Space after m_CurrentKey + psEqual, ///< Just parsed the = sign after a name + psValueInSQuotes, ///< Just parsed a Single-quote sign after the Equal sign + psValueInDQuotes, ///< Just parsed a Double-quote sign after the Equal sign + psValueRaw, ///< Just parsed a raw value without a quote + psAfterValue, ///< Just finished parsing the value, waiting for semicolon or data end + psInvalid, ///< The parser has encountered an invalid input; further parsing is skipped + psFinished, ///< The parser has already been instructed to finish and doesn't expect any more data + } ; + + /// The current state of the parser + eState m_State; + + /// If true, the parser will accept keys without an equal sign and the value + bool m_AllowsKeyOnly; + + /// Buffer for the current Key + AString m_CurrentKey; + + /// Buffer for the current Value; + AString m_CurrentValue; + + +} ; + + + + diff --git a/source/Items/ItemBoat.h b/source/Items/ItemBoat.h new file mode 100644 index 000000000..6e3395f1d --- /dev/null +++ b/source/Items/ItemBoat.h @@ -0,0 +1,54 @@ + +// ItemBoat.h + +// Declares the various boat ItemHandlers + + + + + +#pragma once + +#include "../Entities/Boat.h" + + + + + +class cItemBoatHandler : + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemBoatHandler(int a_ItemType) : + super(a_ItemType) + { + } + + + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + if (a_Dir < 0) + { + return false; + } + + double x = (double)a_BlockX + 0.5; + double y = (double)a_BlockY + 0.5; + double z = (double)a_BlockZ + 0.5; + + cBoat * Boat = NULL; + + Boat = new cBoat (x, y, z); + Boat->Initialize(a_World); + + return true; + } + +} ; + + + + diff --git a/source/Items/ItemComparator.h b/source/Items/ItemComparator.h new file mode 100644 index 000000000..53dbd020d --- /dev/null +++ b/source/Items/ItemComparator.h @@ -0,0 +1,40 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../Simulator/RedstoneSimulator.h" + + + + + +class cItemComparatorHandler : + public cItemHandler +{ +public: + cItemComparatorHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_INACTIVE_COMPARATOR; + a_BlockMeta = cRedstoneSimulator::RepeaterRotationToMetaData(a_Player->GetRotation()); + return true; + } +} ; + + + + diff --git a/source/Items/ItemHandler.cpp b/source/Items/ItemHandler.cpp index 2ae193d52..13f5293b9 100644 --- a/source/Items/ItemHandler.cpp +++ b/source/Items/ItemHandler.cpp @@ -8,11 +8,13 @@ // Handlers: #include "ItemBed.h" +#include "ItemBoat.h" #include "ItemBow.h" #include "ItemBrewingStand.h" #include "ItemBucket.h" #include "ItemCauldron.h" #include "ItemCloth.h" +#include "ItemComparator.h" #include "ItemDoor.h" #include "ItemDye.h" #include "ItemFlowerPot.h" @@ -30,11 +32,9 @@ #include "ItemShears.h" #include "ItemShovel.h" #include "ItemSign.h" -#include "ItemSlab.h" #include "ItemSpawnEgg.h" #include "ItemSugarcane.h" #include "ItemSword.h" -#include "ItemWood.h" #include "../Blocks/BlockHandler.h" @@ -53,7 +53,11 @@ cItemHandler * cItemHandler::GetItemHandler(int a_ItemType) { if (a_ItemType < 0) { - ASSERT(!"Bad item type"); + // Either nothing (-1), or bad value, both cases should return the air handler + if (a_ItemType < -1) + { + ASSERT(!"Bad item type"); + } a_ItemType = 0; } @@ -85,9 +89,11 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType) case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType); case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType); case E_ITEM_BED: return new cItemBedHandler(a_ItemType); + case E_ITEM_BOAT: return new cItemBoatHandler(a_ItemType); case E_ITEM_BOW: return new cItemBowHandler; case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType); case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType); + case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType); case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType); case E_ITEM_EGG: return new cItemEggHandler(); case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler(); @@ -137,18 +143,6 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType) return new cItemSwordHandler(a_ItemType); } - case E_BLOCK_STONE_SLAB: - case E_BLOCK_WOODEN_SLAB: - { - return new cItemSlabHandler(a_ItemType); - } - - case E_BLOCK_LOG: - case E_BLOCK_PLANKS: - { - return new cItemWoodHandler(a_ItemType); - } - case E_ITEM_BUCKET: case E_ITEM_WATER_BUCKET: case E_ITEM_LAVA_BUCKET: @@ -253,7 +247,7 @@ void cItemHandler::OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); cBlockHandler * Handler = cBlockHandler::GetBlockHandler(Block); - if (a_Player->GetGameMode() == gmSurvival) + if (a_Player->IsGameModeSurvival()) { if (!BlockRequiresSpecialTool(Block) || CanHarvestBlock(Block)) { diff --git a/source/Items/ItemShears.h b/source/Items/ItemShears.h index 663fa0170..6a17607ee 100644 --- a/source/Items/ItemShears.h +++ b/source/Items/ItemShears.h @@ -38,7 +38,6 @@ public: a_Player->UseEquippedItem(); return true; } - // TODO: cobweb, vines return false; } diff --git a/source/Items/ItemSlab.h b/source/Items/ItemSlab.h deleted file mode 100644 index 80de05eb5..000000000 --- a/source/Items/ItemSlab.h +++ /dev/null @@ -1,52 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" - - - - - -class cItemSlabHandler : public cItemHandler -{ -public: - cItemSlabHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - - } - - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override - { - BLOCKTYPE Block; - NIBBLETYPE Meta; - a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, Block, Meta); - - if ( - ((a_Dir == 0) || (a_Dir == 1)) // Only when clicking on top or on bottom of the block - && ((Block == E_BLOCK_WOODEN_SLAB) || (Block == E_BLOCK_STONE_SLAB)) // It is a slab - && (Block == a_Item.m_ItemType) // Same slab - && ((Meta & 0x7) == (a_Item.m_ItemDamage & 0x7))) // Same Texture - { - if (a_Player->GetGameMode() == eGameMode_Creative) - { - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, Block - 1, Meta); // Block - 1 simple hack to save one if statement - return true; - } - else - { - if (a_Player->GetInventory().RemoveOneEquippedItem()) - { - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, Block - 1, Meta); // Block - 1 simple hack to save one if statement - return true; - } - } - } - return false; - } -} ; - - - - diff --git a/source/Items/ItemWood.h b/source/Items/ItemWood.h deleted file mode 100644 index 476256c5d..000000000 --- a/source/Items/ItemWood.h +++ /dev/null @@ -1,22 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemWoodHandler : - public cItemHandler -{ -public: - cItemWoodHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - } -} ; - - - - diff --git a/source/Log.cpp b/source/Log.cpp index c8937c380..fc19595db 100644 --- a/source/Log.cpp +++ b/source/Log.cpp @@ -5,7 +5,6 @@ #include <fstream> #include <ctime> -#include "OSSupport/MakeDir.h" #include "OSSupport/IsThread.h" #if defined(ANDROID_NDK) @@ -24,9 +23,9 @@ cLog::cLog(const AString & a_FileName ) s_Log = this; // create logs directory - cMakeDir::MakeDir("logs"); + cFile::CreateFolder(FILE_IO_PREFIX + AString("logs")); - OpenLog( (FILE_IO_PREFIX + std::string("logs/") + a_FileName).c_str() ); + OpenLog((FILE_IO_PREFIX + AString("logs/") + a_FileName).c_str() ); } @@ -45,8 +44,10 @@ cLog::~cLog() cLog* cLog::GetInstance() { - if(s_Log) + if (s_Log != NULL) + { return s_Log; + } new cLog("log.txt"); return s_Log; diff --git a/source/MCLogger.cpp b/source/MCLogger.cpp index 2870d8ba1..4f3e5dc0f 100644 --- a/source/MCLogger.cpp +++ b/source/MCLogger.cpp @@ -37,24 +37,7 @@ cMCLogger::cMCLogger(void) { AString FileName; Printf(FileName, "LOG_%d.txt", (int)time(NULL)); - m_Log = new cLog(FileName); - m_Log->Log("--- Started Log ---\n"); - - s_MCLogger = this; - - #ifdef _WIN32 - // See whether we are writing to a console the default console attrib: - g_ShouldColorOutput = (_isatty(_fileno(stdin)) != 0); - if (g_ShouldColorOutput) - { - CONSOLE_SCREEN_BUFFER_INFO sbi; - GetConsoleScreenBufferInfo(g_Console, &sbi); - g_DefaultConsoleAttrib = sbi.wAttributes; - } - #elif defined (__linux) && !defined(ANDROID_NDK) - g_ShouldColorOutput = isatty(fileno(stdout)); - // TODO: Check if the terminal supports colors, somehow? - #endif + InitLog(FileName); } @@ -63,7 +46,7 @@ cMCLogger::cMCLogger(void) cMCLogger::cMCLogger(const AString & a_FileName) { - m_Log = new cLog(a_FileName); + InitLog(a_FileName); } @@ -84,6 +67,32 @@ cMCLogger::~cMCLogger() +void cMCLogger::InitLog(const AString & a_FileName) +{ + m_Log = new cLog(a_FileName); + m_Log->Log("--- Started Log ---\n"); + + s_MCLogger = this; + + #ifdef _WIN32 + // See whether we are writing to a console the default console attrib: + g_ShouldColorOutput = (_isatty(_fileno(stdin)) != 0); + if (g_ShouldColorOutput) + { + CONSOLE_SCREEN_BUFFER_INFO sbi; + GetConsoleScreenBufferInfo(g_Console, &sbi); + g_DefaultConsoleAttrib = sbi.wAttributes; + } + #elif defined (__linux) && !defined(ANDROID_NDK) + g_ShouldColorOutput = isatty(fileno(stdout)); + // TODO: Check if the terminal supports colors, somehow? + #endif +} + + + + + void cMCLogger::LogSimple(const char* a_Text, int a_LogType /* = 0 */ ) { switch( a_LogType ) diff --git a/source/MCLogger.h b/source/MCLogger.h index 6f7d34e66..c949a4cdf 100644 --- a/source/MCLogger.h +++ b/source/MCLogger.h @@ -13,8 +13,12 @@ class cLog; class cMCLogger // tolua_export { // tolua_export public: // tolua_export + /// Creates a logger with the default filename, "logs/LOG_<timestamp>.log" cMCLogger(void); + + /// Creates a logger with the specified filename inside "logs" folder cMCLogger(const AString & a_FileName); // tolua_export + ~cMCLogger(); // tolua_export void Log(const char* a_Format, va_list a_ArgList); @@ -34,17 +38,25 @@ private: csError, } ; + cCriticalSection m_CriticalSection; + cLog * m_Log; + static cMCLogger * s_MCLogger; + + /// Sets the specified color scheme in the terminal (TODO: if coloring available) void SetColor(eColorScheme a_Scheme); /// Resets the color back to whatever is the default in the terminal void ResetColor(void); - cCriticalSection m_CriticalSection; - cLog * m_Log; - static cMCLogger * s_MCLogger; + /// Common initialization for all constructors, creates a logfile with the specified name and assigns s_MCLogger to this + void InitLog(const AString & a_FileName); }; // tolua_export + + + + extern void LOG(const char* a_Format, ...); extern void LOGINFO(const char* a_Format, ...); extern void LOGWARN(const char* a_Format, ...); diff --git a/source/ManualBindings.cpp b/source/ManualBindings.cpp index 87efecd35..e6605ddb0 100644 --- a/source/ManualBindings.cpp +++ b/source/ManualBindings.cpp @@ -92,6 +92,21 @@ static int tolua_StringSplit(lua_State * tolua_S) +static int tolua_StringSplitAndTrim(lua_State * tolua_S) +{ + cLuaState LuaState(tolua_S); + std::string str = (std::string)tolua_tocppstring(LuaState, 1, 0); + std::string delim = (std::string)tolua_tocppstring(LuaState, 2, 0); + + AStringVector Split = StringSplitAndTrim(str, delim); + LuaState.Push(Split); + return 1; +} + + + + + static int tolua_LOG(lua_State* tolua_S) { const char* str = tolua_tocppstring(tolua_S,1,0); @@ -621,6 +636,167 @@ static int tolua_ForEach(lua_State * tolua_S) +static int tolua_cWorld_GetBlockInfo(lua_State * tolua_S) +{ + // Exported manually, because tolua would generate useless additional parameters (a_BlockType .. a_BlockSkyLight) + // Function signature: GetBlockInfo(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta, BlockSkyLight, BlockBlockLight] + #ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || + !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || + !tolua_isnoobj (tolua_S, 5, &tolua_err) + ) + goto tolua_lerror; + else + #endif + { + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); + int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); + int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); + #ifndef TOLUA_RELEASE + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'GetBlockInfo'", NULL); + } + #endif + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta, BlockSkyLight, BlockBlockLight; + bool res = self->GetBlockInfo(BlockX, BlockY, BlockZ, BlockType, BlockMeta, BlockSkyLight, BlockBlockLight); + tolua_pushboolean(tolua_S, res ? 1 : 0); + if (res) + { + tolua_pushnumber(tolua_S, BlockType); + tolua_pushnumber(tolua_S, BlockMeta); + tolua_pushnumber(tolua_S, BlockSkyLight); + tolua_pushnumber(tolua_S, BlockBlockLight); + return 5; + } + } + } + return 1; + + #ifndef TOLUA_RELEASE +tolua_lerror: + tolua_error(tolua_S, "#ferror in function 'GetBlockInfo'.", &tolua_err); + return 0; + #endif +} + + + + + +static int tolua_cWorld_GetBlockTypeMeta(lua_State * tolua_S) +{ + // Exported manually, because tolua would generate useless additional parameters (a_BlockType, a_BlockMeta) + // Function signature: GetBlockTypeMeta(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta] + #ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || + !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || + !tolua_isnoobj (tolua_S, 5, &tolua_err) + ) + goto tolua_lerror; + else + #endif + { + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); + int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); + int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); + #ifndef TOLUA_RELEASE + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'GetBlockTypeMeta'", NULL); + } + #endif + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + bool res = self->GetBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta); + tolua_pushboolean(tolua_S, res ? 1 : 0); + if (res) + { + tolua_pushnumber(tolua_S, BlockType); + tolua_pushnumber(tolua_S, BlockMeta); + return 3; + } + } + } + return 1; + + #ifndef TOLUA_RELEASE +tolua_lerror: + tolua_error(tolua_S, "#ferror in function 'GetBlockTypeMeta'.", &tolua_err); + return 0; + #endif +} + + + + + +static int tolua_cWorld_GetSignLines(lua_State * tolua_S) +{ + // Exported manually, because tolua would generate useless additional parameters (a_Line1 .. a_Line4) + #ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || + !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || + !tolua_isnoobj (tolua_S, 10, &tolua_err) + ) + goto tolua_lerror; + else + #endif + { + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); + int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); + int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); + #ifndef TOLUA_RELEASE + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'GetSignLines'", NULL); + } + #endif + { + AString Line1, Line2, Line3, Line4; + bool res = self->GetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4); + tolua_pushboolean(tolua_S, res ? 1 : 0); + if (res) + { + tolua_pushstring(tolua_S, Line1.c_str()); + tolua_pushstring(tolua_S, Line2.c_str()); + tolua_pushstring(tolua_S, Line3.c_str()); + tolua_pushstring(tolua_S, Line4.c_str()); + return 5; + } + } + } + return 1; + + #ifndef TOLUA_RELEASE +tolua_lerror: + tolua_error(tolua_S, "#ferror in function 'GetSignLines'.", &tolua_err); + return 0; + #endif +} + + + + + static int tolua_cWorld_SetSignLines(lua_State * tolua_S) { // Exported manually, because tolua would generate useless additional return values (a_Line1 .. a_Line4) @@ -720,6 +896,70 @@ tolua_lerror: +class cLuaWorldTask : + public cWorld::cTask +{ +public: + cLuaWorldTask(cPluginLua & a_Plugin, int a_FnRef) : + m_Plugin(a_Plugin), + m_FnRef(a_FnRef) + { + } + +protected: + cPluginLua & m_Plugin; + int m_FnRef; + + // cWorld::cTask overrides: + virtual void Run(cWorld & a_World) override + { + m_Plugin.Call(m_FnRef, &a_World); + } +} ; + + + + + +static int tolua_cWorld_QueueTask(lua_State * tolua_S) +{ + // Binding for cWorld::QueueTask + // Params: function + + // Retrieve the cPlugin from the LuaState: + cPluginLua * Plugin = GetLuaPlugin(tolua_S); + if (Plugin == NULL) + { + // An error message has been already printed in GetLuaPlugin() + return 0; + } + + // Retrieve the args: + cWorld * self = (cWorld *)tolua_tousertype(tolua_S, 1, 0); + if (self == NULL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); + } + if (!lua_isfunction(tolua_S, 2)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1"); + } + + // Create a reference to the function: + int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FnRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); + } + + self->QueueTask(new cLuaWorldTask(*Plugin, FnRef)); + return 0; +} + + + + + static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S) { cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); @@ -1059,7 +1299,10 @@ static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S) static int tolua_cPluginManager_BindCommand(lua_State * L) { - // Function signature: cPluginManager:BindCommand(Command, Permission, Function, HelpString) + /* Function signatures: + cPluginManager:BindCommand(Command, Permission, Function, HelpString) + cPluginManager.BindCommand(Command, Permission, Function, HelpString) -- without the "self" param + */ cPluginLua * Plugin = GetLuaPlugin(L); if (Plugin == NULL) { @@ -1068,26 +1311,30 @@ static int tolua_cPluginManager_BindCommand(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)) + { + idx++; + } if ( - !tolua_isusertype (L, 1, "cPluginManager", 0, &tolua_err) || - !tolua_iscppstring(L, 2, 0, &tolua_err) || - !tolua_iscppstring(L, 3, 0, &tolua_err) || - !tolua_iscppstring(L, 5, 0, &tolua_err) || - !tolua_isnoobj (L, 6, &tolua_err) + !tolua_iscppstring(L, idx, 0, &tolua_err) || + !tolua_iscppstring(L, idx + 1, 0, &tolua_err) || + !tolua_iscppstring(L, idx + 3, 0, &tolua_err) || + !tolua_isnoobj (L, idx + 4, &tolua_err) ) { tolua_error(L, "#ferror in function 'BindCommand'.", &tolua_err); return 0; } - if (!lua_isfunction(L, 4)) + if (!lua_isfunction(L, idx + 2)) { luaL_error(L, "\"BindCommand\" function expects a function as its 3rd parameter. Command-binding aborted."); return 0; } - cPluginManager * self = (cPluginManager *)tolua_tousertype(L, 1, 0); - AString Command (tolua_tocppstring(L, 2, "")); - AString Permission(tolua_tocppstring(L, 3, "")); - AString HelpString(tolua_tocppstring(L, 5, "")); + cPluginManager * self = cPluginManager::Get(); + AString Command (tolua_tocppstring(L, idx, "")); + AString Permission(tolua_tocppstring(L, idx + 1, "")); + AString HelpString(tolua_tocppstring(L, idx + 3, "")); // Store the function reference: lua_pop(L, 1); // Pop the help string off the stack @@ -1114,37 +1361,42 @@ static int tolua_cPluginManager_BindCommand(lua_State * L) static int tolua_cPluginManager_BindConsoleCommand(lua_State * L) { - // Function signature: cPluginManager:BindConsoleCommand(Command, Function, HelpString) + /* Function signatures: + cPluginManager:BindConsoleCommand(Command, Function, HelpString) + cPluginManager.BindConsoleCommand(Command, Function, HelpString) -- without the "self" param + */ // Get the plugin identification out of LuaState: - lua_getglobal(L, LUA_PLUGIN_INSTANCE_VAR_NAME); - if (!lua_islightuserdata(L, -1)) + cPluginLua * Plugin = GetLuaPlugin(L); + if (Plugin == NULL) { - LOGERROR("cPluginManager:BindConsoleCommand() cannot get plugin instance, what have you done to my Lua state? Command-binding aborted."); + return 0; } - cPluginLua * Plugin = (cPluginLua *)lua_topointer(L, -1); - lua_pop(L, 1); // Read the arguments to this API call: tolua_Error tolua_err; + int idx = 1; + if (tolua_isusertype(L, 1, "cPluginManager", 0, &tolua_err)) + { + idx++; + } if ( - !tolua_isusertype (L, 1, "cPluginManager", 0, &tolua_err) || // self - !tolua_iscppstring(L, 2, 0, &tolua_err) || // Command - !tolua_iscppstring(L, 4, 0, &tolua_err) || // HelpString - !tolua_isnoobj (L, 5, &tolua_err) + !tolua_iscppstring(L, idx, 0, &tolua_err) || // Command + !tolua_iscppstring(L, idx + 2, 0, &tolua_err) || // HelpString + !tolua_isnoobj (L, idx + 3, &tolua_err) ) { tolua_error(L, "#ferror in function 'BindConsoleCommand'.", &tolua_err); return 0; } - if (!lua_isfunction(L, 3)) + if (!lua_isfunction(L, idx + 1)) { luaL_error(L, "\"BindConsoleCommand\" function expects a function as its 2nd parameter. Command-binding aborted."); return 0; } - cPluginManager * self = (cPluginManager *)tolua_tousertype(L, 1, 0); - AString Command (tolua_tocppstring(L, 2, "")); - AString HelpString(tolua_tocppstring(L, 4, "")); + cPluginManager * self = cPluginManager::Get(); + AString Command (tolua_tocppstring(L, idx, "")); + AString HelpString(tolua_tocppstring(L, idx + 2, "")); // Store the function reference: lua_pop(L, 1); // Pop the help string off the stack @@ -1313,14 +1565,16 @@ static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S) tolua_Error tolua_err; tolua_err.array = 0; - tolua_err.index = 0; - tolua_err.type = 0; + tolua_err.index = 3; + tolua_err.type = "function"; std::string Title = ""; int Reference = LUA_REFNIL; - if( tolua_isstring( tolua_S, 2, 0, &tolua_err ) && - lua_isfunction( tolua_S, 3 ) ) + if ( + tolua_isstring(tolua_S, 2, 0, &tolua_err ) && + lua_isfunction(tolua_S, 3 ) + ) { Reference = luaL_ref(tolua_S, LUA_REGISTRYINDEX); Title = ((std::string) tolua_tocppstring(tolua_S,2,0)); @@ -1808,12 +2062,13 @@ static int tolua_cLineBlockTracer_Trace(lua_State * tolua_S) void ManualBindings::Bind(lua_State * tolua_S) { tolua_beginmodule(tolua_S, NULL); - tolua_function(tolua_S, "StringSplit", tolua_StringSplit); - tolua_function(tolua_S, "LOG", tolua_LOG); - tolua_function(tolua_S, "LOGINFO", tolua_LOGINFO); - tolua_function(tolua_S, "LOGWARN", tolua_LOGWARN); - tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN); - tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR); + tolua_function(tolua_S, "StringSplit", tolua_StringSplit); + tolua_function(tolua_S, "StringSplitAndTrim", tolua_StringSplitAndTrim); + tolua_function(tolua_S, "LOG", tolua_LOG); + tolua_function(tolua_S, "LOGINFO", tolua_LOGINFO); + tolua_function(tolua_S, "LOGWARN", tolua_LOGWARN); + tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN); + tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR); tolua_beginmodule(tolua_S, "cLineBlockTracer"); tolua_function(tolua_S, "Trace", tolua_cLineBlockTracer_Trace); @@ -1839,6 +2094,10 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "ForEachEntityInChunk", tolua_ForEachInChunk<cWorld, cEntity, &cWorld::ForEachEntityInChunk>); tolua_function(tolua_S, "ForEachFurnaceInChunk", tolua_ForEachInChunk<cWorld, cFurnaceEntity, &cWorld::ForEachFurnaceInChunk>); tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach< cWorld, cPlayer, &cWorld::ForEachPlayer>); + tolua_function(tolua_S, "GetBlockInfo", tolua_cWorld_GetBlockInfo); + tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cWorld_GetBlockTypeMeta); + tolua_function(tolua_S, "GetSignLines", tolua_cWorld_GetSignLines); + tolua_function(tolua_S, "QueueTask", tolua_cWorld_QueueTask); tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines); tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight); tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines); @@ -1849,12 +2108,12 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cPluginManager"); + tolua_function(tolua_S, "AddHook", tolua_cPluginManager_AddHook); tolua_function(tolua_S, "BindCommand", tolua_cPluginManager_BindCommand); tolua_function(tolua_S, "BindConsoleCommand", tolua_cPluginManager_BindConsoleCommand); tolua_function(tolua_S, "ForEachCommand", tolua_cPluginManager_ForEachCommand); tolua_function(tolua_S, "ForEachConsoleCommand", tolua_cPluginManager_ForEachConsoleCommand); tolua_function(tolua_S, "GetAllPlugins", tolua_cPluginManager_GetAllPlugins); - tolua_function(tolua_S, "AddHook", tolua_cPluginManager_AddHook); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cPlayer"); diff --git a/source/MobTypesManager.cpp b/source/MobTypesManager.cpp index 2d24bd39b..600d7992d 100644 --- a/source/MobTypesManager.cpp +++ b/source/MobTypesManager.cpp @@ -114,29 +114,31 @@ cMonster* cMobTypesManager::NewMonsterFromType(cMonster::eType a_MobType, int a_ // the big switch switch (a_MobType) { - case cMonster::mtMagmaCube: toReturn = new cMagmacube(a_Size); break; - case cMonster::mtSlime: toReturn = new cSlime(a_Size); break; - case cMonster::mtBat: toReturn = new cBat(); break; - case cMonster::mtBlaze: toReturn = new cBlaze(); break; - case cMonster::mtCaveSpider: toReturn = new cCavespider(); break; - case cMonster::mtChicken: toReturn = new cChicken(); break; - case cMonster::mtCow: toReturn = new cCow(); break; - case cMonster::mtCreeper: toReturn = new cCreeper(); break; - case cMonster::mtEnderman: toReturn = new cEnderman(); break; - case cMonster::mtGhast: toReturn = new cGhast(); break; - case cMonster::mtMooshroom: toReturn = new cMooshroom(); break; - case cMonster::mtOcelot: toReturn = new cOcelot(); break; - case cMonster::mtPig: toReturn = new cPig(); break; - case cMonster::mtSheep: toReturn = new cSheep(); break; - case cMonster::mtSilverfish: toReturn = new cSilverfish(); break; - case cMonster::mtSkeleton: toReturn = new cSkeleton(); break; - case cMonster::mtSpider: toReturn = new cSpider(); break; - case cMonster::mtSquid: toReturn = new cSquid(); break; - case cMonster::mtVillager: toReturn = new cVillager(); break; - case cMonster::mtWitch: toReturn = new cWitch(); break; - case cMonster::mtWolf: toReturn = new cWolf(); break; - case cMonster::mtZombie: toReturn = new cZombie(); break; - case cMonster::mtZombiePigman: toReturn = new cZombiepigman(); break; + case cMonster::mtMagmaCube: toReturn = new cMagmaCube(a_Size); break; + case cMonster::mtSlime: toReturn = new cSlime(a_Size); break; + case cMonster::mtBat: toReturn = new cBat(); break; + case cMonster::mtBlaze: toReturn = new cBlaze(); break; + case cMonster::mtCaveSpider: toReturn = new cCavespider(); break; + case cMonster::mtChicken: toReturn = new cChicken(); break; + case cMonster::mtCow: toReturn = new cCow(); break; + case cMonster::mtCreeper: toReturn = new cCreeper(); break; + case cMonster::mtEnderman: toReturn = new cEnderman(); break; + case cMonster::mtGhast: toReturn = new cGhast(); break; + case cMonster::mtMooshroom: toReturn = new cMooshroom(); break; + case cMonster::mtOcelot: toReturn = new cOcelot(); break; + case cMonster::mtPig: toReturn = new cPig(); break; + // TODO: Implement sheep color + case cMonster::mtSheep: toReturn = new cSheep(0); break; + case cMonster::mtSilverfish: toReturn = new cSilverfish(); break; + // TODO: Implement wither geration + case cMonster::mtSkeleton: toReturn = new cSkeleton(false); break; + case cMonster::mtSpider: toReturn = new cSpider(); break; + case cMonster::mtSquid: toReturn = new cSquid(); break; + case cMonster::mtVillager: toReturn = new cVillager(cVillager::vtFarmer); break; + case cMonster::mtWitch: toReturn = new cWitch(); break; + case cMonster::mtWolf: toReturn = new cWolf(); break; + case cMonster::mtZombie: toReturn = new cZombie(false); break; + case cMonster::mtZombiePigman: toReturn = new cZombiePigman(); break; default: { ASSERT(!"Unhandled Mob type"); diff --git a/source/Mobs/Bat.h b/source/Mobs/Bat.h index fd3e00a07..e878d0ee8 100644 --- a/source/Mobs/Bat.h +++ b/source/Mobs/Bat.h @@ -17,6 +17,7 @@ public: CLASS_PROTODEF(cBat); + bool IsHanging(void) const {return false; } } ; diff --git a/source/Mobs/Cavespider.cpp b/source/Mobs/Cavespider.cpp index 569aadcc4..2d50b391a 100644 --- a/source/Mobs/Cavespider.cpp +++ b/source/Mobs/Cavespider.cpp @@ -9,8 +9,7 @@ cCavespider::cCavespider(void) : - // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here - super("Cavespider", 59, "mob.spider.say", "mob.spider.death", 0.9, 0.6) + super("Cavespider", 59, "mob.spider.say", "mob.spider.death", 0.7, 0.5) { } diff --git a/source/Mobs/Creeper.cpp b/source/Mobs/Creeper.cpp index 9b1b68b79..b41b05f42 100644 --- a/source/Mobs/Creeper.cpp +++ b/source/Mobs/Creeper.cpp @@ -2,13 +2,16 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Creeper.h" +#include "../World.h" cCreeper::cCreeper(void) : - super("Creeper", 50, "mob.creeper.say", "mob.creeper.say", 0.6, 1.8) + super("Creeper", 50, "mob.creeper.say", "mob.creeper.say", 0.6, 1.8), + m_bIsBlowing(false), + m_bIsCharged(false) { } @@ -26,3 +29,19 @@ void cCreeper::GetDrops(cItems & a_Drops, cEntity * a_Killer) + +void cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + super::DoTakeDamage(a_TDI); + + if (a_TDI.DamageType == dtLightning) + { + m_bIsCharged = true; + } + + m_World->BroadcastEntityMetadata(*this); +} + + + + diff --git a/source/Mobs/Creeper.h b/source/Mobs/Creeper.h index c1d46f462..c3d4edeae 100644 --- a/source/Mobs/Creeper.h +++ b/source/Mobs/Creeper.h @@ -18,6 +18,15 @@ public: CLASS_PROTODEF(cCreeper); virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + + bool IsBlowing(void) const {return m_bIsBlowing; } + bool IsCharged(void) const {return m_bIsCharged; } + +private: + + bool m_bIsBlowing, m_bIsCharged; + } ; diff --git a/source/Mobs/EnderDragon.cpp b/source/Mobs/EnderDragon.cpp new file mode 100644 index 000000000..64f2bedfa --- /dev/null +++ b/source/Mobs/EnderDragon.cpp @@ -0,0 +1,27 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "EnderDragon.h" + + + + + +cEnderDragon::cEnderDragon(void) : + // TODO: Vanilla source says this, but is it right? Dragons fly, they don't stand + super("EnderDragon", 63, "mob.enderdragon.hit", "mob.enderdragon.end", 16.0, 8.0) +{ +} + + + + + +void cEnderDragon::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + return; +} + + + + diff --git a/source/Mobs/EnderDragon.h b/source/Mobs/EnderDragon.h new file mode 100644 index 000000000..77177edfe --- /dev/null +++ b/source/Mobs/EnderDragon.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cEnderDragon : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cEnderDragon(void); + + CLASS_PROTODEF(cEnderDragon); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/source/Mobs/Enderman.cpp b/source/Mobs/Enderman.cpp index 1dc47876f..c0ea3d6aa 100644 --- a/source/Mobs/Enderman.cpp +++ b/source/Mobs/Enderman.cpp @@ -8,8 +8,10 @@ cEnderman::cEnderman(void) : - // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here - super("Enderman", 58, "mob.endermen.hit", "mob.endermen.death", 0.5, 2.5) + super("Enderman", 58, "mob.endermen.hit", "mob.endermen.death", 0.5, 2.9), + m_bIsScreaming(false), + CarriedBlock(E_BLOCK_AIR), + CarriedMeta(0) { } diff --git a/source/Mobs/Enderman.h b/source/Mobs/Enderman.h index c4f4ee364..32e40e70b 100644 --- a/source/Mobs/Enderman.h +++ b/source/Mobs/Enderman.h @@ -18,6 +18,17 @@ public: CLASS_PROTODEF(cEnderman); virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + + bool IsScreaming(void) const {return m_bIsScreaming; } + BLOCKTYPE GetCarriedBlock(void) const {return CarriedBlock; } + NIBBLETYPE GetCarriedMeta(void) const {return CarriedMeta; } + +private: + + bool m_bIsScreaming; + BLOCKTYPE CarriedBlock; + NIBBLETYPE CarriedMeta; + } ; diff --git a/source/Mobs/Ghast.h b/source/Mobs/Ghast.h index f9b60dfcf..a2adc21b9 100644 --- a/source/Mobs/Ghast.h +++ b/source/Mobs/Ghast.h @@ -18,6 +18,8 @@ public: CLASS_PROTODEF(cGhast); virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + + bool IsCharging(void) const {return false; } } ; diff --git a/source/Mobs/Giant.cpp b/source/Mobs/Giant.cpp new file mode 100644 index 000000000..a02758a43 --- /dev/null +++ b/source/Mobs/Giant.cpp @@ -0,0 +1,27 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Giant.h" + + + + + +cGiant::cGiant(void) : + // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here + super("Giant", 53, "mob.zombie.hurt", "mob.zombie.death", 2.0, 13.5) +{ +} + + + + + +void cGiant::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 10, 50, E_ITEM_ROTTEN_FLESH); +} + + + + diff --git a/source/Mobs/Giant.h b/source/Mobs/Giant.h new file mode 100644 index 000000000..356dd4352 --- /dev/null +++ b/source/Mobs/Giant.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cGiant : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cGiant(void); + + CLASS_PROTODEF(cGiant); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/source/Mobs/Horse.cpp b/source/Mobs/Horse.cpp new file mode 100644 index 000000000..46e7969cc --- /dev/null +++ b/source/Mobs/Horse.cpp @@ -0,0 +1,123 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Horse.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +cHorse::cHorse(int Type, int Color, int Style, int TameTimes) : + super("Horse", 100, "mob.horse.hit", "mob.horse.death", 1.4, 1.6), + m_bHasChest(false), + m_bIsEating(false), + m_bIsRearing(false), + m_bIsMouthOpen(false), + m_bIsTame(false), + m_bIsSaddled(false), + m_Type(Type), + m_Color(Color), + m_Style(Style), + m_Armour(0), + m_TimesToTame(TameTimes), + m_TameAttemptTimes(0), + m_RearTickCount(0) +{ +} + + + + + +void cHorse::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + if (!m_bIsMouthOpen) + { + if (m_World->GetTickRandomNumber(50) == 25) + { + m_bIsMouthOpen = true; + } + } + else + { + if (m_World->GetTickRandomNumber(10) == 5) + { + m_bIsMouthOpen = false; + } + } + + if ((m_Attachee != NULL) && (!m_bIsTame)) + { + if (m_TameAttemptTimes < m_TimesToTame) + { + if (m_World->GetTickRandomNumber(50) == 25) + { + m_World->BroadcastSoundParticleEffect(2000, (int)(floor(GetPosX()) * 8), (int)(floor(GetPosY()) * 8), (int)(floor(GetPosZ()) * 8), 0); + m_World->BroadcastSoundParticleEffect(2000, (int)(floor(GetPosX()) * 8), (int)(floor(GetPosY()) * 8), (int)(floor(GetPosZ()) * 8), 2); + m_World->BroadcastSoundParticleEffect(2000, (int)(floor(GetPosX()) * 8), (int)(floor(GetPosY()) * 8), (int)(floor(GetPosZ()) * 8), 6); + m_World->BroadcastSoundParticleEffect(2000, (int)(floor(GetPosX()) * 8), (int)(floor(GetPosY()) * 8), (int)(floor(GetPosZ()) * 8), 8); + + m_Attachee->Detach(); + m_bIsRearing = true; + } + } + else + { + m_bIsTame = true; + } + } + + if (m_bIsRearing) + { + if (m_RearTickCount == 20) + { + m_bIsRearing = false; + } + else { m_RearTickCount++;} + } + + m_World->BroadcastEntityMetadata(*this); +} + + + + + +void cHorse::OnRightClicked(cPlayer & a_Player) +{ + if (m_Attachee != NULL) + { + if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) + { + a_Player.Detach(); + return; + } + + if (m_Attachee->IsPlayer()) + { + return; + } + + m_Attachee->Detach(); + } + + m_TameAttemptTimes++; + a_Player.AttachTo(this); +} + + + + + +void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER); +} + + + + diff --git a/source/Mobs/Horse.h b/source/Mobs/Horse.h new file mode 100644 index 000000000..be0c23f9b --- /dev/null +++ b/source/Mobs/Horse.h @@ -0,0 +1,44 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cHorse : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cHorse(int Type, int Color, int Style, int TameTimes); + + CLASS_PROTODEF(cHorse); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + + bool IsSaddled (void) const {return m_bIsSaddled; } + bool IsChested (void) const {return m_bHasChest; } + bool IsEating (void) const {return m_bIsEating; } + bool IsRearing (void) const {return m_bIsRearing; } + bool IsMthOpen (void) const {return m_bIsMouthOpen; } + bool IsTame (void) const {return m_bIsTame; } + int GetHorseType (void) const {return m_Type; } + int GetHorseColor (void) const {return m_Color; } + int GetHorseStyle (void) const {return m_Style; } + int GetHorseArmour (void) const {return m_Armour;} + +private: + + bool m_bHasChest, m_bIsEating, m_bIsRearing, m_bIsMouthOpen, m_bIsTame, m_bIsSaddled; + int m_Type, m_Color, m_Style, m_Armour, m_TimesToTame, m_TameAttemptTimes, m_RearTickCount; + +} ; + + + + diff --git a/source/Mobs/IncludeAllMonsters.h b/source/Mobs/IncludeAllMonsters.h index d89a6c5b5..1b436a11f 100644 --- a/source/Mobs/IncludeAllMonsters.h +++ b/source/Mobs/IncludeAllMonsters.h @@ -5,7 +5,11 @@ #include "Cow.h" #include "Creeper.h" #include "Enderman.h" +#include "EnderDragon.h" #include "Ghast.h" +#include "Giant.h" +#include "Horse.h" +#include "IronGolem.h" #include "Magmacube.h" #include "Mooshroom.h" #include "Ocelot.h" @@ -14,10 +18,12 @@ #include "Silverfish.h" #include "Skeleton.h" #include "Slime.h" +#include "SnowGolem.h" #include "Spider.h" #include "Squid.h" #include "Villager.h" #include "Witch.h" +#include "Wither.h" #include "Wolf.h" #include "Zombie.h" #include "Zombiepigman.h" diff --git a/source/Mobs/IronGolem.cpp b/source/Mobs/IronGolem.cpp new file mode 100644 index 000000000..42d312c23 --- /dev/null +++ b/source/Mobs/IronGolem.cpp @@ -0,0 +1,26 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "IronGolem.h" + + + + + +cIronGolem::cIronGolem(void) : + super("IronGolem", 99, "mob.IronGolem.hit", "mob.IronGolem.death", 1.4, 2.9) +{ +} + + + + + +void cIronGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 5, E_ITEM_IRON); +} + + + + diff --git a/source/Mobs/IronGolem.h b/source/Mobs/IronGolem.h new file mode 100644 index 000000000..d49ff4cab --- /dev/null +++ b/source/Mobs/IronGolem.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "PassiveAggressiveMonster.h" + + + + + +class cIronGolem : + public cPassiveAggressiveMonster +{ + typedef cPassiveAggressiveMonster super; + +public: + cIronGolem(void); + + CLASS_PROTODEF(cIronGolem); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/source/Mobs/Magmacube.cpp b/source/Mobs/Magmacube.cpp index 0b9b57e3c..c72b4831b 100644 --- a/source/Mobs/Magmacube.cpp +++ b/source/Mobs/Magmacube.cpp @@ -7,8 +7,8 @@ -cMagmacube::cMagmacube(int a_Size) : - super("Magmacube", 62, "mob.magmacube.big", "mob.magmacube.big", 0.6 * a_Size, 0.6 * a_Size), +cMagmaCube::cMagmaCube(int a_Size) : + super("MagmaCube", 62, "mob.MagmaCube.big", "mob.MagmaCube.big", 0.6 * a_Size, 0.6 * a_Size), m_Size(a_Size) { } @@ -17,7 +17,7 @@ cMagmacube::cMagmacube(int a_Size) : -void cMagmacube::GetDrops(cItems & a_Drops, cEntity * a_Killer) +void cMagmaCube::GetDrops(cItems & a_Drops, cEntity * a_Killer) { AddRandomDropItem(a_Drops, 0, 1, E_ITEM_MAGMA_CREAM); } diff --git a/source/Mobs/Magmacube.h b/source/Mobs/Magmacube.h index e4df4f33d..130952970 100644 --- a/source/Mobs/Magmacube.h +++ b/source/Mobs/Magmacube.h @@ -7,22 +7,23 @@ -class cMagmacube : +class cMagmaCube : public cAggressiveMonster { typedef cAggressiveMonster super; public: - /// Creates a magmacube of the specified size; size is 1 .. 3, with 1 being the smallest - cMagmacube(int a_Size); + /// Creates a MagmaCube of the specified size; size is 1 .. 3, with 1 being the smallest + cMagmaCube(int a_Size); - CLASS_PROTODEF(cMagmacube); + CLASS_PROTODEF(cMagmaCube); virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + int GetSize(void) const { return m_Size; } protected: - /// Size of the magmacube, 1 .. 3, with 1 being the smallest + /// Size of the MagmaCube, 1 .. 3, with 1 being the smallest int m_Size; } ; diff --git a/source/Mobs/Monster.cpp b/source/Mobs/Monster.cpp index b378b2bc6..51ea644d3 100644 --- a/source/Mobs/Monster.cpp +++ b/source/Mobs/Monster.cpp @@ -26,7 +26,7 @@ cMonster::cMonster(const AString & a_ConfigName, char a_ProtocolMobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) - : super(etMob, a_Width, a_Height) + : super(etMonster, a_Width, a_Height) , m_Target(NULL) , m_AttackRate(3) , idle_interval(0) @@ -42,7 +42,7 @@ cMonster::cMonster(const AString & a_ConfigName, char a_ProtocolMobType, const A , m_SeePlayerInterval (0) , m_EMPersonality(AGGRESSIVE) , m_AttackDamage(1.0f) - , m_AttackRange(5.0f) + , m_AttackRange(2.0f) , m_AttackInterval(0) , m_BurnsInDaylight(false) { diff --git a/source/Mobs/Monster.h b/source/Mobs/Monster.h index e08fd518a..be60d9e00 100644 --- a/source/Mobs/Monster.h +++ b/source/Mobs/Monster.h @@ -26,36 +26,36 @@ public: /// This identifies individual monster type, as well as their network type-ID enum eType { + mtBat = E_META_SPAWN_EGG_BAT, + mtBlaze = E_META_SPAWN_EGG_BLAZE, + mtCaveSpider = E_META_SPAWN_EGG_CAVE_SPIDER, + mtChicken = E_META_SPAWN_EGG_CHICKEN, + mtCow = E_META_SPAWN_EGG_COW, mtCreeper = E_META_SPAWN_EGG_CREEPER, - mtSkeleton = E_META_SPAWN_EGG_SKELETON, - mtSpider = E_META_SPAWN_EGG_SPIDER, - mtGiant = E_META_SPAWN_EGG_GIANT, - mtZombie = E_META_SPAWN_EGG_ZOMBIE, - mtSlime = E_META_SPAWN_EGG_SLIME, - mtGhast = E_META_SPAWN_EGG_GHAST, - mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN, + mtEnderDragon = E_META_SPAWN_EGG_ENDER_DRAGON, mtEnderman = E_META_SPAWN_EGG_ENDERMAN, - mtCaveSpider = E_META_SPAWN_EGG_CAVE_SPIDER, - mtSilverfish = E_META_SPAWN_EGG_SILVERFISH, - mtBlaze = E_META_SPAWN_EGG_BLAZE, + mtGhast = E_META_SPAWN_EGG_GHAST, + mtGiant = E_META_SPAWN_EGG_GIANT, + mtHorse = E_META_SPAWN_EGG_HORSE, + mtIronGolem = E_META_SPAWN_EGG_IRON_GOLEM, mtMagmaCube = E_META_SPAWN_EGG_MAGMA_CUBE, - mtEnderDragon = E_META_SPAWN_EGG_ENDER_DRAGON, - mtWither = E_META_SPAWN_EGG_WITHER, - mtBat = E_META_SPAWN_EGG_BAT, - mtWitch = E_META_SPAWN_EGG_WITCH, + mtMooshroom = E_META_SPAWN_EGG_MOOSHROOM, + mtOcelot = E_META_SPAWN_EGG_OCELOT, mtPig = E_META_SPAWN_EGG_PIG, mtSheep = E_META_SPAWN_EGG_SHEEP, - mtCow = E_META_SPAWN_EGG_COW, - mtChicken = E_META_SPAWN_EGG_CHICKEN, - mtSquid = E_META_SPAWN_EGG_SQUID, - mtWolf = E_META_SPAWN_EGG_WOLF, - mtMooshroom = E_META_SPAWN_EGG_MOOSHROOM, + mtSilverfish = E_META_SPAWN_EGG_SILVERFISH, + mtSkeleton = E_META_SPAWN_EGG_SKELETON, + mtSlime = E_META_SPAWN_EGG_SLIME, mtSnowGolem = E_META_SPAWN_EGG_SNOW_GOLEM, - mtOcelot = E_META_SPAWN_EGG_OCELOT, - mtIronGolem = E_META_SPAWN_EGG_IRON_GOLEM, + mtSpider = E_META_SPAWN_EGG_SPIDER, + mtSquid = E_META_SPAWN_EGG_SQUID, mtVillager = E_META_SPAWN_EGG_VILLAGER, - - mtInvalidType, // MG TODO : be sure this is the way we do in this project. (needed inside cMobSpawner::ChooscMonster for instance if nothing can be spawned) + mtWitch = E_META_SPAWN_EGG_WITCH, + mtWither = E_META_SPAWN_EGG_WITHER, + mtWolf = E_META_SPAWN_EGG_WOLF, + mtZombie = E_META_SPAWN_EGG_ZOMBIE, + mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN, + mtInvalidType } ; enum eFamily @@ -115,7 +115,9 @@ public: virtual void InStateEscaping(float a_Dt); virtual void Attack(float a_Dt); - int GetMobType() {return m_MobType;} + + int GetMobType() { return m_MobType; } // tolua_export + int GetAttackRate(){return (int)m_AttackRate;} void SetAttackRate(int ar); void SetAttackRange(float ar); @@ -123,7 +125,12 @@ public: void SetSightDistance(float sd); /// Sets whether the mob burns in daylight. Only evaluated at next burn-decision tick - void SetBurnsInDaylight(bool a_BurnsInDaylight) { a_BurnsInDaylight = a_BurnsInDaylight; } + void SetBurnsInDaylight(bool a_BurnsInDaylight) { m_BurnsInDaylight = a_BurnsInDaylight; } + + // Overridables to handle ageable mobs + virtual bool IsBaby (void) const { return false; } + virtual bool IsTame (void) const { return false; } + virtual bool IsSitting (void) const { return false; } enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState; enum MPersonality{PASSIVE,AGGRESSIVE,COWARDLY} m_EMPersonality; @@ -158,6 +165,7 @@ protected: void AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth = 0); void HandleDaylightBurning(cChunk & a_Chunk); + } ; // tolua_export diff --git a/source/Mobs/Ocelot.h b/source/Mobs/Ocelot.h index 98ea224e2..6d24c5672 100644 --- a/source/Mobs/Ocelot.h +++ b/source/Mobs/Ocelot.h @@ -14,8 +14,7 @@ class cOcelot : public: cOcelot(void) : - // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here - super("Ocelot", 98, "mob.cat.hitt", "mob.cat.hitt", 0.9, 0.5) + super("Ocelot", 98, "mob.cat.hitt", "mob.cat.hitt", 0.6, 0.8) { } diff --git a/source/Mobs/Pig.cpp b/source/Mobs/Pig.cpp index 9df2c2571..cd18c087f 100644 --- a/source/Mobs/Pig.cpp +++ b/source/Mobs/Pig.cpp @@ -2,13 +2,16 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Pig.h" +#include "../Entities/Player.h" +#include "../World.h" cPig::cPig(void) : - super("Pig", 90, "mob.pig.say", "mob.pig.death", 0.9, 0.9) + super("Pig", 90, "mob.pig.say", "mob.pig.death", 0.9, 0.9), + m_bIsSaddled(false) { } @@ -24,3 +27,47 @@ void cPig::GetDrops(cItems & a_Drops, cEntity * a_Killer) + +void cPig::OnRightClicked(cPlayer & a_Player) +{ + if (m_bIsSaddled) + { + if (m_Attachee != NULL) + { + if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) + { + // This player is already sitting in, they want out. + a_Player.Detach(); + return; + } + + if (m_Attachee->IsPlayer()) + { + // Another player is already sitting in here, cannot attach + return; + } + + // Detach whatever is sitting in this pig now: + m_Attachee->Detach(); + } + + // Attach the player to this pig + a_Player.AttachTo(this); + } + else if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE) + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + + // Set saddle state & broadcast metadata + m_bIsSaddled = true; + m_World->BroadcastEntityMetadata(*this); + } +} + + + + + diff --git a/source/Mobs/Pig.h b/source/Mobs/Pig.h index ae790ac2f..4fd0d8db8 100644 --- a/source/Mobs/Pig.h +++ b/source/Mobs/Pig.h @@ -18,6 +18,13 @@ public: CLASS_PROTODEF(cPig); virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + bool IsSaddled(void) const { return m_bIsSaddled; } + +private: + + bool m_bIsSaddled; + } ; diff --git a/source/Mobs/Sheep.cpp b/source/Mobs/Sheep.cpp index 2f371f384..440c5c2b9 100644 --- a/source/Mobs/Sheep.cpp +++ b/source/Mobs/Sheep.cpp @@ -3,15 +3,17 @@ #include "Sheep.h" #include "../BlockID.h" +#include "../Entities/Player.h" +#include "../World.h" -cSheep::cSheep(void) : +cSheep::cSheep(int a_Color) : super("Sheep", 91, "mob.sheep.say", "mob.sheep.say", 0.6, 1.3), m_IsSheared(false), - m_WoolColor(E_META_WOOL_WHITE) + m_WoolColor(a_Color) { } @@ -30,3 +32,25 @@ void cSheep::GetDrops(cItems & a_Drops, cEntity * a_Killer) + +void cSheep::OnRightClicked(cPlayer & a_Player) +{ + if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_SHEARS) && (!m_IsSheared)) + { + m_IsSheared = true; + m_World->BroadcastEntityMetadata(*this); + + if (!a_Player.IsGameModeCreative()) + { + a_Player.UseEquippedItem(); + } + + cItems Drops; + Drops.push_back(cItem(E_BLOCK_WOOL, 4, m_WoolColor)); + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); + } +} + + + + diff --git a/source/Mobs/Sheep.h b/source/Mobs/Sheep.h index 369fc78c5..8293a2c05 100644 --- a/source/Mobs/Sheep.h +++ b/source/Mobs/Sheep.h @@ -13,14 +13,20 @@ class cSheep : typedef cPassiveMonster super; public: - cSheep(void); + cSheep(int a_Color); - bool m_IsSheared; - NIBBLETYPE m_WoolColor; // Uses E_META_WOOL_ constants for colors - CLASS_PROTODEF(cSheep); - + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + bool IsSheared(void) const { return m_IsSheared; } + int GetFurColor(void) const { return m_WoolColor; } + +private: + + bool m_IsSheared; + int m_WoolColor; + } ; diff --git a/source/Mobs/Silverfish.h b/source/Mobs/Silverfish.h index 7d675a9c0..d632ac169 100644 --- a/source/Mobs/Silverfish.h +++ b/source/Mobs/Silverfish.h @@ -14,8 +14,7 @@ class cSilverfish : public: cSilverfish(void) : - // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here - super("Silverfish", 60, "mob.silverfish.hit", "mob.silverfish.kill", 0.9, 0.3) + super("Silverfish", 60, "mob.silverfish.hit", "mob.silverfish.kill", 0.3, 0.7) { } diff --git a/source/Mobs/Skeleton.cpp b/source/Mobs/Skeleton.cpp index 10dad4065..6297b867c 100644 --- a/source/Mobs/Skeleton.cpp +++ b/source/Mobs/Skeleton.cpp @@ -8,8 +8,9 @@ -cSkeleton::cSkeleton(void) : - super("Skeleton", 51, "mob.skeleton.hurt", "mob.skeleton.death", 0.6, 1.8) +cSkeleton::cSkeleton(bool IsWither) : + super("Skeleton", 51, "mob.skeleton.hurt", "mob.skeleton.death", 0.6, 1.8), + m_bIsWither(IsWither) { SetBurnsInDaylight(true); } diff --git a/source/Mobs/Skeleton.h b/source/Mobs/Skeleton.h index d0a2da490..7a4af7e22 100644 --- a/source/Mobs/Skeleton.h +++ b/source/Mobs/Skeleton.h @@ -13,11 +13,17 @@ class cSkeleton : typedef cAggressiveMonster super; public: - cSkeleton(); + cSkeleton(bool IsWither); CLASS_PROTODEF(cSkeleton); virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + bool IsWither(void) const { return m_bIsWither; }; + +private: + + bool m_bIsWither; + } ; diff --git a/source/Mobs/Slime.cpp b/source/Mobs/Slime.cpp index b209ac869..7a9487a06 100644 --- a/source/Mobs/Slime.cpp +++ b/source/Mobs/Slime.cpp @@ -3,8 +3,6 @@ #include "Slime.h" -// TODO: Implement sized slimes - diff --git a/source/Mobs/Slime.h b/source/Mobs/Slime.h index 88136ff32..782c3113f 100644 --- a/source/Mobs/Slime.h +++ b/source/Mobs/Slime.h @@ -19,6 +19,7 @@ public: CLASS_PROTODEF(cSlime); virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + int GetSize(void) const { return m_Size; } protected: diff --git a/source/Mobs/SnowGolem.cpp b/source/Mobs/SnowGolem.cpp new file mode 100644 index 000000000..51125542d --- /dev/null +++ b/source/Mobs/SnowGolem.cpp @@ -0,0 +1,26 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "SnowGolem.h" + + + + + +cSnowGolem::cSnowGolem(void) : + super("SnowGolem", 97, "", "", 0.4, 1.8) +{ +} + + + + + +void cSnowGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 5, E_ITEM_SNOWBALL); +} + + + + diff --git a/source/Mobs/SnowGolem.h b/source/Mobs/SnowGolem.h new file mode 100644 index 000000000..d1344adfd --- /dev/null +++ b/source/Mobs/SnowGolem.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cSnowGolem : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cSnowGolem(void); + + CLASS_PROTODEF(cSnowGolem); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/source/Mobs/Villager.cpp b/source/Mobs/Villager.cpp index 98e5276e1..97d6dc3ca 100644 --- a/source/Mobs/Villager.cpp +++ b/source/Mobs/Villager.cpp @@ -2,16 +2,34 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Villager.h" +#include "../World.h" -cVillager::cVillager(void) : - super("Villager", 120, "", "", 0.6, 1.8) +cVillager::cVillager(eVillagerType VillagerType) : + super("Villager", 120, "", "", 0.6, 1.8), + m_Type(VillagerType) { } + +void cVillager::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + super::DoTakeDamage(a_TDI); + if (a_TDI.Attacker->IsPlayer()) + { + if (m_World->GetTickRandomNumber(5) == 3) + { + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_VILLAGER_ANGRY); + } + } +} + + + + diff --git a/source/Mobs/Villager.h b/source/Mobs/Villager.h index 92267a979..4cd9aaa8e 100644 --- a/source/Mobs/Villager.h +++ b/source/Mobs/Villager.h @@ -13,9 +13,29 @@ class cVillager : typedef cPassiveMonster super; public: - cVillager(); + + enum eVillagerType + { + vtFarmer = 0, + vtLibrarian = 1, + vtPriest = 2, + vtBlacksmith = 3, + vtButcher = 4, + vtGeneric = 5, + vtMax + } ; + + cVillager(eVillagerType VillagerType); CLASS_PROTODEF(cVillager); + + virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + int GetVilType(void) const { return m_Type; } + +private: + + int m_Type; + } ; diff --git a/source/Mobs/Witch.h b/source/Mobs/Witch.h index ce0b49deb..4e637beea 100644 --- a/source/Mobs/Witch.h +++ b/source/Mobs/Witch.h @@ -18,6 +18,8 @@ public: CLASS_PROTODEF(cWitch); virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + + bool IsAngry(void) const {return ((m_EMState == ATTACKING) || (m_EMState == CHASING)); } } ; diff --git a/source/Mobs/Wither.cpp b/source/Mobs/Wither.cpp new file mode 100644 index 000000000..8b77284c8 --- /dev/null +++ b/source/Mobs/Wither.cpp @@ -0,0 +1,26 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Wither.h" + + + + + +cWither::cWither(void) : + super("Wither", 64, "mob.wither.hurt", "mob.wither.death", 0.9, 4.0) +{ +} + + + + + +void cWither::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 1, 1, E_ITEM_NETHER_STAR); +} + + + + diff --git a/source/Mobs/Wither.h b/source/Mobs/Wither.h new file mode 100644 index 000000000..56effc6bb --- /dev/null +++ b/source/Mobs/Wither.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cWither : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cWither(void); + + CLASS_PROTODEF(cWither); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/source/Mobs/Wolf.cpp b/source/Mobs/Wolf.cpp new file mode 100644 index 000000000..e76f991dc --- /dev/null +++ b/source/Mobs/Wolf.cpp @@ -0,0 +1,79 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Wolf.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +cWolf::cWolf(void) : + super("Wolf", 95, "mob.wolf.hurt", "mob.wolf.death", 0.6, 0.8), + m_bIsAngry(false), + m_bIsTame(false), + m_bIsSitting(false), + m_bIsBegging(false) +{ +} + + + + + +void cWolf::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + super::DoTakeDamage(a_TDI); + if (!m_bIsTame) + { + m_bIsAngry = true; + } + m_World->BroadcastEntityMetadata(*this); // Broadcast health and possibly angry face +} + + + + + +void cWolf::OnRightClicked(cPlayer & a_Player) +{ + if ((!m_bIsTame) && (!m_bIsAngry)) + { + if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_BONE) + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + + if (m_World->GetTickRandomNumber(10) == 5) + { + SetMaxHealth(20); + m_bIsTame = true; + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_WOLF_TAMED); + } + else + { + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_WOLF_TAMING); + } + } + } + else if (m_bIsTame) + { + if (m_bIsSitting) + { + m_bIsSitting = false; + } + else + { + m_bIsSitting = true; + } + } + + m_World->BroadcastEntityMetadata(*this); +} + + + + diff --git a/source/Mobs/Wolf.h b/source/Mobs/Wolf.h index 405df80a6..98074ba11 100644 --- a/source/Mobs/Wolf.h +++ b/source/Mobs/Wolf.h @@ -13,13 +13,25 @@ class cWolf : typedef cPassiveAggressiveMonster super; public: - cWolf(void) : - // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here (wiki.vg values are suspicious) - super("Wolf", 95, "mob.wolf.hurt", "mob.wolf.death", 0.9, 0.9) - { - } + cWolf(void); CLASS_PROTODEF(cWolf); + + virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + + bool IsSitting(void) const { return m_bIsSitting; } + bool IsTame(void) const { return m_bIsTame; } + bool IsBegging(void) const { return m_bIsBegging; } + bool IsAngry(void) const { return m_bIsAngry; } + +private: + + bool m_bIsSitting; + bool m_bIsTame; + bool m_bIsBegging; + bool m_bIsAngry; + } ; diff --git a/source/Mobs/Zombie.cpp b/source/Mobs/Zombie.cpp index 9b238baef..f495fe5ee 100644 --- a/source/Mobs/Zombie.cpp +++ b/source/Mobs/Zombie.cpp @@ -8,8 +8,10 @@ -cZombie::cZombie(void) : - super("Zombie", 54, "mob.zombie.hurt", "mob.zombie.death", 0.6, 1.8) +cZombie::cZombie(bool IsVillagerZombie) : + super("Zombie", 54, "mob.zombie.hurt", "mob.zombie.death", 0.6, 1.8), + m_bIsConverting(false), + m_bIsVillagerZombie(IsVillagerZombie) { SetBurnsInDaylight(true); } diff --git a/source/Mobs/Zombie.h b/source/Mobs/Zombie.h index 4835a53c4..148b1121e 100644 --- a/source/Mobs/Zombie.h +++ b/source/Mobs/Zombie.h @@ -12,11 +12,19 @@ class cZombie : typedef cAggressiveMonster super; public: - cZombie(void); + cZombie(bool IsVillagerZombie); CLASS_PROTODEF(cZombie); virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + + bool IsVillagerZombie(void) const {return m_bIsVillagerZombie; } + bool IsConverting(void) const {return m_bIsConverting; } + +private: + + bool m_bIsVillagerZombie, m_bIsConverting; + } ; diff --git a/source/Mobs/Zombiepigman.cpp b/source/Mobs/Zombiepigman.cpp index 09b44816f..1e31a72d9 100644 --- a/source/Mobs/Zombiepigman.cpp +++ b/source/Mobs/Zombiepigman.cpp @@ -8,8 +8,8 @@ -cZombiepigman::cZombiepigman(void) : - super("Zombiepigman", 57, "mob.zombiepig.zpighurt", "mob.zombiepig.zpigdeath", 0.6, 1.8) +cZombiePigman::cZombiePigman(void) : + super("ZombiePigman", 57, "mob.zombiepig.zpighurt", "mob.zombiepig.zpigdeath", 0.6, 1.8) { } @@ -17,23 +17,7 @@ cZombiepigman::cZombiepigman(void) : -void cZombiepigman::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - - // TODO Same as noticed in cSkeleton AND Do they really burn by sun?? :D In the neather is no sun :D - if ((GetWorld()->GetTimeOfDay() < (12000 + 1000)) && !IsOnFire()) - { - // Burn for 10 ticks, then decide again - StartBurning(10); - } -} - - - - - -void cZombiepigman::GetDrops(cItems & a_Drops, cEntity * a_Killer) +void cZombiePigman::GetDrops(cItems & a_Drops, cEntity * a_Killer) { AddRandomDropItem(a_Drops, 0, 1, E_ITEM_ROTTEN_FLESH); AddRandomDropItem(a_Drops, 0, 1, E_ITEM_GOLD_NUGGET); @@ -45,7 +29,7 @@ void cZombiepigman::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cZombiepigman::KilledBy(cEntity * a_Killer) +void cZombiePigman::KilledBy(cEntity * a_Killer) { super::KilledBy(a_Killer); diff --git a/source/Mobs/Zombiepigman.h b/source/Mobs/Zombiepigman.h index fe8c6d047..67991d56a 100644 --- a/source/Mobs/Zombiepigman.h +++ b/source/Mobs/Zombiepigman.h @@ -7,17 +7,16 @@ -class cZombiepigman : +class cZombiePigman : public cPassiveAggressiveMonster { typedef cPassiveAggressiveMonster super; public: - cZombiepigman(void); + cZombiePigman(void); - CLASS_PROTODEF(cZombiepigman); + CLASS_PROTODEF(cZombiePigman); - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; virtual void KilledBy(cEntity * a_Killer) override; } ; diff --git a/source/OSSupport/File.cpp b/source/OSSupport/File.cpp index cc0916711..d2eea498a 100644 --- a/source/OSSupport/File.cpp +++ b/source/OSSupport/File.cpp @@ -6,13 +6,12 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "File.h" +#include <fstream> - -/// Simple constructor - creates an unopened file object, use Open() to open / create a real file cFile::cFile(void) : #ifdef USE_STDIO_FILE m_File(NULL) @@ -27,7 +26,6 @@ cFile::cFile(void) : -/// Constructs and opens / creates the file specified, use IsOpen() to check for success cFile::cFile(const AString & iFileName, eMode iMode) : #ifdef USE_STDIO_FILE m_File(NULL) @@ -42,7 +40,6 @@ cFile::cFile(const AString & iFileName, eMode iMode) : -/// Auto-closes the file, if open cFile::~cFile() { if (IsOpen()) @@ -134,7 +131,6 @@ bool cFile::IsEOF(void) const -/// Reads up to iNumBytes bytes into iBuffer, returns the number of bytes actually read, or -1 on failure; asserts if not open int cFile::Read (void * iBuffer, int iNumBytes) { ASSERT(IsOpen()); @@ -151,7 +147,6 @@ int cFile::Read (void * iBuffer, int iNumBytes) -/// Writes up to iNumBytes bytes from iBuffer, returns the number of bytes actually written, or -1 on failure; asserts if not open int cFile::Write(const void * iBuffer, int iNumBytes) { ASSERT(IsOpen()); @@ -169,7 +164,6 @@ int cFile::Write(const void * iBuffer, int iNumBytes) -/// Seeks to iPosition bytes from file start, returns old position or -1 for failure int cFile::Seek (int iPosition) { ASSERT(IsOpen()); @@ -191,7 +185,6 @@ int cFile::Seek (int iPosition) -/// Returns the current position (bytes from file start) int cFile::Tell (void) const { ASSERT(IsOpen()); @@ -208,7 +201,6 @@ int cFile::Tell (void) const -/// Returns the size of file, in bytes, or -1 for failure; asserts if not open int cFile::GetSize(void) const { ASSERT(IsOpen()); @@ -287,6 +279,87 @@ bool cFile::Rename(const AString & a_OrigFileName, const AString & a_NewFileName +bool cFile::Copy(const AString & a_SrcFileName, const AString & a_DstFileName) +{ + #ifdef _WIN32 + return (CopyFile(a_SrcFileName.c_str(), a_DstFileName.c_str(), true) != 0); + #else + // Other OSs don't have a direct CopyFile equivalent, do it the harder way: + std::ifstream src(a_SrcFileName.c_str(), std::ios::binary); + std::ofstream dst(a_DstFileName.c_str(), std::ios::binary); + if (dst.good()) + { + dst << src.rdbuf(); + return true; + } + else + { + return false; + } + #endif +} + + + + + +bool cFile::IsFolder(const AString & a_Path) +{ + #ifdef _WIN32 + DWORD FileAttrib = GetFileAttributes(a_Path.c_str()); + return ((FileAttrib != INVALID_FILE_ATTRIBUTES) && ((FileAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0)); + #else + struct stat st; + return ((stat(a_Path.c_str(), &st) == 0) && S_ISDIR(st.st_mode)); + #endif +} + + + + + +bool cFile::IsFile(const AString & a_Path) +{ + #ifdef _WIN32 + DWORD FileAttrib = GetFileAttributes(a_Path.c_str()); + return ((FileAttrib != INVALID_FILE_ATTRIBUTES) && ((FileAttrib & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0)); + #else + struct stat st; + return ((stat(a_Path.c_str(), &st) == 0) && S_ISREG(st.st_mode)); + #endif +} + + + + + +int cFile::GetSize(const AString & a_FileName) +{ + struct stat st; + if (stat(a_FileName.c_str(), &st) == 0) + { + return st.st_size; + } + return -1; +} + + + + + +bool cFile::CreateFolder(const AString & a_FolderPath) +{ + #ifdef _WIN32 + return (CreateDirectory(a_FolderPath.c_str(), NULL) != 0); + #else + return (mkdir(a_FolderPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0); + #endif +} + + + + + int cFile::Printf(const char * a_Fmt, ...) { AString buf; diff --git a/source/OSSupport/File.h b/source/OSSupport/File.h index 8a057afa8..cfb3a2019 100644 --- a/source/OSSupport/File.h +++ b/source/OSSupport/File.h @@ -41,9 +41,14 @@ Usage: +// tolua_begin + class cFile { public: + + // tolua_end + #ifdef _WIN32 static const char PathSeparator = '\\'; #else @@ -90,14 +95,33 @@ public: /// Reads the file from current position till EOF into an AString; returns the number of bytes read or -1 for error int ReadRestOfFile(AString & a_Contents); + // tolua_begin + /// Returns true if the file specified exists static bool Exists(const AString & a_FileName); /// Deletes a file, returns true if successful static bool Delete(const AString & a_FileName); - /// Renames a file, returns true if successful. May fail if dest already exists (libc-dependant)! - static bool Rename(const AString & a_OrigFileName, const AString & a_NewFileName); + /// Renames a file or folder, returns true if successful. May fail if dest already exists (libc-dependant)! + static bool Rename(const AString & a_OrigPath, const AString & a_NewPath); + + /// Copies a file, returns true if successful. + static bool Copy(const AString & a_SrcFileName, const AString & a_DstFileName); + + /// Returns true if the specified path is a folder + static bool IsFolder(const AString & a_Path); + + /// Returns true if the specified path is a regular file + static bool IsFile(const AString & a_Path); + + /// Returns the size of the file, or a negative number on error + static int GetSize(const AString & a_FileName); + + /// Creates a new folder with the specified name. Returns true if successful. Path may be relative or absolute + static bool CreateFolder(const AString & a_FolderPath); + + // tolua_end int Printf(const char * a_Fmt, ...); @@ -107,7 +131,7 @@ private: #else HANDLE m_File; #endif -} ; +} ; // tolua_export diff --git a/source/OSSupport/IsThread.cpp b/source/OSSupport/IsThread.cpp index d5fbfcf19..e1ef84c17 100644 --- a/source/OSSupport/IsThread.cpp +++ b/source/OSSupport/IsThread.cpp @@ -53,11 +53,7 @@ static void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName) cIsThread::cIsThread(const AString & iThreadName) : m_ThreadName(iThreadName), m_ShouldTerminate(false), - #ifdef _WIN32 - m_Handle(NULL) - #else // _WIN32 - m_HasStarted(false) - #endif // else _WIN32 + m_Handle(NULL) { } @@ -77,9 +73,9 @@ cIsThread::~cIsThread() bool cIsThread::Start(void) { + ASSERT(m_Handle == NULL); // Has already started one thread? + #ifdef _WIN32 - ASSERT(m_Handle == NULL); // Has already started one thread? - // Create the thread suspended, so that the mHandle variable is valid in the thread procedure DWORD ThreadID = 0; m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &ThreadID); @@ -99,14 +95,11 @@ bool cIsThread::Start(void) #endif // _DEBUG and _MSC_VER #else // _WIN32 - ASSERT(!m_HasStarted); - if (pthread_create(&m_Handle, NULL, thrExecute, this)) { LOGERROR("ERROR: Could not create thread \"%s\", !", m_ThreadName.c_str()); return false; } - m_HasStarted = true; #endif // else _WIN32 return true; @@ -158,7 +151,6 @@ bool cIsThread::Wait(void) LOGD("Thread %s finished", m_ThreadName.c_str()); #endif // LOGD - m_HasStarted = false; return (res == 0); #endif // else _WIN32 } diff --git a/source/OSSupport/IsThread.h b/source/OSSupport/IsThread.h index 9b7f0b73e..2ea8bf6f9 100644 --- a/source/OSSupport/IsThread.h +++ b/source/OSSupport/IsThread.h @@ -48,7 +48,7 @@ public: /// Returns the OS-dependent thread ID for the caller's thread static unsigned long GetCurrentID(void); -private: +protected: AString m_ThreadName; #ifdef _WIN32 @@ -66,7 +66,6 @@ private: #else // _WIN32 pthread_t m_Handle; - bool m_HasStarted; static void * thrExecute(void * a_Param) { diff --git a/source/OSSupport/ListenThread.cpp b/source/OSSupport/ListenThread.cpp index 0890aabc8..ba3198764 100644 --- a/source/OSSupport/ListenThread.cpp +++ b/source/OSSupport/ListenThread.cpp @@ -224,7 +224,10 @@ void cListenThread::Execute(void) if (itr->IsValid() && FD_ISSET(itr->GetSocket(), &fdRead)) { cSocket Client = (m_Family == cSocket::IPv4) ? itr->AcceptIPv4() : itr->AcceptIPv6(); - m_Callback.OnConnectionAccepted(Client); + if (Client.IsValid()) + { + m_Callback.OnConnectionAccepted(Client); + } } } // for itr - m_Sockets[] } // while (!m_ShouldTerminate) diff --git a/source/OSSupport/MakeDir.cpp b/source/OSSupport/MakeDir.cpp deleted file mode 100644 index 10ccfe9ec..000000000 --- a/source/OSSupport/MakeDir.cpp +++ /dev/null @@ -1,25 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "MakeDir.h" - - - - - -void cMakeDir::MakeDir(const AString & a_Directory) -{ -#ifdef _WIN32 - SECURITY_ATTRIBUTES Attrib; - Attrib.nLength = sizeof(SECURITY_ATTRIBUTES); - Attrib.lpSecurityDescriptor = NULL; - Attrib.bInheritHandle = false; - ::CreateDirectory( (FILE_IO_PREFIX + a_Directory).c_str(), &Attrib); -#else - mkdir( (FILE_IO_PREFIX + a_Directory).c_str(), S_IRWXU | S_IRWXG | S_IRWXO); -#endif -} - - - - diff --git a/source/OSSupport/MakeDir.h b/source/OSSupport/MakeDir.h deleted file mode 100644 index e66cf1071..000000000 --- a/source/OSSupport/MakeDir.h +++ /dev/null @@ -1,16 +0,0 @@ - -#pragma once - - - - - -class cMakeDir -{ -public: - static void MakeDir(const AString & a_Directory); -}; - - - - diff --git a/source/Piston.cpp b/source/Piston.cpp index f4244d177..136100922 100644 --- a/source/Piston.cpp +++ b/source/Piston.cpp @@ -21,7 +21,7 @@ extern bool g_BlockPistonBreakable[]; /// Number of ticks that the piston extending / retracting waits before setting the block -const int PISTON_TICK_DELAY = 10; +const int PISTON_TICK_DELAY = 20; @@ -270,7 +270,13 @@ bool cPiston::CanPull(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) return false; } } - return CanPush(a_BlockType, a_BlockMeta) || CanBreakPush(a_BlockType, a_BlockMeta); + + if (CanBreakPush(a_BlockType, a_BlockMeta)) + { + return false; // CanBreakPush returns true, but we need false to prevent pulling + } + + return CanPush(a_BlockType, a_BlockMeta); } diff --git a/source/Plugin.cpp b/source/Plugin.cpp index 229b997cd..98ccfb88c 100644 --- a/source/Plugin.cpp +++ b/source/Plugin.cpp @@ -2,10 +2,6 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Plugin.h" -#include "Entities/Player.h" -#include "World.h" -#include "CommandOutput.h" -#include "Mobs/Monster.h" @@ -32,7 +28,11 @@ cPlugin::~cPlugin() -AString cPlugin::GetLocalDirectory(void) const +AString cPlugin::GetLocalFolder(void) const { return std::string("Plugins/") + m_Directory; -}
\ No newline at end of file +} + + + + diff --git a/source/Plugin.h b/source/Plugin.h index be803bab2..06e5819df 100644 --- a/source/Plugin.h +++ b/source/Plugin.h @@ -121,7 +121,8 @@ public: void SetVersion(int a_Version) { m_Version = a_Version; } const AString & GetDirectory(void) const {return m_Directory; } - AString GetLocalDirectory(void) const; + AString GetLocalDirectory(void) const {return GetLocalFolder(); } // OBSOLETE, use GetLocalFolder() instead + AString GetLocalFolder(void) const; // tolua_end diff --git a/source/PluginLua.cpp b/source/PluginLua.cpp index 81a536838..03aefb098 100644 --- a/source/PluginLua.cpp +++ b/source/PluginLua.cpp @@ -81,6 +81,9 @@ bool cPluginLua::Initialize(void) lua_setglobal(m_LuaState, LUA_PLUGIN_INSTANCE_VAR_NAME); lua_pushstring(m_LuaState, GetName().c_str()); lua_setglobal(m_LuaState, LUA_PLUGIN_NAME_VAR_NAME); + + tolua_pushusertype(m_LuaState, this, "cPluginLua"); + lua_setglobal(m_LuaState, "g_Plugin"); } std::string PluginPath = FILE_IO_PREFIX + GetLocalDirectory() + "/"; diff --git a/source/PluginLua.h b/source/PluginLua.h index fee9c4986..908466966 100644 --- a/source/PluginLua.h +++ b/source/PluginLua.h @@ -137,6 +137,43 @@ public: Returns true if the hook was added successfully. */ bool AddHookRef(int a_HookType, int a_FnRefIdx); + + // The following templates allow calls to arbitrary Lua functions residing in the plugin: + + /// Call a Lua function with 0 args + template <typename FnT> bool Call(FnT a_Fn) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn); + } + + /// Call a Lua function with 1 arg + template <typename FnT, typename ArgT0> bool Call(FnT a_Fn, ArgT0 a_Arg0) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn, a_Arg0); + } + + /// Call a Lua function with 2 args + template <typename FnT, typename ArgT0, typename ArgT1> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1); + } + + /// Call a Lua function with 3 args + template <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2); + } + + /// Call a Lua function with 4 args + template <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2, typename ArgT3> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2, a_Arg3); + } protected: /// Maps command name into Lua function reference diff --git a/source/PluginManager.cpp b/source/PluginManager.cpp index 93ee71926..a557bdc03 100644 --- a/source/PluginManager.cpp +++ b/source/PluginManager.cpp @@ -75,16 +75,16 @@ void cPluginManager::FindPlugins(void) AStringList Files = GetDirectoryContents(PluginsPath.c_str()); for (AStringList::const_iterator itr = Files.begin(); itr != Files.end(); ++itr) { - if (itr->rfind(".") != AString::npos) + if ((*itr == ".") || (*itr == "..") || (!cFile::IsFolder(PluginsPath + *itr))) { - // Ignore files, we only want directories + // We only want folders, and don't want "." or ".." continue; } // Add plugin name/directory to the list - if (m_Plugins.find( *itr ) == m_Plugins.end()) + if (m_Plugins.find(*itr) == m_Plugins.end()) { - m_Plugins[ *itr ] = NULL; + m_Plugins[*itr] = NULL; } } } @@ -95,7 +95,7 @@ void cPluginManager::FindPlugins(void) void cPluginManager::ReloadPluginsNow(void) { - LOG("Loading plugins"); + LOG("-- Loading Plugins --"); m_bReloadPlugins = false; UnloadPluginsNow(); @@ -135,11 +135,15 @@ void cPluginManager::ReloadPluginsNow(void) if (GetNumPlugins() == 0) { - LOG("No plugins loaded"); + LOG("-- No Plugins Loaded --"); + } + else if ((GetNumPlugins() > 1) || (GetNumPlugins() == 0)) + { + LOG("-- Loaded %i Plugins --", GetNumPlugins()); } else { - LOG("Loaded %i plugin(s)", GetNumPlugins()); + LOG("-- Loaded 1 Plugin --"); } } diff --git a/source/Protocol/Protocol125.cpp b/source/Protocol/Protocol125.cpp index 4577e0c16..fb7315468 100644 --- a/source/Protocol/Protocol125.cpp +++ b/source/Protocol/Protocol125.cpp @@ -24,8 +24,27 @@ Documentation: #include "../UI/Window.h" #include "../Root.h" #include "../Server.h" + +#include "../Entities/ProjectileEntity.h" +#include "../Entities/Minecart.h" #include "../Entities/FallingBlock.h" +#include "../Mobs/Monster.h" +#include "../Mobs/Creeper.h" +#include "../Mobs/Bat.h" +#include "../Mobs/Pig.h" +#include "../Mobs/Villager.h" +#include "../Mobs/Zombie.h" +#include "../Mobs/Ghast.h" +#include "../Mobs/Wolf.h" +#include "../Mobs/Sheep.h" +#include "../Mobs/Enderman.h" +#include "../Mobs/Skeleton.h" +#include "../Mobs/Witch.h" +#include "../Mobs/Slime.h" +#include "../Mobs/Magmacube.h" +#include "../Mobs/Horse.h" + @@ -343,8 +362,18 @@ void cProtocol125::SendEntityMetadata(const cEntity & a_Entity) cCSLock Lock(m_CSPacket); WriteByte(PACKET_METADATA); WriteInt (a_Entity.GetUniqueID()); - AString MetaData = GetEntityMetaData(a_Entity); - SendData(MetaData.data(), MetaData.size()); + + WriteCommonMetadata(a_Entity); + if (a_Entity.IsMob()) + { + WriteMobMetadata(((const cMonster &)a_Entity)); + } + else + { + WriteEntityMetadata(a_Entity); + } + WriteByte(0x7f); + Flush(); } @@ -436,15 +465,18 @@ void cProtocol125::SendExplosion(double a_BlockX, double a_BlockY, double a_Bloc WriteDouble (a_BlockZ); WriteFloat (a_Radius); WriteInt (a_BlocksAffected.size()); + int BlockX = (int)a_BlockX; + int BlockY = (int)a_BlockY; + int BlockZ = (int)a_BlockZ; for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(); itr != a_BlocksAffected.end(); ++itr) { - WriteByte ((Byte)(itr->x - a_BlockX)); - WriteByte ((Byte)(itr->y - a_BlockY)); - WriteByte ((Byte)(itr->z - a_BlockZ)); + WriteByte((Byte)(itr->x - BlockX)); + WriteByte((Byte)(itr->y - BlockY)); + WriteByte((Byte)(itr->z - BlockZ)); } - WriteFloat ((float)a_PlayerMotion.x); - WriteFloat ((float)a_PlayerMotion.y); - WriteFloat ((float)a_PlayerMotion.z); + WriteFloat((float)a_PlayerMotion.x); + WriteFloat((float)a_PlayerMotion.y); + WriteFloat((float)a_PlayerMotion.z); Flush(); } @@ -584,7 +616,7 @@ void cProtocol125::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) WriteByte ((unsigned char)PACKET_PLAYER_LIST_ITEM); WriteString(PlayerName); WriteBool (a_IsOnline); - WriteShort (a_Player.GetClientHandle()->GetPing()); + WriteShort (a_IsOnline ? a_Player.GetClientHandle()->GetPing() : 0); Flush(); } @@ -710,8 +742,11 @@ void cProtocol125::SendSpawnMob(const cMonster & a_Mob) WriteByte (0); WriteByte (0); WriteByte (0); - AString MetaData = GetEntityMetaData(a_Mob); - SendData (MetaData.data(), MetaData.size()); + + WriteCommonMetadata(a_Mob); + WriteMobMetadata(a_Mob); + WriteByte(0x7f); + Flush(); } @@ -1614,48 +1649,242 @@ int cProtocol125::ParseItem(cItem & a_Item) -AString cProtocol125::GetEntityMetaData(const cEntity & a_Entity) +void cProtocol125::WriteCommonMetadata(const cEntity & a_Entity) { - // We should send all the metadata here - AString MetaData; - // Common metadata (index 0, byte): - MetaData.push_back(0); - MetaData.push_back(GetEntityMetadataFlags(a_Entity)); - - // TODO: Add more entity-specific metadata - - MetaData.push_back(0x7f); // End metadata - return MetaData; -} - + Byte CommonMetadata = 0; - - - -char cProtocol125::GetEntityMetadataFlags(const cEntity & a_Entity) -{ - char Flags = 0; if (a_Entity.IsOnFire()) { - Flags |= 1; + CommonMetadata |= 0x1; } if (a_Entity.IsCrouched()) { - Flags |= 2; + CommonMetadata |= 0x2; } if (a_Entity.IsRiding()) { - Flags |= 4; + CommonMetadata |= 0x4; } if (a_Entity.IsSprinting()) { - Flags |= 8; + CommonMetadata |= 0x8; } if (a_Entity.IsRclking()) { - Flags |= 16; + CommonMetadata |= 0x10; + } + if (a_Entity.IsInvisible()) + { + CommonMetadata |= 0x20; + } + + WriteByte(0x0); + WriteByte(CommonMetadata); +} + + + + + +void cProtocol125::WriteEntityMetadata(const cEntity & a_Entity) +{ + if (a_Entity.IsMinecart()) + { + WriteByte(0x51); + // No idea how Mojang makes their carts shakey shakey, so here is a complicated one-liner expression that does something similar + WriteInt( (((a_Entity.GetMaxHealth() / 2) - (a_Entity.GetHealth() - (a_Entity.GetMaxHealth() / 2))) * ((const cMinecart &)a_Entity).LastDamage()) * 4 ); + WriteByte(0x52); + WriteInt(1); // Shaking direction, doesn't seem to affect anything + WriteByte(0x73); + WriteFloat((float)(((const cMinecart &)a_Entity).LastDamage() + 10)); // Damage taken / shake effect multiplyer + + if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpFurnace) + { + WriteByte(0x10); + WriteByte(((const cMinecartWithFurnace &)a_Entity).IsFueled() ? 1 : 0); // Fueled? + } + } + else if ((a_Entity.IsProjectile() && ((cProjectileEntity &)a_Entity).GetProjectileKind() == cProjectileEntity::pkArrow)) + { + WriteByte(0x10); + WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0); // Critical hitting arrow? + } +} + + + + + +void cProtocol125::WriteMobMetadata(const cMonster & a_Mob) +{ + switch (a_Mob.GetMobType()) + { + case cMonster::mtCreeper: + { + WriteByte(0x10); + WriteByte(((const cCreeper &)a_Mob).IsBlowing() ? 1 : -1); // Blowing up? + WriteByte(0x11); + WriteByte(((const cCreeper &)a_Mob).IsCharged() ? 1 : 0); // Lightning-charged? + break; + } + case cMonster::mtBat: + { + WriteByte(0x10); + WriteByte(((const cBat &)a_Mob).IsHanging() ? 1 : 0); // Upside down? + break; + } + case cMonster::mtPig: + { + WriteByte(0x10); + WriteByte(((const cPig &)a_Mob).IsSaddled() ? 1 : 0); // Saddled? + break; + } + case cMonster::mtVillager: + { + WriteByte(0x50); + WriteInt(((const cVillager &)a_Mob).GetVilType()); // What sort of TESTIFICATE? + break; + } + case cMonster::mtZombie: + { + WriteByte(0xC); + WriteByte(((const cZombie &)a_Mob).IsBaby() ? 1 : 0); // Babby zombie? + WriteByte(0xD); + WriteByte(((const cZombie &)a_Mob).IsVillagerZombie() ? 1 : 0); // Converted zombie? + WriteByte(0xE); + WriteByte(((const cZombie &)a_Mob).IsConverting() ? 1 : 0); // Converted-but-converting-back zombllager? + break; + } + case cMonster::mtGhast: + { + WriteByte(0x10); + WriteByte(((const cGhast &)a_Mob).IsCharging()); // About to eject un flamé-bol? :P + break; + } + case cMonster::mtWolf: + { + Byte WolfStatus = 0; + if (((const cWolf &)a_Mob).IsSitting()) + { + WolfStatus |= 0x1; + } + if (((const cWolf &)a_Mob).IsAngry()) + { + WolfStatus |= 0x2; + } + if (((const cWolf &)a_Mob).IsTame()) + { + WolfStatus |= 0x4; + } + WriteByte(0x10); + WriteByte(WolfStatus); + + WriteByte(0x72); + WriteFloat((float)(a_Mob.GetHealth())); // Tail health-o-meter (only shown when tamed, by the way) + WriteByte(0x13); + WriteByte(((const cWolf &)a_Mob).IsBegging() ? 1 : 0); // Ultra cute mode? + break; + } + case cMonster::mtSheep: + { + // [1](1111) + // [] = Is sheared? () = Color, from 0 to 15 + + WriteByte(0x10); + Byte SheepMetadata = 0; + SheepMetadata = ((const cSheep &)a_Mob).GetFurColor(); // Fur colour + + if (((const cSheep &)a_Mob).IsSheared()) // Is sheared? + { + SheepMetadata |= 0x16; + } + WriteByte(SheepMetadata); + break; + } + case cMonster::mtEnderman: + { + WriteByte(0x10); + WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedBlock())); // Block that he stole from your house + WriteByte(0x11); + WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedMeta())); // Meta of block that he stole from your house + WriteByte(0x12); + WriteByte(((const cEnderman &)a_Mob).IsScreaming() ? 1 : 0); // Screaming at your face? + break; + } + case cMonster::mtSkeleton: + { + WriteByte(0xD); + WriteByte(((const cSkeleton &)a_Mob).IsWither() ? 1 : 0); // It's a skeleton, but it's not + break; + } + case cMonster::mtWitch: + { + WriteByte(0x15); + WriteByte(((const cWitch &)a_Mob).IsAngry() ? 1 : 0); // Aggravated? Doesn't seem to do anything + break; + } + case cMonster::mtSlime: + case cMonster::mtMagmaCube: + { + WriteByte(0x10); + if (a_Mob.GetMobType() == cMonster::mtSlime) + { + WriteByte(((const cSlime &)a_Mob).GetSize()); // Size of slime - HEWGE, meh, cute BABBY SLIME + } + else + { + WriteByte(((const cMagmaCube &)a_Mob).GetSize()); // Size of slime - HEWGE, meh, cute BABBY SLIME + } + break; + } + case cMonster::mtHorse: + { + int Flags = 0; + if (((const cHorse &)a_Mob).IsTame()) + { + Flags |= 0x2; + } + if (((const cHorse &)a_Mob).IsSaddled()) + { + Flags |= 0x4; + } + if (((const cHorse &)a_Mob).IsChested()) + { + Flags |= 0x8; + } + if (((const cHorse &)a_Mob).IsBaby()) + { + Flags |= 0x10; // IsBred flag, according to wiki.vg - don't think it does anything in multiplayer + } + if (((const cHorse &)a_Mob).IsEating()) + { + Flags |= 0x20; + } + if (((const cHorse &)a_Mob).IsRearing()) + { + Flags |= 0x40; + } + if (((const cHorse &)a_Mob).IsMthOpen()) + { + Flags |= 0x80; + } + WriteByte(0x50); + WriteInt(Flags); + + WriteByte(0x13); + WriteByte(((const cHorse &)a_Mob).GetHorseType()); // Type of horse (donkey, chestnut, etc.) + + WriteByte(0x54); + int Appearance = 0; + Appearance = ((const cHorse &)a_Mob).GetHorseColor(); // Mask FF + Appearance |= ((const cHorse &)a_Mob).GetHorseStyle() * 256; // Mask FF00, so multiply by 256 + WriteInt(Appearance); + + WriteByte(0x56); + WriteInt(((const cHorse &)a_Mob).GetHorseArmour()); // Horshey armour + break; + } } - return Flags; } diff --git a/source/Protocol/Protocol125.h b/source/Protocol/Protocol125.h index c5c8cd1a0..ae198780c 100644 --- a/source/Protocol/Protocol125.h +++ b/source/Protocol/Protocol125.h @@ -142,11 +142,14 @@ protected: /// Parses one item, "slot" as the protocol wiki calls it, from m_ReceivedData; returns the usual ParsePacket() codes virtual int ParseItem(cItem & a_Item); - /// Returns the entity metadata representation - AString GetEntityMetaData(const cEntity & a_Entity); - - /// Returns the entity common metadata, index 0 (generic flags) - char GetEntityMetadataFlags(const cEntity & a_Entity); + /// Writes the COMMON entity metadata + void WriteCommonMetadata(const cEntity & a_Entity); + + /// Writes normal entity metadata + void WriteEntityMetadata(const cEntity & a_Entity); + + /// Writes mobile entity metadata + void WriteMobMetadata(const cMonster & a_Mob); } ; diff --git a/source/Protocol/Protocol132.cpp b/source/Protocol/Protocol132.cpp index 26a1a9fad..53159a3b3 100644 --- a/source/Protocol/Protocol132.cpp +++ b/source/Protocol/Protocol132.cpp @@ -416,8 +416,11 @@ void cProtocol132::SendSpawnMob(const cMonster & a_Mob) WriteShort ((short)(a_Mob.GetSpeedX() * 400)); WriteShort ((short)(a_Mob.GetSpeedY() * 400)); WriteShort ((short)(a_Mob.GetSpeedZ() * 400)); - AString MetaData = GetEntityMetaData(a_Mob); - SendData (MetaData.data(), MetaData.size()); + + WriteCommonMetadata(a_Mob); + WriteMobMetadata(a_Mob); + WriteByte(0x7f); + Flush(); } @@ -452,8 +455,17 @@ void cProtocol132::SendTabCompletionResults(const AStringVector & a_Results) void cProtocol132::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) { - // Not used in 1.3.2 - // Does it unload chunks on its own? + // Unloading the chunk is done by sending a "map chunk" packet + // with IncludeInitialize set to true and primary bitmap set to 0: + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_CHUNK_DATA); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + WriteBool(true); // IncludeInitialize + WriteShort(0); // Primary bitmap + WriteShort(0); // Add bitmap + WriteInt(0); + Flush(); } diff --git a/source/Protocol/Protocol16x.cpp b/source/Protocol/Protocol16x.cpp index be5b45f19..0eac7b081 100644 --- a/source/Protocol/Protocol16x.cpp +++ b/source/Protocol/Protocol16x.cpp @@ -7,6 +7,8 @@ Implements the 1.6.x protocol classes: - release 1.6.1 protocol (#73) - cProtocol162 - release 1.6.2 protocol (#74) + - release 1.6.3 protocol (#77) - no relevant changes + - release 1.6.4 protocol (#78) - no relevant changes (others may be added later in the future for the 1.6 release series) */ diff --git a/source/Protocol/Protocol16x.h b/source/Protocol/Protocol16x.h index 077c7958b..2447f90a7 100644 --- a/source/Protocol/Protocol16x.h +++ b/source/Protocol/Protocol16x.h @@ -7,6 +7,8 @@ Declares the 1.6.x protocol classes: - release 1.6.1 protocol (#73) - cProtocol162 - release 1.6.2 protocol (#74) + - release 1.6.3 protocol (#77) - no relevant changes + - release 1.6.4 protocol (#78) - no relevant changes (others may be added later in the future for the 1.6 release series) */ diff --git a/source/Protocol/ProtocolRecognizer.cpp b/source/Protocol/ProtocolRecognizer.cpp index 853018329..fe99b22e1 100644 --- a/source/Protocol/ProtocolRecognizer.cpp +++ b/source/Protocol/ProtocolRecognizer.cpp @@ -55,6 +55,8 @@ AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion) case PROTO_VERSION_1_5_2: return "1.5.2"; case PROTO_VERSION_1_6_1: return "1.6.1"; case PROTO_VERSION_1_6_2: return "1.6.2"; + case PROTO_VERSION_1_6_3: return "1.6.3"; + case PROTO_VERSION_1_6_4: return "1.6.4"; } ASSERT(!"Unknown protocol version"); return Printf("Unknown protocol (%d)", a_ProtocolVersion); @@ -707,6 +709,8 @@ bool cProtocolRecognizer::TryRecognizeProtocol(void) return true; } case PROTO_VERSION_1_6_2: + case PROTO_VERSION_1_6_3: + case PROTO_VERSION_1_6_4: { m_Protocol = new cProtocol162(m_Client); return true; @@ -746,6 +750,8 @@ void cProtocolRecognizer::HandleServerPing(void) case PROTO_VERSION_1_5_2: case PROTO_VERSION_1_6_1: case PROTO_VERSION_1_6_2: + case PROTO_VERSION_1_6_3: + case PROTO_VERSION_1_6_4: { // The server list ping now has 1 more byte of "magic". Mojang just loves to complicate stuff. // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3101#Server_List_Ping_.280xFE.29 diff --git a/source/Protocol/ProtocolRecognizer.h b/source/Protocol/ProtocolRecognizer.h index 2178d5e61..c53288230 100644 --- a/source/Protocol/ProtocolRecognizer.h +++ b/source/Protocol/ProtocolRecognizer.h @@ -18,8 +18,8 @@ // Adjust these if a new protocol is added or an old one is removed: -#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2" -#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74" +#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4" +#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74, 77, 78" @@ -42,6 +42,8 @@ public: PROTO_VERSION_1_5_2 = 61, PROTO_VERSION_1_6_1 = 73, PROTO_VERSION_1_6_2 = 74, + PROTO_VERSION_1_6_3 = 77, + PROTO_VERSION_1_6_4 = 78, PROTO_VERSION_NEXT, PROTO_VERSION_LATEST = PROTO_VERSION_NEXT - 1, ///< Automatically assigned to the last protocol version, this serves as the default for PrimaryServerVersion diff --git a/source/RCONServer.cpp b/source/RCONServer.cpp index 04c06c887..93f2ccdd3 100644 --- a/source/RCONServer.cpp +++ b/source/RCONServer.cpp @@ -78,8 +78,8 @@ protected: cRCONServer::cRCONServer(cServer & a_Server) : m_Server(a_Server), - m_ListenThread4(*this, cSocket::IPv4, "RCON"), - m_ListenThread6(*this, cSocket::IPv6, "RCON") + m_ListenThread4(*this, cSocket::IPv4, "RCON IPv4"), + m_ListenThread6(*this, cSocket::IPv6, "RCON IPv6") { } diff --git a/source/Root.cpp b/source/Root.cpp index 3933535f1..290a5269a 100644 --- a/source/Root.cpp +++ b/source/Root.cpp @@ -17,16 +17,23 @@ #include "Protocol/ProtocolRecognizer.h" // for protocol version constants #include "CommandOutput.h" #include "DeadlockDetect.h" +#include "OSSupport/Timer.h" #include "../iniFile/iniFile.h" -#include <iostream> +#ifdef _WIN32 + #include <psapi.h> +#elif defined(__linux__) + #include <fstream> +#elif defined(__APPLE__) + #include <mach/mach.h> +#endif -cRoot* cRoot::s_Root = 0; +cRoot* cRoot::s_Root = NULL; @@ -98,6 +105,9 @@ void cRoot::Start(void) m_bStop = false; while (!m_bStop) { + cTimer Time; + long long mseconds = Time.GetNowTime(); + m_bRestart = false; LoadGlobalSettings(); @@ -125,58 +135,57 @@ void cRoot::Start(void) LOG("Starting server..."); if (!m_Server->InitServer(IniFile)) { - LOGERROR("Failed to start server, shutting down."); + LOGERROR("Failure starting server, aborting..."); return; } IniFile.WriteFile(); - cIniFile WebIniFile("webadmin.ini"); - if (!WebIniFile.ReadFile()) - { - LOGWARNING("webadmin.ini inaccessible, wabadmin is disabled"); - } - - if (WebIniFile.GetValueB("WebAdmin", "Enabled", false)) - { - LOG("Creating WebAdmin..."); - m_WebAdmin = new cWebAdmin(8080); - } + LOG("Initialising WebAdmin..."); + m_WebAdmin = new cWebAdmin(); + m_WebAdmin->Init(); - LOG("Loading settings..."); + LOGD("Loading settings..."); m_GroupManager = new cGroupManager(); m_CraftingRecipes = new cCraftingRecipes; m_FurnaceRecipe = new cFurnaceRecipe(); - LOG("Loading worlds..."); + LOGD("Loading worlds..."); LoadWorlds(); - LOG("Loading plugin manager..."); + LOGD("Loading plugin manager..."); m_PluginManager = new cPluginManager(); m_PluginManager->ReloadPluginsNow(); - LOG("Loading MonsterConfig..."); + LOGD("Loading MonsterConfig..."); m_MonsterConfig = new cMonsterConfig; // This sets stuff in motion - LOG("Starting Authenticator..."); + LOGD("Starting Authenticator..."); m_Authenticator.Start(); - LOG("Starting worlds..."); + LOGD("Starting worlds..."); StartWorlds(); - LOG("Starting deadlock detector..."); + LOGD("Starting deadlock detector..."); dd.Start(); - LOG("Starting server..."); + LOGD("Finalising startup..."); m_Server->Start(); + + LOG("Starting WebAdmin..."); + m_WebAdmin->Start(); #if !defined(ANDROID_NDK) - LOG("Starting InputThread..."); + LOGD("Starting InputThread..."); m_InputThread = new cThread( InputThread, this, "cRoot::InputThread" ); m_InputThread->Start( false ); // We should NOT wait? Otherwise we can´t stop the server from other threads than the input thread #endif - LOG("Initialization done, server running now."); + long long finishmseconds = Time.GetNowTime(); + finishmseconds -= mseconds; + + LOG("Startup complete, took %i ms!", finishmseconds); + while (!m_bStop && !m_bRestart) // These are modified by external threads { cSleep::MilliSleep(1000); @@ -190,37 +199,37 @@ void cRoot::Start(void) LOG("Shutting down server..."); m_Server->Shutdown(); - LOG("Shutting down deadlock detector..."); + LOGD("Shutting down deadlock detector..."); dd.Stop(); - LOG("Stopping world threads..."); + LOGD("Stopping world threads..."); StopWorlds(); - LOG("Stopping authenticator..."); + LOGD("Stopping authenticator..."); m_Authenticator.Stop(); - LOG("Freeing MonsterConfig..."); + LOGD("Freeing MonsterConfig..."); delete m_MonsterConfig; m_MonsterConfig = NULL; - LOG("Stopping WebAdmin..."); + LOGD("Stopping WebAdmin..."); delete m_WebAdmin; m_WebAdmin = NULL; - LOG("Unloading recipes..."); + LOGD("Unloading recipes..."); delete m_FurnaceRecipe; m_FurnaceRecipe = NULL; delete m_CraftingRecipes; m_CraftingRecipes = NULL; - LOG("Forgetting groups..."); + LOGD("Forgetting groups..."); delete m_GroupManager; m_GroupManager = 0; - LOG("Unloading worlds..."); + LOGD("Unloading worlds..."); UnloadWorlds(); - LOG("Stopping plugin manager..."); + LOGD("Stopping plugin manager..."); delete m_PluginManager; m_PluginManager = NULL; cItemHandler::Deinit(); cBlockHandler::Deinit(); - LOG("Destroying server..."); + LOG("Cleaning up..."); //delete HeartBeat; HeartBeat = 0; delete m_Server; m_Server = 0; - LOG("Shutdown done."); + LOG("Shutdown successful!"); } delete m_Log; m_Log = 0; @@ -568,6 +577,110 @@ AString cRoot::GetProtocolVersionTextFromInt(int a_ProtocolVersion) +int cRoot::GetVirtualRAMUsage(void) +{ + #ifdef _WIN32 + PROCESS_MEMORY_COUNTERS_EX pmc; + if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS *)&pmc, sizeof(pmc))) + { + return (int)(pmc.PrivateUsage / 1024); + } + return -1; + #elif defined(__linux__) + // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process + std::ifstream StatFile("/proc/self/status"); + if (!StatFile.good()) + { + return -1; + } + while (StatFile.good()) + { + AString Line; + std::getline(StatFile, Line); + if (strncmp(Line.c_str(), "VmSize:", 7) == 0) + { + int res = atoi(Line.c_str() + 8); + return (res == 0) ? -1 : res; // If parsing failed, return -1 + } + } + return -1; + #elif defined (__APPLE__) + // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process + struct task_basic_info t_info; + mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; + + if (KERN_SUCCESS == task_info( + mach_task_self(), + TASK_BASIC_INFO, + (task_info_t)&t_info, + &t_info_count + )) + { + return (int)(t_info.virtual_size / 1024); + } + return -1; + #else + LOGINFO("%s: Unknown platform, cannot query memory usage", __FUNCTION__); + return -1; + #endif +} + + + + + +int cRoot::GetPhysicalRAMUsage(void) +{ + #ifdef _WIN32 + PROCESS_MEMORY_COUNTERS pmc; + if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) + { + return (int)(pmc.WorkingSetSize / 1024); + } + return -1; + #elif defined(__linux__) + // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process + std::ifstream StatFile("/proc/self/status"); + if (!StatFile.good()) + { + return -1; + } + while (StatFile.good()) + { + AString Line; + std::getline(StatFile, Line); + if (strncmp(Line.c_str(), "VmRSS:", 7) == 0) + { + int res = atoi(Line.c_str() + 8); + return (res == 0) ? -1 : res; // If parsing failed, return -1 + } + } + return -1; + #elif defined (__APPLE__) + // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process + struct task_basic_info t_info; + mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; + + if (KERN_SUCCESS == task_info( + mach_task_self(), + TASK_BASIC_INFO, + (task_info_t)&t_info, + &t_info_count + )) + { + return (int)(t_info.resident_size / 1024); + } + return -1; + #else + LOGINFO("%s: Unknown platform, cannot query memory usage", __FUNCTION__); + return -1; + #endif +} + + + + + void cRoot::LogChunkStats(cCommandOutputCallback & a_Output) { int SumNumValid = 0; diff --git a/source/Root.h b/source/Root.h index 194b1cbb5..2b15d3461 100644 --- a/source/Root.h +++ b/source/Root.h @@ -2,6 +2,7 @@ #pragma once #include "Authenticator.h" +#include "HTTPServer/HTTPServer.h" @@ -104,8 +105,18 @@ public: /// Finds a player from a partial or complete player name and calls the callback - case-insensitive bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + // tolua_begin + /// Returns the textual description of the protocol version: 49 -> "1.4.4". Provided specifically for Lua API - static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum); // tolua_export + static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum); + + /// Returns the amount of virtual RAM used, in KiB. Returns a negative number on error + static int GetVirtualRAMUsage(void); + + /// Returns the amount of virtual RAM used, in KiB. Returns a negative number on error + static int GetPhysicalRAMUsage(void); + + // tolua_end private: class cCommand @@ -141,6 +152,7 @@ private: cWebAdmin * m_WebAdmin; cPluginManager * m_PluginManager; cAuthenticator m_Authenticator; + cHTTPServer m_HTTPServer; cMCLogger * m_Log; diff --git a/source/Server.cpp b/source/Server.cpp index dd18f8d3d..879bfae5a 100644 --- a/source/Server.cpp +++ b/source/Server.cpp @@ -103,8 +103,8 @@ void cServer::cTickThread::Execute(void) // cServer: cServer::cServer(void) : - m_ListenThreadIPv4(*this, cSocket::IPv4, "Client"), - m_ListenThreadIPv6(*this, cSocket::IPv6, "Client"), + m_ListenThreadIPv4(*this, cSocket::IPv4, "Client IPv4"), + m_ListenThreadIPv6(*this, cSocket::IPv6, "Client IPv6"), m_bIsConnected(false), m_bRestarting(false), m_RCONServer(*this), @@ -206,7 +206,6 @@ bool cServer::InitServer(cIniFile & a_SettingsIni) return false; } - LOG("Starting up server."); LOGINFO("Compatible clients: %s", MCS_CLIENT_VERSIONS); LOGINFO("Compatible protocol versions %s", MCS_PROTOCOL_VERSIONS); @@ -292,7 +291,7 @@ void cServer::PrepareKeys(void) // TODO: Save and load key for persistence across sessions // But generating the key takes only a moment, do we even need that? - LOG("Generating protocol encryption keypair..."); + LOGD("Generating protocol encryption keypair..."); time_t CurTime = time(NULL); CryptoPP::RandomPool rng; diff --git a/source/Simulator/FireSimulator.cpp b/source/Simulator/FireSimulator.cpp index 587f45306..ac3fb9695 100644 --- a/source/Simulator/FireSimulator.cpp +++ b/source/Simulator/FireSimulator.cpp @@ -221,6 +221,7 @@ void cFireSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) { + bool IsBlockBelowSolid = false; if (a_RelY > 0) { BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ); @@ -233,10 +234,7 @@ int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, in { return m_BurnStepTimeFuel; } - } - if ((a_RelY < cChunkDef::Height - 1) && IsFuel(a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ))) - { - return m_BurnStepTimeFuel; + IsBlockBelowSolid = g_BlockIsSolid[BlockBelow]; } for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) @@ -251,6 +249,15 @@ int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, in } } } // for i - gCrossCoords[] + + if (!IsBlockBelowSolid && (a_RelY >= 0)) + { + // Checked through everything, nothing was flammable + // If block below isn't solid, we can't have fire, it would be a non-fueled fire + // SetBlock just to make sure fire doesn't spawn + a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0); + return 0; + } return m_BurnStepTimeNonfuel; } diff --git a/source/Simulator/SandSimulator.cpp b/source/Simulator/SandSimulator.cpp index f4f0cdc80..87fb83357 100644 --- a/source/Simulator/SandSimulator.cpp +++ b/source/Simulator/SandSimulator.cpp @@ -258,7 +258,7 @@ void cSandSimulator::FinishFalling( // Create a pickup instead: cItems Pickups; Pickups.Add((ENUM_ITEM_ID)a_FallingBlockType, 1, a_FallingBlockMeta); - a_World->SpawnItemPickups(Pickups, (double)a_BlockX + 0.5, (double)a_BlockY + 0.5, (double)a_BlockZ + 0.5, 0); + a_World->SpawnItemPickups(Pickups, (double)a_BlockX + 0.5, (double)a_BlockY + 0.5, (double)a_BlockZ + 0.5); } diff --git a/source/StringUtils.cpp b/source/StringUtils.cpp index cb91a4da7..d52b1323f 100644 --- a/source/StringUtils.cpp +++ b/source/StringUtils.cpp @@ -196,6 +196,23 @@ AString & StrToUpper(AString & s) +AString & StrToLower(AString & s) +{ + AString::iterator i = s.begin(); + AString::iterator end = s.end(); + + while (i != end) + { + *i = (char)tolower(*i); + ++i; + } + return s; +} + + + + + int NoCaseCompare(const AString & s1, const AString & s2) { #ifdef _MSC_VER @@ -658,3 +675,141 @@ AString StripColorCodes(const AString & a_Message) + +AString URLDecode(const AString & a_String) +{ + AString res; + size_t len = a_String.length(); + res.reserve(len); + for (size_t i = 0; i < len; i++) + { + char ch = a_String[i]; + if ((ch != '%') || (i > len - 3)) + { + res.push_back(ch); + continue; + } + // Decode the hex value: + char hi = a_String[i + 1], lo = a_String[i + 2]; + if ((hi >= '0') && (hi <= '9')) + { + hi = hi - '0'; + } + else if ((hi >= 'a') && (hi <= 'f')) + { + hi = hi - 'a' + 10; + } + else if ((hi >= 'A') && (hi <= 'F')) + { + hi = hi - 'F' + 10; + } + else + { + res.push_back(ch); + continue; + } + if ((lo >= '0') && (lo <= '9')) + { + lo = lo - '0'; + } + else if ((lo >= 'a') && (lo <= 'f')) + { + lo = lo - 'a' + 10; + } + else if ((lo >= 'A') && (lo <= 'F')) + { + lo = lo - 'A' + 10; + } + else + { + res.push_back(ch); + continue; + } + res.push_back((hi << 4) | lo); + i += 2; + } // for i - a_String[] + return res; +} + + + + + +AString ReplaceAllCharOccurrences(const AString & a_String, char a_From, char a_To) +{ + AString res(a_String); + std::replace(res.begin(), res.end(), a_From, a_To); + return res; +} + + + + + +/// Converts one Hex character in a Base64 encoding into the data value +static inline int UnBase64(char c) +{ + if (c >='A' && c <= 'Z') + { + return c - 'A'; + } + if (c >='a' && c <= 'z') + { + return c - 'a' + 26; + } + if (c >= '0' && c <= '9') + { + return c - '0' + 52; + } + if (c == '+') + { + return 62; + } + if (c == '/') + { + return 63; + } + if (c == '=') + { + return -1; + } + return -2; +} + + + + + +AString Base64Decode(const AString & a_Base64String) +{ + AString res; + size_t i, len = a_Base64String.size(); + int o, c; + res.resize((len * 4) / 3 + 5, 0); // Approximate the upper bound on the result length + for (o = 0, i = 0; i < len; i++) + { + c = UnBase64(a_Base64String[i]); + if (c >= 0) + { + switch (o & 7) + { + case 0: res[o >> 3] |= (c << 2); break; + case 6: res[o >> 3] |= (c >> 4); res[(o >> 3) + 1] |= (c << 4); break; + case 4: res[o >> 3] |= (c >> 2); res[(o >> 3) + 1] |= (c << 6); break; + case 2: res[o >> 3] |= c; break; + } + o += 6; + } + if (c == -1) + { + // Error while decoding, invalid input. Return as much as we've decoded: + res.resize(o >> 3); + return res; + } + } + res.resize(o >> 3); + return res;} + + + + diff --git a/source/StringUtils.h b/source/StringUtils.h index 211799e91..ec9ba96ce 100644 --- a/source/StringUtils.h +++ b/source/StringUtils.h @@ -45,6 +45,9 @@ extern AString TrimString(const AString & str); // tolua_export /// In-place string conversion to uppercase; returns the same string extern AString & StrToUpper(AString & s); +/// In-place string conversion to lowercase; returns the same string +extern AString & StrToLower(AString & s); + /// Case-insensitive string comparison; returns 0 if the strings are the same extern int NoCaseCompare(const AString & s1, const AString & s2); // tolua_export @@ -72,6 +75,15 @@ extern AString EscapeString(const AString & a_Message); // tolua_export /// Removes all control codes used by MC for colors and styles extern AString StripColorCodes(const AString & a_Message); // tolua_export +/// URL-Decodes the given string, replacing all "%HH" into the correct characters. Invalid % sequences are left intact +extern AString URLDecode(const AString & a_String); // Cannot export to Lua automatically - would generated an extra return value + +/// Replaces all occurrences of char a_From inside a_String with char a_To. +extern AString ReplaceAllCharOccurrences(const AString & a_String, char a_From, char a_To); // Needn't export to Lua, since Lua doesn't have chars anyway + +/// Decodes a Base64-encoded string into the raw data +extern AString Base64Decode(const AString & a_Base64String); + // If you have any other string helper functions, declare them here diff --git a/source/UI/SlotArea.cpp b/source/UI/SlotArea.cpp index 9213d4ff8..0a37e82b0 100644 --- a/source/UI/SlotArea.cpp +++ b/source/UI/SlotArea.cpp @@ -793,7 +793,7 @@ void cSlotAreaTemporary::TossItems(cPlayer & a_Player, int a_Begin, int a_End) double vX = 0, vY = 0, vZ = 0; EulerToVector(-a_Player.GetRotation(), a_Player.GetPitch(), vZ, vX, vY); vY = -vY * 2 + 1.f; - a_Player.GetWorld()->SpawnItemPickups(Drops, a_Player.GetPosX(), a_Player.GetPosY() + 1.6f, a_Player.GetPosZ(), vX * 2, vY * 2, vZ * 2); + a_Player.GetWorld()->SpawnItemPickups(Drops, a_Player.GetPosX(), a_Player.GetPosY() + 1.6f, a_Player.GetPosZ(), vX * 3, vY * 3, vZ * 3); } diff --git a/source/WebAdmin.cpp b/source/WebAdmin.cpp index 77a5865d3..316513f11 100644 --- a/source/WebAdmin.cpp +++ b/source/WebAdmin.cpp @@ -14,13 +14,8 @@ #include "Server.h" #include "Root.h" -#include "../iniFile/iniFile.h" - -#ifdef _WIN32 - #include <psapi.h> -#else - #include <sys/resource.h> -#endif +#include "HTTPServer/HTTPMessage.h" +#include "HTTPServer/HTTPConnection.h" @@ -47,37 +42,11 @@ public: -cWebAdmin * WebAdmin = NULL; - - - - - -cWebAdmin::cWebAdmin( int a_Port /* = 8080 */ ) : - m_Port(a_Port), - m_bConnected(false), - m_TemplateScript("<webadmin_template>") +cWebAdmin::cWebAdmin(void) : + m_IsInitialized(false), + m_TemplateScript("<webadmin_template>"), + m_IniFile("webadmin.ini") { - WebAdmin = this; - m_Event = new cEvent(); - Init( m_Port ); -} - - - - - -cWebAdmin::~cWebAdmin() -{ - - WebAdmin = 0; - m_WebServer->Stop(); - - delete m_WebServer; - delete m_IniFile; - - m_Event->Wait(); - delete m_Event; } @@ -103,180 +72,42 @@ void cWebAdmin::RemovePlugin( cWebPlugin * a_Plugin ) -void cWebAdmin::Request_Handler(webserver::http_request* r) +bool cWebAdmin::Init(void) { - if( WebAdmin == 0 ) return; - LOG("Path: %s", r->path_.c_str() ); - - if (r->path_ == "/") - { - r->answer_ += "<h1>MCServer WebAdmin</h1>"; - r->answer_ += "<center>"; - r->answer_ += "<form method='get' action='webadmin/'>"; - r->answer_ += "<input type='submit' value='Log in'>"; - r->answer_ += "</form>"; - r->answer_ += "</center>"; - return; - } - - if (r->path_.empty() || r->path_[0] != '/') + if (!m_IniFile.ReadFile()) { - r->answer_ += "<h1>Bad request</h1>"; - r->answer_ += "<p>"; - r->answer_ = r->path_; // TODO: Shouldn't we sanitize this? Possible security issue. - r->answer_ += "</p>"; - return; + return false; } - AStringVector Split = StringSplit(r->path_.substr(1), "/"); - - if (Split.empty() || (Split[0] != "webadmin" && Split[0] != "~webadmin")) + if (!m_IniFile.GetValueSetB("WebAdmin", "Enabled", true)) { - r->answer_ += "<h1>Bad request</h1>"; - return; + // WebAdmin is disabled, bail out faking a success + return true; } - if (!r->authentication_given_) - { - r->answer_ += "no auth"; - r->auth_realm_ = "MCServer WebAdmin"; - } - - bool bDontShowTemplate = false; - if (Split[0] == "~webadmin") - { - bDontShowTemplate = true; - } + AString PortsIPv4 = m_IniFile.GetValueSet("WebAdmin", "Port", "8080"); + AString PortsIPv6 = m_IniFile.GetValueSet("WebAdmin", "PortsIPv6", ""); - AString UserPassword = WebAdmin->m_IniFile->GetValue( "User:"+r->username_, "Password", ""); - if ((UserPassword != "") && (r->password_ == UserPassword)) + if (!m_HTTPServer.Initialize(PortsIPv4, PortsIPv6)) { - AString Template; - - HTTPTemplateRequest TemplateRequest; - TemplateRequest.Request.Username = r->username_; - TemplateRequest.Request.Method = r->method_; - TemplateRequest.Request.Params = r->params_; - TemplateRequest.Request.PostParams = r->params_post_; - TemplateRequest.Request.Path = r->path_.substr(1); - - for( unsigned int i = 0; i < r->multipart_formdata_.size(); ++i ) - { - webserver::formdata& fd = r->multipart_formdata_[i]; - - HTTPFormData HTTPfd;//( fd.value_ ); - HTTPfd.Value = fd.value_; - HTTPfd.Type = fd.content_type_; - HTTPfd.Name = fd.name_; - LOGINFO("Form data name: %s", fd.name_.c_str() ); - TemplateRequest.Request.FormData[ fd.name_ ] = HTTPfd; - } - - // Try to get the template from the Lua template script - bool bLuaTemplateSuccessful = false; - if (!bDontShowTemplate) - { - bLuaTemplateSuccessful = WebAdmin->m_TemplateScript.Call("ShowPage", WebAdmin, &TemplateRequest, cLuaState::Return, Template); - } - - if (!bLuaTemplateSuccessful) - { - AString BaseURL = WebAdmin->GetBaseURL(Split); - AString Menu; - Template = bDontShowTemplate ? "{CONTENT}" : WebAdmin->GetTemplate(); - AString FoundPlugin; - - for (PluginList::iterator itr = WebAdmin->m_Plugins.begin(); itr != WebAdmin->m_Plugins.end(); ++itr) - { - cWebPlugin* WebPlugin = *itr; - std::list< std::pair<AString, AString> > NameList = WebPlugin->GetTabNames(); - for( std::list< std::pair<AString, AString> >::iterator Names = NameList.begin(); Names != NameList.end(); ++Names ) - { - Menu += "<li><a href='" + BaseURL + WebPlugin->GetWebTitle().c_str() + "/" + (*Names).second + "'>" + (*Names).first + "</a></li>"; - } - } - - sWebAdminPage Page = WebAdmin->GetPage(TemplateRequest.Request); - AString Content = Page.Content; - FoundPlugin = Page.PluginName; - if (!Page.TabName.empty()) - FoundPlugin += " - " + Page.TabName; - - if( FoundPlugin.empty() ) // Default page - { - Content.clear(); - FoundPlugin = "Current Game"; - Content += "<h4>Server Name:</h4>"; - Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID() ) + "</p>"; - - Content += "<h4>Plugins:</h4><ul>"; - cPluginManager* PM = cRoot::Get()->GetPluginManager(); - if( PM ) - { - const cPluginManager::PluginMap & List = PM->GetAllPlugins(); - for( cPluginManager::PluginMap::const_iterator itr = List.begin(); itr != List.end(); ++itr ) - { - if( itr->second == NULL ) continue; - AString VersionNum; - AppendPrintf(Content, "<li>%s V.%i</li>", itr->second->GetName().c_str(), itr->second->GetVersion()); - } - } - Content += "</ul>"; - Content += "<h4>Players:</h4><ul>"; - - cPlayerAccum PlayerAccum; - cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players - if( World != NULL ) - { - World->ForEachPlayer(PlayerAccum); - Content.append(PlayerAccum.m_Contents); - } - Content += "</ul><br>"; - } - - - - if (!bDontShowTemplate && (Split.size() > 1)) - { - Content += "\n<p><a href='" + BaseURL + "'>Go back</a></p>"; - } - - AString MemUsage = GetMemoryUsage(); - ReplaceString(Template, "{MEM}", MemUsage); - ReplaceString(Template, "{USERNAME}", r->username_); - ReplaceString(Template, "{MENU}", Menu); - ReplaceString(Template, "{PLUGIN_NAME}", FoundPlugin); - ReplaceString(Template, "{CONTENT}", Content); - ReplaceString(Template, "{TITLE}", "MCServer"); - - AString NumChunks; - Printf(NumChunks, "%d", cRoot::Get()->GetTotalChunkCount()); - ReplaceString(Template, "{NUMCHUNKS}", NumChunks); - } - - r->answer_ = Template; - } - else - { - r->answer_ += "Wrong username/password"; - r->auth_realm_ = "MCServer WebAdmin"; + return false; } + m_IsInitialized = true; + return true; } -bool cWebAdmin::Init(int a_Port) +bool cWebAdmin::Start(void) { - m_Port = a_Port; - - m_IniFile = new cIniFile("webadmin.ini"); - if (m_IniFile->ReadFile()) + if (!m_IsInitialized) { - m_Port = m_IniFile->GetValueI("WebAdmin", "Port", 8080); + // Not initialized + return false; } - + // Initialize the WebAdmin template script and load the file m_TemplateScript.Create(); if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua")) @@ -285,75 +116,196 @@ bool cWebAdmin::Init(int a_Port) m_TemplateScript.Close(); } - - LOG("Starting WebAdmin on port %i", m_Port); - -#ifdef _WIN32 - HANDLE hThread = CreateThread( - NULL, // default security - 0, // default stack size - ListenThread, // name of the thread function - this, // thread parameters - 0, // default startup flags - NULL); - CloseHandle( hThread ); // Just close the handle immediately -#else - pthread_t LstnThread; - pthread_create( &LstnThread, 0, ListenThread, this); -#endif - - return true; + return m_HTTPServer.Start(*this); } -#ifdef _WIN32 -DWORD WINAPI cWebAdmin::ListenThread(LPVOID lpParam) -#else -void *cWebAdmin::ListenThread( void *lpParam ) -#endif +AString cWebAdmin::GetTemplate() { - cWebAdmin* self = (cWebAdmin*)lpParam; + AString retVal = ""; + + char SourceFile[] = "webadmin/template.html"; - self->m_WebServer = new webserver(self->m_Port, Request_Handler ); - if (!self->m_WebServer->Begin()) + cFile f; + if (!f.Open(SourceFile, cFile::fmRead)) { - LOGWARN("WebServer failed to start! WebAdmin is disabled"); + return ""; } - self->m_Event->Set(); - return 0; + // copy the file into the buffer: + f.ReadRestOfFile(retVal); + + return retVal; } -AString cWebAdmin::GetTemplate() +void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) { - AString retVal = ""; + if (!a_Request.HasAuth()) + { + a_Connection.SendNeedAuth("MCServer WebAdmin"); + return; + } - char SourceFile[] = "webadmin/template.html"; + // Check auth: + AString UserPassword = m_IniFile.GetValue("User:" + a_Request.GetAuthUsername(), "Password", ""); + if ((UserPassword == "") || (a_Request.GetAuthPassword() != UserPassword)) + { + a_Connection.SendNeedAuth("MCServer WebAdmin - bad username or password"); + return; + } + + // Check if the contents should be wrapped in the template: + AString URL = a_Request.GetBareURL(); + ASSERT(URL.length() > 0); + bool ShouldWrapInTemplate = ((URL.length() > 1) && (URL[1] != '~')); + + // Retrieve the request data: + cWebadminRequestData * Data = (cWebadminRequestData *)(a_Request.GetUserData()); + if (Data == NULL) + { + a_Connection.SendStatusAndReason(500, "Bad UserData"); + return; + } + + // Wrap it all up for the Lua call: + AString Template; + HTTPTemplateRequest TemplateRequest; + TemplateRequest.Request.Username = a_Request.GetAuthUsername(); + TemplateRequest.Request.Method = a_Request.GetMethod(); + TemplateRequest.Request.Path = URL.substr(1); + + if (Data->m_Form.Finish()) + { + for (cHTTPFormParser::const_iterator itr = Data->m_Form.begin(), end = Data->m_Form.end(); itr != end; ++itr) + { + HTTPFormData HTTPfd; + HTTPfd.Value = itr->second; + HTTPfd.Type = ""; + HTTPfd.Name = itr->first; + TemplateRequest.Request.FormData[itr->first] = HTTPfd; + TemplateRequest.Request.PostParams[itr->first] = itr->second; + } // for itr - Data->m_Form[] + + // Parse the URL into individual params: + size_t idxQM = a_Request.GetURL().find('?'); + if (idxQM != AString::npos) + { + cHTTPFormParser URLParams(cHTTPFormParser::fpkURL, a_Request.GetURL().c_str() + idxQM + 1, a_Request.GetURL().length() - idxQM - 1, *Data); + URLParams.Finish(); + for (cHTTPFormParser::const_iterator itr = URLParams.begin(), end = URLParams.end(); itr != end; ++itr) + { + TemplateRequest.Request.Params[itr->first] = itr->second; + } // for itr - URLParams[] + } + } + + // Try to get the template from the Lua template script + if (ShouldWrapInTemplate) + { + if (m_TemplateScript.Call("ShowPage", this, &TemplateRequest, cLuaState::Return, Template)) + { + cHTTPResponse Resp; + Resp.SetContentType("text/html"); + a_Connection.Send(Resp); + a_Connection.Send(Template.c_str(), Template.length()); + return; + } + a_Connection.SendStatusAndReason(500, "m_TemplateScript failed"); + return; + } + + AString BaseURL = GetBaseURL(URL); + AString Menu; + Template = "{CONTENT}"; + AString FoundPlugin; - cFile f; - if (!f.Open(SourceFile, cFile::fmRead)) + for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) { - return ""; + cWebPlugin * WebPlugin = *itr; + std::list< std::pair<AString, AString> > NameList = WebPlugin->GetTabNames(); + for (std::list< std::pair<AString, AString> >::iterator Names = NameList.begin(); Names != NameList.end(); ++Names) + { + Menu += "<li><a href='" + BaseURL + WebPlugin->GetWebTitle().c_str() + "/" + (*Names).second + "'>" + (*Names).first + "</a></li>"; + } } - // copy the file into the buffer: - f.ReadRestOfFile(retVal); + sWebAdminPage Page = GetPage(TemplateRequest.Request); + AString Content = Page.Content; + FoundPlugin = Page.PluginName; + if (!Page.TabName.empty()) + { + FoundPlugin += " - " + Page.TabName; + } - return retVal; + if (FoundPlugin.empty()) // Default page + { + Content = GetDefaultPage(); + } + + if (ShouldWrapInTemplate && (URL.size() > 1)) + { + Content += "\n<p><a href='" + BaseURL + "'>Go back</a></p>"; + } + + int MemUsageKiB = GetMemoryUsage(); + if (MemUsageKiB > 0) + { + ReplaceString(Template, "{MEM}", Printf("%.02f", (double)MemUsageKiB / 1024)); + ReplaceString(Template, "{MEMKIB}", Printf("%d", MemUsageKiB)); + } + else + { + ReplaceString(Template, "{MEM}", "unknown"); + ReplaceString(Template, "{MEMKIB}", "unknown"); + } + ReplaceString(Template, "{USERNAME}", a_Request.GetAuthUsername()); + ReplaceString(Template, "{MENU}", Menu); + ReplaceString(Template, "{PLUGIN_NAME}", FoundPlugin); + ReplaceString(Template, "{CONTENT}", Content); + ReplaceString(Template, "{TITLE}", "MCServer"); + + AString NumChunks; + Printf(NumChunks, "%d", cRoot::Get()->GetTotalChunkCount()); + ReplaceString(Template, "{NUMCHUNKS}", NumChunks); + + cHTTPResponse Resp; + Resp.SetContentType("text/html"); + a_Connection.Send(Resp); + a_Connection.Send(Template.c_str(), Template.length()); +} + + + + + +void cWebAdmin::HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +{ + static const char LoginForm[] = \ + "<h1>MCServer WebAdmin</h1>" \ + "<center>" \ + "<form method='get' action='webadmin/'>" \ + "<input type='submit' value='Log in'>" \ + "</form>" \ + "</center>"; + cHTTPResponse Resp; + Resp.SetContentType("text/html"); + a_Connection.Send(Resp); + a_Connection.Send(LoginForm, sizeof(LoginForm) - 1); + a_Connection.FinishResponse(); } -sWebAdminPage cWebAdmin::GetPage(const HTTPRequest& a_Request) +sWebAdminPage cWebAdmin::GetPage(const HTTPRequest & a_Request) { sWebAdminPage Page; AStringVector Split = StringSplit(a_Request.Path, "/"); @@ -362,7 +314,7 @@ sWebAdminPage cWebAdmin::GetPage(const HTTPRequest& a_Request) AString FoundPlugin; if (Split.size() > 1) { - for (PluginList::iterator itr = WebAdmin->m_Plugins.begin(); itr != WebAdmin->m_Plugins.end(); ++itr) + for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) { if ((*itr)->GetWebTitle() == Split[1]) { @@ -385,6 +337,41 @@ sWebAdminPage cWebAdmin::GetPage(const HTTPRequest& a_Request) +AString cWebAdmin::GetDefaultPage(void) +{ + AString Content; + Content += "<h4>Server Name:</h4>"; + Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID() ) + "</p>"; + + Content += "<h4>Plugins:</h4><ul>"; + cPluginManager * PM = cPluginManager::Get(); + const cPluginManager::PluginMap & List = PM->GetAllPlugins(); + for (cPluginManager::PluginMap::const_iterator itr = List.begin(); itr != List.end(); ++itr) + { + if (itr->second == NULL) + { + continue; + } + AString VersionNum; + AppendPrintf(Content, "<li>%s V.%i</li>", itr->second->GetName().c_str(), itr->second->GetVersion()); + } + Content += "</ul>"; + Content += "<h4>Players:</h4><ul>"; + + cPlayerAccum PlayerAccum; + cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players + if( World != NULL ) + { + World->ForEachPlayer(PlayerAccum); + Content.append(PlayerAccum.m_Contents); + } + Content += "</ul><br>"; + return Content; +} + + + + AString cWebAdmin::GetBaseURL( const AString& a_URL ) { return GetBaseURL(StringSplit(a_URL, "/")); @@ -412,26 +399,90 @@ AString cWebAdmin::GetBaseURL( const AStringVector& a_URLSplit ) -AString cWebAdmin::GetMemoryUsage(void) +int cWebAdmin::GetMemoryUsage(void) +{ + LOGWARNING("%s: This function is obsolete, use cRoot::GetPhysicalRAMUsage() or cRoot::GetVirtualRAMUsage() instead", __FUNCTION__); + return cRoot::GetPhysicalRAMUsage(); +} + + + + + +void cWebAdmin::OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) { - AString MemUsage; -#ifndef _WIN32 - rusage resource_usage; - if (getrusage(RUSAGE_SELF, &resource_usage) != 0) + const AString & URL = a_Request.GetURL(); + if ( + (strncmp(URL.c_str(), "/webadmin", 9) == 0) || + (strncmp(URL.c_str(), "/~webadmin", 10) == 0) + ) { - MemUsage = "Error :("; + a_Request.SetUserData(new cWebadminRequestData(a_Request)); + return; } - else + if (URL == "/") + { + // The root needs no body handler and is fully handled in the OnRequestFinished() call + return; + } + // TODO: Handle other requests +} + + + + + +void cWebAdmin::OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) +{ + cRequestData * Data = (cRequestData *)(a_Request.GetUserData()); + if (Data == NULL) + { + return; + } + Data->OnBody(a_Data, a_Size); +} + + + + + +void cWebAdmin::OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +{ + const AString & URL = a_Request.GetURL(); + if ( + (strncmp(URL.c_str(), "/webadmin", 9) == 0) || + (strncmp(URL.c_str(), "/~webadmin", 10) == 0) + ) + { + HandleWebadminRequest(a_Connection, a_Request); + } + else if (URL == "/") { - Printf(MemUsage, "%0.2f", ((double)resource_usage.ru_maxrss / 1024 / 1024) ); + // The root needs no body handler and is fully handled in the OnRequestFinished() call + HandleRootRequest(a_Connection, a_Request); } -#else - HANDLE hProcess = GetCurrentProcess(); - PROCESS_MEMORY_COUNTERS pmc; - if( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc) ) ) + else { - Printf(MemUsage, "%0.2f", (pmc.WorkingSetSize / 1024.f / 1024.f) ); + // TODO: Handle other requests } -#endif - return MemUsage; + + // Delete any request data assigned to the request: + cRequestData * Data = (cRequestData *)(a_Request.GetUserData()); + delete Data; } + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWebAdmin::cWebadminRequestData + +void cWebAdmin::cWebadminRequestData::OnBody(const char * a_Data, int a_Size) +{ + m_Form.Parse(a_Data, a_Size); +} + + + + diff --git a/source/WebAdmin.h b/source/WebAdmin.h index a62b532f1..72c77ddfb 100644 --- a/source/WebAdmin.h +++ b/source/WebAdmin.h @@ -1,8 +1,26 @@ + +// WebAdmin.h + +// Declares the cWebAdmin class representing the admin interface over http protocol, and related services (API) + #pragma once -#include "../WebServer/WebServer.h" #include "OSSupport/Socket.h" #include "LuaState.h" +#include "../iniFile/iniFile.h" +#include "HTTPServer/HTTPServer.h" +#include "HTTPServer/HTTPFormParser.h" + + + + + +// Disable MSVC warnings: +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4355) // 'this' : used in base member initializer list +#endif + @@ -10,7 +28,6 @@ // fwd: class cStringMap; class cEvent; -class cIniFile; class cWebPlugin; @@ -39,8 +56,14 @@ struct HTTPRequest AString Path; AString Username; // tolua_end + + /// Parameters given in the URL, after the questionmark StringStringMap Params; // >> EXPORTED IN MANUALBINDINGS << + + /// Parameters posted as a part of a form - either in the URL (GET method) or in the body (POST method) StringStringMap PostParams; // >> EXPORTED IN MANUALBINDINGS << + + /// Same as PostParams FormDataMap FormData; // >> EXPORTED IN MANUALBINDINGS << } ; // tolua_export @@ -73,7 +96,8 @@ struct sWebAdminPage // tolua_begin -class cWebAdmin +class cWebAdmin : + public cHTTPServer::cCallbacks { public: // tolua_end @@ -81,57 +105,108 @@ public: typedef std::list< cWebPlugin* > PluginList; - cWebAdmin( int a_Port = 8080 ); - ~cWebAdmin(); + cWebAdmin(void); - bool Init( int a_Port ); + /// Initializes the object. Returns true if successfully initialized and ready to start + bool Init(void); + + /// Starts the HTTP server taking care of the admin. Returns true if successful + bool Start(void); - void AddPlugin( cWebPlugin* a_Plugin ); - void RemovePlugin( cWebPlugin* a_Plugin ); + void AddPlugin( cWebPlugin* a_Plugin ); + void RemovePlugin( cWebPlugin* a_Plugin ); // TODO: Convert this to the auto-locking callback mechanism used for looping players in worlds and such PluginList GetPlugins() const { return m_Plugins; } // >> EXPORTED IN MANUALBINDINGS << - static void Request_Handler(webserver::http_request* r); - // tolua_begin - static AString GetMemoryUsage(void); - - int GetPort() { return m_Port; } + + /// Returns the amount of currently used memory, in KiB, or -1 if it cannot be queried + static int GetMemoryUsage(void); sWebAdminPage GetPage(const HTTPRequest& a_Request); + + /// Returns the contents of the default page - the list of plugins and players + AString GetDefaultPage(void); + AString GetBaseURL(const AString& a_URL); // tolua_end AString GetBaseURL(const AStringVector& a_URLSplit); + +protected: + /// Common base class for request body data handlers + class cRequestData + { + public: + virtual ~cRequestData() {} // Force a virtual destructor in all descendants + + /// Called when a new chunk of body data is received + virtual void OnBody(const char * a_Data, int a_Size) = 0; + } ; + + /// The body handler for requests in the "/webadmin" and "/~webadmin" paths + class cWebadminRequestData : + public cRequestData, + public cHTTPFormParser::cCallbacks + { + public: + cHTTPFormParser m_Form; + + + cWebadminRequestData(cHTTPRequest & a_Request) : + m_Form(a_Request, *this) + { + } + + // cRequestData overrides: + virtual void OnBody(const char * a_Data, int a_Size) override; + + // cHTTPFormParser::cCallbacks overrides. Files are ignored: + virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) override {} + virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) override {} + virtual void OnFileEnd(cHTTPFormParser & a_Parser) override {} + } ; + + + /// Set to true if Init() succeeds and the webadmin isn't to be disabled + bool m_IsInitialized; + /// The webadmin.ini file, used for the settings and allowed logins + cIniFile m_IniFile; -private: - int m_Port; + PluginList m_Plugins; - bool m_bConnected; - cSocket m_ListenSocket; + /// The Lua template script to provide templates: + cLuaState m_TemplateScript; + + /// The HTTP server which provides the underlying HTTP parsing, serialization and events + cHTTPServer m_HTTPServer; - cIniFile * m_IniFile; - PluginList m_Plugins; - cEvent * m_Event; + AString GetTemplate(void); + + /// Handles requests coming to the "/webadmin" or "/~webadmin" URLs + void HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); + + /// Handles requests for the root page + void HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); + + // cHTTPServer::cCallbacks overrides: + virtual void OnRequestBegun (cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override; + virtual void OnRequestBody (cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) override; + virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override; +} ; // tolua_export - webserver * m_WebServer; - /// The Lua template script to provide templates: - cLuaState m_TemplateScript; - #ifdef _WIN32 - static DWORD WINAPI ListenThread(LPVOID lpParam); - #else - static void * ListenThread(void * lpParam); - #endif - AString GetTemplate(); -} ; // tolua_export +// Revert MSVC warnings back to orignal state: +#if defined(_MSC_VER) + #pragma warning(pop) +#endif diff --git a/source/World.cpp b/source/World.cpp index e5011e65d..7a9bf46af 100644 --- a/source/World.cpp +++ b/source/World.cpp @@ -33,8 +33,6 @@ #include "MobSpawner.h" #include "MobTypesManager.h" - -#include "OSSupport/MakeDir.h" #include "MersenneTwister.h" #include "Generating/Trees.h" #include "PluginManager.h" @@ -236,7 +234,7 @@ cWorld::cWorld(const AString & a_WorldName) : { LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str()); - cMakeDir::MakeDir(m_WorldName.c_str()); + cFile::CreateFolder(FILE_IO_PREFIX + m_WorldName); } @@ -510,7 +508,7 @@ void cWorld::Start(void) m_LastSave = 0; m_LastUnload = 0; - // preallocate some memory for ticking blocks so we don�t need to allocate that often + // preallocate some memory for ticking blocks so we don't need to allocate that often m_BlockTickQueue.reserve(1000); m_BlockTickQueueCopy.reserve(1000); @@ -604,7 +602,7 @@ void cWorld::Tick(float a_Dt) m_ChunkMap->Tick(a_Dt); TickClients(a_Dt); - TickQueuedBlocks(a_Dt); + TickQueuedBlocks(); TickQueuedTasks(); GetSimulatorManager()->Simulate(a_Dt); @@ -788,7 +786,7 @@ void cWorld::TickQueuedTasks(void) } // Execute and delete each task: - for (cTasks::iterator itr = m_Tasks.begin(), end = m_Tasks.end(); itr != end; ++itr) + for (cTasks::iterator itr = Tasks.begin(), end = Tasks.end(); itr != end; ++itr) { (*itr)->Run(*this); delete *itr; @@ -1038,7 +1036,7 @@ void cWorld::GrowTree(int a_X, int a_Y, int a_Z) -void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, char a_SaplingMeta) +void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, NIBBLETYPE a_SaplingMeta) { cNoise Noise(m_Generator.GetSeed()); sSetBlockVector Logs, Other; @@ -1290,7 +1288,7 @@ void cWorld::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlock -void cWorld::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockType) +void cWorld::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) { MTRand Rand; m_ChunkMap->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, Rand); @@ -1459,25 +1457,11 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr) { float SpeedX = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500)); - float SpeedY = (float)(a_FlyAwaySpeed * r1.randInt(1000)); + float SpeedY = 1; float SpeedZ = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500)); - // Add random offset to the spawn position: - int MicroX = (int)(a_BlockX * 32) + (r1.randInt(16) + r1.randInt(16) - 16); - int MicroY = (int)(a_BlockY * 32) + (r1.randInt(16) + r1.randInt(16) - 16); - int MicroZ = (int)(a_BlockZ * 32) + (r1.randInt(16) + r1.randInt(16) - 16); - - // TODO 2013_05_12 _X: Because spawning pickups with nonzero speed causes them to bug (FS #338), - // I decided to temporarily reset the speed to zero to fix it, until we have proper pickup physics - SpeedX = SpeedY = SpeedZ = 0; - - // TODO 2013_05_12 _X: It seems that pickups bug out even with zero speed, trying mid-block position: - MicroX = (int)(floor(a_BlockX) * 32) + 16; - MicroY = (int)(floor(a_BlockY) * 32) + 16; - MicroZ = (int)(floor(a_BlockZ) * 32) + 16; - cPickup * Pickup = new cPickup( - MicroX, MicroY, MicroZ, + a_BlockX, a_BlockY, a_BlockZ, *itr, SpeedX, SpeedY, SpeedZ ); Pickup->Initialize(this); @@ -1490,25 +1474,10 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ) { - // TODO 2013_05_12 _X: Because spawning pickups with nonzero speed causes them to bug (FS #338), - // I decided to temporarily reset the speed to zero to fix it, until we have proper pickup physics - a_SpeedX = a_SpeedY = a_SpeedZ = 0; - - MTRand r1; for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr) { - // Add random offset to the spawn position: - int MicroX = (int)(a_BlockX * 32) + (r1.randInt(16) + r1.randInt(16) - 16); - int MicroY = (int)(a_BlockY * 32) + (r1.randInt(16) + r1.randInt(16) - 16); - int MicroZ = (int)(a_BlockZ * 32) + (r1.randInt(16) + r1.randInt(16) - 16); - - // TODO 2013_05_12 _X: It seems that pickups bug out even with zero speed, trying mid-block position: - MicroX = (int)(floor(a_BlockX) * 32) + 16; - MicroY = (int)(floor(a_BlockY) * 32) + 16; - MicroZ = (int)(floor(a_BlockZ) * 32) + 16; - cPickup * Pickup = new cPickup( - MicroX, MicroY, MicroZ, + a_BlockX, a_BlockY, a_BlockZ, *itr, (float)a_SpeedX, (float)a_SpeedY, (float)a_SpeedZ ); Pickup->Initialize(this); @@ -2477,7 +2446,7 @@ void cWorld::GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLigh -void cWorld::TickQueuedBlocks(float a_Dt) +void cWorld::TickQueuedBlocks(void) { if (m_BlockTickQueue.empty()) { @@ -2489,15 +2458,16 @@ void cWorld::TickQueuedBlocks(float a_Dt) for (std::vector<BlockTickQueueItem *>::iterator itr = m_BlockTickQueueCopy.begin(); itr != m_BlockTickQueueCopy.end(); itr++) { BlockTickQueueItem *Block = (*itr); - Block->ToWait -= a_Dt; - if (Block->ToWait <= 0) + Block->TicksToWait -= 1; + if (Block->TicksToWait <= 0) { + // TODO: Handle the case when the chunk is already unloaded BlockHandler(GetBlock(Block->X, Block->Y, Block->Z))->OnUpdate(this, Block->X, Block->Y, Block->Z); - delete Block; //We don't have to remove it from the vector, this will happen automatically on the next tick + delete Block; // We don't have to remove it from the vector, this will happen automatically on the next tick } else { - m_BlockTickQueue.push_back(Block); //Keep the block in the queue + m_BlockTickQueue.push_back(Block); // Keep the block in the queue } } // for itr - m_BlockTickQueueCopy[] } @@ -2506,13 +2476,13 @@ void cWorld::TickQueuedBlocks(float a_Dt) -void cWorld::QueueBlockForTick(int a_BlockX, int a_BlockY, int a_BlockZ, float a_TimeToWait) +void cWorld::QueueBlockForTick(int a_BlockX, int a_BlockY, int a_BlockZ, int a_TicksToWait) { BlockTickQueueItem * Block = new BlockTickQueueItem; Block->X = a_BlockX; Block->Y = a_BlockY; Block->Z = a_BlockZ; - Block->ToWait = a_TimeToWait; + Block->TicksToWait = a_TicksToWait; m_BlockTickQueue.push_back(Block); } @@ -2538,6 +2508,10 @@ bool cWorld::IsBlockDirectlyWatered(int a_BlockX, int a_BlockY, int a_BlockZ) int cWorld::SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eType a_MonsterType) { cMonster * Monster = NULL; + + int SlSize = GetTickRandomNumber(2) + 1; // 1 .. 3 - Slime + int ShColor = GetTickRandomNumber(15); // 0 .. 15 - Sheep + bool SkType = GetDimension() == biNether; // Skeleton cMobTypesManager::NewMonsterFromType(a_MonsterType); Monster->SetPosition(a_PosX, a_PosY, a_PosZ); diff --git a/source/World.h b/source/World.h index 359fbe68e..a91007b17 100644 --- a/source/World.h +++ b/source/World.h @@ -91,20 +91,13 @@ public: } ;
- // tolua_begin
-
- static const char * GetClassStatic(void)
+ static const char * GetClassStatic(void) // Needed for ManualBindings's ForEach templates
{
return "cWorld";
}
- /// Return time in seconds
- inline static float GetTime(void)
- {
- LOGWARNING("cWorld:GetTime() is obsolete, use GetWorldAge() or GetTimeOfDay() for a specific world instead.");
- return 0;
- }
-
+ // tolua_begin
+
int GetTicksUntilWeatherChange(void) const { return m_WeatherInterval; }
Int64 GetWorldAge(void) const { return m_WorldAge; }
Int64 GetTimeOfDay(void) const { return m_TimeOfDay; }
@@ -121,12 +114,6 @@ public: BroadcastTimeUpdate();
}
- void SetWorldTime(Int64 a_TimeOfDay)
- {
- LOGWARNING("cWorld:SetWorldTime() is obsolete, use SetTimeOfDay() instead");
- SetTimeOfDay(a_TimeOfDay);
- }
-
/// Returns the current game mode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable
eGameMode GetGameMode(void) const { return m_GameMode; }
@@ -335,10 +322,15 @@ public: void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_MetaData);
NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ);
NIBBLETYPE GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ);
- bool GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
- bool GetBlockInfo (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight);
+
+ // tolua_end
+
+ bool GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); // TODO: Exported in ManualBindings.cpp
+ bool GetBlockInfo (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); // TODO: Exported in ManualBindings.cpp
// TODO: NIBBLETYPE GetBlockActualLight(int a_BlockX, int a_BlockY, int a_BlockZ);
+ // tolua_begin
+
// Vector3i variants:
void FastSetBlock(const Vector3i & a_Pos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) { FastSetBlock( a_Pos.x, a_Pos.y, a_Pos.z, a_BlockType, a_BlockMeta ); }
BLOCKTYPE GetBlock (const Vector3i & a_Pos ) { return GetBlock( a_Pos.x, a_Pos.y, a_Pos.z ); }
@@ -439,21 +431,26 @@ public: bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Exported in ManualBindings.cpp
/// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found
- bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // tolua_export
+ bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Exported in ManualBindings.cpp
/// a_Player is using block entity at [x, y, z], handle that:
- void UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) {m_ChunkMap->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); }
+ void UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) {m_ChunkMap->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); } // tolua_export
/// Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback
bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback);
- void GrowTree (int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export
- void GrowTreeFromSapling(int a_BlockX, int a_BlockY, int a_BlockZ, char a_SaplingMeta); // tolua_export
- void GrowTreeByBiome (int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export
-
void GrowTreeImage(const sSetBlockVector & a_Blocks);
// tolua_begin
+
+ /// Grows a tree at the specified coords, either from a sapling there, or based on the biome
+ void GrowTree (int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Grows a tree at the specified coords, based on the sapling meta provided
+ void GrowTreeFromSapling(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_SaplingMeta);
+
+ /// Grows a tree at the specified coords, based on the biome in the place
+ void GrowTreeByBiome (int a_BlockX, int a_BlockY, int a_BlockZ);
/// Grows the plant at the specified block to its ripe stage (bonemeal used); returns false if the block is not growable. If a_IsBonemeal is true, block is not grown if not allowed in world.ini
bool GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal = false);
@@ -462,7 +459,7 @@ public: void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
/// Grows a melon or a pumpkin next to the block specified (assumed to be the stem)
- void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockType);
+ void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType);
/// Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config
void GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
@@ -506,13 +503,13 @@ public: }
/// Saves all chunks immediately. Dangerous interface, may deadlock, use QueueSaveAllChunks() instead
- void SaveAllChunks(void); // tolua_export
+ void SaveAllChunks(void);
/// Queues a task to save all chunks onto the tick thread. The prefferred way of saving chunks from external sources
void QueueSaveAllChunks(void); // tolua_export
/// Queues a task onto the tick thread. The task object will be deleted once the task is finished
- void QueueTask(cTask * a_Task);
+ void QueueTask(cTask * a_Task); // Exported in ManualBindings.cpp
/// Returns the number of chunks loaded
int GetNumChunks() const; // tolua_export
@@ -534,17 +531,19 @@ public: /// Stops threads that belong to this world (part of deinit)
void Stop(void);
- void TickQueuedBlocks(float a_Dt);
+ /// Processes the blocks queued for ticking with a delay (m_BlockTickQueue[])
+ void TickQueuedBlocks(void);
struct BlockTickQueueItem
{
int X;
int Y;
int Z;
- float ToWait;
+ int TicksToWait;
};
- void QueueBlockForTick(int a_BlockX, int a_BlockY, int a_BlockZ, float a_TimeToWait); // tolua_export
+ /// Queues the block to be ticked after the specified number of game ticks
+ void QueueBlockForTick(int a_BlockX, int a_BlockY, int a_BlockZ, int a_TicksToWait); // tolua_export
// tolua_begin
/// Casts a thunderbolt at the specified coords
@@ -556,8 +555,16 @@ public: /// Forces a weather change in the next game tick
void ChangeWeather (void);
- /// Returns the current weather
+ /// Returns the current weather. Instead of comparing values directly to the weather constants, use IsWeatherXXX() functions, if possible
eWeather GetWeather (void) const { return m_Weather; };
+
+ bool IsWeatherSunny(void) const { return (m_Weather == wSunny); }
+ bool IsWeatherRain (void) const { return (m_Weather == wRain); }
+ bool IsWeatherStorm(void) const { return (m_Weather == wStorm); }
+
+ /// Returns true if the current weather has any precipitation - rain or storm
+ bool IsWeatherWet (void) const { return (m_Weather != wSunny); }
+
// tolua_end
cChunkGenerator & GetGenerator(void) { return m_Generator; }
@@ -584,7 +591,7 @@ public: /// Appends all usernames starting with a_Text (case-insensitive) into Results
void TabCompleteUserName(const AString & a_Text, AStringVector & a_Results);
-
+
private:
friend class cRoot;
@@ -638,7 +645,7 @@ private: std::vector<int> m_RSList;
std::vector<BlockTickQueueItem *> m_BlockTickQueue;
- std::vector<BlockTickQueueItem *> m_BlockTickQueueCopy; //Second is for safely removing the objects from the queue
+ std::vector<BlockTickQueueItem *> m_BlockTickQueueCopy; // Second is for safely removing the objects from the queue
cSimulatorManager * m_SimulatorManager;
cSandSimulator * m_SandSimulator;
diff --git a/source/WorldStorage/NBTChunkSerializer.cpp b/source/WorldStorage/NBTChunkSerializer.cpp index ef6165142..c9013b1b3 100644 --- a/source/WorldStorage/NBTChunkSerializer.cpp +++ b/source/WorldStorage/NBTChunkSerializer.cpp @@ -16,9 +16,9 @@ #include "../ItemGrid.h" #include "../StringCompression.h" #include "../Entities/Entity.h" -#include "../OSSupport/MakeDir.h" #include "FastNBT.h" #include "../Entities/FallingBlock.h" +#include "../Entities/Boat.h" #include "../Entities/Minecart.h" #include "../Mobs/Monster.h" #include "../Entities/Pickup.h" @@ -252,6 +252,17 @@ void cNBTChunkSerializer::AddBasicEntity(cEntity * a_Entity, const AString & a_C +void cNBTChunkSerializer::AddBoatEntity(cBoat * a_Boat) +{ + m_Writer.BeginCompound(""); + AddBasicEntity(a_Boat, "Boat"); + m_Writer.EndCompound(); +} + + + + + void cNBTChunkSerializer::AddFallingBlockEntity(cFallingBlock * a_FallingBlock) { m_Writer.BeginCompound(""); @@ -360,6 +371,7 @@ void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile) } case cProjectileEntity::pkFireCharge: case cProjectileEntity::pkWitherSkull: + case cProjectileEntity::pkEnderPearl: { m_Writer.BeginList("Motion", TAG_Double); m_Writer.AddDouble("", a_Projectile->GetSpeedX()); @@ -461,6 +473,7 @@ void cNBTChunkSerializer::Entity(cEntity * a_Entity) switch (a_Entity->GetEntityType()) { + case cEntity::etBoat: AddBoatEntity ((cBoat *) a_Entity); break; case cEntity::etFallingBlock: AddFallingBlockEntity((cFallingBlock *) a_Entity); break; case cEntity::etMinecart: AddMinecartEntity ((cMinecart *) a_Entity); break; case cEntity::etMonster: AddMonsterEntity ((cMonster *) a_Entity); break; diff --git a/source/WorldStorage/NBTChunkSerializer.h b/source/WorldStorage/NBTChunkSerializer.h index 481c578f3..9d4ac208c 100644 --- a/source/WorldStorage/NBTChunkSerializer.h +++ b/source/WorldStorage/NBTChunkSerializer.h @@ -19,6 +19,7 @@ class cFastNBTWriter; class cEntity; class cBlockEntity; +class cBoat; class cChestEntity; class cDispenserEntity; class cDropperEntity; @@ -94,6 +95,7 @@ protected: // Entities: void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName); + void AddBoatEntity (cBoat * a_Boat); void AddFallingBlockEntity(cFallingBlock * a_FallingBlock); void AddMinecartEntity (cMinecart * a_Minecart); void AddMonsterEntity (cMonster * a_Monster); diff --git a/source/WorldStorage/WSSAnvil.cpp b/source/WorldStorage/WSSAnvil.cpp index fa0066dc6..537e2f723 100644 --- a/source/WorldStorage/WSSAnvil.cpp +++ b/source/WorldStorage/WSSAnvil.cpp @@ -20,9 +20,9 @@ #include "../Item.h" #include "../ItemGrid.h" #include "../StringCompression.h" -#include "../OSSupport/MakeDir.h" #include "FastNBT.h" #include "../Mobs/Monster.h" +#include "../Entities/Boat.h" #include "../Entities/FallingBlock.h" #include "../Entities/Minecart.h" #include "../Entities/Pickup.h" @@ -199,7 +199,7 @@ cWSSAnvil::cMCAFile * cWSSAnvil::LoadMCAFile(const cChunkCoords & a_Chunk) // Load it anew: AString FileName; Printf(FileName, "%s/region", m_World->GetName().c_str()); - cMakeDir::MakeDir(FileName); + cFile::CreateFolder(FILE_IO_PREFIX + FileName); AppendPrintf(FileName, "/r.%d.%d.mca", RegionX, RegionZ); cMCAFile * f = new cMCAFile(FileName, RegionX, RegionZ); if (f == NULL) @@ -911,7 +911,11 @@ void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParse void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength) { - if (strncmp(a_IDTag, "FallingBlock", a_IDTagLength) == 0) + if (strncmp(a_IDTag, "Boat", a_IDTagLength) == 0) + { + LoadBoatFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "FallingBlock", a_IDTagLength) == 0) { LoadFallingBlockFromNBT(a_Entities, a_NBT, a_EntityTagIdx); } @@ -987,6 +991,20 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a +void cWSSAnvil::LoadBoatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr<cBoat> Boat(new cBoat(0, 0, 0)); + if (!LoadEntityBaseFromNBT(*Boat.get(), a_NBT, a_TagIdx)) + { + return; + } + a_Entities.push_back(Boat.release()); +} + + + + + void cWSSAnvil::LoadFallingBlockFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { // TODO diff --git a/source/WorldStorage/WSSAnvil.h b/source/WorldStorage/WSSAnvil.h index d92a2df72..7685d2236 100644 --- a/source/WorldStorage/WSSAnvil.h +++ b/source/WorldStorage/WSSAnvil.h @@ -140,6 +140,7 @@ protected: void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength); + void LoadBoatFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadFallingBlockFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadMinecartRFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadMinecartCFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); |