summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore20
-rw-r--r--MCServer/.gitignore1
-rw-r--r--MCServer/Plugins/APIDump/APIDesc.lua797
-rw-r--r--MCServer/Plugins/APIDump/LICENSE-prettify.txt191
-rw-r--r--MCServer/Plugins/APIDump/WebWorldThreads.html121
-rw-r--r--MCServer/Plugins/APIDump/lang-lua.js2
-rw-r--r--MCServer/Plugins/APIDump/main.css30
-rw-r--r--MCServer/Plugins/APIDump/main.lua842
-rw-r--r--MCServer/Plugins/APIDump/prettify.css1
-rw-r--r--MCServer/Plugins/APIDump/prettify.js30
-rw-r--r--VC2008/.gitignore1
-rw-r--r--VC2008/MCServer.vcproj36
-rw-r--r--source/AllToLua.pkg1
-rw-r--r--source/Bindings.cpp414
-rw-r--r--source/Bindings.h2
-rw-r--r--source/BlockEntities/DropSpenserEntity.h8
-rw-r--r--source/BlockEntities/HopperEntity.h9
-rw-r--r--source/BlockID.cpp43
-rw-r--r--source/Chunk.cpp497
-rw-r--r--source/Chunk.h35
-rw-r--r--source/ChunkDef.h55
-rw-r--r--source/ChunkMap.cpp60
-rw-r--r--source/ChunkMap.h15
-rw-r--r--source/Globals.h24
-rw-r--r--source/Inventory.cpp2
-rw-r--r--source/LuaWindow.h9
-rw-r--r--source/ManualBindings.cpp44
-rw-r--r--source/MobCensus.cpp92
-rw-r--r--source/MobCensus.h59
-rw-r--r--source/MobFamilyCollecter.cpp26
-rw-r--r--source/MobFamilyCollecter.h39
-rw-r--r--source/MobProximityCounter.cpp83
-rw-r--r--source/MobProximityCounter.h65
-rw-r--r--source/MobSpawner.cpp270
-rw-r--r--source/MobSpawner.h75
-rw-r--r--source/Mobs/AggressiveMonster.cpp6
-rw-r--r--source/Mobs/AggressiveMonster.h3
-rw-r--r--source/Mobs/Bat.cpp15
-rw-r--r--source/Mobs/Bat.h7
-rw-r--r--source/Mobs/Blaze.cpp2
-rw-r--r--source/Mobs/Cavespider.cpp2
-rw-r--r--source/Mobs/Chicken.cpp2
-rw-r--r--source/Mobs/Cow.cpp10
-rw-r--r--source/Mobs/Creeper.cpp2
-rw-r--r--source/Mobs/EnderDragon.cpp2
-rw-r--r--source/Mobs/Enderman.cpp2
-rw-r--r--source/Mobs/Ghast.cpp2
-rw-r--r--source/Mobs/Giant.cpp2
-rw-r--r--source/Mobs/Horse.cpp56
-rw-r--r--source/Mobs/IncludeAllMonsters.h29
-rw-r--r--source/Mobs/IronGolem.cpp2
-rw-r--r--source/Mobs/Magmacube.cpp2
-rw-r--r--source/Mobs/Monster.cpp247
-rw-r--r--source/Mobs/Monster.h54
-rw-r--r--source/Mobs/Mooshroom.cpp2
-rw-r--r--source/Mobs/Ocelot.h2
-rw-r--r--source/Mobs/PassiveAggressiveMonster.cpp4
-rw-r--r--source/Mobs/PassiveAggressiveMonster.h2
-rw-r--r--source/Mobs/PassiveMonster.cpp5
-rw-r--r--source/Mobs/PassiveMonster.h3
-rw-r--r--source/Mobs/Pig.cpp2
-rw-r--r--source/Mobs/Sheep.cpp2
-rw-r--r--source/Mobs/Silverfish.h2
-rw-r--r--source/Mobs/Skeleton.cpp2
-rw-r--r--source/Mobs/Slime.cpp2
-rw-r--r--source/Mobs/SnowGolem.cpp2
-rw-r--r--source/Mobs/Spider.cpp2
-rw-r--r--source/Mobs/Squid.cpp3
-rw-r--r--source/Mobs/Squid.h1
-rw-r--r--source/Mobs/Villager.cpp2
-rw-r--r--source/Mobs/Witch.cpp2
-rw-r--r--source/Mobs/Wither.cpp2
-rw-r--r--source/Mobs/Wolf.cpp2
-rw-r--r--source/Mobs/Zombie.cpp2
-rw-r--r--source/Mobs/Zombiepigman.cpp2
-rw-r--r--source/Protocol/ProtocolRecognizer.cpp6
-rw-r--r--source/Root.cpp3
-rw-r--r--source/Root.h10
-rw-r--r--source/WebAdmin.cpp87
-rw-r--r--source/WebAdmin.h61
-rw-r--r--source/World.cpp316
-rw-r--r--source/World.h10
82 files changed, 3824 insertions, 1163 deletions
diff --git a/.gitignore b/.gitignore
index 7706ecc0a..b0ad11d53 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,23 @@ cloc.xsl
*.suo
/EveryNight.cmd
*.sublime-*
+
+# emacs stuff
+*.*~
+
+# world inside source
+ChunkWorx.ini
+groups.ini
+items.ini
+monsters.ini
+settings.ini
+terrain.ini
+users.ini
+webadmin.ini
+world.ini
+crafting.txt
+motd.txt
+logs
+players
+world
+world_nether
diff --git a/MCServer/.gitignore b/MCServer/.gitignore
index d9d869986..c03bcbe99 100644
--- a/MCServer/.gitignore
+++ b/MCServer/.gitignore
@@ -19,3 +19,4 @@ helgrind.log
valgrind.log
motd.txt
*.deuser
+*.dmp
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua
index 2bede6323..0940931cd 100644
--- a/MCServer/Plugins/APIDump/APIDesc.lua
+++ b/MCServer/Plugins/APIDump/APIDesc.lua
@@ -15,7 +15,7 @@ g_APIDesc =
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" ),
@@ -25,12 +25,17 @@ g_APIDesc =
{ Params = "Parameter list 2", Return = "Return values list 2", Notes = "Notes 2" },
}
} ,
-
+
Constants =
{
ConstantName = { Notes = "Notes about the constant" },
} ,
-
+
+ Variables =
+ {
+ VariableName = { Type = "string", Notes = "Notes about the variable" },
+ } ,
+
AdditionalInfo = -- Paragraphs to be exported after the function definitions table
{
{
@@ -42,17 +47,17 @@ g_APIDesc =
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" },
@@ -63,17 +68,17 @@ g_APIDesc =
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 = [[
@@ -177,7 +182,7 @@ g_APIDesc =
msImprint = { Notes = "Src overwrites Dst anywhere where Dst has non-air blocks" },
msLake = { Notes = "Special mode for merging lake images" },
},
-
+
AdditionalInfo =
{
{
@@ -263,7 +268,7 @@ g_APIDesc =
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" },
@@ -290,9 +295,9 @@ g_APIDesc =
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" },
@@ -301,7 +306,7 @@ g_APIDesc =
{ 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 =
+ 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" },
@@ -311,8 +316,8 @@ g_APIDesc =
{
},
},
-
- cBoundingBox =
+
+ cBoundingBox =
{
Desc = [[
Represents two sets of coordinates, minimum and maximum for each direction; thus defining an
@@ -357,17 +362,37 @@ g_APIDesc =
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" },
+ Black = { Notes = "" },
+ Blue = { Notes = "" },
+ Bold = { Notes = "" },
+ Color = { Notes = "The first character of the color-code-sequence, §" },
+ DarkPurple = { Notes = "" },
+ Delimiter = { Notes = "The first character of the color-code-sequence, §" },
+ Gold = { Notes = "" },
+ Gray = { Notes = "" },
+ Green = { Notes = "" },
+ Italic = { Notes = "" },
+ LightBlue = { Notes = "" },
+ LightGray = { Notes = "" },
+ LightGreen = { Notes = "" },
+ LightPurple = { Notes = "" },
+ Navy = { Notes = "" },
Plain = { Notes = "Resets all formatting to normal" },
+ Purple = { Notes = "" },
+ Random = { Notes = "Random letters and symbols animate instead of the text" },
+ Red = { Notes = "" },
+ Rose = { Notes = "" },
+ Strikethrough = { Notes = "" },
+ Underlined = { Notes = "" },
+ White = { Notes = "" },
+ Yellow = { Notes = "" },
},
},
@@ -385,9 +410,9 @@ g_APIDesc =
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}}." },
@@ -426,7 +451,7 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(),
{{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" },
@@ -488,7 +513,7 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(),
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" },
@@ -520,7 +545,7 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(),
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." },
@@ -530,7 +555,7 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(),
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 =
+ 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" },
@@ -557,7 +582,7 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(),
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 =
+ 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" },
@@ -610,8 +635,8 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(),
},
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" },
+ p1 = { Type = "{{Vector3i}}", Notes = "The first corner. Usually the lesser of the two coords in each set" },
+ p2 = { Type = "{{Vector3i}}", Notes = "The second corner. Usually the larger of the two coords in each set" },
},
},
@@ -619,7 +644,7 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(),
{
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).
+ <p>An object of this class can be created from scratch when generating chunks ({{OnChunkGenerated|OnChunkGenerated}} and {{OnChunkGenerating|OnChunkGenerating}} hooks).
]],
Functions =
{
@@ -635,7 +660,7 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(),
{
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).
+ <p>An object of this class can be created from scratch when generating chunks ({{OnChunkGenerated|OnChunkGenerated}} and {{OnChunkGenerating|OnChunkGenerating}} hooks).
]],
Functions =
{
@@ -662,13 +687,13 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(),
ContentsWidth = { Notes = "Width (X) of the {{cItemGrid}} representing the contents" },
ContentsHeight = { Notes = "Height (Y) of the {{cItemGrid}} representing the contents" },
},
-
- Inherits = "cBlockEntity";
+
+ Inherits = "cBlockEntityWithItems";
},
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.
+ 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.
]],
@@ -750,12 +775,12 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(),
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" },
+ 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" },
+ 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" },
@@ -763,23 +788,26 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(),
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" },
+ 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" },
+ GetWorld = { Params = "", Return = "{{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." },
+ IsFallingBlock = { Params = "", Return = "bool", Notes = "Returns true if the entity represents a {{cFallingBlock}} entity." },
+ IsInvisible = { Params = "", Return = "bool", Notes = "Returns true if the entity is invisible" },
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}}" },
+ IsProjectile = { Params = "", Return = "bool", Notes = "Returns true if the entity is a {{cProjectileEntity}} descendant." },
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" },
@@ -836,7 +864,7 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(),
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" },
+ etProjectile = { Notes = "The entity is a {{cProjectileEntity}} descendant" },
etTNT = { Notes = "The entity is a {{cTNTEntity}}" },
},
},
@@ -850,7 +878,7 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(),
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." },
@@ -865,14 +893,14 @@ cFile:Delete("/usr/bin/virus.exe");
},
- cFireChargeEntity =
+ 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)
@@ -909,7 +937,7 @@ cFile:Delete("/usr/bin/virus.exe");
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.
@@ -930,6 +958,28 @@ cFile:Delete("/usr/bin/virus.exe");
},
},
+ cHopperEntity =
+ {
+ Desc = [[
+ This class represents a hopper block entity in the world.</p>
+ <p>
+ Plugins may use this class during chunk generation ({{OnChunkGenerated|HOOK_CHUNK_GENERATED}} and
+ {{OnChunkGenerating|HOOK_CHUNK_GENERATING}}) to add hoppers to the generated chunk.
+ ]],
+ Functions =
+ {
+ constructor = { Params = "BlockX, BlockY, BlockZ", Return = "cHopperEntity", Notes = "Creates and returns a new hopper at the specified coords." },
+ GetOutputBlockPos = { Params = "BlockMeta", Return = "bool, BlockX, BlockY, BlockZ", Notes = "Returns whether the hopper is attached, and if so, the block coords of the block receiving the output items, based on the given meta." },
+ },
+ Constants =
+ {
+ ContentsHeight = { Notes = "Height (Y) of the internal {{cItemGrid}} representing the hopper contents." },
+ ContentsWidth = { Notes = "Width (X) of the internal {{cItemGrid}} representing the hopper contents." },
+ TICKS_PER_TRANSFER = { Notes = "Number of ticks between when the hopper transfers items." },
+ },
+ Inherits = "cBlockEntityWithItems",
+ },
+
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.
@@ -998,6 +1048,34 @@ cFile:Delete("/usr/bin/virus.exe");
Constants =
{
},
+ AdditionalInfo =
+ {
+ {
+ Header = "Practical usage",
+ Contents = [[
+ If you want to use cIniFile you need to know a couple of things; what is the key name and what
+ is the value name. Below is a demonstration of what is what.</p>
+<pre class="prettyprint lang-ini">
+; Comment line
+[KeyName1]
+ValueName1=Value1
+ValueName2=Value2
+
+[KeyName2]
+ValueName1=Value3
+</pre></p>
+ <p>
+ cIniFile is very easy to use. For example, you can find out what port the server is supposed to
+ use according to settings.ini by using this little snippet:
+<pre class="prettyprint lang-lua">
+local IniFile = cIniFile("settings.ini");
+if (IniFile:ReadFile()) then
+ ServerPort = IniFile:GetValueI("Server", "Port");
+end
+</pre>
+ ]],
+ },
+ },
},
cInventory =
@@ -1007,8 +1085,12 @@ Internally, the class uses three {{cItemGrid|cItemGrid}} objects to store the co
<li>Armor</li>
<li>Inventory</li>
<li>Hotbar</li>
-These ItemGrids are available in the API and can be manipulated by the plugins, too.
-]],
+These ItemGrids are available in the API and can be manipulated by the plugins, too.</p>
+ <p>
+ When using the raw slot access functions, such as GetSlot() and SetSlot(), the slots are numbered
+ consecutively, each ItemGrid has its offset and count. To future-proff your plugins, use the named
+ constants instead of hard-coded numbers.
+ ]],
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" },
@@ -1060,9 +1142,16 @@ These ItemGrids are available in the API and can be manipulated by the plugins,
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.
+ {{cEnchantments}} class and are accessible through the m_Enchantments variable.</p>
+ <p>
+ To test if a cItem object represents an empty item, do not compare the item type nor the item count,
+ but rather use the IsEmpty() function.</p>
+ <p>
+ To translate from a cItem to its string representation, use the {{Globals#functions|global function}}
+ ItemToString(), ItemTypeToString() or ItemToFullString(). To translate from a string to a cItem,
+ use the StringToItem() global function.
]],
-
+
Functions =
{
constructor =
@@ -1081,25 +1170,70 @@ These ItemGrids are available in the API and can be manipulated by the plugins,
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" },
+ IsSameType = { Params = "cItem", Return = "bool", Notes = "Returns true if the item in the parameter is of the same ItemType as the one stored in the object. This is true even if the two items have different enchantments" },
+ IsStackableWith = { Params = "cItem", Return = "bool", Notes = "Returns true if the item in the parameter is stackable with the one stored in the object. Two items with different enchantments cannot be stacked" },
},
- Constants =
+ Variables =
{
+ m_Enchantments = { Type = "{{cEnchantments}}", Notes = "The enchantments that this item has" },
+ m_ItemCount = { Type = "number", Notes = "Number of items in this stack" },
+ m_ItemDamage = { Type = "number", Notes = "The damage of the item. Zero means no damage. Maximum damage can be queried with GetMaxDamage()" },
+ m_ItemType = { Type = "number", Notes = "The item type. One of E_ITEM_ or E_BLOCK_ constants" },
+ },
+ AdditionalInfo =
+ {
+ {
+ Header = "Usage notes",
+ Contents = [[
+ Note that the object contained in a cItem class is quite complex and quite often new Minecraft
+ versions add more stuff. Therefore it is recommended to copy cItem objects using the
+ copy-constructor ("local copy = cItem(original);"), this is the only way that guarantees that
+ the object will be copied at full, even with future versions of MCServer.
+ ]],
+ },
+ {
+ Header = "Example code",
+ Contents = [[
+ The following code shows how to create items in several different ways (adapted from the Debuggers plugin):
+<pre class="prettyprint lang-lua">
+-- empty item:
+local Item1 = cItem();
+
+-- enchanted sword, enchantment given as numeric string (bad style; see Item5):
+local Item2 = cItem(E_ITEM_DIAMOND_SWORD, 1, 0, "1=1");
+
+-- 1 undamaged shovel, no enchantment:
+local Item3 = cItem(E_ITEM_DIAMOND_SHOVEL);
+
+-- Add the Unbreaking enchantment. Note that Vanilla's levelcap isn't enforced:
+Item3.m_Enchantments:SetLevel(cEnchantments.enchUnbreaking, 4);
+
+-- 1 undamaged pickaxe, no enchantment:
+local Item4 = cItem(E_ITEM_DIAMOND_PICKAXE);
+
+-- Add multiple enchantments:
+Item4.m_Enchantments:SetLevel(cEnchantments.enchUnbreaking, 5);
+Item4.m_Enchantments:SetLevel(cEnchantments.enchEfficiency, 3);
+
+-- enchanted chestplate, enchantment given as textual stringdesc (good style)
+local Item5 = cItem(E_ITEM_DIAMOND_CHESTPLATE, 1, 0, "thorns=1;unbreaking=3");
+</pre>
+]],
+ },
},
},
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>{{cChestEntity|Chest}} contents</li>
<li>(TODO) Chest minecart contents</li>
-<li>{{cDispenserEntity|Dispenser|| 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>{{cPlayer|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.
@@ -1160,6 +1294,40 @@ These ItemGrids are available in the API and can be manipulated by the plugins,
Constants =
{
},
+ AdditionalInfo =
+ {
+ {
+ Header = "Code example: Add items to player inventory",
+ Contents = [[
+ The following code tries to add 32 sticks to a player's main inventory:
+<pre class="prettyprint lang-lua">
+local Items = cItem(E_ITEM_STICK, 32);
+local PlayerMainInventory = Player:GetInventorySlots(); -- PlayerMainInventory is of type cItemGrid
+local NumAdded = PlayerMainInventory:AddItem(Items);
+if (NumAdded == Items.m_ItemCount) then
+ -- All the sticks did fit
+ LOG("Added 32 sticks");
+else
+ -- Some (or all) of the sticks didn't fit
+ LOG("Tried to add 32 sticks, but only " .. NumAdded .. " could fit");
+end
+</pre>
+ ]],
+ },
+ {
+ Header = "Code example: Damage an item",
+ Contents = [[
+ The following code damages the helmet in the player's armor and destroys it if it reaches max damage:
+<pre class="prettyprint lang-lua">
+local PlayerArmor = Player:GetArmorSlots(); -- PlayerArmor is of type cItemGrid
+if (PlayerArmor:DamageItem(0)) then -- Helmet is at SlotNum 0
+ -- The helmet has reached max damage, destroy it:
+ PlayerArmor:EmptySlot(0);
+end
+</pre>
+ ]],
+ },
+ }, -- AdditionalInfo
},
cItems =
@@ -1206,7 +1374,7 @@ various events. See below for further information.
{
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 =
{
{
@@ -1261,17 +1429,17 @@ function HandleSpideyCmd(a_Split, a_Player)
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>
@@ -1280,7 +1448,7 @@ end
},
}, -- 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.
@@ -1375,10 +1543,10 @@ a_Player:OpenWindow(Window);
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}}
+ Desc = [[cPawn is a controllable pawn object, controlled by either AI or a player. cPawn inherits all functions and members of {{cEntity}}
]],
Functions =
{
@@ -1482,7 +1650,7 @@ a_Player:OpenWindow(Window);
Constants = {},
Inherits = "cPlugin",
},
-
+
cPluginManager =
{
Desc = [[
@@ -1595,7 +1763,7 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
Constants = {},
Inherits = "cEntity",
},
-
+
cRoot =
{
Desc = [[There is always only one cRoot object in MCServer. cRoot manages all the important objects such as {{cServer|cServer}}
@@ -1656,7 +1824,7 @@ Sign entities are saved and loaded from disk when the chunk they reside in is sa
Constants =
{
},
-
+
Inherits = "cBlockEntity";
},
@@ -1671,7 +1839,7 @@ Sign entities are saved and loaded from disk when the chunk they reside in is sa
{
},
},
-
+
cThrownEggEntity =
{
Desc = "",
@@ -1679,7 +1847,7 @@ Sign entities are saved and loaded from disk when the chunk they reside in is sa
Constants = {},
Inherits = "cProjectileEntity",
},
-
+
cThrownEnderPearlEntity =
{
Desc = "",
@@ -1687,7 +1855,7 @@ Sign entities are saved and loaded from disk when the chunk they reside in is sa
Constants = {},
Inherits = "cProjectileEntity",
},
-
+
cThrownSnowballEntity =
{
Desc = "",
@@ -1695,7 +1863,7 @@ Sign entities are saved and loaded from disk when the chunk they reside in is sa
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.
@@ -1713,17 +1881,20 @@ Sign entities are saved and loaded from disk when the chunk they reside in is sa
cWebAdmin =
{
Desc = "",
- Functions = {},
+ Functions =
+ {
+ GetHTMLEscapedString = { Params = "string", Return = "string", Notes = "Gets the HTML escaped representation of a requested string. This is useful for user input and game data that is not guaranteed to be escaped already." },
+ },
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.
@@ -1789,7 +1960,7 @@ Sign entities are saved and loaded from disk when the chunk they reside in is sa
<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" },
@@ -1797,7 +1968,7 @@ Sign entities are saved and loaded from disk when the chunk they reside in is sa
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." },
+ CreateProjectile = { Params = "X, Y, Z, {{cProjectileEntity|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." },
@@ -2054,8 +2225,8 @@ World:ForEachEntity(
},
},
},
-
-
+
+
Hooks =
{
HOOK_BLOCK_TO_PICKUPS =
@@ -2101,10 +2272,10 @@ function OnBlockToPickups(a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_Blo
-- 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;
@@ -2197,17 +2368,17 @@ function OnChunkGenerated(a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc)
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 (
@@ -2408,7 +2579,7 @@ end;
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>
+ See also {{OnExploding|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
@@ -2451,7 +2622,7 @@ end;
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>
+ See also {{OnExploded|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
@@ -2894,6 +3065,256 @@ end;
]],
}, -- HOOK_PLAYER_RIGHT_CLICK
+ HOOK_PLAYER_RIGHT_CLICKING_ENTITY =
+ {
+ CalledWhen = "A player has right-clicked an entity. Plugins may override / refuse.",
+ DefaultFnName = "OnPlayerRightClickingEntity", -- also used as pagename
+ Desc = [[
+ This hook is called when the {{cPlayer|player}} right-clicks an {{cEntity|entity}}. Plugins may
+ override the default behavior or even cancel the default processing.
+ ]],
+ Params =
+ {
+ { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who has right-clicked the entity" },
+ { Name = "Entity", Type = "{{cEntity}} descendant", Notes = "The entity that has been right-clicked" },
+ },
+ Returns = [[
+ If the functino returns false or no value, MCServer calls other plugins' callbacks and finally does
+ the default processing for the right-click. If the function returns true, no other callbacks are
+ called and the default processing is skipped.
+ ]],
+ }, -- HOOK_PLAYER_RIGHT_CLICKING_ENTITY
+
+ HOOK_PLAYER_SHOOTING =
+ {
+ CalledWhen = "When the player releases the bow, shooting an arrow (other projectiles: unknown)",
+ DefaultFnName = "OnPlayerShooting", -- also used as pagename
+ Desc = [[
+ This hook is called when the {{cPlayer|player}} shoots their bow. It is called for the actual
+ release of the {{cArrowEntity|arrow}}. FIXME: It is currently unknown whether other
+ {{cProjectileEntity|projectiles}} (snowballs, eggs) trigger this hook.</p>
+ <p>
+ To get the player's position and direction, use the {{cPlayer}}:GetEyePosition() and
+ cPlayer:GetLookVector() functions. Note that for shooting a bow, the position for the arrow creation
+ is not at the eye pos, some adjustments are required. FIXME: Export the {{cPlayer}} function for
+ this adjustment.
+ ]],
+ Params =
+ {
+ { Name = "Player", Type = "{{cPlayer}}", Notes = "The player shooting" },
+ },
+ Returns = [[
+ If the function returns false or no value, the next plugin's callback is called, and finally
+ MCServer creates the projectile. If the functino returns true, no other callback is called and no
+ projectile is created.
+ ]],
+ }, -- HOOK_PLAYER_SHOOTING
+
+ HOOK_PLAYER_SPAWNED =
+ {
+ CalledWhen = "After a player (re)spawns in the world to which they belong to.",
+ DefaultFnName = "OnPlayerSpawned", -- also used as pagename
+ Desc = [[
+ This hook is called after a {{cPlayer|player}} has spawned in the world. It is called after
+ {{OnLogin|HOOK_LOGIN}} and {{OnPlayerJoined|HOOK_PLAYER_JOINED}}, after the player name has been
+ authenticated, the initial worldtime, inventory and health have been sent to the player and the
+ player spawn packet has been broadcast to all players near enough to the player spawn place. This is
+ a notification-only event, plugins wishing to refuse player's entry should kick the player using the
+ {{cPlayer}}:Kick() function.</p>
+ <p>
+ This hook is also called when the player respawns after death (and a respawn packet is received from
+ the client, meaning the player has already clicked the Respawn button).
+ ]],
+ Params =
+ {
+ { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who has (re)spawned" },
+ },
+ Returns = [[
+ If the function returns false or no value, other plugins' callbacks are called. If the function
+ returns true, no other callbacks are called for this event. There is no overridable behavior.
+ ]],
+ }, -- HOOK_PLAYER_SPAWNED
+
+ HOOK_PLAYER_TOSSING_ITEM =
+ {
+ CalledWhen = "A player is tossing an item. Plugin may override / refuse.",
+ DefaultFnName = "OnPlayerTossingItem", -- also used as pagename
+ Desc = [[
+ This hook is called when a {{cPlayer|player}} has tossed an item (Q keypress). The
+ {{cPickup|pickup}} has not been spawned yet. Plugins may disallow the tossing, but in that case they
+ need to clean up - the player's client already thinks the item has been tossed so the
+ {{cInventory|inventory}} needs to be re-sent to the player.</p>
+ <p>
+ To get the item that is about to be tossed, call the {{cPlayer}}:GetEquippedItem() function.
+ ]],
+ Params =
+ {
+ { Name = "Player", Type = "{{cPlayer}}", Notes = "The player tossing an item" },
+ },
+ Returns = [[
+ If the function returns false or no value, other plugins' callbacks are called and finally MCServer
+ creates the pickup for the item and tosses it, using {{cPlayer}}:TossItem. If the function returns
+ true, no other callbacks are called for this event and MCServer doesn't toss the item.
+ ]],
+ }, -- HOOK_PLAYER_TOSSING_ITEM
+
+ HOOK_PLAYER_USED_BLOCK =
+ {
+ CalledWhen = "A player has just used a block (chest, furnace…). Notification only.",
+ DefaultFnName = "OnPlayerUsedBlock", -- also used as pagename
+ Desc = [[
+ This hook is called after a {{cPlayer|player}} has right-clicked a block that can be used, such as a
+ {{cChestEntity|chest}} or a lever. It is called after MCServer processes the usage (sends the UI
+ handling packets / toggles redstone). Note that for UI-related blocks, the player is most likely
+ still using the UI. This is a notification-only event.</p>
+ <p>
+ Note that the block coords given in this callback are for the (solid) block that is being clicked,
+ not the air block between it and the player.</p>
+ <p>
+ To get the world at which the right-click occurred, use the {{cPlayer}}:GetWorld() function.</p>
+ <p>
+ See also the {{OnPlayerUsingBlock|HOOK_PLAYER_USING_BLOCK}} for a similar hook called before the
+ use, the {{OnPlayerUsingItem|HOOK_PLAYER_USING_ITEM}} and {{OnPlayerUsedItem|HOOK_PLAYER_USED_ITEM}}
+ for similar hooks called when a player interacts with any block with a usable item in hand, such as
+ a bucket.
+ ]],
+ Params =
+ {
+ { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who used the block" },
+ { Name = "BlockX", Type = "number", Notes = "X-coord of the clicked block" },
+ { Name = "BlockY", Type = "number", Notes = "Y-coord of the clicked block" },
+ { Name = "BlockZ", Type = "number", Notes = "Z-coord of the clicked block" },
+ { Name = "BlockFace", Type = "number", Notes = "Face of clicked block which has been clicked. One of the BLOCK_FACE_ constants" },
+ { Name = "CursorX", Type = "number", Notes = "X-coord of the cursor crosshair on the block being clicked" },
+ { Name = "CursorY", Type = "number", Notes = "Y-coord of the cursor crosshair on the block being clicked" },
+ { Name = "CursorZ", Type = "number", Notes = "Z-coord of the cursor crosshair on the block being clicked" },
+ { Name = "BlockType", Type = "number", Notes = "Block type of the clicked block" },
+ { Name = "BlockMeta", Type = "number", Notes = "Block meta of the clicked block" },
+ },
+ Returns = [[
+ If the function returns false or no value, other plugins' callbacks are called. If the function
+ returns true, no other callbacks are called for this event.
+ ]],
+ }, -- HOOK_PLAYER_USED_BLOCK
+
+ HOOK_PLAYER_USED_ITEM =
+ {
+ CalledWhen = "A player has used an item in hand (bucket...)",
+ DefaultFnName = "OnPlayerUsedItem", -- also used as pagename
+ Desc = [[
+ This hook is called after a {{cPlayer|player}} has right-clicked a block with an {{cItem|item}} that
+ can be used (is not placeable, is not food and clicked block is not use-able), such as a bucket or a
+ hoe. It is called after MCServer processes the usage (places fluid / turns dirt to farmland).
+ This is an information-only hook, there is no way to cancel the event anymore.</p>
+ <p>
+ Note that the block coords given in this callback are for the (solid) block that is being clicked,
+ not the air block between it and the player.</p>
+ <p>
+ To get the world at which the right-click occurred, use the {{cPlayer}}:GetWorld() function. To get
+ the item that the player is using, use the {{cPlayer}}:GetEquippedItem() function.</p>
+ <p>
+ See also the {{OnPlayerUsingItem|HOOK_PLAYER_USING_ITEM}} for a similar hook called before the use,
+ the {{OnPlayerUsingBlock|HOOK_PLAYER_USING_BLOCK}} and {{OnPlayerUsedBlock|HOOK_PLAYER_USED_BLOCK}}
+ for similar hooks called when a player interacts with a block, such as a chest.
+ ]],
+ Params =
+ {
+ { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who used the item" },
+ { Name = "BlockX", Type = "number", Notes = "X-coord of the clicked block" },
+ { Name = "BlockY", Type = "number", Notes = "Y-coord of the clicked block" },
+ { Name = "BlockZ", Type = "number", Notes = "Z-coord of the clicked block" },
+ { Name = "BlockFace", Type = "number", Notes = "Face of clicked block which has been clicked. One of the BLOCK_FACE_ constants" },
+ { Name = "CursorX", Type = "number", Notes = "X-coord of the cursor crosshair on the block being clicked" },
+ { Name = "CursorY", Type = "number", Notes = "Y-coord of the cursor crosshair on the block being clicked" },
+ { Name = "CursorZ", Type = "number", Notes = "Z-coord of the cursor crosshair on the block being clicked" },
+ { Name = "BlockType", Type = "number", Notes = "Block type of the clicked block" },
+ { Name = "BlockMeta", Type = "number", Notes = "Block meta of the clicked block" },
+ },
+ Returns = [[
+ If the function returns false or no value, other plugins' callbacks are called. If the function
+ returns true, no other callbacks are called for this event.
+ ]],
+ }, -- HOOK_PLAYER_USED_ITEM
+
+ HOOK_PLAYER_USING_BLOCK =
+ {
+ CalledWhen = "Just before a player uses a block (chest, furnace...). Plugin may override / refuse.",
+ DefaultFnName = "OnPlayerUsingBlock", -- also used as pagename
+ Desc = [[
+ This hook is called when a {{cPlayer|player}} has right-clicked a block that can be used, such as a
+ {{cChestEntity|chest}} or a lever. It is called before MCServer processes the usage (sends the UI
+ handling packets / toggles redstone). Plugins may refuse the interaction by returning true.</p>
+ <p>
+ Note that the block coords given in this callback are for the (solid) block that is being clicked,
+ not the air block between it and the player.</p>
+ <p>
+ To get the world at which the right-click occurred, use the {{cPlayer}}:GetWorld() function.</p>
+ <p>
+ See also the {{OnPlayerUsedBlock|HOOK_PLAYER_USED_BLOCK}} for a similar hook called after the use, the
+ {{OnPlayerUsingItem|HOOK_PLAYER_USING_ITEM}} and {{OnPlayerUsedItem|HOOK_PLAYER_USED_ITEM}} for
+ similar hooks called when a player interacts with any block with a usable item in hand, such as a
+ bucket.
+ ]],
+ Params =
+ {
+ { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who is using the block" },
+ { Name = "BlockX", Type = "number", Notes = "X-coord of the clicked block" },
+ { Name = "BlockY", Type = "number", Notes = "Y-coord of the clicked block" },
+ { Name = "BlockZ", Type = "number", Notes = "Z-coord of the clicked block" },
+ { Name = "BlockFace", Type = "number", Notes = "Face of clicked block which has been clicked. One of the BLOCK_FACE_ constants" },
+ { Name = "CursorX", Type = "number", Notes = "X-coord of the cursor crosshair on the block being clicked" },
+ { Name = "CursorY", Type = "number", Notes = "Y-coord of the cursor crosshair on the block being clicked" },
+ { Name = "CursorZ", Type = "number", Notes = "Z-coord of the cursor crosshair on the block being clicked" },
+ { Name = "BlockType", Type = "number", Notes = "Block type of the clicked block" },
+ { Name = "BlockMeta", Type = "number", Notes = "Block meta of the clicked block" },
+ },
+ Returns = [[
+ If the function returns false or no value, other plugins' callbacks are called and then MCServer
+ processes the interaction. If the function returns true, no other callbacks are called for this
+ event and the interaction is silently dropped.
+ ]],
+ }, -- HOOK_PLAYER_USING_BLOCK
+
+ HOOK_PLAYER_USING_ITEM =
+ {
+ CalledWhen = "Just before a player uses an item in hand (bucket...). Plugin may override / refuse.",
+ DefaultFnName = "OnPlayerUsingItem", -- also used as pagename
+ Desc = [[
+ This hook is called when a {{cPlayer|player}} has right-clicked a block with an {{cItem|item}} that
+ can be used (is not placeable, is not food and clicked block is not use-able), such as a bucket or a
+ hoe. It is called before MCServer processes the usage (places fluid / turns dirt to farmland).
+ Plugins may refuse the interaction by returning true.</p>
+ <p>
+ Note that the block coords given in this callback are for the (solid) block that is being clicked,
+ not the air block between it and the player.</p>
+ <p>
+ To get the world at which the right-click occurred, use the {{cPlayer}}:GetWorld() function. To get
+ the item that the player is using, use the {{cPlayer}}:GetEquippedItem() function.</p>
+ <p>
+ See also the {{OnPlayerUsedItem|HOOK_PLAYER_USED_ITEM}} for a similar hook called after the use, the
+ {{OnPlayerUsingBlock|HOOK_PLAYER_USING_BLOCK}} and {{OnPlayerUsedBlock|HOOK_PLAYER_USED_BLOCK}} for
+ similar hooks called when a player interacts with a block, such as a chest.
+ ]],
+ Params =
+ {
+ { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who is using the item" },
+ { Name = "BlockX", Type = "number", Notes = "X-coord of the clicked block" },
+ { Name = "BlockY", Type = "number", Notes = "Y-coord of the clicked block" },
+ { Name = "BlockZ", Type = "number", Notes = "Z-coord of the clicked block" },
+ { Name = "BlockFace", Type = "number", Notes = "Face of clicked block which has been clicked. One of the BLOCK_FACE_ constants" },
+ { Name = "CursorX", Type = "number", Notes = "X-coord of the cursor crosshair on the block being clicked" },
+ { Name = "CursorY", Type = "number", Notes = "Y-coord of the cursor crosshair on the block being clicked" },
+ { Name = "CursorZ", Type = "number", Notes = "Z-coord of the cursor crosshair on the block being clicked" },
+ { Name = "BlockType", Type = "number", Notes = "Block type of the clicked block" },
+ { Name = "BlockMeta", Type = "number", Notes = "Block meta of the clicked block" },
+ },
+ Returns = [[
+ If the function returns false or no value, other plugins' callbacks are called and then MCServer
+ processes the interaction. If the function returns true, no other callbacks are called for this
+ event and the interaction is silently dropped.
+ ]],
+ }, -- HOOK_PLAYER_USING_ITEM
+
HOOK_POST_CRAFTING =
{
CalledWhen = "After the built-in recipes are checked and a recipe was found.",
@@ -3051,8 +3472,203 @@ end;
]],
}, -- HOOK_SPAWNING_MONSTER
+ HOOK_TAKE_DAMAGE =
+ {
+ CalledWhen = "An {{cEntity|entity}} is taking any kind of damage",
+ DefaultFnName = "OnTakeDamage", -- also used as pagename
+ Desc = [[
+ This hook is called when any {{cEntity}} descendant, such as a {{cPlayer|player}} or a
+ {{cMonster|mob}}, takes any kind of damage. The plugins may modify the amount of damage or effects
+ with this hook by editting the {{TakeDamageInfo}} object passed.</p>
+ <p>
+ This hook is called after the final damage is calculated, including all the possible weapon
+ {{cEnchantments|enchantments}}, armor protection and potion effects.
+ ]],
+ Params =
+ {
+ { Name = "Receiver", Type = "{{cEntity}} descendant", Notes = "The entity taking damage" },
+ { Name = "TDI", Type = "{{TakeDamageInfo}}", Notes = "The damage type, cause and effects. Plugins may modify this object to alter the final damage applied." },
+ },
+ Returns = [[
+ If the function returns false or no value, other plugins' callbacks are called and then the server
+ applies the final values from the TDI object to Receiver. If the function returns true, no other
+ callbacks are called, and no damage nor effects are applied.
+ ]],
+ }, -- HOOK_TAKE_DAMAGE
+
+ HOOK_TICK =
+ {
+ CalledWhen = "Every server tick (approximately 20 times per second)",
+ DefaultFnName = "OnTick", -- also used as pagename
+ Desc = [[
+ This hook is called every game tick (50 msec, or 20 times a second). If the server is overloaded,
+ the interval is larger, which is indicated by the TimeDelta parameter.</p>
+ <p>
+ This hook is called in the context of the server-tick thread, that is, the thread that takes care of
+ {{cClientHandle|client connections}} before they're assigned to {{cPlayer|player entities}}, and
+ processing console commands.
+ ]],
+ Params =
+ {
+ { Name = "TimeDelta", Type = "number", Notes = "The number of milliseconds elapsed since the last server tick. Will not be less than 50 msec." },
+ },
+ Returns = [[
+ If the function returns false or no value, other plugins' callbacks are called. If the function
+ returns true, no other callbacks are called. There is no overridable behavior.
+ ]],
+ }, -- HOOK_TICK
+
+ HOOK_UPDATED_SIGN =
+ {
+ CalledWhen = "After the sign text is updated. Notification only.",
+ DefaultFnName = "OnUpdatedSign", -- also used as pagename
+ Desc = [[
+ This hook is called after a sign has had its text updated. The text is already updated at this
+ point.</p>
+ <p>The update may have been caused either by a {{cPlayer|player}} directly updating the sign, or by
+ a plugin changing the sign text using the API.</p>
+ <p>
+ See also the {{OnUpdatingSign|HOOK_UPDATING_SIGN}} hook for a similar hook called before the update,
+ with a chance to modify the text.
+ ]],
+ Params =
+ {
+ { Name = "World", Type = "{{cWorld}}", Notes = "The world in which the sign resides" },
+ { Name = "BlockX", Type = "number", Notes = "X-coord of the sign" },
+ { Name = "BlockY", Type = "number", Notes = "Y-coord of the sign" },
+ { Name = "BlockZ", Type = "number", Notes = "Z-coord of the sign" },
+ { Name = "Line1", Type = "string", Notes = "1st line of the new text" },
+ { Name = "Line2", Type = "string", Notes = "2nd line of the new text" },
+ { Name = "Line3", Type = "string", Notes = "3rd line of the new text" },
+ { Name = "Line4", Type = "string", Notes = "4th line of the new text" },
+ { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who is changing the text. May be nil for non-player updates." }
+ },
+ Returns = [[
+ If the function returns false or no value, other plugins' callbacks are called. If the function
+ returns true, no other callbacks are called. There is no overridable behavior.
+ ]],
+ }, -- HOOK_UPDATED_SIGN
+ HOOK_UPDATING_SIGN =
+ {
+ CalledWhen = "Before the sign text is updated. Plugin may modify the text / refuse.",
+ DefaultFnName = "OnUpdatingSign", -- also used as pagename
+ Desc = [[
+ This hook is called when a sign text is about to be updated, either as a result of player's
+ manipulation or any other event, such as a plugin setting the sign text. Plugins may modify the text
+ or refuse the update altogether.</p>
+ <p>
+ See also the {{OnUpdatedSign|HOOK_UPDATED_SIGN}} hook for a similar hook called after the update.
+ ]],
+ Params =
+ {
+ { Name = "World", Type = "{{cWorld}}", Notes = "The world in which the sign resides" },
+ { Name = "BlockX", Type = "number", Notes = "X-coord of the sign" },
+ { Name = "BlockY", Type = "number", Notes = "Y-coord of the sign" },
+ { Name = "BlockZ", Type = "number", Notes = "Z-coord of the sign" },
+ { Name = "Line1", Type = "string", Notes = "1st line of the new text" },
+ { Name = "Line2", Type = "string", Notes = "2nd line of the new text" },
+ { Name = "Line3", Type = "string", Notes = "3rd line of the new text" },
+ { Name = "Line4", Type = "string", Notes = "4th line of the new text" },
+ { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who is changing the text. May be nil for non-player updates." }
+ },
+ Returns = [[
+ The function may return up to five values. If the function returns true as the first value, no other
+ callbacks are called for this event and the sign is not updated. If the function returns no value or
+ false as its first value, other plugins' callbacks are called.</p>
+ <p>
+ The other up to four values returned are used to update the sign text, line by line, respectively.
+ Note that other plugins may again update the texts (if the first value returned is false).
+ ]],
+ CodeExamples =
+ {
+ {
+ Title = "Add player signature",
+ Desc = "The following example appends a player signature to the last line, if the sign is updated by a player:",
+ Code = [[
+function OnUpdatingSign(World, BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4, Player)
+ if (Player == nil) then
+ -- Not changed by a player
+ return false;
+ end
+
+ -- Sign with playername, allow other plugins to interfere:
+ return false, Line1, Line2, Line3, Line4 .. Player:GetName();
+end
+ ]],
+ }
+ } ,
+ }, -- HOOK_UPDATING_SIGN
+
+ HOOK_WEATHER_CHANGED =
+ {
+ CalledWhen = "The weather has changed",
+ DefaultFnName = "OnWeatherChanged", -- also used as pagename
+ Desc = [[
+ This hook is called after the weather has changed in a {{cWorld|world}}. The new weather has already
+ been sent to the clients.</p>
+ <p>
+ See also the {{OnWeatherChanging|HOOK_WEATHER_CHANGING}} hook for a similar hook called before the
+ change.
+ ]],
+ Params =
+ {
+ { Name = "World", Type = "{{cWorld}}", Notes = "World for which the weather has changed" },
+ },
+ 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 behavior.
+ ]],
+ }, -- HOOK_WEATHER_CHANGED
+
+ HOOK_WEATHER_CHANGING =
+ {
+ CalledWhen = "The weather is about to change",
+ DefaultFnName = "OnWeatherChanging", -- also used as pagename
+ Desc = [[
+ This hook is called when the current weather has expired and a new weather is selected. Plugins may
+ override the new weather setting.</p>
+ <p>
+ The new weather setting is sent to the clients only after this hook has been processed.</p>
+ <p>
+ See also the {{OnWeatherChanged|HOOK_WEATHER_CHANGED}} hook for a similar hook called after the
+ change.
+ ]],
+ Params =
+ {
+ { Name = "World", Type = "{{cWorld}}", Notes = "World for which the weather is changing" },
+ { Name = "Weather", Type = "number", Notes = "The newly selected weather. One of wSunny, wRain, wStorm" },
+ },
+ Returns = [[
+ If the function returns false or no value, the server calls other plugins' callbacks and finally
+ sets the weather. If the function returns true, the server takes the second returned value (wSunny
+ by default) and sets it as the new weather. No other plugins' callbacks are called in this case.
+ ]],
+ }, -- HOOK_WEATHER_CHANGING
+
+ HOOK_WORLD_TICK =
+ {
+ CalledWhen = "Every world tick (about 20 times per second), separately for each world",
+ DefaultFnName = "OnWorldTick", -- also used as pagename
+ Desc = [[
+ This hook is called for each {{cWorld|world}} every tick (50 msec, or 20 times a second). If the
+ world is overloaded, the interval is larger, which is indicated by the TimeDelta parameter.</p>
+ <p>
+ This hook is called in the world's tick thread context and thus has access to all world data
+ guaranteed without blocking.
+ ]],
+ Params =
+ {
+ { Name = "World", Type = "{{cWorld}}", Notes = "World that is ticking" },
+ { Name = "TimeDelta", Type = "number", Notes = "The number of milliseconds since the previous game tick. Will not be less than 50 msec" },
+ },
+ 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 behavior.
+ ]],
+ }, -- HOOK_WORLD_TICK
+
}, -- Hooks[]
-
+
IgnoreClasses =
{
@@ -3064,8 +3680,10 @@ end;
"os",
"string",
"table",
+ "g_TrackedPages",
+ "g_Stats",
},
-
+
IgnoreFunctions =
{
"Globals.assert",
@@ -3076,19 +3694,28 @@ end;
"%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",
+ "ListMissingPages",
+ "ListUndocumentedObjects",
+ "ListUnexportedObjects",
"ReadDescriptions",
"ReadHooks",
"WriteHtmlClass",
"WriteHtmlHook",
+ "WriteStats",
},
+ IgnoreVariables =
+ {
+ "__.*__", -- tolua exports multiple inheritance this way
+ } ,
+
ExtraPages =
{
-- No sorting is provided for these, they will be output in the same order as defined here
diff --git a/MCServer/Plugins/APIDump/LICENSE-prettify.txt b/MCServer/Plugins/APIDump/LICENSE-prettify.txt
new file mode 100644
index 000000000..b7f86df20
--- /dev/null
+++ b/MCServer/Plugins/APIDump/LICENSE-prettify.txt
@@ -0,0 +1,191 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ Copyright 2011 Mike Samuel et al
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/MCServer/Plugins/APIDump/WebWorldThreads.html b/MCServer/Plugins/APIDump/WebWorldThreads.html
index a77209b0b..2f117ab7c 100644
--- a/MCServer/Plugins/APIDump/WebWorldThreads.html
+++ b/MCServer/Plugins/APIDump/WebWorldThreads.html
@@ -1,64 +1,69 @@
+<!DOCTYPE html>
<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>
+ <head>
+ <title>MCServer - Webserver vs World threads</title>
+ <link rel="stylesheet" type="text/css" href="main.css" />
+ <link rel="stylesheet" type="text/css" href="prettify.css" />
+ <script src="prettify.js"></script>
+ <script 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>
-<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>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>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>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>
+ <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>
+ <script>
+ prettyPrint();
+ </script>
+ </body>
</html> \ No newline at end of file
diff --git a/MCServer/Plugins/APIDump/lang-lua.js b/MCServer/Plugins/APIDump/lang-lua.js
new file mode 100644
index 000000000..7e44cca0a
--- /dev/null
+++ b/MCServer/Plugins/APIDump/lang-lua.js
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$))/,null,"\"'"]],[["com",/^--(?:\[(=*)\[[\S\s]*?(?:]\1]|$)|[^\n\r]*)/],["str",/^\[(=*)\[[\S\s]*?(?:]\1]|$)/],["kwd",/^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,null],["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i],
+["pln",/^[_a-z]\w*/i],["pun",/^[^\w\t\n\r \xa0][^\w\t\n\r "'+=\xa0-]*/]]),["lua"]);
diff --git a/MCServer/Plugins/APIDump/main.css b/MCServer/Plugins/APIDump/main.css
index 777f6d71a..5cc603a3f 100644
--- a/MCServer/Plugins/APIDump/main.css
+++ b/MCServer/Plugins/APIDump/main.css
@@ -1,3 +1,8 @@
+html
+{
+ background-color: #C0C0C0;
+}
+
table
{
background-color: #fff;
@@ -25,4 +30,27 @@ pre
{
border: 1px solid #ccc;
background-color: #eee;
-} \ No newline at end of file
+}
+
+body
+{
+ min-width: 800px;
+ width: 95%;
+ margin: 10px auto;
+ background-color: white;
+ border: 4px #FF8C00 solid;
+ border-radius: 20px;
+ font-family: Calibri, Trebuchet MS;
+}
+
+header
+{
+ text-align: center;
+ font-family: Segoe UI Light, Helvetica;
+}
+
+#content
+{
+ padding: 0px 25px 25px 25px;
+}
+
diff --git a/MCServer/Plugins/APIDump/main.lua b/MCServer/Plugins/APIDump/main.lua
index 163c505b2..2db8b4b1b 100644
--- a/MCServer/Plugins/APIDump/main.lua
+++ b/MCServer/Plugins/APIDump/main.lua
@@ -10,6 +10,22 @@
-- Global variables:
g_Plugin = nil;
g_PluginFolder = "";
+g_TrackedPages = {}; -- List of tracked pages, to be checked later whether they exist. Each item is an array of referring pagenames.
+g_Stats = -- Statistics about the documentation
+{
+ NumTotalClasses = 0,
+ NumUndocumentedClasses = 0,
+ NumTotalFunctions = 0,
+ NumUndocumentedFunctions = 0,
+ NumTotalConstants = 0,
+ NumUndocumentedConstants = 0,
+ NumTotalVariables = 0,
+ NumUndocumentedVariables = 0,
+ NumTotalHooks = 0,
+ NumUndocumentedHooks = 0,
+ NumTrackedLinks = 0,
+ NumInvalidLinks = 0,
+}
@@ -22,7 +38,7 @@ function Initialize(Plugin)
Plugin:SetName("APIDump");
Plugin:SetVersion(1);
- LOG("Initialized " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
+ LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
g_PluginFolder = Plugin:GetLocalFolder();
@@ -88,7 +104,9 @@ function CreateAPITables()
{Name = "IsInside"}
},
Constants = {
- }
+ },
+ Variables = {
+ },
Descendants = {}, -- Will be filled by ReadDescriptions(), array of class APIs (references to other member in the tree)
}},
{
@@ -97,12 +115,14 @@ function CreateAPITables()
{Name = "Clear"},
{Name = "CopyFrom"},
...
- }
+ },
Constants = {
{Name = "baTypes", Value = 0},
{Name = "baMetas", Value = 1},
...
- }
+ },
+ Variables = {
+ },
...
}}
};
@@ -116,7 +136,7 @@ function CreateAPITables()
};
--]]
- local Globals = {Functions = {}, Constants = {}, Descendants = {}};
+ local Globals = {Functions = {}, Constants = {}, Variables = {}, Descendants = {}};
local API = {};
local function Add(a_APIContainer, a_ObjName, a_ObjValue)
@@ -131,10 +151,25 @@ function CreateAPITables()
end
local function ParseClass(a_ClassName, a_ClassObj)
- local res = {Name = a_ClassName, Functions = {}, Constants = {}, Descendants = {}};
+ local res = {Name = a_ClassName, Functions = {}, Constants = {}, Variables = {}, Descendants = {}};
+ -- Add functions and constants:
for i, v in pairs(a_ClassObj) do
Add(res, i, v);
end
+
+ -- Member variables:
+ local SetField = a_ClassObj[".set"] or {};
+ if ((a_ClassObj[".get"] ~= nil) and (type(a_ClassObj[".get"]) == "table")) then
+ for k, v in pairs(a_ClassObj[".get"]) do
+ if (SetField[k] == nil) then
+ -- It is a read-only variable, add it as a constant:
+ table.insert(res.Constants, {Name = k, Value = ""});
+ else
+ -- It is a read-write variable, add it as a variable:
+ table.insert(res.Variables, { Name = k });
+ end
+ end
+ end
return res;
end
@@ -174,6 +209,8 @@ function DumpAPIHtml()
end
);
+ g_Stats.NumTotalClasses = #API;
+
-- Add Globals into the API:
Globals.Name = "Globals";
table.insert(API, Globals);
@@ -212,48 +249,70 @@ function DumpAPIHtml()
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>
- ]]);
+ f:write([[<!DOCTYPE html>
+<html>
+ <head>
+ <title>MCServer API - Index</title>
+ <link rel="stylesheet" type="text/css" href="main.css" />
+ </head>
+ <body>
+ <div id="content">
+ <header>
+ <h1>MCServer API - Index</h1>
+ <hr />
+ </header>
+ <p>The API reference is divided into the following sections:</p>
+
+ <ul>
+ <li><a href="#classes">Class index</a></li>
+ <li><a href="#hooks">Hooks</a></li>
+ <li><a href="#extra">Extra pages</a></li>
+ <li><a href="#docstats">Documentation statistics</a></li>
+ </ul>
+
+ <hr />
+ <a name="classes"><h2>Class index</h2></a>
+ <p>The following classes are available in the MCServer Lua scripting language:</p>
+
+ <ul>
+]]);
for i, cls in ipairs(API) do
- f:write("<li><a href=\"" .. cls.Name .. ".html\">" .. cls.Name .. "</a></li>\n");
+ 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>
- ]]);
+ f:write([[ </ul>
+
+ <hr />
+ <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");
+ f:write(" <tr>\n <td>" .. hook.Name .. "</td>\n <td><i>(No documentation yet)</i></td>\n </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");
+ f:write(" <tr>\n <td><a href=\"" .. hook.DefaultFnName .. ".html\">" .. hook.Name .. "</a></td>\n <td>" .. LinkifyString(hook.CalledWhen, hook.Name) .. "</td>\n </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>]]);
+ f:write([[ </table>
+
+ <hr />
+ <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
@@ -262,120 +321,31 @@ function DumpAPIHtml()
cFile:Delete(DstFileName);
end
cFile:Copy(SrcFileName, DstFileName);
- f:write("<li><a href=\"" .. extra.FileName .. "\">" .. extra.Title .. "</a></li>\n");
+ 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();
+ f:write(" <li>" .. extra.Title .. " <i>(file is missing)</i></li>\n");
end
- cssf:close();
end
+ f:write("</ul>");
+
+ -- Copy the static files to the output folder (overwrite any existing):
+ cFile:Copy(g_Plugin:GetLocalFolder() .. "/main.css", "API/main.css");
+ cFile:Copy(g_Plugin:GetLocalFolder() .. "/prettify.js", "API/prettify.js");
+ cFile:Copy(g_Plugin:GetLocalFolder() .. "/prettify.css", "API/prettify.css");
+ cFile:Copy(g_Plugin:GetLocalFolder() .. "/lang-lua.js", "API/lang-lua.js");
- -- 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 documentation problems:
+ ListUndocumentedObjects(API, UndocumentedHooks);
+ ListUnexportedObjects();
+ ListMissingPages();
+
+ WriteStats(f);
- -- 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
+ f:write([[ </ul>
+ </div>
+ </body>
+</html>]]);
+ f:close();
LOG("API subfolder written");
end
@@ -424,6 +394,19 @@ function ReadDescriptions(a_API)
return false;
end
+ -- Returns true if the member variable (specified by its fully qualified name) is to be ignored
+ local function IsVariableIgnored(a_VarName)
+ if (g_APIDesc.IgnoreVariables == nil) then
+ return false;
+ end;
+ for i, name in ipairs(g_APIDesc.IgnoreVariables) do
+ if (a_VarName: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
@@ -478,6 +461,7 @@ function ReadDescriptions(a_API)
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
+ cls.UndocumentedVariables = {}; -- This will contain names of all the variables that are not documented
local DoxyFunctions = {}; -- This will contain all the API functions together with their documentation
@@ -513,6 +497,12 @@ function ReadDescriptions(a_API)
-- Replace functions with their described and overload-expanded versions:
cls.Functions = DoxyFunctions;
+ else -- if (APIDesc.Functions ~= nil)
+ for j, func in ipairs(cls.Functions) do
+ if not(IsFunctionIgnored(cls.Name .. "." .. FnName)) then
+ table.insert(cls.UndocumentedFunctions, FnName);
+ end
+ end
end -- if (APIDesc.Functions ~= nil)
if (APIDesc.Constants ~= nil) then
@@ -529,11 +519,46 @@ function ReadDescriptions(a_API)
CnDesc.IsExported = true;
end
end -- for j, cons
- end -- if (APIDesc.Constants ~= nil)
- else
+ else -- if (APIDesc.Constants ~= nil)
+ for j, cons in ipairs(cls.Constants) do
+ if not(IsConstantIgnored(cls.Name .. "." .. cons.Name)) then
+ table.insert(cls.UndocumentedConstants, cons.Name);
+ end
+ end
+ end -- else if (APIDesc.Constants ~= nil)
+
+ -- Assign member variables' descriptions:
+ if (APIDesc.Variables ~= nil) then
+ for j, var in ipairs(cls.Variables) do
+ local VarDesc = APIDesc.Variables[var.Name];
+ if (VarDesc == nil) then
+ -- Not documented
+ if not(IsVariableIgnored(cls.Name .. "." .. var.Name)) then
+ table.insert(cls.UndocumentedVariables, var.Name);
+ end
+ else
+ -- Copy all documentation:
+ for k, v in pairs(VarDesc) do
+ var[k] = v
+ end
+ end
+ end -- for j, var
+ else -- if (APIDesc.Variables ~= nil)
+ for j, var in ipairs(cls.Variables) do
+ if not(IsVariableIgnored(cls.Name .. "." .. var.Name)) then
+ table.insert(cls.UndocumentedVariables, var.Name);
+ end
+ end
+ end -- else if (APIDesc.Variables ~= nil)
+
+ else -- if (APIDesc ~= nil)
+
-- Class is not documented at all, add all its members to Undocumented lists:
cls.UndocumentedFunctions = {};
cls.UndocumentedConstants = {};
+ cls.UndocumentedVariables = {};
+ cls.Variables = cls.Variables or {};
+ g_Stats.NumUndocumentedClasses = g_Stats.NumUndocumentedClasses + 1;
for j, func in ipairs(cls.Functions) do
local FnName = func.DocID or func.Name;
if not(IsFunctionIgnored(cls.Name .. "." .. FnName)) then
@@ -545,6 +570,11 @@ function ReadDescriptions(a_API)
table.insert(cls.UndocumentedConstants, cons.Name);
end
end -- for j, cons - cls.Constants[]
+ for j, var in ipairs(cls.Variables) do
+ if not(IsConstantIgnored(cls.Name .. "." .. var.Name)) then
+ table.insert(cls.UndocumentedVariables, var.Name);
+ end
+ end -- for j, var - cls.Variables[]
end -- else if (APIDesc ~= nil)
-- Remove ignored functions:
@@ -576,6 +606,22 @@ function ReadDescriptions(a_API)
return (c1.Name < c2.Name);
end
);
+
+ -- Remove ignored functions:
+ local NewVariables = {};
+ for j, var in ipairs(cls.Variables) do
+ if (not(IsVariableIgnored(cls.Name .. "." .. var.Name))) then
+ table.insert(NewVariables, var);
+ end
+ end -- for j, var
+ cls.Variables = NewVariables;
+
+ -- Sort the member variables:
+ table.sort(cls.Variables,
+ function(v1, v2)
+ return (v1.Name < v2.Name);
+ end
+ );
end -- for i, cls
-- Sort the descendants lists:
@@ -609,6 +655,7 @@ function ReadHooks(a_Hooks)
end
end
end -- for i, hook - a_Hooks[]
+ g_Stats.NumTotalHooks = #a_Hooks;
end
@@ -616,7 +663,18 @@ end
-- Make a link out of anything with the special linkifying syntax {{link|title}}
-function LinkifyString(a_String)
+function LinkifyString(a_String, a_Referrer)
+ assert(a_Referrer ~= nil);
+ assert(a_Referrer ~= "");
+
+ --- Adds a page to the list of tracked pages (to be checked for existence at the end)
+ local function AddTrackedPage(a_PageName)
+ local Pg = (g_TrackedPages[a_PageName] or {});
+ table.insert(Pg, a_Referrer);
+ g_TrackedPages[a_PageName] = Pg;
+ end
+
+ --- Creates the HTML for the specified link and title
local function CreateLink(Link, Title)
if (Link:sub(1, 7) == "http://") then
-- The link is a full absolute URL, do not modify, do not track:
@@ -630,16 +688,17 @@ function LinkifyString(a_String)
return "<a href=\"" .. Link .. "\">" .. Title .. "</a>";
end
-- Anchor in another page:
- -- TODO: track this link
- return "<a href=\"" .. Link:sub(1, idxHash - 1) .. ".html#" .. Link:sub(idxHash + 1) .. "\">" .. Title .. "</a>";
+ local PageName = Link:sub(1, idxHash - 1);
+ AddTrackedPage(PageName);
+ return "<a href=\"" .. PageName .. ".html#" .. Link:sub(idxHash + 1) .. "\">" .. Title .. "</a>";
end
-- Link without anchor:
- -- TODO; track this link
+ AddTrackedPage(Link);
return "<a href=\"" .. Link .. ".html\">" .. Title .. "</a>";
end
+ -- Linkify the strings using the CreateLink() function:
local txt = a_String:gsub("{{([^|}]*)|([^}]*)}}", CreateLink) -- {{link|title}}
-
txt = txt:gsub("{{([^|}]*)}}", -- {{LinkAndTitle}}
function(LinkAndTitle)
local idxHash = LinkAndTitle:find("#");
@@ -670,16 +729,52 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI)
end
if (a_InheritedName ~= nil) then
- cf:write("<h2>Functions inherited from " .. a_InheritedName .. "</h2>");
+ cf:write(" <h2>Functions inherited from " .. a_InheritedName .. "</h2>\n");
end
- cf:write("<table><tr><th>Name</th><th>Parameters</th><th>Return value</th><th>Notes</th></tr>\n");
+ cf:write(" <table>\n <tr>\n <th>Name</th>\n <th>Parameters</th>\n <th>Return value</th>\n <th>Notes</th>\n </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");
+ cf:write(" <tr>\n <td>" .. func.Name .. "</td>\n");
+ cf:write(" <td>" .. LinkifyString(func.Params or "", (a_InheritedName or a_ClassAPI.Name)).. "</td>\n");
+ cf:write(" <td>" .. LinkifyString(func.Return or "", (a_InheritedName or a_ClassAPI.Name)).. "</td>\n");
+ cf:write(" <td>" .. LinkifyString(func.Notes or "<i>(undocumented)</i>", (a_InheritedName or a_ClassAPI.Name)) .. "</td>\n </tr>\n");
+ end
+ cf:write(" </table>\n\n");
+ end
+
+ local function WriteConstants(a_Constants, a_InheritedName)
+ if (#a_Constants == 0) then
+ return;
+ end
+
+ if (a_InheritedName ~= nil) then
+ cf:write(" <h2>Constants inherited from " .. a_InheritedName .. "</h2>\n");
+ end
+
+ cf:write(" <table>\n <tr>\n <th>Name</th>\n <th>Value</th>\n <th>Notes</th>\n </tr>\n");
+ for i, cons in ipairs(a_Constants) do
+ cf:write(" <tr>\n <td>" .. cons.Name .. "</td>\n");
+ cf:write(" <td>" .. cons.Value .. "</td>\n");
+ cf:write(" <td>" .. LinkifyString(cons.Notes or "", a_InheritedName or a_ClassAPI.Name) .. "</td>\n </tr>\n");
+ end
+ cf:write(" </table>\n\n");
+ end
+
+ local function WriteVariables(a_Variables, a_InheritedName)
+ if (#a_Variables == 0) then
+ return;
+ end
+
+ if (a_InheritedName ~= nil) then
+ cf:write(" <h2>Member variables inherited from " .. a_InheritedName .. "</h2>\n");
end
- cf:write("</table>\n");
+
+ cf:write(" <table>\n <tr>\n <th>Name</th>\n <th>Type</th>\n <th>Notes</th>\n </tr>\n");
+ for i, var in ipairs(a_Variables) do
+ cf:write(" <tr>\n <td>" .. var.Name .. "</td>\n");
+ cf:write(" <td>" .. LinkifyString(var.Type or "<i>(undocumented)</i>", a_InheritedName or a_ClassAPI.Name) .. "</td>\n");
+ cf:write(" <td>" .. LinkifyString(var.Notes or "", a_InheritedName or a_ClassAPI.Name) .. "</td>\n </tr>\n");
+ end
+ cf:write(" </table>\n\n");
end
local function WriteDescendants(a_Descendants)
@@ -694,6 +789,8 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI)
end
cf:write("</ul>\n");
end
+
+ local ClassName = a_ClassAPI.Name;
-- Build an array of inherited classes chain:
local InheritanceChain = {};
@@ -703,81 +800,128 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI)
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>
- ]]);
+ cf:write([[<!DOCTYPE html>
+<html>
+ <head>
+ <title>MCServer API - ]] .. a_ClassAPI.Name .. [[ Class</title>
+ <link rel="stylesheet" type="text/css" href="main.css" />
+ <link rel="stylesheet" type="text/css" href="prettify.css" />
+ <script src="prettify.js"></script>
+ <script src="lang-lua.js"></script>
+ </head>
+ <body>
+ <div id="content">
+ <header>
+ <h1>]] .. a_ClassAPI.Name .. [[</h1>
+ <hr />
+ </header>
+ <h1>Contents</h1>
+
+ <ul>
+]]);
local HasInheritance = ((#a_ClassAPI.Descendants > 0) or (a_ClassAPI.Inherits ~= nil));
+ local HasConstants = (#a_ClassAPI.Constants > 0);
+ local HasFunctions = (#a_ClassAPI.Functions > 0);
+ local HasVariables = (#a_ClassAPI.Variables > 0);
+ for idx, cls in ipairs(InheritanceChain) do
+ HasConstants = HasConstants or (#cls.Constants > 0);
+ HasFunctions = HasFunctions or (#cls.Functions > 0);
+ HasVariables = HasVariables or (#cls.Variables > 0);
+ end
+
-- Write the table of contents:
if (HasInheritance) then
- cf:write("<li><a href=\"#inherits\">Inheritance</a></li>\n");
+ cf:write(" <li><a href=\"#inherits\">Inheritance</a></li>\n");
+ end
+ if (HasConstants) then
+ cf:write(" <li><a href=\"#constants\">Constants</a></li>\n");
+ end
+ if (HasVariables) then
+ cf:write(" <li><a href=\"#variables\">Member variables</a></li>\n");
+ end
+ if (HasFunctions) then
+ cf:write(" <li><a href=\"#functions\">Functions</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");
+ cf:write(" <li><a href=\"#additionalinfo_" .. i .. "\">" .. (additional.Header or "<i>(No header)</i>").. "</a></li>\n");
end
end
- cf:write("</ul>");
+ cf:write(" </ul>\n\n");
-- Write the class description:
- cf:write("<a name=\"desc\"><h1>" .. a_ClassAPI.Name .. " class</h1></a>\n");
+ cf:write(" <a name=\"desc\"><hr /><h1>" .. ClassName .. " class</h1></a>\n");
if (a_ClassAPI.Desc ~= nil) then
- cf:write("<p>");
- cf:write(LinkifyString(a_ClassAPI.Desc));
- cf:write("</p>\n");
+ cf:write(" <p>");
+ cf:write(LinkifyString(a_ClassAPI.Desc, ClassName));
+ cf:write(" </p>\n\n");
end;
-- Write the inheritance, if available:
if (HasInheritance) then
- cf:write("<a name=\"inherits\"><h1>Inheritance</h1></a>\n");
+ cf:write(" <a name=\"inherits\">\n <hr /><h1>Inheritance</h1></a>\n");
if (#InheritanceChain > 0) then
- cf:write("<p>This class inherits from the following parent classes:<ul>\n");
+ cf:write(" <p>This class inherits from the following parent classes:</p>\n\n <ul>\n");
for i, cls in ipairs(InheritanceChain) do
- cf:write("<li><a href=\"" .. cls.Name .. ".html\">" .. cls.Name .. "</a></li>\n");
+ cf:write(" <li><a href=\"" .. cls.Name .. ".html\">" .. cls.Name .. "</a></li>\n");
end
- cf:write("</ul></p>\n");
+ cf:write(" </ul>\n\n");
end
if (#a_ClassAPI.Descendants > 0) then
- cf:write("<p>This class has the following descendants:\n");
+ cf:write(" <p>This class has the following descendants:\n");
WriteDescendants(a_ClassAPI.Descendants);
- cf:write("</p>\n");
+ cf:write(" </p>\n\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");
+ if (HasConstants) then
+ cf:write(" <a name=\"constants\"><hr /><h1>Constants</h1></a>\n");
+ WriteConstants(a_ClassAPI.Constants, nil);
+ g_Stats.NumTotalConstants = g_Stats.NumTotalConstants + #a_ClassAPI.Constants;
+ for i, cls in ipairs(InheritanceChain) do
+ WriteConstants(cls.Constants, cls.Name);
+ end;
+ end;
+
+ -- Write the member variables:
+ if (HasVariables) then
+ cf:write(" <a name=\"variables\"><hr /><h1>Member variables</h1></a>\n");
+ WriteVariables(a_ClassAPI.Variables, nil);
+ g_Stats.NumTotalVariables = g_Stats.NumTotalVariables + #a_ClassAPI.Variables;
+ for i, cls in ipairs(InheritanceChain) do
+ WriteVariables(cls.Variables, cls.Name);
+ end;
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);
+ if (HasFunctions) then
+ cf:write(" <a name=\"functions\"><hr /><h1>Functions</h1></a>\n");
+ WriteFunctions(a_ClassAPI.Functions, nil);
+ g_Stats.NumTotalFunctions = g_Stats.NumTotalFunctions + #a_ClassAPI.Functions;
+ for i, cls in ipairs(InheritanceChain) do
+ WriteFunctions(cls.Functions, cls.Name);
+ end
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));
+ cf:write(" <a name=\"additionalinfo_" .. i .. "\"><h1>" .. additional.Header .. "</h1></a>\n");
+ cf:write(LinkifyString(additional.Contents, ClassName));
end
end
- cf:write("</body></html>");
+ cf:write([[
+ </div>
+ <script>
+ prettyPrint();
+ </script>
+ </body>
+</html>
+ ]]);
cf:close();
end
@@ -792,18 +936,29 @@ function WriteHtmlHook(a_Hook)
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 .. "(");
+ local HookName = a_Hook.DefaultFnName;
+
+ f:write([[<!DOCTYPE html>
+<html>
+ <head>
+ <title>MCServer API - ]] .. HookName .. [[ Hook</title>
+ <link rel="stylesheet" type="text/css" href="main.css" />
+ <link rel="stylesheet" type="text/css" href="prettify.css" />
+ <script src="prettify.js"></script>
+ <script src="lang-lua.js"></script>
+ </head>
+ <body>
+ <div id="content">
+ <header>
+ <h1>]] .. a_Hook.Name .. [[</h1>
+ <hr />
+ </header>
+ <p>
+]]);
+ f:write(LinkifyString(a_Hook.Desc, HookName));
+ f:write(" </p>\n <hr /><h1>Callback function</h1>\n <p>The default name for the callback function is ");
+ f:write(a_Hook.DefaultFnName .. ". It has the following signature:\n\n");
+ f:write(" <pre class=\"prettyprint lang-lua\">function " .. HookName .. "(");
if (a_Hook.Params == nil) then
a_Hook.Params = {};
end
@@ -813,26 +968,269 @@ function WriteHtmlHook(a_Hook)
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");
+ f:write(")</pre>\n\n <hr /><h1>Parameters:</h1>\n\n <table>\n <tr>\n <th>Name</th>\n <th>Type</th>\n <th>Notes</th>\n </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>
- ]]);
+ f:write(" <tr>\n <td>" .. param.Name .. "</td>\n <td>" .. LinkifyString(param.Type, HookName) .. "</td>\n <td>" .. LinkifyString(param.Notes, HookName) .. "</td>\n </tr>\n");
+ end
+ f:write(" </table>\n\n <p>" .. (a_Hook.Returns or "") .. "</p>\n\n");
+ f:write([[ <hr /><h1>Code examples</h1>
+ <h2>Registering the callback</h2>
+
+]]);
+ f:write(" <pre class=\"prettyprint lang-lua\">\n");
+ f:write([[cPluginManager.AddHook(cPluginManager.]] .. a_Hook.Name .. ", My" .. a_Hook.DefaultFnName .. [[);]]);
+ f:write("</pre>\n\n");
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");
+ f:write(" <h2>" .. (example.Title or "<i>missing Title</i>") .. "</h2>\n");
+ f:write(" <p>" .. (example.Desc or "<i>missing Desc</i>") .. "</p>\n\n");
+ f:write(" <pre class=\"prettyprint lang-lua\">" .. (example.Code or "<i>missing Code</i>") .. "\n </pre>\n\n");
end
+ f:write([[ </div>
+ <script>
+ prettyPrint();
+ </script>
+ </body>
+</html>]]);
f:close();
end
+
+--- Writes a list of undocumented objects into a file
+function ListUndocumentedObjects(API, UndocumentedHooks)
+ 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));
+ local HasVariables = ((cls.UndocumentedVariables ~= nil) and (#cls.UndocumentedVariables > 0));
+ g_Stats.NumUndocumentedFunctions = g_Stats.NumUndocumentedFunctions + #cls.UndocumentedFunctions;
+ g_Stats.NumUndocumentedConstants = g_Stats.NumUndocumentedConstants + #cls.UndocumentedConstants;
+ g_Stats.NumUndocumentedVariables = g_Stats.NumUndocumentedVariables + #cls.UndocumentedVariables;
+ if (HasFunctions or HasConstants or HasVariables) 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.UndocumentedFunctions[]
+ 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.UndocumentedConstants[]
+ f:write("\t\t\t},\n\n");
+ end
+
+ if (HasVariables) then
+ f:write("\t\t\tVariables =\n\t\t\t{\n");
+ table.sort(cls.UndocumentedVariables);
+ for j, vn in ipairs(cls.UndocumentedVariables) do
+ f:write("\t\t\t\t" .. vn .. " = { Type = \"\", Notes = \"\" },\n");
+ end -- for j, fn - cls.UndocumentedVariables[]
+ f:write("\t\t\t},\n\n");
+ end
+
+ if (HasFunctions or HasConstants or HasVariables) 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
+ f:write("\t},\n");
+ end
+ f:write("}\n\n\n\n");
+ f:close();
+ end
+ g_Stats.NumUndocumentedHooks = #UndocumentedHooks;
+end
+
+
+
+
+
+--- Lists the API objects that are documented but not available in the API:
+function ListUnexportedObjects()
+ 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
+end
+
+
+
+
+
+function ListMissingPages()
+ local MissingPages = {};
+ local NumLinks = 0;
+ for PageName, Referrers in pairs(g_TrackedPages) do
+ NumLinks = NumLinks + 1;
+ if not(cFile:Exists("API/" .. PageName .. ".html")) then
+ table.insert(MissingPages, {Name = PageName, Refs = Referrers} );
+ end
+ end;
+ g_Stats.NumTrackedLinks = NumLinks;
+ g_TrackedPages = {};
+
+ if (#MissingPages == 0) then
+ -- No missing pages, congratulations!
+ return;
+ end
+
+ -- Sort the pages by name:
+ table.sort(MissingPages,
+ function (Page1, Page2)
+ return (Page1.Name < Page2.Name);
+ end
+ );
+
+ -- Output the pages:
+ local f, err = io.open("API/_missingPages.txt", "w");
+ if (f == nil) then
+ LOGWARNING("Cannot open _missingPages.txt for writing: '" .. err .. "'. There are " .. #MissingPages .. " pages missing.");
+ return;
+ end
+ for idx, pg in ipairs(MissingPages) do
+ f:write(pg.Name .. ":\n");
+ -- Sort and output the referrers:
+ table.sort(pg.Refs);
+ f:write("\t" .. table.concat(pg.Refs, "\n\t"));
+ f:write("\n\n");
+ end
+ f:close();
+ g_Stats.NumInvalidLinks = #MissingPages;
+end
+
+
+
+
+
+--- Writes the documentation statistics (in g_Stats) into the given HTML file
+function WriteStats(f)
+ local function ExportMeter(a_Percent)
+ local Color;
+ if (a_Percent > 95) then
+ Color = "green";
+ elseif (a_Percent > 50) then
+ Color = "orange";
+ else
+ Color = "red";
+ end
+
+ local meter = {
+ "\n",
+ "<div style=\"background-color: black; padding: 1px; width: 100px\">\n",
+ "<div style=\"background-color: ",
+ Color,
+ "; width: ",
+ a_Percent,
+ "%; height: 16px\"></div></div>\n</td><td>",
+ string.format("%.2f", a_Percent),
+ " %",
+ };
+ return table.concat(meter, "");
+ end
+
+ f:write([[
+ <hr /><a name="docstats"><h2>Documentation statistics</h2></a>
+ <table><tr><th>Object</th><th>Total</th><th>Documented</th><th>Undocumented</th><th colspan="2">Documented %</th></tr>
+ ]]);
+ f:write("<tr><td>Classes</td><td>", g_Stats.NumTotalClasses);
+ f:write("</td><td>", g_Stats.NumTotalClasses - g_Stats.NumUndocumentedClasses);
+ f:write("</td><td>", g_Stats.NumUndocumentedClasses);
+ f:write("</td><td>", ExportMeter(100 * (g_Stats.NumTotalClasses - g_Stats.NumUndocumentedClasses) / g_Stats.NumTotalClasses));
+ f:write("</td></tr>\n");
+
+ f:write("<tr><td>Functions</td><td>", g_Stats.NumTotalFunctions);
+ f:write("</td><td>", g_Stats.NumTotalFunctions - g_Stats.NumUndocumentedFunctions);
+ f:write("</td><td>", g_Stats.NumUndocumentedFunctions);
+ f:write("</td><td>", ExportMeter(100 * (g_Stats.NumTotalFunctions - g_Stats.NumUndocumentedFunctions) / g_Stats.NumTotalFunctions));
+ f:write("</td></tr>\n");
+
+ f:write("<tr><td>Member variables</td><td>", g_Stats.NumTotalVariables);
+ f:write("</td><td>", g_Stats.NumTotalVariables - g_Stats.NumUndocumentedVariables);
+ f:write("</td><td>", g_Stats.NumUndocumentedVariables);
+ f:write("</td><td>", ExportMeter(100 * (g_Stats.NumTotalVariables - g_Stats.NumUndocumentedVariables) / g_Stats.NumTotalVariables));
+ f:write("</td></tr>\n");
+
+ f:write("<tr><td>Constants</td><td>", g_Stats.NumTotalConstants);
+ f:write("</td><td>", g_Stats.NumTotalConstants - g_Stats.NumUndocumentedConstants);
+ f:write("</td><td>", g_Stats.NumUndocumentedConstants);
+ f:write("</td><td>", ExportMeter(100 * (g_Stats.NumTotalConstants - g_Stats.NumUndocumentedConstants) / g_Stats.NumTotalConstants));
+ f:write("</td></tr>\n");
+
+ f:write("<tr><td>Hooks</td><td>", g_Stats.NumTotalHooks);
+ f:write("</td><td>", g_Stats.NumTotalHooks - g_Stats.NumUndocumentedHooks);
+ f:write("</td><td>", g_Stats.NumUndocumentedHooks);
+ f:write("</td><td>", ExportMeter(100 * (g_Stats.NumTotalHooks - g_Stats.NumUndocumentedHooks) / g_Stats.NumTotalHooks));
+ f:write("</td></tr>\n");
+
+ f:write([[
+ </table>
+ <p>There are ]], g_Stats.NumTrackedLinks, " internal links, ", g_Stats.NumInvalidLinks, " of them are invalid.</p>"
+ );
+end
+
+
+
+
diff --git a/MCServer/Plugins/APIDump/prettify.css b/MCServer/Plugins/APIDump/prettify.css
new file mode 100644
index 000000000..d44b3a228
--- /dev/null
+++ b/MCServer/Plugins/APIDump/prettify.css
@@ -0,0 +1 @@
+.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} \ No newline at end of file
diff --git a/MCServer/Plugins/APIDump/prettify.js b/MCServer/Plugins/APIDump/prettify.js
new file mode 100644
index 000000000..7b990496d
--- /dev/null
+++ b/MCServer/Plugins/APIDump/prettify.js
@@ -0,0 +1,30 @@
+!function(){var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
+(function(){function S(a){function d(e){var b=e.charCodeAt(0);if(b!==92)return b;var a=e.charAt(1);return(b=r[a])?b:"0"<=a&&a<="7"?parseInt(e.substring(1),8):a==="u"||a==="x"?parseInt(e.substring(2),16):e.charCodeAt(1)}function g(e){if(e<32)return(e<16?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);return e==="\\"||e==="-"||e==="]"||e==="^"?"\\"+e:e}function b(e){var b=e.substring(1,e.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),e=[],a=
+b[0]==="^",c=["["];a&&c.push("^");for(var a=a?1:0,f=b.length;a<f;++a){var h=b[a];if(/\\[bdsw]/i.test(h))c.push(h);else{var h=d(h),l;a+2<f&&"-"===b[a+1]?(l=d(b[a+2]),a+=2):l=h;e.push([h,l]);l<65||h>122||(l<65||h>90||e.push([Math.max(65,h)|32,Math.min(l,90)|32]),l<97||h>122||e.push([Math.max(97,h)&-33,Math.min(l,122)&-33]))}}e.sort(function(e,a){return e[0]-a[0]||a[1]-e[1]});b=[];f=[];for(a=0;a<e.length;++a)h=e[a],h[0]<=f[1]+1?f[1]=Math.max(f[1],h[1]):b.push(f=h);for(a=0;a<b.length;++a)h=b[a],c.push(g(h[0])),
+h[1]>h[0]&&(h[1]+1>h[0]&&c.push("-"),c.push(g(h[1])));c.push("]");return c.join("")}function s(e){for(var a=e.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),c=a.length,d=[],f=0,h=0;f<c;++f){var l=a[f];l==="("?++h:"\\"===l.charAt(0)&&(l=+l.substring(1))&&(l<=h?d[l]=-1:a[f]=g(l))}for(f=1;f<d.length;++f)-1===d[f]&&(d[f]=++x);for(h=f=0;f<c;++f)l=a[f],l==="("?(++h,d[h]||(a[f]="(?:")):"\\"===l.charAt(0)&&(l=+l.substring(1))&&l<=h&&
+(a[f]="\\"+d[l]);for(f=0;f<c;++f)"^"===a[f]&&"^"!==a[f+1]&&(a[f]="");if(e.ignoreCase&&m)for(f=0;f<c;++f)l=a[f],e=l.charAt(0),l.length>=2&&e==="["?a[f]=b(l):e!=="\\"&&(a[f]=l.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var x=0,m=!1,j=!1,k=0,c=a.length;k<c;++k){var i=a[k];if(i.ignoreCase)j=!0;else if(/[a-z]/i.test(i.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){m=!0;j=!1;break}}for(var r={b:8,t:9,n:10,v:11,
+f:12,r:13},n=[],k=0,c=a.length;k<c;++k){i=a[k];if(i.global||i.multiline)throw Error(""+i);n.push("(?:"+s(i)+")")}return RegExp(n.join("|"),j?"gi":"g")}function T(a,d){function g(a){var c=a.nodeType;if(c==1){if(!b.test(a.className)){for(c=a.firstChild;c;c=c.nextSibling)g(c);c=a.nodeName.toLowerCase();if("br"===c||"li"===c)s[j]="\n",m[j<<1]=x++,m[j++<<1|1]=a}}else if(c==3||c==4)c=a.nodeValue,c.length&&(c=d?c.replace(/\r\n?/g,"\n"):c.replace(/[\t\n\r ]+/g," "),s[j]=c,m[j<<1]=x,x+=c.length,m[j++<<1|1]=
+a)}var b=/(?:^|\s)nocode(?:\s|$)/,s=[],x=0,m=[],j=0;g(a);return{a:s.join("").replace(/\n$/,""),d:m}}function H(a,d,g,b){d&&(a={a:d,e:a},g(a),b.push.apply(b,a.g))}function U(a){for(var d=void 0,g=a.firstChild;g;g=g.nextSibling)var b=g.nodeType,d=b===1?d?a:g:b===3?V.test(g.nodeValue)?a:d:d;return d===a?void 0:d}function C(a,d){function g(a){for(var j=a.e,k=[j,"pln"],c=0,i=a.a.match(s)||[],r={},n=0,e=i.length;n<e;++n){var z=i[n],w=r[z],t=void 0,f;if(typeof w==="string")f=!1;else{var h=b[z.charAt(0)];
+if(h)t=z.match(h[1]),w=h[0];else{for(f=0;f<x;++f)if(h=d[f],t=z.match(h[1])){w=h[0];break}t||(w="pln")}if((f=w.length>=5&&"lang-"===w.substring(0,5))&&!(t&&typeof t[1]==="string"))f=!1,w="src";f||(r[z]=w)}h=c;c+=z.length;if(f){f=t[1];var l=z.indexOf(f),B=l+f.length;t[2]&&(B=z.length-t[2].length,l=B-f.length);w=w.substring(5);H(j+h,z.substring(0,l),g,k);H(j+h+l,f,I(w,f),k);H(j+h+B,z.substring(B),g,k)}else k.push(j+h,w)}a.g=k}var b={},s;(function(){for(var g=a.concat(d),j=[],k={},c=0,i=g.length;c<i;++c){var r=
+g[c],n=r[3];if(n)for(var e=n.length;--e>=0;)b[n.charAt(e)]=r;r=r[1];n=""+r;k.hasOwnProperty(n)||(j.push(r),k[n]=q)}j.push(/[\S\s]/);s=S(j)})();var x=d.length;return g}function v(a){var d=[],g=[];a.tripleQuotedStrings?d.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?d.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
+q,"'\"`"]):d.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&g.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var b=a.hashComments;b&&(a.cStyleComments?(b>1?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),g.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,q])):d.push(["com",
+/^#[^\n\r]*/,q,"#"]));a.cStyleComments&&(g.push(["com",/^\/\/[^\n\r]*/,q]),g.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));if(b=a.regexLiterals){var s=(b=b>1?"":"\n\r")?".":"[\\S\\s]";g.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+s+"|\\x5B(?:[^\\x5C\\x5D"+b+"]|\\x5C"+
+s+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&g.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&g.push(["kwd",RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),q]);d.push(["pln",/^\s+/,q," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");g.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,
+q],["pun",RegExp(b),q]);return C(d,g)}function J(a,d,g){function b(a){var c=a.nodeType;if(c==1&&!x.test(a.className))if("br"===a.nodeName)s(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&g){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(j.createTextNode(d),a.nextSibling),s(a),c||a.parentNode.removeChild(a)}}function s(a){function b(a,c){var d=
+c?a.cloneNode(!1):a,e=a.parentNode;if(e){var e=b(e,1),g=a.nextSibling;e.appendChild(d);for(var i=g;i;i=g)g=i.nextSibling,e.appendChild(i)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var x=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,j=a.ownerDocument,k=j.createElement("li");a.firstChild;)k.appendChild(a.firstChild);for(var c=[k],i=0;i<c.length;++i)b(c[i]);d===(d|0)&&c[0].setAttribute("value",d);var r=j.createElement("ol");
+r.className="linenums";for(var d=Math.max(0,d-1|0)||0,i=0,n=c.length;i<n;++i)k=c[i],k.className="L"+(i+d)%10,k.firstChild||k.appendChild(j.createTextNode("\u00a0")),r.appendChild(k);a.appendChild(r)}function p(a,d){for(var g=d.length;--g>=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*</.test(d)?"default-markup":"default-code";return F[a]}function K(a){var d=a.h;try{var g=T(a.c,a.i),b=g.a;
+a.a=b;a.d=g.d;a.e=0;I(d,b)(a);var s=/\bMSIE\s(\d+)/.exec(navigator.userAgent),s=s&&+s[1]<=8,d=/\n/g,x=a.a,m=x.length,g=0,j=a.d,k=j.length,b=0,c=a.g,i=c.length,r=0;c[i]=m;var n,e;for(e=n=0;e<i;)c[e]!==c[e+2]?(c[n++]=c[e++],c[n++]=c[e++]):e+=2;i=n;for(e=n=0;e<i;){for(var p=c[e],w=c[e+1],t=e+2;t+2<=i&&c[t+1]===w;)t+=2;c[n++]=p;c[n++]=w;e=t}c.length=n;var f=a.c,h;if(f)h=f.style.display,f.style.display="none";try{for(;b<k;){var l=j[b+2]||m,B=c[r+2]||m,t=Math.min(l,B),A=j[b+1],G;if(A.nodeType!==1&&(G=x.substring(g,
+t))){s&&(G=G.replace(d,"\r"));A.nodeValue=G;var L=A.ownerDocument,o=L.createElement("span");o.className=c[r+1];var v=A.parentNode;v.replaceChild(o,A);o.appendChild(A);g<l&&(j[b+1]=A=L.createTextNode(x.substring(t,l)),v.insertBefore(A,o.nextSibling))}g=t;g>=l&&(b+=2);g>=B&&(r+=2)}}finally{if(f)f.style.display=h}}catch(u){D.console&&console.log(u&&u.stack||u)}}var D=window,y=["break,continue,do,else,for,if,return,while"],E=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
+"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],M=[E,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],N=[E,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"],
+O=[N,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],E=[E,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],P=[y,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
+Q=[y,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],W=[y,"as,assert,const,copy,drop,enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv,pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"],y=[y,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],R=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,
+V=/\S/,X=v({keywords:[M,O,E,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",P,Q,y],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),F={};p(X,["default-code"]);p(C([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",
+/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);p(C([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],
+["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);p(C([],[["atv",/^[\S\s]+/]]),["uq.val"]);p(v({keywords:M,hashComments:!0,cStyleComments:!0,types:R}),["c","cc","cpp","cxx","cyc","m"]);p(v({keywords:"null,true,false"}),["json"]);p(v({keywords:O,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:R}),
+["cs"]);p(v({keywords:N,cStyleComments:!0}),["java"]);p(v({keywords:y,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);p(v({keywords:P,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);p(v({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),["perl","pl","pm"]);p(v({keywords:Q,
+hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);p(v({keywords:E,cStyleComments:!0,regexLiterals:!0}),["javascript","js"]);p(v({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);p(v({keywords:W,cStyleComments:!0,multilineStrings:!0}),["rc","rs","rust"]);
+p(C([],[["str",/^[\S\s]+/]]),["regex"]);var Y=D.PR={createSimpleLexer:C,registerLangHandler:p,sourceDecorator:v,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:D.prettyPrintOne=function(a,d,g){var b=document.createElement("div");b.innerHTML="<pre>"+a+"</pre>";b=b.firstChild;g&&J(b,g,!0);K({h:d,j:g,c:b,i:1});
+return b.innerHTML},prettyPrint:D.prettyPrint=function(a,d){function g(){for(var b=D.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;i<p.length&&c.now()<b;i++){for(var d=p[i],j=h,k=d;k=k.previousSibling;){var m=k.nodeType,o=(m===7||m===8)&&k.nodeValue;if(o?!/^\??prettify\b/.test(o):m!==3||/\S/.test(k.nodeValue))break;if(o){j={};o.replace(/\b(\w+)=([\w%+\-.:]+)/g,function(a,b,c){j[b]=c});break}}k=d.className;if((j!==h||e.test(k))&&!v.test(k)){m=!1;for(o=d.parentNode;o;o=o.parentNode)if(f.test(o.tagName)&&
+o.className&&e.test(o.className)){m=!0;break}if(!m){d.className+=" prettyprinted";m=j.lang;if(!m){var m=k.match(n),y;if(!m&&(y=U(d))&&t.test(y.tagName))m=y.className.match(n);m&&(m=m[1])}if(w.test(d.tagName))o=1;else var o=d.currentStyle,u=s.defaultView,o=(o=o?o.whiteSpace:u&&u.getComputedStyle?u.getComputedStyle(d,q).getPropertyValue("white-space"):0)&&"pre"===o.substring(0,3);u=j.linenums;if(!(u=u==="true"||+u))u=(u=k.match(/\blinenums\b(?::(\d+))?/))?u[1]&&u[1].length?+u[1]:!0:!1;u&&J(d,u,o);r=
+{h:m,c:d,j:u,i:o};K(r)}}}i<p.length?setTimeout(g,250):"function"===typeof a&&a()}for(var b=d||document.body,s=b.ownerDocument||document,b=[b.getElementsByTagName("pre"),b.getElementsByTagName("code"),b.getElementsByTagName("xmp")],p=[],m=0;m<b.length;++m)for(var j=0,k=b[m].length;j<k;++j)p.push(b[m][j]);var b=q,c=Date;c.now||(c={now:function(){return+new Date}});var i=0,r,n=/\blang(?:uage)?-([\w.]+)(?!\S)/,e=/\bprettyprint\b/,v=/\bprettyprinted\b/,w=/pre|xmp/i,t=/^code$/i,f=/^(?:pre|code|xmp)$/i,
+h={};g()}};typeof define==="function"&&define.amd&&define("google-code-prettify",[],function(){return Y})})();}()
diff --git a/VC2008/.gitignore b/VC2008/.gitignore
index 537b749dc..27d2f5ebe 100644
--- a/VC2008/.gitignore
+++ b/VC2008/.gitignore
@@ -1,6 +1,7 @@
Debug/
Debug profiled/
Release/
+Release profiled/
*.user
*.ncb
*.suo
diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj
index 2e5b71ffe..8c432280f 100644
--- a/VC2008/MCServer.vcproj
+++ b/VC2008/MCServer.vcproj
@@ -707,6 +707,38 @@
>
</File>
<File
+ RelativePath="..\source\MobCensus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\MobCensus.h"
+ >
+ </File>
+ <File
+ RelativePath="..\source\MobFamilyCollecter.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\MobFamilyCollecter.h"
+ >
+ </File>
+ <File
+ RelativePath="..\source\MobProximityCounter.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\MobProximityCounter.h"
+ >
+ </File>
+ <File
+ RelativePath="..\source\MobSpawner.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\MobSpawner.h"
+ >
+ </File>
+ <File
RelativePath="..\source\MonsterConfig.cpp"
>
</File>
@@ -920,6 +952,10 @@
>
</File>
<File
+ RelativePath="..\source\Mobs\Bat.cpp"
+ >
+ </File>
+ <File
RelativePath="..\source\Mobs\Bat.h"
>
</File>
diff --git a/source/AllToLua.pkg b/source/AllToLua.pkg
index 00257e460..6d4a4083a 100644
--- a/source/AllToLua.pkg
+++ b/source/AllToLua.pkg
@@ -47,6 +47,7 @@ $cfile "BlockEntities/DropSpenserEntity.h"
$cfile "BlockEntities/DispenserEntity.h"
$cfile "BlockEntities/DropperEntity.h"
$cfile "BlockEntities/FurnaceEntity.h"
+$cfile "BlockEntities/HopperEntity.h"
$cfile "WebAdmin.h"
$cfile "WebPlugin.h"
$cfile "Root.h"
diff --git a/source/Bindings.cpp b/source/Bindings.cpp
index c241bad75..54a3e161b 100644
--- a/source/Bindings.cpp
+++ b/source/Bindings.cpp
@@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
-** Generated automatically by tolua++-1.0.92 on 10/13/13 18:01:21.
+** Generated automatically by tolua++-1.0.92 on 10/23/13 13:30:23.
*/
#ifndef __cplusplus
@@ -46,6 +46,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S);
#include "BlockEntities/DispenserEntity.h"
#include "BlockEntities/DropperEntity.h"
#include "BlockEntities/FurnaceEntity.h"
+#include "BlockEntities/HopperEntity.h"
#include "WebAdmin.h"
#include "WebPlugin.h"
#include "Root.h"
@@ -74,9 +75,9 @@ static int tolua_collect_cItem (lua_State* tolua_S)
return 0;
}
-static int tolua_collect_cFurnaceEntity (lua_State* tolua_S)
+static int tolua_collect_Vector3f (lua_State* tolua_S)
{
- cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0);
+ Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0);
Mtolua_delete(self);
return 0;
}
@@ -144,9 +145,9 @@ static int tolua_collect_cPickup (lua_State* tolua_S)
return 0;
}
-static int tolua_collect_sWebAdminPage (lua_State* tolua_S)
+static int tolua_collect_cItems (lua_State* tolua_S)
{
- sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0);
+ cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0);
Mtolua_delete(self);
return 0;
}
@@ -172,9 +173,16 @@ static int tolua_collect_cBoundingBox (lua_State* tolua_S)
return 0;
}
-static int tolua_collect_Vector3f (lua_State* tolua_S)
+static int tolua_collect_sWebAdminPage (lua_State* tolua_S)
{
- Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0);
+ sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+
+static int tolua_collect_cHopperEntity (lua_State* tolua_S)
+{
+ cHopperEntity* self = (cHopperEntity*) tolua_tousertype(tolua_S,1,0);
Mtolua_delete(self);
return 0;
}
@@ -186,16 +194,16 @@ static int tolua_collect_Vector3i (lua_State* tolua_S)
return 0;
}
-static int tolua_collect_cIniFile (lua_State* tolua_S)
+static int tolua_collect_cFurnaceEntity (lua_State* tolua_S)
{
- cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0);
Mtolua_delete(self);
return 0;
}
-static int tolua_collect_cItems (lua_State* tolua_S)
+static int tolua_collect_cIniFile (lua_State* tolua_S)
{
- cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0);
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
Mtolua_delete(self);
return 0;
}
@@ -244,7 +252,7 @@ static void tolua_reg_types (lua_State* tolua_S)
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,"cHopperEntity");
tolua_usertype(tolua_S,"cBlockEntityWithItems");
tolua_usertype(tolua_S,"cWindow");
tolua_usertype(tolua_S,"cGroup");
@@ -255,24 +263,24 @@ static void tolua_reg_types (lua_State* tolua_S)
tolua_usertype(tolua_S,"cBlockArea");
tolua_usertype(tolua_S,"cTracer");
tolua_usertype(tolua_S,"cStringMap");
+ tolua_usertype(tolua_S,"cBoundingBox");
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,"Vector3i");
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,"cWebPlugin");
- tolua_usertype(tolua_S,"cWebAdmin");
tolua_usertype(tolua_S,"cIniFile");
+ tolua_usertype(tolua_S,"cWebAdmin");
tolua_usertype(tolua_S,"sWebAdminPage");
- tolua_usertype(tolua_S,"cItem");
tolua_usertype(tolua_S,"cPawn");
tolua_usertype(tolua_S,"cPlayer");
tolua_usertype(tolua_S,"cGroupManager");
- tolua_usertype(tolua_S,"cBlockEntityWindowOwner");
+ tolua_usertype(tolua_S,"cItem");
tolua_usertype(tolua_S,"HTTPRequest");
tolua_usertype(tolua_S,"cProjectileEntity");
tolua_usertype(tolua_S,"cItemGrid::cListener");
@@ -18309,23 +18317,6 @@ static int tolua_AllToLua_cDropSpenserEntity_SetRedstonePower00(lua_State* tolua
}
#endif //#ifndef TOLUA_DISABLE
-/* get function: __cBlockEntityWindowOwner__ of class cDropSpenserEntity */
-#ifndef TOLUA_DISABLE_tolua_get_cDropSpenserEntity___cBlockEntityWindowOwner__
-static int tolua_get_cDropSpenserEntity___cBlockEntityWindowOwner__(lua_State* tolua_S)
-{
- cDropSpenserEntity* self = (cDropSpenserEntity*) tolua_tousertype(tolua_S,1,0);
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable '__cBlockEntityWindowOwner__'",NULL);
-#endif
-#ifdef __cplusplus
- tolua_pushusertype(tolua_S,(void*)static_cast<cBlockEntityWindowOwner*>(self), "cBlockEntityWindowOwner");
-#else
- tolua_pushusertype(tolua_S,(void*)((cBlockEntityWindowOwner*)self), "cBlockEntityWindowOwner");
-#endif
- return 1;
-}
-#endif //#ifndef TOLUA_DISABLE
-
/* method: new of class cDispenserEntity */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cDispenserEntity_new00
static int tolua_AllToLua_cDispenserEntity_new00(lua_State* tolua_S)
@@ -18864,6 +18855,75 @@ static int tolua_AllToLua_cFurnaceEntity_HasFuelTimeLeft00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
+/* method: new of class cHopperEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cHopperEntity_new00
+static int tolua_AllToLua_cHopperEntity_new00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cHopperEntity",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
+ {
+ 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));
+ {
+ cHopperEntity* tolua_ret = (cHopperEntity*) Mtolua_new((cHopperEntity)(a_BlockX,a_BlockY,a_BlockZ));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cHopperEntity");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cHopperEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cHopperEntity_new00_local
+static int tolua_AllToLua_cHopperEntity_new00_local(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cHopperEntity",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
+ {
+ 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));
+ {
+ cHopperEntity* tolua_ret = (cHopperEntity*) Mtolua_new((cHopperEntity)(a_BlockX,a_BlockY,a_BlockZ));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cHopperEntity");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* get function: Name of class HTTPFormData */
#ifndef TOLUA_DISABLE_tolua_get_HTTPFormData_Name
static int tolua_get_HTTPFormData_Name(lua_State* tolua_S)
@@ -19164,34 +19224,6 @@ static int tolua_set_sWebAdminPage_TabName(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* method: GetMemoryUsage of class cWebAdmin */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetMemoryUsage00
-static int tolua_AllToLua_cWebAdmin_GetMemoryUsage00(lua_State* tolua_S)
-{
-#ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertable(tolua_S,1,"cWebAdmin",0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,2,&tolua_err)
- )
- goto tolua_lerror;
- else
-#endif
- {
- {
- int tolua_ret = (int) cWebAdmin::GetMemoryUsage();
- tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
- }
- }
- return 1;
-#ifndef TOLUA_RELEASE
- tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'GetMemoryUsage'.",&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)
@@ -19303,6 +19335,37 @@ static int tolua_AllToLua_cWebAdmin_GetBaseURL00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
+/* method: GetHTMLEscapedString of class cWebAdmin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00
+static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cWebAdmin",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_Input = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ {
+ AString tolua_ret = (AString) cWebAdmin::GetHTMLEscapedString(a_Input);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_Input);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetHTMLEscapedString'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* method: GetWebTitle of class cWebPlugin */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebPlugin_GetWebTitle00
static int tolua_AllToLua_cWebPlugin_GetWebTitle00(lua_State* tolua_S)
@@ -19400,36 +19463,6 @@ static int tolua_AllToLua_cWebPlugin_SafeString00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* get function: m_PrimaryServerVersion of class cRoot */
-#ifndef TOLUA_DISABLE_tolua_get_cRoot_m_PrimaryServerVersion
-static int tolua_get_cRoot_m_PrimaryServerVersion(lua_State* tolua_S)
-{
- cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0);
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_PrimaryServerVersion'",NULL);
-#endif
- tolua_pushnumber(tolua_S,(lua_Number)self->m_PrimaryServerVersion);
- return 1;
-}
-#endif //#ifndef TOLUA_DISABLE
-
-/* set function: m_PrimaryServerVersion of class cRoot */
-#ifndef TOLUA_DISABLE_tolua_set_cRoot_m_PrimaryServerVersion
-static int tolua_set_cRoot_m_PrimaryServerVersion(lua_State* tolua_S)
-{
- cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0);
-#ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_PrimaryServerVersion'",NULL);
- if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
- tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
-#endif
- self->m_PrimaryServerVersion = ((int) tolua_tonumber(tolua_S,2,0))
-;
- return 0;
-}
-#endif //#ifndef TOLUA_DISABLE
-
/* method: Get of class cRoot */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_Get00
static int tolua_AllToLua_cRoot_Get00(lua_State* tolua_S)
@@ -29145,50 +29178,186 @@ static int tolua_AllToLua_cLuaWindow_GetContents00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* get function: __cItemGrid of class cLuaWindow */
-#ifndef TOLUA_DISABLE_tolua_get_cLuaWindow___cItemGrid__cListener__
-static int tolua_get_cLuaWindow___cItemGrid__cListener__(lua_State* tolua_S)
+/* method: GetMobType of class cMonster */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetMobType00
+static int tolua_AllToLua_cMonster_GetMobType00(lua_State* tolua_S)
{
- cLuaWindow* self = (cLuaWindow*) tolua_tousertype(tolua_S,1,0);
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable '__cItemGrid'",NULL);
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cMonster",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
#endif
-#ifdef __cplusplus
- tolua_pushusertype(tolua_S,(void*)static_cast<cItemGrid::cListener*>(self), "cItemGrid::cListener");
-#else
- tolua_pushusertype(tolua_S,(void*)((cItemGrid::cListener*)self), "cItemGrid::cListener");
+ {
+ const cMonster* self = (const cMonster*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMobType'", NULL);
#endif
+ {
+ cMonster::eType tolua_ret = (cMonster::eType) 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
-/* method: GetMobType of class cMonster */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetMobType00
-static int tolua_AllToLua_cMonster_GetMobType00(lua_State* tolua_S)
+/* method: GetMobFamily of class cMonster */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetMobFamily00
+static int tolua_AllToLua_cMonster_GetMobFamily00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
- !tolua_isusertype(tolua_S,1,"cMonster",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,1,"const 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);
+ const cMonster* self = (const cMonster*) tolua_tousertype(tolua_S,1,0);
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMobType'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMobFamily'", NULL);
#endif
{
- int tolua_ret = (int) self->GetMobType();
+ cMonster::eFamily tolua_ret = (cMonster::eFamily) self->GetMobFamily();
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);
+ tolua_error(tolua_S,"#ferror in function 'GetMobFamily'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: MobTypeToString of class cMonster */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_MobTypeToString00
+static int tolua_AllToLua_cMonster_MobTypeToString00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cMonster::eType a_MobType = ((cMonster::eType) (int) tolua_tonumber(tolua_S,2,0));
+ {
+ AString tolua_ret = (AString) cMonster::MobTypeToString(a_MobType);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'MobTypeToString'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: StringToMobType of class cMonster */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_StringToMobType00
+static int tolua_AllToLua_cMonster_StringToMobType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cMonster",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_MobTypeName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ {
+ cMonster::eType tolua_ret = (cMonster::eType) cMonster::StringToMobType(a_MobTypeName);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_MobTypeName);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'StringToMobType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: FamilyFromType of class cMonster */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_FamilyFromType00
+static int tolua_AllToLua_cMonster_FamilyFromType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cMonster::eType a_MobType = ((cMonster::eType) (int) tolua_tonumber(tolua_S,2,0));
+ {
+ cMonster::eFamily tolua_ret = (cMonster::eFamily) cMonster::FamilyFromType(a_MobType);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'FamilyFromType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSpawnRate of class cMonster */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetSpawnRate00
+static int tolua_AllToLua_cMonster_GetSpawnRate00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cMonster::eFamily a_MobFamily = ((cMonster::eFamily) (int) tolua_tonumber(tolua_S,2,0));
+ {
+ int tolua_ret = (int) cMonster::GetSpawnRate(a_MobFamily);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSpawnRate'.",&tolua_err);
return 0;
#endif
}
@@ -30747,7 +30916,6 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"AddDropSpenserDir",tolua_AllToLua_cDropSpenserEntity_AddDropSpenserDir00);
tolua_function(tolua_S,"Activate",tolua_AllToLua_cDropSpenserEntity_Activate00);
tolua_function(tolua_S,"SetRedstonePower",tolua_AllToLua_cDropSpenserEntity_SetRedstonePower00);
- tolua_variable(tolua_S,"__cBlockEntityWindowOwner__",tolua_get_cDropSpenserEntity___cBlockEntityWindowOwner__,NULL);
tolua_endmodule(tolua_S);
#ifdef __cplusplus
tolua_cclass(tolua_S,"cDispenserEntity","cDispenserEntity","cDropSpenserEntity",tolua_collect_cDispenserEntity);
@@ -30794,6 +30962,19 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"GetFuelBurnTimeLeft",tolua_AllToLua_cFurnaceEntity_GetFuelBurnTimeLeft00);
tolua_function(tolua_S,"HasFuelTimeLeft",tolua_AllToLua_cFurnaceEntity_HasFuelTimeLeft00);
tolua_endmodule(tolua_S);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"cHopperEntity","cHopperEntity","cBlockEntityWithItems",tolua_collect_cHopperEntity);
+ #else
+ tolua_cclass(tolua_S,"cHopperEntity","cHopperEntity","cBlockEntityWithItems",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"cHopperEntity");
+ tolua_constant(tolua_S,"ContentsHeight",cHopperEntity::ContentsHeight);
+ tolua_constant(tolua_S,"ContentsWidth",cHopperEntity::ContentsWidth);
+ tolua_constant(tolua_S,"TICKS_PER_TRANSFER",cHopperEntity::TICKS_PER_TRANSFER);
+ tolua_function(tolua_S,"new",tolua_AllToLua_cHopperEntity_new00);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cHopperEntity_new00_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cHopperEntity_new00_local);
+ tolua_endmodule(tolua_S);
tolua_cclass(tolua_S,"HTTPFormData","HTTPFormData","",NULL);
tolua_beginmodule(tolua_S,"HTTPFormData");
tolua_variable(tolua_S,"Name",tolua_get_HTTPFormData_Name,tolua_set_HTTPFormData_Name);
@@ -30822,10 +31003,10 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_endmodule(tolua_S);
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,"GetPage",tolua_AllToLua_cWebAdmin_GetPage00);
tolua_function(tolua_S,"GetDefaultPage",tolua_AllToLua_cWebAdmin_GetDefaultPage00);
tolua_function(tolua_S,"GetBaseURL",tolua_AllToLua_cWebAdmin_GetBaseURL00);
+ tolua_function(tolua_S,"GetHTMLEscapedString",tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00);
tolua_endmodule(tolua_S);
tolua_cclass(tolua_S,"cWebPlugin","cWebPlugin","",NULL);
tolua_beginmodule(tolua_S,"cWebPlugin");
@@ -30835,7 +31016,6 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_endmodule(tolua_S);
tolua_cclass(tolua_S,"cRoot","cRoot","",NULL);
tolua_beginmodule(tolua_S,"cRoot");
- tolua_variable(tolua_S,"m_PrimaryServerVersion",tolua_get_cRoot_m_PrimaryServerVersion,tolua_set_cRoot_m_PrimaryServerVersion);
tolua_function(tolua_S,"Get",tolua_AllToLua_cRoot_Get00);
tolua_function(tolua_S,"GetServer",tolua_AllToLua_cRoot_GetServer00);
tolua_function(tolua_S,"GetDefaultWorld",tolua_AllToLua_cRoot_GetDefaultWorld00);
@@ -31244,10 +31424,10 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,".call",tolua_AllToLua_cLuaWindow_new00_local);
tolua_function(tolua_S,"delete",tolua_AllToLua_cLuaWindow_delete00);
tolua_function(tolua_S,"GetContents",tolua_AllToLua_cLuaWindow_GetContents00);
- tolua_variable(tolua_S,"__cItemGrid__cListener__",tolua_get_cLuaWindow___cItemGrid__cListener__,NULL);
tolua_endmodule(tolua_S);
tolua_cclass(tolua_S,"cMonster","cMonster","cPawn",NULL);
tolua_beginmodule(tolua_S,"cMonster");
+ tolua_constant(tolua_S,"mtInvalidType",cMonster::mtInvalidType);
tolua_constant(tolua_S,"mtBat",cMonster::mtBat);
tolua_constant(tolua_S,"mtBlaze",cMonster::mtBlaze);
tolua_constant(tolua_S,"mtCaveSpider",cMonster::mtCaveSpider);
@@ -31277,7 +31457,17 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_constant(tolua_S,"mtWolf",cMonster::mtWolf);
tolua_constant(tolua_S,"mtZombie",cMonster::mtZombie);
tolua_constant(tolua_S,"mtZombiePigman",cMonster::mtZombiePigman);
+ tolua_constant(tolua_S,"mfHostile",cMonster::mfHostile);
+ tolua_constant(tolua_S,"mfPassive",cMonster::mfPassive);
+ tolua_constant(tolua_S,"mfAmbient",cMonster::mfAmbient);
+ tolua_constant(tolua_S,"mfWater",cMonster::mfWater);
+ tolua_constant(tolua_S,"mfMaxplusone",cMonster::mfMaxplusone);
tolua_function(tolua_S,"GetMobType",tolua_AllToLua_cMonster_GetMobType00);
+ tolua_function(tolua_S,"GetMobFamily",tolua_AllToLua_cMonster_GetMobFamily00);
+ tolua_function(tolua_S,"MobTypeToString",tolua_AllToLua_cMonster_MobTypeToString00);
+ tolua_function(tolua_S,"StringToMobType",tolua_AllToLua_cMonster_StringToMobType00);
+ tolua_function(tolua_S,"FamilyFromType",tolua_AllToLua_cMonster_FamilyFromType00);
+ tolua_function(tolua_S,"GetSpawnRate",tolua_AllToLua_cMonster_GetSpawnRate00);
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 1d567520c..620dbea84 100644
--- a/source/Bindings.h
+++ b/source/Bindings.h
@@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
-** Generated automatically by tolua++-1.0.92 on 10/13/13 18:01:22.
+** Generated automatically by tolua++-1.0.92 on 10/23/13 13:30:24.
*/
/* Exported function */
diff --git a/source/BlockEntities/DropSpenserEntity.h b/source/BlockEntities/DropSpenserEntity.h
index f2f1eba36..0e9039915 100644
--- a/source/BlockEntities/DropSpenserEntity.h
+++ b/source/BlockEntities/DropSpenserEntity.h
@@ -29,10 +29,10 @@ class cServer;
-// tolua_begin
-class cDropSpenserEntity :
- public cBlockEntityWithItems,
- public cBlockEntityWindowOwner
+class cDropSpenserEntity : // tolua_export
+ public cBlockEntityWindowOwner,
+ // tolua_begin
+ public cBlockEntityWithItems
{
typedef cBlockEntityWithItems super;
diff --git a/source/BlockEntities/HopperEntity.h b/source/BlockEntities/HopperEntity.h
index a49868660..1a7650581 100644
--- a/source/BlockEntities/HopperEntity.h
+++ b/source/BlockEntities/HopperEntity.h
@@ -38,15 +38,12 @@ public:
/// Constructor used for normal operation
cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
- // tolua_begin
-
/** Returns the block coords of the block receiving the output items, based on the meta
- Returns false if unattached
+ Returns false if unattached.
+ Exported in ManualBindings.cpp
*/
bool GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ);
- // tolua_end
-
static const char * GetClassStatic(void) { return "cHopperEntity"; }
protected:
@@ -95,7 +92,7 @@ protected:
/// Moves one piece to the specified entity's contents' slot. Returns true if contents have changed.
bool MoveItemsToSlot(cBlockEntityWithItems & a_Entity, int a_DstSlotNum);
-} ;
+} ; // tolua_export
diff --git a/source/BlockID.cpp b/source/BlockID.cpp
index 177652a46..95e1a63bf 100644
--- a/source/BlockID.cpp
+++ b/source/BlockID.cpp
@@ -79,40 +79,43 @@ public:
bool ResolveItem(const AString & a_ItemName, cItem & a_Item)
{
- ItemMap::iterator itr = m_Map.find(a_ItemName);
+ // Split into parts divided by either ':' or '^'
+ AStringVector Split = StringSplitAndTrim(a_ItemName, ":^");
+ if (Split.empty())
+ {
+ return false;
+ }
+
+ ItemMap::iterator itr = m_Map.find(Split[0]);
if (itr != m_Map.end())
{
+ // Resolved as a string, assign the type and the default damage / count
a_Item.m_ItemType = itr->second.first;
a_Item.m_ItemDamage = itr->second.second;
if (a_Item.m_ItemDamage == -1)
{
a_Item.m_ItemDamage = 0;
}
- a_Item.m_ItemCount = 1;
- return true;
- }
-
- // Not a resolvable string, try pure numbers: "45:6", "45^6" etc.
- AStringVector Split = StringSplit(a_ItemName, ":");
- if (Split.size() == 1)
- {
- Split = StringSplit(a_ItemName, "^");
}
- if (Split.empty())
- {
- return false;
- }
- a_Item.m_ItemType = (short)atoi(Split[0].c_str());
- if ((a_Item.m_ItemType == 0) && (Split[0] != "0"))
+ else
{
- // Parsing the number failed
- return false;
+ // Not a resolvable string, try pure numbers: "45:6", "45^6" etc.
+ a_Item.m_ItemType = (short)atoi(Split[0].c_str());
+ if ((a_Item.m_ItemType == 0) && (Split[0] != "0"))
+ {
+ // Parsing the number failed
+ return false;
+ }
}
+
+ // Parse the damage, if present:
if (Split.size() < 2)
{
+ // Not present, set the item as valid and return success:
a_Item.m_ItemCount = 1;
return true;
}
+
a_Item.m_ItemDamage = atoi(Split[1].c_str());
if ((a_Item.m_ItemDamage == 0) && (Split[1] != "0"))
{
@@ -662,6 +665,7 @@ public:
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;
g_BlockTransparent[E_BLOCK_LEAVES] = true;
g_BlockTransparent[E_BLOCK_LEVER] = true;
g_BlockTransparent[E_BLOCK_MELON_STEM] = true;
@@ -674,11 +678,14 @@ public:
g_BlockTransparent[E_BLOCK_RED_MUSHROOM] = true;
g_BlockTransparent[E_BLOCK_RED_ROSE] = true;
g_BlockTransparent[E_BLOCK_SIGN_POST] = true;
+ g_BlockTransparent[E_BLOCK_STATIONARY_LAVA] = true;
+ g_BlockTransparent[E_BLOCK_STATIONARY_WATER] = true;
g_BlockTransparent[E_BLOCK_STONE_PRESSURE_PLATE] = true;
g_BlockTransparent[E_BLOCK_SNOW] = true;
g_BlockTransparent[E_BLOCK_TALL_GRASS] = true;
g_BlockTransparent[E_BLOCK_TORCH] = true;
g_BlockTransparent[E_BLOCK_VINES] = true;
+ g_BlockTransparent[E_BLOCK_WATER] = true;
g_BlockTransparent[E_BLOCK_WALLSIGN] = true;
g_BlockTransparent[E_BLOCK_WOODEN_DOOR] = true;
g_BlockTransparent[E_BLOCK_WOODEN_PRESSURE_PLATE] = true;
diff --git a/source/Chunk.cpp b/source/Chunk.cpp
index db533f642..c7bac879a 100644
--- a/source/Chunk.cpp
+++ b/source/Chunk.cpp
@@ -30,6 +30,9 @@
#include "PluginManager.h"
#include "Blocks/BlockHandler.h"
#include "Simulator/FluidSimulator.h"
+#include "MobCensus.h"
+#include "MobSpawner.h"
+
#include <json/json.h>
@@ -429,6 +432,133 @@ void cChunk::Stay(bool a_Stay)
+void cChunk::CollectMobCensus(cMobCensus& toFill)
+{
+ toFill.CollectSpawnableChunk(*this);
+ std::list<const Vector3d*> playerPositions;
+ cPlayer* currentPlayer;
+ for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr)
+ {
+ currentPlayer = (*itr)->GetPlayer();
+ playerPositions.push_back(&(currentPlayer->GetPosition()));
+ }
+
+ Vector3d currentPosition;
+ for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
+ {
+ //LOGD("Counting entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass());
+ if ((*itr)->IsMob())
+ {
+ cMonster& Monster = (cMonster&)(**itr);
+ currentPosition = Monster.GetPosition();
+ for (std::list<const Vector3d*>::const_iterator itr2 = playerPositions.begin(); itr2 != playerPositions.end(); itr2 ++)
+ {
+ toFill.CollectMob(Monster,*this,(currentPosition-**itr2).SqrLength());
+ }
+ }
+ } // for itr - m_Entitites[]
+}
+
+
+
+
+void cChunk::getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ)
+{
+ ASSERT(a_MaxX * a_MaxY * a_MaxZ * 8 < 0x00ffffff);
+ int Random = m_World->GetTickRandomNumber(0x00ffffff);
+ a_X = Random % (a_MaxX * 2);
+ a_Y = (Random / (a_MaxX * 2)) % (a_MaxY * 2);
+ a_Z = ((Random / (a_MaxX * 2)) / (a_MaxY * 2)) % (a_MaxZ * 2);
+ a_X /= 2;
+ a_Y /= 2;
+ a_Z /= 2;
+}
+
+
+
+
+
+void cChunk::getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z)
+{
+ // MG TODO : check if this kind of optimization (only one random call) is still needed
+ // MG TODO : if so propagate it
+
+ getThreeRandomNumber(a_X, a_Y, a_Z, Width, Height-2, Width);
+ a_Y++;
+}
+
+
+
+
+
+void cChunk::SpawnMobs(cMobSpawner& a_MobSpawner)
+{
+ int Center_X,Center_Y,Center_Z;
+ getRandomBlockCoords(Center_X,Center_Y,Center_Z);
+
+ BLOCKTYPE PackCenterBlock = GetBlock(Center_X, Center_Y, Center_Z);
+ if (a_MobSpawner.CheckPackCenter(PackCenterBlock))
+ {
+ a_MobSpawner.NewPack();
+ int NumberOfTries = 0;
+ int NumberOfSuccess = 0;
+ int MaxNbOfSuccess = 4; // this can be changed during the process for Wolves and Ghass
+ while (NumberOfTries < 12 && NumberOfSuccess < MaxNbOfSuccess)
+ {
+ const int HorizontalRange = 20; // MG TODO : relocate
+ const int VerticalRange = 0; // MG TODO : relocate
+ int Try_X, Try_Y, Try_Z;
+ getThreeRandomNumber(Try_X, Try_Y, Try_Z, 2*HorizontalRange+1 , 2*VerticalRange+1 , 2*HorizontalRange+1);
+ Try_X -= HorizontalRange;
+ Try_Y -= VerticalRange;
+ Try_Z -= HorizontalRange;
+ Try_X += Center_X;
+ Try_Y += Center_Y;
+ Try_Z += Center_Z;
+
+ ASSERT(Try_Y > 0);
+ ASSERT(Try_Y < cChunkDef::Height-1);
+
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ BLOCKTYPE BlockType_below;
+ NIBBLETYPE BlockMeta_below;
+ BLOCKTYPE BlockType_above;
+ NIBBLETYPE BlockMeta_above;
+ if (UnboundedRelGetBlock(Try_X, Try_Y , Try_Z, BlockType, BlockMeta) &&
+ UnboundedRelGetBlock(Try_X, Try_Y-1, Try_Z, BlockType_below, BlockMeta_below)&&
+ UnboundedRelGetBlock(Try_X, Try_Y+1, Try_Z, BlockType_above, BlockMeta_above)
+ )
+ {
+ EMCSBiome Biome = m_ChunkMap->GetBiomeAt (Try_X, Try_Z);
+ // MG TODO :
+ // Moon cycle (for slime)
+ // check player and playerspawn presence < 24 blocks
+ // check mobs presence on the block
+
+ // MG TODO: fix the "light" thing, I'm pretty sure that UnboundedRelGetBlock s not returning the right thing
+
+ // MG TODO : check that "Level" really means Y
+ cEntity* newMob = a_MobSpawner.TryToSpawnHere(BlockType, BlockMeta, BlockType_below, BlockMeta_below, BlockType_above, BlockMeta_above, Biome, Try_Y, MaxNbOfSuccess);
+ if (newMob)
+ {
+ int WorldX, WorldY, WorldZ;
+ PositionToWorldPosition(Try_X, Try_Y, Try_Z, WorldX, WorldY, WorldZ);
+ newMob->SetPosition(WorldX, WorldY, WorldZ);
+ LOGD("Spawning %s #%i at %d,%d,%d",newMob->GetClass(),newMob->GetUniqueID(),WorldX, WorldY, WorldZ);
+ NumberOfSuccess++;
+ }
+ }
+
+ NumberOfTries++;
+ }
+ }
+
+}
+
+
+
+
void cChunk::Tick(float a_Dt)
{
@@ -457,10 +587,14 @@ void cChunk::Tick(float a_Dt)
m_IsDirty = (*itr)->Tick(a_Dt, *this) | m_IsDirty;
}
- // Tick all entities in this chunk:
+ // Tick all entities in this chunk (except mobs):
for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
{
- (*itr)->Tick(a_Dt, *this);
+ // Mobs are tickes inside MobTick (as we don't have to tick them if they are far away from players)
+ if (!((*itr)->IsMob()))
+ {
+ (*itr)->Tick(a_Dt, *this);
+ }
} // for itr - m_Entitites[]
// Remove all entities that were scheduled for removal:
@@ -923,45 +1057,14 @@ bool cChunk::UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE
LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY);
return false;
}
-
- // Is it in this chunk?
- if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width))
- {
- if (!IsValid())
- {
- return false;
- }
- int BlockIdx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
- a_BlockType = GetBlock(BlockIdx);
- a_BlockMeta = GetMeta(BlockIdx);
- return true;
- }
-
- // Not in this chunk, try walking the neighbors first:
- if ((a_RelX < 0) && (m_NeighborXM != NULL))
- {
- return m_NeighborXM->UnboundedRelGetBlock(a_RelX + cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
- }
- if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL))
- {
- return m_NeighborXP->UnboundedRelGetBlock(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
- }
- if ((a_RelZ < 0) && (m_NeighborZM != NULL))
- {
- return m_NeighborZM->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ + cChunkDef::Width, a_BlockType, a_BlockMeta);
- }
- if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL))
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
{
- return m_NeighborZP->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ - cChunkDef::Width, a_BlockType, a_BlockMeta);
+ // The chunk is not available, bail out
+ return false;
}
-
- // Neighbors not available, use the chunkmap to locate the chunk:
- return m_ChunkMap->LockedGetBlock(
- m_PosX * cChunkDef::Width + a_RelX,
- ZERO_CHUNK_Y * cChunkDef::Height + a_RelY,
- m_PosZ * cChunkDef::Width + a_RelZ,
- a_BlockType, a_BlockMeta
- );
+ Chunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
+ return true;
}
@@ -975,44 +1078,14 @@ bool cChunk::UnboundedRelGetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKT
LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY);
return false;
}
-
- // Is it in this chunk?
- if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width))
- {
- if (!IsValid())
- {
- return false;
- }
- int BlockIdx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
- a_BlockType = GetBlock(BlockIdx);
- return true;
- }
-
- // Not in this chunk, try walking the neighbors first:
- if ((a_RelX < 0) && (m_NeighborXM != NULL))
- {
- return m_NeighborXM->UnboundedRelGetBlockType(a_RelX + cChunkDef::Width, a_RelY, a_RelZ, a_BlockType);
- }
- if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL))
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
{
- return m_NeighborXP->UnboundedRelGetBlockType(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockType);
- }
- if ((a_RelZ < 0) && (m_NeighborZM != NULL))
- {
- return m_NeighborZM->UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ + cChunkDef::Width, a_BlockType);
- }
- if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL))
- {
- return m_NeighborZP->UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ - cChunkDef::Width, a_BlockType);
+ // The chunk is not available, bail out
+ return false;
}
-
- // Neighbors not available, use the chunkmap to locate the chunk:
- return m_ChunkMap->LockedGetBlockType(
- m_PosX * cChunkDef::Width + a_RelX,
- ZERO_CHUNK_Y * cChunkDef::Height + a_RelY,
- m_PosZ * cChunkDef::Width + a_RelZ,
- a_BlockType
- );
+ a_BlockType = Chunk->GetBlock(a_RelX, a_RelY, a_RelZ);
+ return true;
}
@@ -1026,44 +1099,56 @@ bool cChunk::UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLE
LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY);
return false;
}
-
- // Is it in this chunk?
- if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width))
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
{
- if (!IsValid())
- {
- return false;
- }
- int BlockIdx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
- a_BlockMeta = GetMeta(BlockIdx);
- return true;
+ // The chunk is not available, bail out
+ return false;
}
+ a_BlockMeta = Chunk->GetMeta(a_RelX, a_RelY, a_RelZ);
+ return true;
+}
+
+
- // Not in this chunk, try walking the neighbors first:
- if ((a_RelX < 0) && (m_NeighborXM != NULL))
+
+
+bool cChunk::UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockBlockLight) const
+{
+ if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height))
{
- return m_NeighborXM->UnboundedRelGetBlockMeta(a_RelX + cChunkDef::Width, a_RelY, a_RelZ, a_BlockMeta);
+ LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY);
+ return false;
}
- if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL))
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
{
- return m_NeighborXP->UnboundedRelGetBlockMeta(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockMeta);
+ // The chunk is not available, bail out
+ return false;
}
- if ((a_RelZ < 0) && (m_NeighborZM != NULL))
+ a_BlockBlockLight = Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ);
+ return true;
+}
+
+
+
+
+
+bool cChunk::UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockSkyLight) const
+{
+ if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height))
{
- return m_NeighborZM->UnboundedRelGetBlockMeta(a_RelX, a_RelY, a_RelZ + cChunkDef::Width, a_BlockMeta);
+ LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY);
+ return false;
}
- if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL))
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
{
- return m_NeighborZP->UnboundedRelGetBlockMeta(a_RelX, a_RelY, a_RelZ - cChunkDef::Width, a_BlockMeta);
+ // The chunk is not available, bail out
+ return false;
}
-
- // Neighbors not available, use the chunkmap to locate the chunk:
- return m_ChunkMap->LockedGetBlockMeta(
- m_PosX * cChunkDef::Width + a_RelX,
- ZERO_CHUNK_Y * cChunkDef::Height + a_RelY,
- m_PosZ * cChunkDef::Width + a_RelZ,
- a_BlockMeta
- );
+ a_BlockSkyLight = Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ);
+ return true;
}
@@ -1077,44 +1162,15 @@ bool cChunk::UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE
LOGWARNING("UnboundedRelSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY);
return false;
}
-
- // Is it in this chunk?
- if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width))
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
{
- if (!IsValid())
- {
- return false;
- }
- SetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
- return true;
- }
-
- // Not in this chunk, try walking the neighbors first:
- if ((a_RelX < 0) && (m_NeighborXM != NULL))
- {
- return m_NeighborXM->UnboundedRelSetBlock(a_RelX + cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
- }
- if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL))
- {
- return m_NeighborXP->UnboundedRelSetBlock(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
- }
- if ((a_RelZ < 0) && (m_NeighborZM != NULL))
- {
- return m_NeighborZM->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ + cChunkDef::Width, a_BlockType, a_BlockMeta);
- }
- if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL))
- {
- return m_NeighborZP->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ - cChunkDef::Width, a_BlockType, a_BlockMeta);
+ // The chunk is not available, bail out
+ return false;
}
-
- // Neighbors not available, use the chunkmap to locate the chunk:
- return m_ChunkMap->LockedSetBlock(
- m_PosX * cChunkDef::Width + a_RelX,
- ZERO_CHUNK_Y * cChunkDef::Height + a_RelY,
- m_PosZ * cChunkDef::Width + a_RelZ,
- a_BlockType, a_BlockMeta
- );
-}
+ Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
+ return true;
+}
@@ -1127,43 +1183,14 @@ bool cChunk::UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKT
LOGWARNING("UnboundedRelFastSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY);
return false;
}
-
- // Is it in this chunk?
- if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width))
- {
- if (!IsValid())
- {
- return false;
- }
- FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
- return true;
- }
-
- // Not in this chunk, try walking the neighbors first:
- if ((a_RelX < 0) && (m_NeighborXM != NULL))
- {
- return m_NeighborXM->UnboundedRelFastSetBlock(a_RelX + cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
- }
- if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL))
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
{
- return m_NeighborXP->UnboundedRelFastSetBlock(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
- }
- if ((a_RelZ < 0) && (m_NeighborZM != NULL))
- {
- return m_NeighborZM->UnboundedRelFastSetBlock(a_RelX, a_RelY, a_RelZ + cChunkDef::Width, a_BlockType, a_BlockMeta);
- }
- if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL))
- {
- return m_NeighborZP->UnboundedRelFastSetBlock(a_RelX, a_RelY, a_RelZ - cChunkDef::Width, a_BlockType, a_BlockMeta);
+ // The chunk is not available, bail out
+ return false;
}
-
- // Neighbors not available, use the chunkmap to locate the chunk:
- return m_ChunkMap->LockedFastSetBlock(
- m_PosX * cChunkDef::Width + a_RelX,
- ZERO_CHUNK_Y * cChunkDef::Height + a_RelY,
- m_PosZ * cChunkDef::Width + a_RelZ,
- a_BlockType, a_BlockMeta
- );
+ Chunk->FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
+ return true;
}
@@ -1177,44 +1204,18 @@ void cChunk::UnboundedQueueTickBlock(int a_RelX, int a_RelY, int a_RelZ)
// Outside of chunkmap
return;
}
-
- // Is it in this chunk?
- if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width))
- {
- QueueTickBlock(a_RelX, a_RelY, a_RelZ);
- return;
- }
-
- // Not in this chunk, try walking the neighbors first:
- if ((a_RelX < 0) && (m_NeighborXM != NULL))
- {
- m_NeighborXM->UnboundedQueueTickBlock(a_RelX + cChunkDef::Width, a_RelY, a_RelZ);
- return;
- }
- if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL))
- {
- m_NeighborXP->UnboundedQueueTickBlock(a_RelX - cChunkDef::Width, a_RelY, a_RelZ);
- return;
- }
- if ((a_RelZ < 0) && (m_NeighborZM != NULL))
- {
- m_NeighborZM->UnboundedQueueTickBlock(a_RelX, a_RelY, a_RelZ + cChunkDef::Width);
- return;
- }
- if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL))
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk != NULL) && Chunk->IsValid())
{
- m_NeighborZP->UnboundedQueueTickBlock(a_RelX, a_RelY, a_RelZ - cChunkDef::Width);
- return;
+ Chunk->QueueTickBlock(a_RelX, a_RelY, a_RelZ);
}
-
- // Neighbors not available, ignore altogether
}
-int cChunk::GetHeight( int a_X, int a_Z )
+int cChunk::GetHeight(int a_X, int a_Z)
{
ASSERT((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width));
@@ -2340,66 +2341,58 @@ cChunk * cChunk::GetRelNeighborChunk(int a_RelX, int a_RelZ)
-cChunk * cChunk::GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ)
+cChunk * cChunk::GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ) const
{
- bool ReturnThis = true;
- int RelX = a_RelX;
+ cChunk * ToReturn = const_cast<cChunk *>(this);
+
+ // The most common case: inside this chunk:
+ if (
+ (a_RelX >= 0) && (a_RelX < Width) &&
+ (a_RelZ >= 0) && (a_RelZ < Width)
+ )
+ {
+ return ToReturn;
+ }
+
+ // Request for a different chunk, calculate chunk offset:
+ int RelX = a_RelX; // Make a local copy of the coords (faster access)
int RelZ = a_RelZ;
- if (a_RelX < 0)
+ while ((RelX >= Width) && (ToReturn != NULL))
{
- if (m_NeighborXM != NULL)
- {
- RelX = a_RelX + cChunkDef::Width;
- cChunk * Candidate = m_NeighborXM->GetRelNeighborChunkAdjustCoords(RelX, RelZ);
- if (Candidate != NULL)
- {
- a_RelX = RelX;
- a_RelZ = RelZ;
- return Candidate;
- }
- }
- // Going X-first failed, but if the request is crossing Z as well, let's try the Z-first later on.
- ReturnThis = false;
+ RelX -= Width;
+ ToReturn = ToReturn->m_NeighborXP;
}
- else if (a_RelX >= cChunkDef::Width)
+ while ((RelX < 0) && (ToReturn != NULL))
{
- if (m_NeighborXP != NULL)
- {
- RelX = a_RelX - cChunkDef::Width;
- cChunk * Candidate = m_NeighborXP->GetRelNeighborChunkAdjustCoords(RelX, RelZ);
- if (Candidate != NULL)
- {
- a_RelX = RelX;
- a_RelZ = RelZ;
- return Candidate;
- }
- }
- // Going X-first failed, but if the request is crossing Z as well, let's try the Z-first later on.
- ReturnThis = false;
+ RelX += Width;
+ ToReturn = ToReturn->m_NeighborXM;
}
-
- if (a_RelZ < 0)
+ while ((RelZ >= Width) && (ToReturn != NULL))
{
- if (m_NeighborZM != NULL)
- {
- a_RelZ += cChunkDef::Width;
- return m_NeighborZM->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
- // For requests crossing both X and Z, the X-first way has been already tried
- }
- return NULL;
+ RelZ -= Width;
+ ToReturn = ToReturn->m_NeighborZP;
}
- else if (a_RelZ >= cChunkDef::Width)
+ while ((RelZ < 0) && (ToReturn != NULL))
{
- if (m_NeighborZP != NULL)
- {
- a_RelZ -= cChunkDef::Width;
- return m_NeighborZP->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
- // For requests crossing both X and Z, the X-first way has been already tried
- }
- return NULL;
+ RelZ += Width;
+ ToReturn = ToReturn->m_NeighborZM;
+ }
+ if (ToReturn != NULL)
+ {
+ a_RelX = RelX;
+ a_RelZ = RelZ;
+ return ToReturn;
}
- return (ReturnThis ? this : NULL);
+ // The chunk cannot be walked through neighbors, find it through the chunkmap:
+ int AbsX = a_RelX + m_PosX * Width;
+ int AbsZ = a_RelZ + m_PosZ * Width;
+ int DstChunkX, DstChunkZ;
+ BlockToChunk(AbsX, AbsZ, DstChunkX, DstChunkZ);
+ ToReturn = m_ChunkMap->FindChunk(DstChunkX, DstChunkZ);
+ a_RelX = AbsX - DstChunkX * Width;
+ a_RelZ = AbsZ - DstChunkZ * Width;
+ return ToReturn;
}
diff --git a/source/Chunk.h b/source/Chunk.h
index c979b7928..e709a4718 100644
--- a/source/Chunk.h
+++ b/source/Chunk.h
@@ -49,6 +49,8 @@ class cPickup;
class cChunkDataSerializer;
class cBlockArea;
class cFluidSimulatorData;
+class cMobCensus;
+class cMobSpawner;
typedef std::list<cClientHandle *> cClientHandleList;
typedef cItemCallback<cEntity> cEntityCallback;
@@ -124,6 +126,12 @@ public:
/// Sets or resets the internal flag that prevents chunk from being unloaded
void Stay(bool a_Stay = true);
+ /// Recence all mobs proximities to players in order to know what to do with them
+ void CollectMobCensus(cMobCensus& toFill);
+
+ /// Try to Spawn Monsters inside chunk
+ void SpawnMobs(cMobSpawner& a_MobSpawner);
+
void Tick(float a_Dt);
int GetPosX(void) const { return m_PosX; }
@@ -163,11 +171,12 @@ public:
cChunk * GetRelNeighborChunk(int a_RelX, int a_RelZ);
/**
- Returns the chunk into which the relatively-specified block belongs, by walking the neighbors.
+ Returns the chunk into which the relatively-specified block belongs.
Also modifies the relative coords from this-relative to return-relative.
- Will return self if appropriate. Returns NULL if not reachable through neighbors.
+ Will return self if appropriate.
+ Will try walking the neighbors first; if that fails, will query the chunkmap
*/
- cChunk * GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ);
+ cChunk * GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ) const;
EMCSBiome GetBiomeAt(int a_RelX, int a_RelZ) const {return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ); }
@@ -291,19 +300,25 @@ public:
inline NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockLight, a_RelX, a_RelY, a_RelZ); }
inline NIBBLETYPE GetSkyLight (int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_RelX, a_RelY, a_RelZ); }
- /// Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success; only usable in Tick()
+ /// Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
- /// Same as GetBlockType(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success; only usable in Tick()
+ /// Same as GetBlockType(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
bool UnboundedRelGetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType) const;
- /// Same as GetBlockMeta(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success; only usable in Tick()
+ /// Same as GetBlockMeta(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
bool UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockMeta) const;
- /// Same as SetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success; only usable in Tick()
+ /// Same as GetBlockBlockLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ bool UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight) const;
+
+ /// Same as GetBlockSkyLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ bool UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_SkyLight) const;
+
+ /// Same as SetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
bool UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
- /// Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success; only usable in Tick()
+ /// Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
bool UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
/// Same as QueueTickBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s in such a case), ignores unsuccessful attempts
@@ -385,6 +400,10 @@ private:
cSandSimulatorChunkData m_SandSimulatorData;
+ // pick up a random block of this chunk
+ void getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z);
+ void getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ);
+
void RemoveBlockEntity(cBlockEntity * a_BlockEntity);
void AddBlockEntity (cBlockEntity * a_BlockEntity);
diff --git a/source/ChunkDef.h b/source/ChunkDef.h
index 9db88f293..d6630df7e 100644
--- a/source/ChunkDef.h
+++ b/source/ChunkDef.h
@@ -208,10 +208,15 @@ public:
inline static unsigned int MakeIndex(int x, int y, int z )
{
- if( x < cChunkDef::Width && x > -1 && y < cChunkDef::Height && y > -1 && z < cChunkDef::Width && z > -1 )
+ if (
+ (x < Width) && (x > -1) &&
+ (y < Height) && (y > -1) &&
+ (z < Width) && (z > -1)
+ )
{
return MakeIndexNoCheck(x, y, z);
}
+ ASSERT(!"cChunkDef::MakeIndex(): coords out of chunk range!");
return INDEX_OUT_OF_RANGE;
}
@@ -256,6 +261,7 @@ public:
inline static void SetBlock(BLOCKTYPE * a_BlockTypes, int a_Index, BLOCKTYPE a_Type)
{
+ ASSERT((a_Index >= 0) && (a_Index <= NumBlocks));
a_BlockTypes[a_Index] = a_Type;
}
@@ -271,41 +277,50 @@ public:
inline static BLOCKTYPE GetBlock(const BLOCKTYPE * a_BlockTypes, int a_Idx)
{
- ASSERT((a_Idx >= 0) && (a_Idx < Width * Width * Height));
+ ASSERT((a_Idx >= 0) && (a_Idx < NumBlocks));
return a_BlockTypes[a_Idx];
}
inline static int GetHeight(const HeightMap & a_HeightMap, int a_X, int a_Z)
{
+ ASSERT((a_X >= 0) && (a_X <= Width));
+ ASSERT((a_Z >= 0) && (a_Z <= Width));
return a_HeightMap[a_X + Width * a_Z];
}
inline static void SetHeight(HeightMap & a_HeightMap, int a_X, int a_Z, unsigned char a_Height)
{
+ ASSERT((a_X >= 0) && (a_X <= Width));
+ ASSERT((a_Z >= 0) && (a_Z <= Width));
a_HeightMap[a_X + Width * a_Z] = a_Height;
}
inline static EMCSBiome GetBiome(const BiomeMap & a_BiomeMap, int a_X, int a_Z)
{
+ ASSERT((a_X >= 0) && (a_X <= Width));
+ ASSERT((a_Z >= 0) && (a_Z <= Width));
return a_BiomeMap[a_X + Width * a_Z];
}
inline static void SetBiome(BiomeMap & a_BiomeMap, int a_X, int a_Z, EMCSBiome a_Biome)
{
+ ASSERT((a_X >= 0) && (a_X <= Width));
+ ASSERT((a_Z >= 0) && (a_Z <= Width));
a_BiomeMap[a_X + Width * a_Z] = a_Biome;
}
static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int a_BlockIdx)
{
- if ((a_BlockIdx > -1) && (a_BlockIdx < cChunkDef::NumBlocks))
+ if ((a_BlockIdx > -1) && (a_BlockIdx < NumBlocks))
{
return (a_Buffer[a_BlockIdx / 2] >> ((a_BlockIdx & 1) * 4)) & 0x0f;
}
+ ASSERT(!"cChunkDef::GetNibble(): index out of chunk range!");
return 0;
}
@@ -317,38 +332,48 @@ public:
int Index = MakeIndexNoCheck(x, y, z);
return (a_Buffer[Index / 2] >> ((Index & 1) * 4)) & 0x0f;
}
+ ASSERT(!"cChunkDef::GetNibble(): coords out of chunk range!");
return 0;
}
static void SetNibble(NIBBLETYPE * a_Buffer, int a_BlockIdx, NIBBLETYPE a_Nibble)
{
- if ((a_BlockIdx > -1) && (a_BlockIdx < cChunkDef::NumBlocks))
+ if ((a_BlockIdx < 0) || (a_BlockIdx >= NumBlocks))
{
- a_Buffer[a_BlockIdx / 2] = (
- (a_Buffer[a_BlockIdx / 2] & (0xf0 >> ((a_BlockIdx & 1) * 4))) | // The untouched nibble
- ((a_Nibble & 0x0f) << ((a_BlockIdx & 1) * 4)) // The nibble being set
- );
+ ASSERT(!"cChunkDef::SetNibble(): index out of range!");
+ return;
}
+ a_Buffer[a_BlockIdx / 2] = (
+ (a_Buffer[a_BlockIdx / 2] & (0xf0 >> ((a_BlockIdx & 1) * 4))) | // The untouched nibble
+ ((a_Nibble & 0x0f) << ((a_BlockIdx & 1) * 4)) // The nibble being set
+ );
}
static void SetNibble(NIBBLETYPE * a_Buffer, int x, int y, int z, NIBBLETYPE a_Nibble)
{
- if ((x < cChunkDef::Width) && (x > -1) && (y < cChunkDef::Height) && (y > -1) && (z < cChunkDef::Width) && (z > -1))
+ if (
+ (x >= Width) || (x < 0) ||
+ (y >= Height) || (y < 0) ||
+ (z >= Width) || (z < 0)
+ )
{
- int Index = MakeIndexNoCheck(x, y, z);
- a_Buffer[Index / 2] = (
- (a_Buffer[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble
- ((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set
- );
+ ASSERT(!"cChunkDef::SetNibble(): index out of range!");
+ return;
}
+
+ int Index = MakeIndexNoCheck(x, y, z);
+ a_Buffer[Index / 2] = (
+ (a_Buffer[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble
+ ((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set
+ );
}
inline static char GetNibble(const NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos )
{
- return GetNibble( a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z );
+ return GetNibble(a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z );
}
diff --git a/source/ChunkMap.cpp b/source/ChunkMap.cpp
index 3c098fdfe..c3bd5f33d 100644
--- a/source/ChunkMap.cpp
+++ b/source/ChunkMap.cpp
@@ -12,6 +12,8 @@
#include "BlockArea.h"
#include "PluginManager.h"
#include "Entities/TNTEntity.h"
+#include "MobCensus.h"
+#include "MobSpawner.h"
#ifndef _WIN32
#include <cstdlib> // abs
@@ -2152,6 +2154,32 @@ void cChunkMap::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ)
+void cChunkMap::CollectMobCensus(cMobCensus& a_ToFill)
+{
+ cCSLock Lock(m_CSLayers);
+ for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ {
+ (*itr)->CollectMobCensus(a_ToFill);
+ } // for itr - m_Layers
+}
+
+
+
+
+
+
+void cChunkMap::SpawnMobs(cMobSpawner& a_MobSpawner)
+{
+ cCSLock Lock(m_CSLayers);
+ for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ {
+ (*itr)->SpawnMobs(a_MobSpawner);
+ } // for itr - m_Layers
+}
+
+
+
+
void cChunkMap::Tick(float a_Dt)
{
@@ -2310,6 +2338,38 @@ cChunk * cChunkMap::cChunkLayer::FindChunk(int a_ChunkX, int a_ChunkZ)
+void cChunkMap::cChunkLayer::CollectMobCensus(cMobCensus& a_ToFill)
+{
+ for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++)
+ {
+ // We do count every Mobs in the world. But we are assuming that every chunk not loaded by any client
+ // doesn't affect us. Normally they should not have mobs because every "too far" mobs despawn
+ // If they have (f.i. when player disconnect) we assume we don't have to make them live or despawn
+ if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients())
+ {
+ m_Chunks[i]->CollectMobCensus(a_ToFill);
+ }
+ } // for i - m_Chunks[]
+}
+
+
+
+
+
+
+void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner& a_MobSpawner)
+{
+ for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++)
+ {
+ // We only spawn close to players
+ if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients())
+ {
+ m_Chunks[i]->SpawnMobs(a_MobSpawner);
+ }
+ } // for i - m_Chunks[]
+}
+
+
void cChunkMap::cChunkLayer::Tick(float a_Dt)
{
diff --git a/source/ChunkMap.h b/source/ChunkMap.h
index fcb164f7b..f68cb6472 100644
--- a/source/ChunkMap.h
+++ b/source/ChunkMap.h
@@ -26,6 +26,8 @@ class cPawn;
class cPickup;
class cChunkDataSerializer;
class cBlockArea;
+class cMobCensus;
+class cMobSpawner;
typedef std::list<cClientHandle *> cClientHandleList;
typedef cChunk * cChunkPtr;
@@ -263,9 +265,15 @@ public:
/// Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config
void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
- /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call
+ /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call
void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ);
+ /// Make a Mob census, of all mobs, their family, their chunk and theyr distance to closest player
+ void CollectMobCensus(cMobCensus& a_ToFill);
+
+ /// Try to Spawn Monsters inside all Chunks
+ void SpawnMobs(cMobSpawner& a_MobSpawner);
+
void Tick(float a_Dt);
void UnloadUnusedChunks(void);
@@ -309,6 +317,11 @@ private:
void Save(void);
void UnloadUnusedChunks(void);
+ /// Collect a mob census, of all mobs, their megatype, their chunk and their distance o closest player
+ void CollectMobCensus(cMobCensus& a_ToFill);
+ /// Try to Spawn Monsters inside all Chunks
+ void SpawnMobs(cMobSpawner& a_MobSpawner);
+
void Tick(float a_Dt);
void RemoveClient(cClientHandle * a_Client);
diff --git a/source/Globals.h b/source/Globals.h
index 1e531f7f3..ef79e4cf1 100644
--- a/source/Globals.h
+++ b/source/Globals.h
@@ -12,24 +12,24 @@
#if defined(_MSC_VER)
// MSVC produces warning C4481 on the override keyword usage, so disable the warning altogether
#pragma warning(disable:4481)
-
+
// Disable some warnings that we don't care about:
#pragma warning(disable:4100)
#define OBSOLETE __declspec(deprecated)
-
+
// No alignment needed in MSVC
#define ALIGN_8
#define ALIGN_16
-
+
#elif defined(__GNUC__)
// TODO: Can GCC explicitly mark classes as abstract (no instances can be created)?
#define abstract
-
+
// TODO: Can GCC mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class)
#define override
-
+
#define OBSOLETE __attribute__((deprecated))
#define ALIGN_8 __attribute__((aligned(8)))
@@ -41,13 +41,13 @@
#else
#error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler"
-
+
/*
// Copy and uncomment this into another #elif section based on your compiler identification
-
+
// Explicitly mark classes as abstract (no instances can be created)
#define abstract
-
+
// Mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class)
#define override
@@ -92,17 +92,17 @@ typedef unsigned short UInt16;
// OS-dependent stuff:
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
-
+
#define _WIN32_WINNT 0x501 // We want to target WinXP and higher
-
+
#include <Windows.h>
#include <winsock2.h>
#include <Ws2tcpip.h> // IPv6 stuff
-
+
// Windows SDK defines min and max macros, messing up with our std::min and std::max usage
#undef min
#undef max
-
+
// Windows SDK defines GetFreeSpace as a constant, probably a Win16 API remnant
#ifdef GetFreeSpace
#undef GetFreeSpace
diff --git a/source/Inventory.cpp b/source/Inventory.cpp
index c104db4c7..d5fc7f0d8 100644
--- a/source/Inventory.cpp
+++ b/source/Inventory.cpp
@@ -374,7 +374,7 @@ bool cInventory::DamageItem(int a_SlotNum, short a_Amount)
}
// The item has broken, remove it:
- Grid->EmptySlot(a_SlotNum);
+ Grid->EmptySlot(GridSlotNum);
return true;
}
diff --git a/source/LuaWindow.h b/source/LuaWindow.h
index 5a0685ebb..4c32c263e 100644
--- a/source/LuaWindow.h
+++ b/source/LuaWindow.h
@@ -23,8 +23,6 @@ class cPluginLua;
-// tolua_begin
-
/** A window that has been created by a Lua plugin and is handled entirely by that plugin
This object needs extra care with its lifetime management:
- It is created by Lua, so Lua expects to garbage-collect it later
@@ -35,9 +33,10 @@ Additionally, to forbid Lua from deleting this object while it is used by player
cPlayer:OpenWindow check if the window is of this class, and if so, make a global Lua reference for this object.
This reference needs to be unreferenced in the Destroy() function.
*/
-class cLuaWindow :
- public cWindow,
- public cItemGrid::cListener
+class cLuaWindow : // tolua_export
+ public cItemGrid::cListener,
+ // tolua_begin
+ public cWindow
{
typedef cWindow super;
diff --git a/source/ManualBindings.cpp b/source/ManualBindings.cpp
index e6605ddb0..466701bf8 100644
--- a/source/ManualBindings.cpp
+++ b/source/ManualBindings.cpp
@@ -17,6 +17,7 @@
#include "BlockEntities/DispenserEntity.h"
#include "BlockEntities/DropperEntity.h"
#include "BlockEntities/FurnaceEntity.h"
+#include "BlockEntities/HopperEntity.h"
#include "md5/md5.h"
#include "LuaWindow.h"
#include "LineBlockTracer.h"
@@ -2059,6 +2060,45 @@ static int tolua_cLineBlockTracer_Trace(lua_State * tolua_S)
+static int tolua_cHopperEntity_GetOutputBlockPos(lua_State * tolua_S)
+{
+ // function cHopperEntity::GetOutputBlockPos()
+ // Exported manually because tolua would require meaningless params
+
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cHopperEntity") ||
+ !L.CheckParamNumber (2) ||
+ !L.CheckParamEnd (3)
+ )
+ {
+ return 0;
+ }
+ cHopperEntity * self = (cHopperEntity *)tolua_tousertype(tolua_S, 1, 0);
+ if (self == NULL)
+ {
+ tolua_error(tolua_S, "invalid 'self' in function 'cHopperEntity::GetOutputBlockPos()'", NULL);
+ return 0;
+ }
+
+ NIBBLETYPE a_BlockMeta = ((NIBBLETYPE)tolua_tonumber(tolua_S, 2, 0));
+ int a_OutputX, a_OutputY, a_OutputZ;
+ bool res = self->GetOutputBlockPos(a_BlockMeta, a_OutputX, a_OutputY, a_OutputZ);
+ tolua_pushboolean(tolua_S, res);
+ if (res)
+ {
+ tolua_pushnumber(tolua_S, (lua_Number)a_OutputX);
+ tolua_pushnumber(tolua_S, (lua_Number)a_OutputY);
+ tolua_pushnumber(tolua_S, (lua_Number)a_OutputZ);
+ return 4;
+ }
+ return 1;
+}
+
+
+
+
+
void ManualBindings::Bind(lua_State * tolua_S)
{
tolua_beginmodule(tolua_S, NULL);
@@ -2070,6 +2110,10 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN);
tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR);
+ tolua_beginmodule(tolua_S, "cHopperEntity");
+ tolua_function(tolua_S, "GetOutputBlockPos", tolua_cHopperEntity_GetOutputBlockPos);
+ tolua_endmodule(tolua_S);
+
tolua_beginmodule(tolua_S, "cLineBlockTracer");
tolua_function(tolua_S, "Trace", tolua_cLineBlockTracer_Trace);
tolua_endmodule(tolua_S);
diff --git a/source/MobCensus.cpp b/source/MobCensus.cpp
new file mode 100644
index 000000000..66b5932bc
--- /dev/null
+++ b/source/MobCensus.cpp
@@ -0,0 +1,92 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "MobCensus.h"
+
+
+
+
+
+void cMobCensus::CollectMob(cMonster & a_Monster, cChunk & a_Chunk, double a_Distance)
+{
+ m_ProximityCounter.CollectMob(a_Monster, a_Chunk, a_Distance);
+ m_MobFamilyCollecter.CollectMob(a_Monster);
+}
+
+
+
+
+
+bool cMobCensus::IsCapped(cMonster::eFamily a_MobFamily)
+{
+ bool toReturn = true;
+ const int ratio = 319; // this should be 256 as we are only supposed to take account from chunks that are in 17x17 from a player
+ // but for now, we use all chunks loaded by players. that means 19 x 19 chunks. That's why we use 256 * (19*19) / (17*17) = 319
+ // MG TODO : code the correct count
+ if ((GetCapMultiplier(a_MobFamily) * GetNumChunks()) / ratio >= m_MobFamilyCollecter.GetNumberOfCollectedMobs(a_MobFamily))
+ {
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
+int cMobCensus::GetCapMultiplier(cMonster::eFamily a_MobFamily)
+{
+ switch (a_MobFamily)
+ {
+ case cMonster::mfHostile: return 79;
+ case cMonster::mfPassive: return 11;
+ case cMonster::mfAmbient: return 16;
+ case cMonster::mfWater: return 5;
+ }
+ ASSERT(!"Unhandled mob family");
+ return -1;
+}
+
+
+
+
+
+void cMobCensus::CollectSpawnableChunk(cChunk & a_Chunk)
+{
+ m_EligibleForSpawnChunks.insert(&a_Chunk);
+}
+
+
+
+
+
+int cMobCensus::GetNumChunks(void)
+{
+ return m_EligibleForSpawnChunks.size();
+}
+
+
+
+
+
+cMobProximityCounter & cMobCensus::GetProximityCounter(void)
+{
+ return m_ProximityCounter;
+}
+
+
+
+
+
+void cMobCensus::Logd()
+{
+ LOGD("Hostile mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfHostile), IsCapped(cMonster::mfHostile) ? "(capped)" : "");
+ LOGD("Ambient mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfAmbient), IsCapped(cMonster::mfAmbient) ? "(capped)" : "");
+ LOGD("Water mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfWater), IsCapped(cMonster::mfWater) ? "(capped)" : "");
+ LOGD("Passive mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfPassive), IsCapped(cMonster::mfPassive) ? "(capped)" : "");
+}
+
+
+
+
+
diff --git a/source/MobCensus.h b/source/MobCensus.h
new file mode 100644
index 000000000..e3892bec6
--- /dev/null
+++ b/source/MobCensus.h
@@ -0,0 +1,59 @@
+
+#pragma once
+
+#include "MobProximityCounter.h"
+#include "MobFamilyCollecter.h"
+
+
+
+
+// fwd:
+class cChunk;
+class cMonster;
+
+
+
+
+
+/** This class is used to collect information, for each Mob, what is the distance of the closest player
+it was first being designed in order to make mobs spawn / despawn / act
+as the behaviour and even life of mobs depends on the distance to closest player
+
+as side effect : it also collect the chunks that are elligible for spawning
+as side effect 2 : it also know the caps for mobs number and can compare census to this numbers
+*/
+class cMobCensus
+{
+public:
+ /// Returns the nested proximity counter
+ cMobProximityCounter & GetProximityCounter(void);
+
+ // collect an elligible Chunk for Mob Spawning
+ // MG TODO : code the correct rule (not loaded chunk but short distant from players)
+ void CollectSpawnableChunk(cChunk & a_Chunk);
+
+ /// Collect a mob - it's distance to player, it's family ...
+ void CollectMob(cMonster& a_Monster, cChunk& a_Chunk, double a_Distance);
+
+ /// Returns true if the family is capped (i.e. there are more mobs of this family than max)
+ bool IsCapped(cMonster::eFamily a_MobFamily);
+
+ /// log the results of census to server console
+ void Logd(void);
+
+protected :
+ cMobProximityCounter m_ProximityCounter;
+ cMobFamilyCollecter m_MobFamilyCollecter;
+
+ std::set<cChunk *> m_EligibleForSpawnChunks;
+
+ /// Returns the number of chunks that are elligible for spawning (for now, the loaded, valid chunks)
+ int GetNumChunks();
+
+ /// Returns the cap multiplier value of the given monster family
+ static int GetCapMultiplier(cMonster::eFamily a_MobFamily);
+} ;
+
+
+
+
diff --git a/source/MobFamilyCollecter.cpp b/source/MobFamilyCollecter.cpp
new file mode 100644
index 000000000..e9c69e078
--- /dev/null
+++ b/source/MobFamilyCollecter.cpp
@@ -0,0 +1,26 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "MobFamilyCollecter.h"
+#include "Mobs/Monster.h"
+
+
+
+void cMobFamilyCollecter::CollectMob(cMonster & a_Monster)
+{
+ cMonster::eFamily MobFamily = a_Monster.GetMobFamily();
+ m_Mobs[MobFamily].insert(&a_Monster);
+}
+
+
+
+
+
+int cMobFamilyCollecter::GetNumberOfCollectedMobs(cMonster::eFamily a_Family)
+{
+ return m_Mobs[a_Family].size();
+}
+
+
+
+
diff --git a/source/MobFamilyCollecter.h b/source/MobFamilyCollecter.h
new file mode 100644
index 000000000..6cef133b5
--- /dev/null
+++ b/source/MobFamilyCollecter.h
@@ -0,0 +1,39 @@
+
+#pragma once
+
+#include <map>
+#include <set>
+#include "BlockID.h"
+#include "Mobs/Monster.h" //this is a side-effect of keeping Mobfamily inside Monster class. I'd prefer to keep both (Mobfamily and Monster) inside a "Monster" namespace MG TODO : do it
+
+
+
+
+// fwd:
+class cChunk;
+
+
+
+
+
+/** This class is used to collect the list of mobs for each family
+*/
+class cMobFamilyCollecter
+{
+public :
+ typedef const std::set<cMonster::eFamily> tMobFamilyList;
+
+ // collect a mob
+ void CollectMob(cMonster & a_Monster);
+
+ // return the number of mobs for this family
+ int GetNumberOfCollectedMobs(cMonster::eFamily a_Family);
+
+protected :
+ std::map<cMonster::eFamily, std::set<cMonster *> > m_Mobs;
+
+} ;
+
+
+
+
diff --git a/source/MobProximityCounter.cpp b/source/MobProximityCounter.cpp
new file mode 100644
index 000000000..583a71579
--- /dev/null
+++ b/source/MobProximityCounter.cpp
@@ -0,0 +1,83 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "MobProximityCounter.h"
+
+#include "Entities/Entity.h"
+#include "Chunk.h"
+
+void cMobProximityCounter::CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance)
+{
+// LOGD("Collecting monster %s, with distance %f",a_Monster->GetClass(),a_Distance);
+ tMonsterToDistance::iterator it = m_MonsterToDistance.find(&a_Monster);
+ if (it == m_MonsterToDistance.end())
+ {
+ sDistanceAndChunk newDistanceAndChunk(a_Distance,a_Chunk);
+ std::pair<tMonsterToDistance::iterator,bool> result = m_MonsterToDistance.insert(tMonsterToDistance::value_type(&a_Monster,newDistanceAndChunk));
+ if (!result.second)
+ {
+ ASSERT(!"A collected Monster was not found inside distance map using find(), but insert() said there already is a key for it");
+ }
+ }
+ else
+ {
+ if (a_Distance < it->second.m_Distance)
+ {
+ it->second.m_Distance = a_Distance;
+ it->second.m_Chunk = a_Chunk;
+ }
+ }
+
+ m_EligibleForSpawnChunks.insert(&a_Chunk);
+
+}
+
+void cMobProximityCounter::convertMaps()
+{
+ for(tMonsterToDistance::const_iterator itr = m_MonsterToDistance.begin(); itr != m_MonsterToDistance.end(); itr++)
+ {
+ m_DistanceToMonster.insert(tDistanceToMonster::value_type(itr->second.m_Distance,sMonsterAndChunk(*itr->first,itr->second.m_Chunk)));
+ }
+}
+
+cMobProximityCounter::sIterablePair cMobProximityCounter::getMobWithinThosesDistances(double a_DistanceMin, double a_DistanceMax)
+{
+ sIterablePair toReturn;
+ toReturn.m_Count = 0;
+ toReturn.m_Begin = m_DistanceToMonster.end();
+ toReturn.m_End = m_DistanceToMonster.end();
+
+ a_DistanceMin *= a_DistanceMin;// this is because is use square distance
+ a_DistanceMax *= a_DistanceMax;
+
+ if (m_DistanceToMonster.size() <= 0)
+ {
+ convertMaps();
+ }
+
+ for(tDistanceToMonster::const_iterator itr = m_DistanceToMonster.begin(); itr != m_DistanceToMonster.end(); itr++)
+ {
+ if (toReturn.m_Begin == m_DistanceToMonster.end())
+ {
+ if (a_DistanceMin == -1 || itr->first > a_DistanceMin)
+ {
+ toReturn.m_Begin = itr; // this is the first one with distance > a_DistanceMin;
+ }
+ }
+
+ if (toReturn.m_Begin != m_DistanceToMonster.end())
+ {
+ if (a_DistanceMax != -1 && itr->first > a_DistanceMax)
+ {
+ toReturn.m_End = itr; // this is just after the last one with distance < a_DistanceMax
+ // Note : if we are not going through this, it's ok, toReturn.m_End will be end();
+ break;
+ }
+ else
+ {
+ toReturn.m_Count ++;
+ }
+ }
+ }
+ return toReturn;
+}
diff --git a/source/MobProximityCounter.h b/source/MobProximityCounter.h
new file mode 100644
index 000000000..8a67139aa
--- /dev/null
+++ b/source/MobProximityCounter.h
@@ -0,0 +1,65 @@
+
+#pragma once
+
+#include <set>
+
+class cChunk;
+class cEntity;
+
+
+// This class is used to collect, for each Mob, what is the distance of the closest player
+// it was first being designed in order to make mobs spawn / despawn / act
+// as the behaviour and even life of mobs depends on the distance to closest player
+class cMobProximityCounter
+{
+protected :
+ // structs used for later maps (see m_MonsterToDistance and m_DistanceToMonster)
+ struct sDistanceAndChunk
+ {
+ sDistanceAndChunk(double a_Distance, cChunk& a_Chunk) : m_Distance(a_Distance), m_Chunk(a_Chunk) {}
+ double m_Distance;
+ cChunk& m_Chunk;
+ };
+ struct sMonsterAndChunk
+ {
+ sMonsterAndChunk(cEntity& a_Monster, cChunk& a_Chunk) : m_Monster(a_Monster), m_Chunk(a_Chunk) {}
+ cEntity& m_Monster;
+ cChunk& m_Chunk;
+ };
+
+public :
+ typedef std::map<cEntity*,sDistanceAndChunk> tMonsterToDistance;
+ typedef std::multimap<double,sMonsterAndChunk> tDistanceToMonster;
+
+protected :
+ // this map is filled during collection phase, it will be later transformed into DistanceToMonster
+ tMonsterToDistance m_MonsterToDistance;
+
+ // this map is generated after collection phase, in order to access monster by distance to player
+ tDistanceToMonster m_DistanceToMonster;
+
+ // this are the collected chunks. Used to determinate the number of elligible chunk for spawning.
+ std::set<cChunk*> m_EligibleForSpawnChunks;
+
+protected :
+ // transform monsterToDistance map (that was usefull for collecting) into distanceToMonster
+ // that will be usefull for picking up.
+ void convertMaps();
+
+public :
+ // count a mob on a specified chunk with specified distance to an unkown player
+ // if the distance is shortest than the one collected, this become the new closest
+ // distance and the chunk become the "hosting" chunk (that is the one that will perform the action)
+ void CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance);
+
+ // return the mobs that are within the range of distance of the closest player they are
+ // that means that if a mob is 30 m from a player and 150 m from another one. It will be
+ // in the range [0..50] but not in [100..200]
+ struct sIterablePair{
+ tDistanceToMonster::const_iterator m_Begin;
+ tDistanceToMonster::const_iterator m_End;
+ int m_Count;
+ };
+ sIterablePair getMobWithinThosesDistances(double a_DistanceMin, double a_DistanceMax);
+
+};
diff --git a/source/MobSpawner.cpp b/source/MobSpawner.cpp
new file mode 100644
index 000000000..1b3796f70
--- /dev/null
+++ b/source/MobSpawner.cpp
@@ -0,0 +1,270 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "MobSpawner.h"
+#include "Mobs/IncludeAllMonsters.h"
+
+
+
+
+
+cMobSpawner::cMobSpawner(cMonster::eFamily a_MonsterFamily,const std::set<cMonster::eType>& a_AllowedTypes) :
+ m_MonsterFamily(a_MonsterFamily),
+ m_NewPack(true),
+ m_MobType(cMonster::mtInvalidType)
+{
+ for (std::set<cMonster::eType>::const_iterator itr = a_AllowedTypes.begin(); itr != a_AllowedTypes.end(); itr++)
+ {
+ if (cMonster::FamilyFromType(*itr) == a_MonsterFamily)
+ {
+ m_AllowedTypes.insert(*itr);
+ }
+ }
+}
+
+
+
+
+
+bool cMobSpawner::CheckPackCenter(BLOCKTYPE a_BlockType)
+{
+ // Packs of non-water mobs can only be centered on an air block
+ // Packs of water mobs can only be centered on a water block
+ if (m_MonsterFamily == cMonster::mfWater)
+ {
+ return IsBlockWater(a_BlockType);
+ }
+ else
+ {
+ return a_BlockType == E_BLOCK_AIR;
+ }
+}
+
+
+
+
+
+void cMobSpawner::addIfAllowed(cMonster::eType toAdd, std::set<cMonster::eType>& toAddIn)
+{
+ std::set<cMonster::eType>::iterator itr = m_AllowedTypes.find(toAdd);
+ if (itr != m_AllowedTypes.end())
+ {
+ toAddIn.insert(toAdd);
+ }
+}
+
+
+
+
+
+cMonster::eType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
+{
+ std::set<cMonster::eType> allowedMobs;
+
+ if (a_Biome == biMushroomIsland || a_Biome == biMushroomShore)
+ {
+ addIfAllowed(cMonster::mtMooshroom, allowedMobs);
+ }
+ else if (a_Biome == biNether)
+ {
+ addIfAllowed(cMonster::mtGhast, allowedMobs);
+ addIfAllowed(cMonster::mtZombiePigman, allowedMobs);
+ addIfAllowed(cMonster::mtMagmaCube, allowedMobs);
+ }
+ /*else if (a_Biome == biEnder) MG TODO : figure out what are the biomes of the ender
+ {
+ addIfAllowed(cMonster::mtEnderman, allowedMobs);
+ }*/
+ else
+ {
+ addIfAllowed(cMonster::mtBat, allowedMobs);
+ addIfAllowed(cMonster::mtSpider, allowedMobs);
+ addIfAllowed(cMonster::mtZombie, allowedMobs);
+ addIfAllowed(cMonster::mtSkeleton, allowedMobs);
+ addIfAllowed(cMonster::mtCreeper, allowedMobs);
+ addIfAllowed(cMonster::mtSquid, allowedMobs);
+
+ if (a_Biome != biDesert && a_Biome != biBeach && a_Biome != biOcean)
+ {
+ addIfAllowed(cMonster::mtSheep, allowedMobs);
+ addIfAllowed(cMonster::mtPig, allowedMobs);
+ addIfAllowed(cMonster::mtCow, allowedMobs);
+ addIfAllowed(cMonster::mtChicken, allowedMobs);
+ addIfAllowed(cMonster::mtEnderman, allowedMobs);
+ addIfAllowed(cMonster::mtSlime, allowedMobs); // MG TODO : much more complicated rule
+
+ if (a_Biome == biForest || a_Biome == biForestHills || a_Biome == biTaiga || a_Biome == biTaigaHills)
+ {
+ addIfAllowed(cMonster::mtWolf, allowedMobs);
+ }
+ else if (a_Biome == biJungle || a_Biome == biJungleHills)
+ {
+ addIfAllowed(cMonster::mtOcelot, allowedMobs);
+ }
+ }
+ }
+
+ int allowedMobsSize = allowedMobs.size();
+ if (allowedMobsSize > 0)
+ {
+ std::set<cMonster::eType>::iterator itr = allowedMobs.begin();
+ int iRandom = m_Random.NextInt(allowedMobsSize,a_Biome);
+
+ for(int i = 0; i < iRandom; i++)
+ {
+ itr++;
+ }
+
+ return *itr;
+ }
+ return cMonster::mtInvalidType;
+}
+
+
+
+
+
+bool cMobSpawner::CanSpawnHere(cMonster::eType a_MobType, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, BLOCKTYPE a_BlockType_below, NIBBLETYPE a_BlockMeta_below, BLOCKTYPE a_BlockType_above, NIBBLETYPE a_BlockMeta_above, EMCSBiome a_Biome, int a_Level)
+{
+ bool toReturn = false;
+ std::set<cMonster::eType>::iterator itr = m_AllowedTypes.find(a_MobType);
+ if (itr != m_AllowedTypes.end())
+ {
+ // MG TODO : find a nicer paging
+ if (a_MobType == cMonster::mtSquid)
+ {
+ toReturn = (
+ IsBlockLiquid(a_BlockType) &&
+ a_Level >= 45 &&
+ a_Level <= 62
+ );
+ }
+ else if (a_MobType == cMonster::mtBat)
+ {
+ toReturn = a_Level <= 60; // MG TODO : find a real rule
+ }
+ else
+ {
+ if (
+ a_BlockType == E_BLOCK_AIR &&
+ a_BlockType_above == E_BLOCK_AIR &&
+ ! (g_BlockTransparent[a_BlockType_below])
+ )
+ {
+ if (a_MobType == cMonster::mtChicken || a_MobType == cMonster::mtPig || a_MobType == cMonster::mtCow || a_MobType == cMonster::mtSheep)
+ {
+ toReturn = (
+ a_BlockType_below == E_BLOCK_GRASS /*&& // MG TODO
+ a_LightLevel >= 9 */
+ );
+ }
+ else if (a_MobType == cMonster::mtOcelot)
+ {
+ toReturn = (
+ a_Level >= 62 &&
+ (
+ a_BlockType_below == E_BLOCK_GRASS ||
+ a_BlockType_below == E_BLOCK_LEAVES
+ ) &&
+ m_Random.NextInt(3,a_Biome) != 0
+ );
+ }
+ else if (a_MobType == cMonster::mtCreeper || a_MobType == cMonster::mtSkeleton || a_MobType == cMonster::mtZombie || a_MobType == cMonster::mtSpider || a_MobType == cMonster::mtEnderman || a_MobType == cMonster::mtZombiePigman)
+ {
+ toReturn = true /*a_LightLevel <= 7 MG TODO*/;
+ /*if (a_SunLight) MG TODO
+ {
+ if (m_Random.NextInt(2,a_Biome) != 0)
+ {
+ toReturn = false;
+ }
+ }*/
+ }
+ else if (a_MobType == cMonster::mtSlime)
+ {
+ toReturn = a_Level <= 40;
+ // MG TODO : much more complicated rules
+ }
+ else if (a_MobType == cMonster::mtGhast)
+ {
+ toReturn = m_Random.NextInt(20,a_Biome) == 0;
+ }
+ else
+ {
+ LOGD("MG TODO : check I've got a Rule to write for type %d",a_MobType);
+ toReturn = true;
+ }
+ }
+ }
+ }
+ return toReturn;
+}
+
+
+
+
+
+cMonster* cMobSpawner::TryToSpawnHere(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, BLOCKTYPE a_BlockType_below, NIBBLETYPE a_BlockMeta_below, BLOCKTYPE a_BlockType_above, NIBBLETYPE a_BlockMeta_above, EMCSBiome a_Biome, int a_Level, int& a_MaxPackSize)
+{
+ cMonster* toReturn = NULL;
+ if (m_NewPack)
+ {
+ m_MobType = ChooseMobType(a_Biome);
+ if (m_MobType == cMonster::mtInvalidType)
+ {
+ return toReturn;
+ }
+ if (m_MobType == cMonster::mtWolf)
+ {
+ a_MaxPackSize = 8;
+ }
+ else if (m_MobType == cMonster::mtGhast)
+ {
+ a_MaxPackSize = 1;
+ }
+ m_NewPack = false;
+ }
+
+
+ if (CanSpawnHere(m_MobType, a_BlockType, a_BlockMeta, a_BlockType_below, a_BlockMeta_below, a_BlockType_above, a_BlockMeta_above, a_Biome, a_Level))
+ {
+ cMonster * newMob = cMonster::NewMonsterFromType(m_MobType);
+ if (newMob)
+ {
+ m_Spawned.insert(newMob);
+ }
+ toReturn = newMob;
+ }
+ return toReturn;
+}
+
+
+
+
+
+void cMobSpawner::NewPack()
+{
+ m_NewPack = true;
+}
+
+
+
+
+
+cMobSpawner::tSpawnedContainer & cMobSpawner::getSpawned(void)
+{
+ return m_Spawned;
+}
+
+
+
+
+
+bool cMobSpawner::CanSpawnAnything(void)
+{
+ return !m_AllowedTypes.empty();
+}
+
+
+
+
diff --git a/source/MobSpawner.h b/source/MobSpawner.h
new file mode 100644
index 000000000..ba2a18f2e
--- /dev/null
+++ b/source/MobSpawner.h
@@ -0,0 +1,75 @@
+
+#pragma once
+
+#include <set>
+#include "BlockID.h"
+#include "ChunkDef.h"
+#include "FastRandom.h"
+#include "Mobs/Monster.h" //this is a side-effect of keeping Mobfamily inside Monster class. I'd prefer to keep both (Mobfamily and Monster) inside a "Monster" namespace MG TODO : do it
+
+
+
+
+// fwd:
+class cChunk;
+
+
+
+
+
+/** This class is used to determine which monster can be spawned in which place
+it is essentially static (eg. Squids spawn in water, Zombies spawn in dark places)
+but it also has dynamic part depending on the world.ini settings.
+*/
+class cMobSpawner
+{
+public :
+ // constructor
+ // a_MobFamily is the Family of mobs that this spawner will spawn
+ // a_AllowedTypes is the set of types allowed for mobs it will spawn. Empty set
+ // would result in no spawn at all
+ // Allowed mobs thah are not of the right Family will not be include (no warning)
+ cMobSpawner(cMonster::eFamily MobFamily, const std::set<cMonster::eType> & a_AllowedTypes);
+
+ /// Check if specified block can be a Pack center for this spawner
+ bool CheckPackCenter(BLOCKTYPE a_BlockType);
+
+ // Try to create a monster here
+ // if this is the first of a Pack : determine the type of monster
+ // BlockType & BlockMeta are used to decide what kind of Mob can Spawn here
+ // MaxPackSize is set to the maximal size for a pack this type of mob
+ cMonster * TryToSpawnHere(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, BLOCKTYPE a_BlockType_below, NIBBLETYPE a_BlockMeta_below, BLOCKTYPE a_BlockType_above, NIBBLETYPE a_BlockMeta_above, EMCSBiome a_Biome, int a_Level, int& a_MaxPackSize);
+
+ // mark the beginning of a new Pack
+ // all mobs of the same Pack are the same type
+ void NewPack(void);
+
+ // return true if there is at least one allowed type
+ bool CanSpawnAnything(void);
+
+ typedef const std::set<cMonster *> tSpawnedContainer;
+ tSpawnedContainer & getSpawned(void);
+
+protected :
+ // return true if specified type of mob can spawn on specified block
+ bool CanSpawnHere(cMonster::eType a_MobType, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, BLOCKTYPE a_BlockType_below, NIBBLETYPE a_BlockMeta_below, BLOCKTYPE a_BlockType_above, NIBBLETYPE a_BlockMeta_above, EMCSBiome a_Biome, int a_Level);
+
+ // return a random type that can spawn on specified biome.
+ // returns E_ENTITY_TYPE_DONOTUSE if none is possible
+ cMonster::eType ChooseMobType(EMCSBiome a_Biome);
+
+ // add toAdd inside toAddIn, if toAdd is in m_AllowedTypes
+ void addIfAllowed(cMonster::eType toAdd, std::set<cMonster::eType> & toAddIn);
+
+protected :
+ cMonster::eFamily m_MonsterFamily;
+ std::set<cMonster::eType> m_AllowedTypes;
+ bool m_NewPack;
+ cMonster::eType m_MobType;
+ std::set<cMonster*> m_Spawned;
+ cFastRandom m_Random;
+} ;
+
+
+
+
diff --git a/source/Mobs/AggressiveMonster.cpp b/source/Mobs/AggressiveMonster.cpp
index 2eae772d7..ee6252656 100644
--- a/source/Mobs/AggressiveMonster.cpp
+++ b/source/Mobs/AggressiveMonster.cpp
@@ -12,8 +12,8 @@
-cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, char a_ProtocolMobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) :
- super(a_ConfigName, a_ProtocolMobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height),
+cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) :
+ super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height),
m_ChaseTime(999999)
{
m_EMPersonality = AGGRESSIVE;
@@ -95,5 +95,3 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk)
}
-
-
diff --git a/source/Mobs/AggressiveMonster.h b/source/Mobs/AggressiveMonster.h
index 1eff1831e..5a0d93f3d 100644
--- a/source/Mobs/AggressiveMonster.h
+++ b/source/Mobs/AggressiveMonster.h
@@ -13,12 +13,13 @@ class cAggressiveMonster :
typedef cMonster super;
public:
- cAggressiveMonster(const AString & a_ConfigName, char a_ProtocolMobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
+ cAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
virtual void Tick (float a_Dt, cChunk & a_Chunk) override;
virtual void InStateChasing(float a_Dt) override;
virtual void EventSeePlayer(cEntity *) override;
+
protected:
float m_ChaseTime;
diff --git a/source/Mobs/Bat.cpp b/source/Mobs/Bat.cpp
new file mode 100644
index 000000000..b9c82996b
--- /dev/null
+++ b/source/Mobs/Bat.cpp
@@ -0,0 +1,15 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Bat.h"
+#include "../Vector3d.h"
+#include "../Chunk.h"
+
+
+cBat::cBat(void) :
+ // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here
+ super("Bat", mtBat, "mob.bat.hurt", "mob.bat.death", 0.7, 0.7)
+{
+}
+
+
diff --git a/source/Mobs/Bat.h b/source/Mobs/Bat.h
index 0b50e06cd..e878d0ee8 100644
--- a/source/Mobs/Bat.h
+++ b/source/Mobs/Bat.h
@@ -13,13 +13,10 @@ class cBat :
typedef cPassiveMonster super;
public:
- cBat(void) :
- super("Bat", 65, "mob.bat.hurt", "mob.bat.death", 0.5, 0.9)
- {
- }
+ cBat(void);
CLASS_PROTODEF(cBat);
-
+
bool IsHanging(void) const {return false; }
} ;
diff --git a/source/Mobs/Blaze.cpp b/source/Mobs/Blaze.cpp
index dbbccf417..74c82c081 100644
--- a/source/Mobs/Blaze.cpp
+++ b/source/Mobs/Blaze.cpp
@@ -9,7 +9,7 @@
cBlaze::cBlaze(void) :
// TODO: The size is only a guesstimate, measure in vanilla and fix the size values here
- super("Blaze", 61, "mob.blaze.hit", "mob.blaze.death", 0.7, 1.8)
+ super("Blaze", mtBlaze, "mob.blaze.hit", "mob.blaze.death", 0.7, 1.8)
{
}
diff --git a/source/Mobs/Cavespider.cpp b/source/Mobs/Cavespider.cpp
index 2d50b391a..aba1ff9f5 100644
--- a/source/Mobs/Cavespider.cpp
+++ b/source/Mobs/Cavespider.cpp
@@ -9,7 +9,7 @@
cCavespider::cCavespider(void) :
- super("Cavespider", 59, "mob.spider.say", "mob.spider.death", 0.7, 0.5)
+ super("Cavespider", mtCaveSpider, "mob.spider.say", "mob.spider.death", 0.7, 0.5)
{
}
diff --git a/source/Mobs/Chicken.cpp b/source/Mobs/Chicken.cpp
index 3da9781d3..434a32f94 100644
--- a/source/Mobs/Chicken.cpp
+++ b/source/Mobs/Chicken.cpp
@@ -14,7 +14,7 @@
cChicken::cChicken(void) :
- super("Chicken", 93, "mob.chicken.hurt", "mob.chicken.hurt", 0.3, 0.4)
+ super("Chicken", mtChicken, "mob.chicken.hurt", "mob.chicken.hurt", 0.3, 0.4)
{
}
diff --git a/source/Mobs/Cow.cpp b/source/Mobs/Cow.cpp
index 431a6916d..9eb74dac2 100644
--- a/source/Mobs/Cow.cpp
+++ b/source/Mobs/Cow.cpp
@@ -2,6 +2,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Cow.h"
+#include "../Entities/Player.h"
@@ -10,7 +11,7 @@
cCow::cCow(void) :
- super("Cow", 92, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3)
+ super("Cow", mtCow, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3)
{
}
@@ -24,6 +25,10 @@ void cCow::GetDrops(cItems & a_Drops, cEntity * a_Killer)
AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF);
}
+
+
+
+
void cCow::OnRightClicked(cPlayer & a_Player)
{
if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_BUCKET))
@@ -31,9 +36,8 @@ void cCow::OnRightClicked(cPlayer & a_Player)
if (!a_Player.IsGameModeCreative())
{
a_Player.GetInventory().RemoveOneEquippedItem();
- a_Player.GetInventory().AddItem(E_ITEM_MILK)
+ a_Player.GetInventory().AddItem(E_ITEM_MILK);
}
-
}
}
diff --git a/source/Mobs/Creeper.cpp b/source/Mobs/Creeper.cpp
index b41b05f42..4e11ae13e 100644
--- a/source/Mobs/Creeper.cpp
+++ b/source/Mobs/Creeper.cpp
@@ -9,7 +9,7 @@
cCreeper::cCreeper(void) :
- super("Creeper", 50, "mob.creeper.say", "mob.creeper.say", 0.6, 1.8),
+ super("Creeper", mtCreeper, "mob.creeper.say", "mob.creeper.say", 0.6, 1.8),
m_bIsBlowing(false),
m_bIsCharged(false)
{
diff --git a/source/Mobs/EnderDragon.cpp b/source/Mobs/EnderDragon.cpp
index 64f2bedfa..acd81cde1 100644
--- a/source/Mobs/EnderDragon.cpp
+++ b/source/Mobs/EnderDragon.cpp
@@ -9,7 +9,7 @@
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)
+ super("EnderDragon", mtEnderDragon, "mob.enderdragon.hit", "mob.enderdragon.end", 16.0, 8.0)
{
}
diff --git a/source/Mobs/Enderman.cpp b/source/Mobs/Enderman.cpp
index c0ea3d6aa..a784131e4 100644
--- a/source/Mobs/Enderman.cpp
+++ b/source/Mobs/Enderman.cpp
@@ -8,7 +8,7 @@
cEnderman::cEnderman(void) :
- super("Enderman", 58, "mob.endermen.hit", "mob.endermen.death", 0.5, 2.9),
+ super("Enderman", mtEnderman, "mob.endermen.hit", "mob.endermen.death", 0.5, 2.9),
m_bIsScreaming(false),
CarriedBlock(E_BLOCK_AIR),
CarriedMeta(0)
diff --git a/source/Mobs/Ghast.cpp b/source/Mobs/Ghast.cpp
index 288d0c28a..419c8474d 100644
--- a/source/Mobs/Ghast.cpp
+++ b/source/Mobs/Ghast.cpp
@@ -8,7 +8,7 @@
cGhast::cGhast(void) :
- super("Ghast", 56, "mob.ghast.scream", "mob.ghast.death", 4, 4)
+ super("Ghast", mtGhast, "mob.ghast.scream", "mob.ghast.death", 4, 4)
{
}
diff --git a/source/Mobs/Giant.cpp b/source/Mobs/Giant.cpp
index a02758a43..f41977535 100644
--- a/source/Mobs/Giant.cpp
+++ b/source/Mobs/Giant.cpp
@@ -9,7 +9,7 @@
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)
+ super("Giant", mtGiant, "mob.zombie.hurt", "mob.zombie.death", 2.0, 13.5)
{
}
diff --git a/source/Mobs/Horse.cpp b/source/Mobs/Horse.cpp
index c2a8f6ed0..f9705a451 100644
--- a/source/Mobs/Horse.cpp
+++ b/source/Mobs/Horse.cpp
@@ -10,7 +10,7 @@
cHorse::cHorse(int Type, int Color, int Style, int TameTimes) :
- super("Horse", 100, "mob.horse.hit", "mob.horse.death", 1.4, 1.6),
+ super("Horse", mtHorse, "mob.horse.hit", "mob.horse.death", 1.4, 1.6),
m_bHasChest(false),
m_bIsEating(false),
m_bIsRearing(false),
@@ -76,8 +76,12 @@ void cHorse::Tick(float a_Dt, cChunk & a_Chunk)
if (m_RearTickCount == 20)
{
m_bIsRearing = false;
+ m_RearTickCount = 0;
+ }
+ else
+ {
+ m_RearTickCount++;
}
- else { m_RearTickCount++;}
}
m_World->BroadcastEntityMetadata(*this);
@@ -89,35 +93,45 @@ void cHorse::Tick(float a_Dt, cChunk & a_Chunk)
void cHorse::OnRightClicked(cPlayer & a_Player)
{
- if (m_Attachee != NULL)
+ if (!m_bIsSaddled && m_bIsTame)
{
- if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID())
+ if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE)
{
- a_Player.Detach();
- return;
+ // Saddle the horse:
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+ m_bIsSaddled = true;
+ m_World->BroadcastEntityMetadata(*this);
}
-
- if (m_Attachee->IsPlayer())
+ else if (!a_Player.GetEquippedItem().IsEmpty())
{
- return;
+ // The horse doesn't like being hit, make it rear:
+ m_bIsRearing = true;
+ m_RearTickCount = 0;
}
-
- m_Attachee->Detach();
}
-
- m_TameAttemptTimes++;
- a_Player.AttachTo(this);
-
- if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE)
+ else
{
- if (!a_Player.IsGameModeCreative())
+ if (m_Attachee != NULL)
{
- a_Player.GetInventory().RemoveOneEquippedItem();
+ if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID())
+ {
+ a_Player.Detach();
+ return;
+ }
+
+ if (m_Attachee->IsPlayer())
+ {
+ return;
+ }
+
+ m_Attachee->Detach();
}
- // Set saddle state & broadcast metadata
- m_bIsSaddled = true;
- m_World->BroadcastEntityMetadata(*this);
+ m_TameAttemptTimes++;
+ a_Player.AttachTo(this);
}
}
diff --git a/source/Mobs/IncludeAllMonsters.h b/source/Mobs/IncludeAllMonsters.h
new file mode 100644
index 000000000..1b436a11f
--- /dev/null
+++ b/source/Mobs/IncludeAllMonsters.h
@@ -0,0 +1,29 @@
+#include "Bat.h"
+#include "Blaze.h"
+#include "Cavespider.h"
+#include "Chicken.h"
+#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"
+#include "Pig.h"
+#include "Sheep.h"
+#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
index 42d312c23..47c961098 100644
--- a/source/Mobs/IronGolem.cpp
+++ b/source/Mobs/IronGolem.cpp
@@ -8,7 +8,7 @@
cIronGolem::cIronGolem(void) :
- super("IronGolem", 99, "mob.IronGolem.hit", "mob.IronGolem.death", 1.4, 2.9)
+ super("IronGolem", mtIronGolem, "mob.IronGolem.hit", "mob.IronGolem.death", 1.4, 2.9)
{
}
diff --git a/source/Mobs/Magmacube.cpp b/source/Mobs/Magmacube.cpp
index c72b4831b..86447ff6b 100644
--- a/source/Mobs/Magmacube.cpp
+++ b/source/Mobs/Magmacube.cpp
@@ -8,7 +8,7 @@
cMagmaCube::cMagmaCube(int a_Size) :
- super("MagmaCube", 62, "mob.MagmaCube.big", "mob.MagmaCube.big", 0.6 * a_Size, 0.6 * a_Size),
+ super("MagmaCube", mtMagmaCube, "mob.MagmaCube.big", "mob.MagmaCube.big", 0.6 * a_Size, 0.6 * a_Size),
m_Size(a_Size)
{
}
diff --git a/source/Mobs/Monster.cpp b/source/Mobs/Monster.cpp
index 334229a42..7e35b97f1 100644
--- a/source/Mobs/Monster.cpp
+++ b/source/Mobs/Monster.cpp
@@ -1,7 +1,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-#include "Monster.h"
+#include "IncludeAllMonsters.h"
#include "../Root.h"
#include "../Server.h"
#include "../ClientHandle.h"
@@ -16,14 +16,56 @@
#include "../Vector3d.h"
#include "../Tracer.h"
#include "../Chunk.h"
+#include "../FastRandom.h"
-// #include "../../iniFile/iniFile.h"
-
-cMonster::cMonster(const AString & a_ConfigName, char a_ProtocolMobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height)
+/** Map for eType <-> string
+Needs to be alpha-sorted by the strings, because binary search is used in StringToMobType()
+The strings need to be lowercase (for more efficient comparisons in StringToMobType())
+*/
+static const struct
+{
+ cMonster::eType m_Type;
+ const char * m_lcName;
+} g_MobTypeNames[] =
+{
+ {cMonster::mtBat, "bat"},
+ {cMonster::mtBlaze, "blaze"},
+ {cMonster::mtCaveSpider, "cavespider"},
+ {cMonster::mtChicken, "chicken"},
+ {cMonster::mtCow, "cow"},
+ {cMonster::mtCreeper, "creeper"},
+ {cMonster::mtEnderman, "enderman"},
+ {cMonster::mtGhast, "ghast"},
+ {cMonster::mtHorse, "horse"},
+ {cMonster::mtMagmaCube, "magmacube"},
+ {cMonster::mtMooshroom, "mooshroom"},
+ {cMonster::mtOcelot, "ocelot"},
+ {cMonster::mtPig, "pig"},
+ {cMonster::mtSheep, "sheep"},
+ {cMonster::mtSilverfish, "silverfish"},
+ {cMonster::mtSkeleton, "skeleton"},
+ {cMonster::mtSlime, "slime"},
+ {cMonster::mtSpider, "spider"},
+ {cMonster::mtSquid, "squid"},
+ {cMonster::mtVillager, "villager"},
+ {cMonster::mtWitch, "witch"},
+ {cMonster::mtWolf, "wolf"},
+ {cMonster::mtZombie, "zombie"},
+ {cMonster::mtZombiePigman, "zombiepigman"},
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMonster:
+
+cMonster::cMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height)
: super(etMonster, a_Width, a_Height)
, m_Target(NULL)
, m_AttackRate(3)
@@ -32,7 +74,7 @@ cMonster::cMonster(const AString & a_ConfigName, char a_ProtocolMobType, const A
, m_DestinationTime( 0 )
, m_DestroyTimer( 0 )
, m_Jump(0)
- , m_MobType(a_ProtocolMobType)
+ , m_MobType(a_MobType)
, m_SoundHurt(a_SoundHurt)
, m_SoundDeath(a_SoundDeath)
, m_EMState(IDLE)
@@ -465,6 +507,189 @@ void cMonster::SetSightDistance(float sd)
+AString cMonster::MobTypeToString(cMonster::eType a_MobType)
+{
+ // Mob types aren't sorted, so we need to search linearly:
+ for (int i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++)
+ {
+ if (g_MobTypeNames[i].m_Type == a_MobType)
+ {
+ return g_MobTypeNames[i].m_lcName;
+ }
+ }
+
+ // Not found:
+ return "";
+}
+
+
+
+
+
+cMonster::eType cMonster::StringToMobType(const AString & a_Name)
+{
+ AString lcName(a_Name);
+ StrToLower(lcName);
+
+ // Binary-search for the lowercase name:
+ int lo = 0, hi = ARRAYCOUNT(g_MobTypeNames) - 1;
+ while (hi - lo > 1)
+ {
+ int mid = (lo + hi) / 2;
+ int res = strcmp(g_MobTypeNames[mid].m_lcName, lcName.c_str());
+ if (res == 0)
+ {
+ return g_MobTypeNames[mid].m_Type;
+ }
+ if (res < 0)
+ {
+ lo = mid;
+ }
+ else
+ {
+ hi = mid;
+ }
+ }
+ // Range has collapsed to at most two elements, compare each:
+ if (strcmp(g_MobTypeNames[lo].m_lcName, lcName.c_str()) == 0)
+ {
+ return g_MobTypeNames[lo].m_Type;
+ }
+ if ((lo != hi) && (strcmp(g_MobTypeNames[hi].m_lcName, lcName.c_str()) == 0))
+ {
+ return g_MobTypeNames[hi].m_Type;
+ }
+
+ // Not found:
+ return mtInvalidType;
+}
+
+
+
+
+
+cMonster::eFamily cMonster::FamilyFromType(eType a_Type)
+{
+ switch (a_Type)
+ {
+ case mtBat: return mfAmbient;
+ case mtBlaze: return mfHostile;
+ case mtCaveSpider: return mfHostile;
+ case mtChicken: return mfPassive;
+ case mtCow: return mfPassive;
+ case mtCreeper: return mfHostile;
+ case mtEnderman: return mfHostile;
+ case mtGhast: return mfHostile;
+ case mtHorse: return mfPassive;
+ case mtMagmaCube: return mfHostile;
+ case mtMooshroom: return mfHostile;
+ case mtOcelot: return mfHostile;
+ case mtPig: return mfPassive;
+ case mtSheep: return mfPassive;
+ case mtSilverfish: return mfHostile;
+ case mtSkeleton: return mfHostile;
+ case mtSlime: return mfHostile;
+ case mtSpider: return mfHostile;
+ case mtSquid: return mfWater;
+ case mtVillager: return mfPassive;
+ case mtWitch: return mfHostile;
+ case mtWolf: return mfHostile;
+ case mtZombie: return mfHostile;
+ case mtZombiePigman: return mfHostile;
+ } ;
+ ASSERT(!"Unhandled mob type");
+ return mfMaxplusone;
+}
+
+
+
+
+
+int cMonster::GetSpawnRate(cMonster::eFamily a_MobFamily)
+{
+ switch (a_MobFamily)
+ {
+ case mfHostile: return 1;
+ case mfPassive: return 400;
+ case mfAmbient: return 400;
+ case mfWater: return 400;
+ }
+ ASSERT(!"Unhandled mob family");
+ return -1;
+}
+
+
+
+
+
+cMonster * cMonster::NewMonsterFromType(cMonster::eType a_MobType, int a_Size)
+{
+ cFastRandom Random;
+
+ cMonster * toReturn = NULL;
+
+ // unspecified size get rand[1,3] for Monsters that need size
+ switch (a_MobType)
+ {
+ case mtMagmaCube:
+ case mtSlime:
+ {
+ if (a_Size == -1)
+ {
+ a_Size = Random.NextInt(2, a_MobType) + 1;
+ }
+ if ((a_Size <= 0) || (a_Size >= 4))
+ {
+ ASSERT(!"Random for size was supposed to pick in [1..3] and picked outside");
+ a_Size = 1;
+ }
+ break;
+ }
+ default: break;
+ } // switch (a_MobType)
+
+ // Create the mob entity
+ switch (a_MobType)
+ {
+ case mtMagmaCube: toReturn = new cMagmaCube(a_Size); break;
+ case mtSlime: toReturn = new cSlime(a_Size); break;
+ case mtBat: toReturn = new cBat(); break;
+ case mtBlaze: toReturn = new cBlaze(); break;
+ case mtCaveSpider: toReturn = new cCavespider(); break;
+ case mtChicken: toReturn = new cChicken(); break;
+ case mtCow: toReturn = new cCow(); break;
+ case mtCreeper: toReturn = new cCreeper(); break;
+ case mtEnderman: toReturn = new cEnderman(); break;
+ case mtGhast: toReturn = new cGhast(); break;
+ // TODO:
+ // case cMonster::mtHorse: toReturn = new cHorse(); break;
+ case mtMooshroom: toReturn = new cMooshroom(); break;
+ case mtOcelot: toReturn = new cOcelot(); break;
+ case mtPig: toReturn = new cPig(); break;
+ // TODO: Implement sheep color
+ case mtSheep: toReturn = new cSheep(0); break;
+ case mtSilverfish: toReturn = new cSilverfish(); break;
+ // TODO: Implement wither skeleton geration
+ case mtSkeleton: toReturn = new cSkeleton(false); break;
+ case mtSpider: toReturn = new cSpider(); break;
+ case mtSquid: toReturn = new cSquid(); break;
+ case mtVillager: toReturn = new cVillager(cVillager::vtFarmer); break;
+ case mtWitch: toReturn = new cWitch(); break;
+ case mtWolf: toReturn = new cWolf(); break;
+ case mtZombie: toReturn = new cZombie(false); break;
+ case mtZombiePigman: toReturn = new cZombiePigman(); break;
+ default:
+ {
+ ASSERT(!"Unhandled Mob type");
+ }
+ }
+ return toReturn;
+}
+
+
+
+
+
void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth)
{
MTRand r1;
@@ -493,8 +718,8 @@ void cMonster::HandleDaylightBurning(cChunk & a_Chunk)
return;
}
- int RelX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width;
- int RelZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width;
+ int RelX = (int)floor(GetPosX()) - GetChunkX() * cChunkDef::Width;
+ int RelZ = (int)floor(GetPosZ()) - GetChunkZ() * cChunkDef::Width;
if (
(a_Chunk.GetSkyLight(RelX, RelY, RelZ) == 15) && // In the daylight
(a_Chunk.GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand
@@ -510,3 +735,11 @@ void cMonster::HandleDaylightBurning(cChunk & a_Chunk)
+cMonster::eFamily cMonster::GetMobFamily(void) const
+{
+ return FamilyFromType(m_MobType);
+}
+
+
+
+
diff --git a/source/Mobs/Monster.h b/source/Mobs/Monster.h
index d784f2eec..14c72ed73 100644
--- a/source/Mobs/Monster.h
+++ b/source/Mobs/Monster.h
@@ -26,6 +26,8 @@ public:
/// This identifies individual monster type, as well as their network type-ID
enum eType
{
+ mtInvalidType = -1,
+
mtBat = E_META_SPAWN_EGG_BAT,
mtBlaze = E_META_SPAWN_EGG_BLAZE,
mtCaveSpider = E_META_SPAWN_EGG_CAVE_SPIDER,
@@ -55,19 +57,31 @@ public:
mtWolf = E_META_SPAWN_EGG_WOLF,
mtZombie = E_META_SPAWN_EGG_ZOMBIE,
mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN,
+ } ;
+
+ enum eFamily
+ {
+ mfHostile = 0, // Spider, Zombies ...
+ mfPassive = 1, // Cows, Pigs
+ mfAmbient = 2, // Bats
+ mfWater = 3, // Squid
+ mfMaxplusone, // Nothing. Be sure this is the last and the others are in order
} ;
// tolua_end
+ enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState;
+ enum MPersonality{PASSIVE,AGGRESSIVE,COWARDLY} m_EMPersonality;
+
float m_SightDistance;
/** Creates the mob object.
* If a_ConfigName is not empty, the configuration is loaded using GetMonsterConfig()
- * a_ProtocolMobType is the ID of the mob used in the protocol ( http://wiki.vg/Entities#Mobs , 2012_12_22)
+ * a_MobType is the type of the mob (also used in the protocol ( http://wiki.vg/Entities#Mobs , 2012_12_22))
* a_SoundHurt and a_SoundDeath are assigned into m_SoundHurt and m_SoundDeath, respectively
*/
- cMonster(const AString & a_ConfigName, char a_ProtocolMobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
+ cMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
CLASS_PROTODEF(cMonster);
@@ -82,7 +96,11 @@ public:
virtual void MoveToPosition(const Vector3f & a_Position);
virtual bool ReachedDestination(void);
- char GetMobType(void) const {return m_MobType; }
+ // tolua_begin
+ eType GetMobType(void) const {return m_MobType; }
+ eFamily GetMobFamily(void) const;
+ // tolua_end
+
const char * GetState();
void SetState(const AString & str);
@@ -103,8 +121,6 @@ public:
virtual void Attack(float a_Dt);
- int GetMobType() { return m_MobType; } // tolua_export
-
int GetAttackRate(){return (int)m_AttackRate;}
void SetAttackRate(int ar);
void SetAttackRange(float ar);
@@ -119,9 +135,31 @@ public:
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;
+ // tolua_begin
+
+ /// Translates MobType enum to a string
+ static AString MobTypeToString(eType a_MobType);
+
+ /// Translates MobType string to the enum
+ static eType StringToMobType(const AString & a_MobTypeName);
+ /// Returns the mob family based on the type
+ static eFamily FamilyFromType(eType a_MobType);
+
+ /// Returns the spawn rate (number of game ticks between spawn attempts) for the given mob family
+ static int GetSpawnRate(cMonster::eFamily a_MobFamily);
+
+ // tolua_end
+
+ /** Creates a new object of the specified mob.
+ a_MobType is the type of the mob to be created
+ a_Size is the size (for mobs with size)
+ if a_Size is let to -1 for entities that need size, size will be random
+ asserts and returns null if mob type is not specified
+ asserts if invalid size for mobs that need size
+ */
+ static cMonster * NewMonsterFromType(eType a_MobType, int a_Size = -1);
+
protected:
cEntity * m_Target;
@@ -137,7 +175,7 @@ protected:
float m_DestroyTimer;
float m_Jump;
- char m_MobType;
+ eType m_MobType;
AString m_SoundHurt;
AString m_SoundDeath;
diff --git a/source/Mobs/Mooshroom.cpp b/source/Mobs/Mooshroom.cpp
index 5d2c901ba..940e2db44 100644
--- a/source/Mobs/Mooshroom.cpp
+++ b/source/Mobs/Mooshroom.cpp
@@ -14,7 +14,7 @@
cMooshroom::cMooshroom(void) :
- super("Mooshroom", 96, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3)
+ super("Mooshroom", mtMooshroom, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3)
{
}
diff --git a/source/Mobs/Ocelot.h b/source/Mobs/Ocelot.h
index 6d24c5672..adb7a1f75 100644
--- a/source/Mobs/Ocelot.h
+++ b/source/Mobs/Ocelot.h
@@ -14,7 +14,7 @@ class cOcelot :
public:
cOcelot(void) :
- super("Ocelot", 98, "mob.cat.hitt", "mob.cat.hitt", 0.6, 0.8)
+ super("Ocelot", mtOcelot, "mob.cat.hitt", "mob.cat.hitt", 0.6, 0.8)
{
}
diff --git a/source/Mobs/PassiveAggressiveMonster.cpp b/source/Mobs/PassiveAggressiveMonster.cpp
index e473137a9..28de65905 100644
--- a/source/Mobs/PassiveAggressiveMonster.cpp
+++ b/source/Mobs/PassiveAggressiveMonster.cpp
@@ -9,8 +9,8 @@
-cPassiveAggressiveMonster::cPassiveAggressiveMonster(const AString & a_ConfigName, char a_ProtocolMobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) :
- super(a_ConfigName, a_ProtocolMobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height)
+cPassiveAggressiveMonster::cPassiveAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) :
+ super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height)
{
m_EMPersonality = PASSIVE;
}
diff --git a/source/Mobs/PassiveAggressiveMonster.h b/source/Mobs/PassiveAggressiveMonster.h
index 243dfff38..2c5ef30b1 100644
--- a/source/Mobs/PassiveAggressiveMonster.h
+++ b/source/Mobs/PassiveAggressiveMonster.h
@@ -13,7 +13,7 @@ class cPassiveAggressiveMonster :
typedef cAggressiveMonster super;
public:
- cPassiveAggressiveMonster(const AString & a_ConfigName, char a_ProtocolMobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
+ cPassiveAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
} ;
diff --git a/source/Mobs/PassiveMonster.cpp b/source/Mobs/PassiveMonster.cpp
index 7a6140c04..91ceb5a53 100644
--- a/source/Mobs/PassiveMonster.cpp
+++ b/source/Mobs/PassiveMonster.cpp
@@ -9,8 +9,8 @@
-cPassiveMonster::cPassiveMonster(const AString & a_ConfigName, char a_ProtocolMobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) :
- super(a_ConfigName, a_ProtocolMobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height)
+cPassiveMonster::cPassiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) :
+ super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height)
{
m_EMPersonality = PASSIVE;
}
@@ -56,3 +56,4 @@ void cPassiveMonster::Tick(float a_Dt, cChunk & a_Chunk)
+
diff --git a/source/Mobs/PassiveMonster.h b/source/Mobs/PassiveMonster.h
index ae0bea3fb..14a6be6b1 100644
--- a/source/Mobs/PassiveMonster.h
+++ b/source/Mobs/PassiveMonster.h
@@ -13,12 +13,13 @@ class cPassiveMonster :
typedef cMonster super;
public:
- cPassiveMonster(const AString & a_ConfigName, char a_ProtocolMobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
+ cPassiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
/// When hit by someone, run away
virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+
} ;
diff --git a/source/Mobs/Pig.cpp b/source/Mobs/Pig.cpp
index cd18c087f..5427cf35f 100644
--- a/source/Mobs/Pig.cpp
+++ b/source/Mobs/Pig.cpp
@@ -10,7 +10,7 @@
cPig::cPig(void) :
- super("Pig", 90, "mob.pig.say", "mob.pig.death", 0.9, 0.9),
+ super("Pig", mtPig, "mob.pig.say", "mob.pig.death", 0.9, 0.9),
m_bIsSaddled(false)
{
}
diff --git a/source/Mobs/Sheep.cpp b/source/Mobs/Sheep.cpp
index 440c5c2b9..0d7d43e27 100644
--- a/source/Mobs/Sheep.cpp
+++ b/source/Mobs/Sheep.cpp
@@ -11,7 +11,7 @@
cSheep::cSheep(int a_Color) :
- super("Sheep", 91, "mob.sheep.say", "mob.sheep.say", 0.6, 1.3),
+ super("Sheep", mtSheep, "mob.sheep.say", "mob.sheep.say", 0.6, 1.3),
m_IsSheared(false),
m_WoolColor(a_Color)
{
diff --git a/source/Mobs/Silverfish.h b/source/Mobs/Silverfish.h
index d632ac169..a6e11c49d 100644
--- a/source/Mobs/Silverfish.h
+++ b/source/Mobs/Silverfish.h
@@ -14,7 +14,7 @@ class cSilverfish :
public:
cSilverfish(void) :
- super("Silverfish", 60, "mob.silverfish.hit", "mob.silverfish.kill", 0.3, 0.7)
+ super("Silverfish", mtSilverfish, "mob.silverfish.hit", "mob.silverfish.kill", 0.3, 0.7)
{
}
diff --git a/source/Mobs/Skeleton.cpp b/source/Mobs/Skeleton.cpp
index 6297b867c..37a724848 100644
--- a/source/Mobs/Skeleton.cpp
+++ b/source/Mobs/Skeleton.cpp
@@ -9,7 +9,7 @@
cSkeleton::cSkeleton(bool IsWither) :
- super("Skeleton", 51, "mob.skeleton.hurt", "mob.skeleton.death", 0.6, 1.8),
+ super("Skeleton", mtSkeleton, "mob.skeleton.hurt", "mob.skeleton.death", 0.6, 1.8),
m_bIsWither(IsWither)
{
SetBurnsInDaylight(true);
diff --git a/source/Mobs/Slime.cpp b/source/Mobs/Slime.cpp
index 7a9487a06..19f376c21 100644
--- a/source/Mobs/Slime.cpp
+++ b/source/Mobs/Slime.cpp
@@ -9,7 +9,7 @@
/// Creates a slime of the specified size; size is 1 .. 3, with 1 being the smallest
cSlime::cSlime(int a_Size) :
- super("Slime", 55, "mob.slime.attack", "mob.slime.attack", 0.6 * a_Size, 0.6 * a_Size),
+ super("Slime", mtSlime, "mob.slime.attack", "mob.slime.attack", 0.6 * a_Size, 0.6 * a_Size),
m_Size(a_Size)
{
}
diff --git a/source/Mobs/SnowGolem.cpp b/source/Mobs/SnowGolem.cpp
index 51125542d..9e199f87e 100644
--- a/source/Mobs/SnowGolem.cpp
+++ b/source/Mobs/SnowGolem.cpp
@@ -8,7 +8,7 @@
cSnowGolem::cSnowGolem(void) :
- super("SnowGolem", 97, "", "", 0.4, 1.8)
+ super("SnowGolem", mtSnowGolem, "", "", 0.4, 1.8)
{
}
diff --git a/source/Mobs/Spider.cpp b/source/Mobs/Spider.cpp
index 2f244cdbc..b19a5dcef 100644
--- a/source/Mobs/Spider.cpp
+++ b/source/Mobs/Spider.cpp
@@ -8,7 +8,7 @@
cSpider::cSpider(void) :
- super("Spider", 52, "mob.spider.say", "mob.spider.death", 1.4, 0.9)
+ super("Spider", mtSpider, "mob.spider.say", "mob.spider.death", 1.4, 0.9)
{
}
diff --git a/source/Mobs/Squid.cpp b/source/Mobs/Squid.cpp
index cb796f5ec..a311108ae 100644
--- a/source/Mobs/Squid.cpp
+++ b/source/Mobs/Squid.cpp
@@ -10,7 +10,7 @@
cSquid::cSquid(void) :
- super("Squid", 94, "", "", 0.95, 0.95)
+ super("Squid", mtSquid, "", "", 0.95, 0.95)
{
}
@@ -54,4 +54,3 @@ void cSquid::Tick(float a_Dt, cChunk & a_Chunk)
-
diff --git a/source/Mobs/Squid.h b/source/Mobs/Squid.h
index 35d7295b3..ad299b95c 100644
--- a/source/Mobs/Squid.h
+++ b/source/Mobs/Squid.h
@@ -20,6 +20,7 @@ public:
CLASS_PROTODEF(cSquid);
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+
} ;
diff --git a/source/Mobs/Villager.cpp b/source/Mobs/Villager.cpp
index 97d6dc3ca..7f89fb6cc 100644
--- a/source/Mobs/Villager.cpp
+++ b/source/Mobs/Villager.cpp
@@ -9,7 +9,7 @@
cVillager::cVillager(eVillagerType VillagerType) :
- super("Villager", 120, "", "", 0.6, 1.8),
+ super("Villager", mtVillager, "", "", 0.6, 1.8),
m_Type(VillagerType)
{
}
diff --git a/source/Mobs/Witch.cpp b/source/Mobs/Witch.cpp
index b29783853..25d27041f 100644
--- a/source/Mobs/Witch.cpp
+++ b/source/Mobs/Witch.cpp
@@ -8,7 +8,7 @@
cWitch::cWitch(void) :
- super("Witch", 66, "", "", 0.6, 1.8)
+ super("Witch", mtWitch, "", "", 0.6, 1.8)
{
}
diff --git a/source/Mobs/Wither.cpp b/source/Mobs/Wither.cpp
index 8b77284c8..c46e0beab 100644
--- a/source/Mobs/Wither.cpp
+++ b/source/Mobs/Wither.cpp
@@ -8,7 +8,7 @@
cWither::cWither(void) :
- super("Wither", 64, "mob.wither.hurt", "mob.wither.death", 0.9, 4.0)
+ super("Wither", mtWither, "mob.wither.hurt", "mob.wither.death", 0.9, 4.0)
{
}
diff --git a/source/Mobs/Wolf.cpp b/source/Mobs/Wolf.cpp
index e76f991dc..2baeb4b7b 100644
--- a/source/Mobs/Wolf.cpp
+++ b/source/Mobs/Wolf.cpp
@@ -10,7 +10,7 @@
cWolf::cWolf(void) :
- super("Wolf", 95, "mob.wolf.hurt", "mob.wolf.death", 0.6, 0.8),
+ super("Wolf", mtWolf, "mob.wolf.hurt", "mob.wolf.death", 0.6, 0.8),
m_bIsAngry(false),
m_bIsTame(false),
m_bIsSitting(false),
diff --git a/source/Mobs/Zombie.cpp b/source/Mobs/Zombie.cpp
index f495fe5ee..1752ec390 100644
--- a/source/Mobs/Zombie.cpp
+++ b/source/Mobs/Zombie.cpp
@@ -9,7 +9,7 @@
cZombie::cZombie(bool IsVillagerZombie) :
- super("Zombie", 54, "mob.zombie.hurt", "mob.zombie.death", 0.6, 1.8),
+ super("Zombie", mtZombie, "mob.zombie.hurt", "mob.zombie.death", 0.6, 1.8),
m_bIsConverting(false),
m_bIsVillagerZombie(IsVillagerZombie)
{
diff --git a/source/Mobs/Zombiepigman.cpp b/source/Mobs/Zombiepigman.cpp
index 1e31a72d9..6ac89ed4c 100644
--- a/source/Mobs/Zombiepigman.cpp
+++ b/source/Mobs/Zombiepigman.cpp
@@ -9,7 +9,7 @@
cZombiePigman::cZombiePigman(void) :
- super("ZombiePigman", 57, "mob.zombiepig.zpighurt", "mob.zombiepig.zpigdeath", 0.6, 1.8)
+ super("ZombiePigman", mtZombiePigman, "mob.zombiepig.zpighurt", "mob.zombiepig.zpigdeath", 0.6, 1.8)
{
}
diff --git a/source/Protocol/ProtocolRecognizer.cpp b/source/Protocol/ProtocolRecognizer.cpp
index fe99b22e1..ceada1944 100644
--- a/source/Protocol/ProtocolRecognizer.cpp
+++ b/source/Protocol/ProtocolRecognizer.cpp
@@ -727,7 +727,7 @@ bool cProtocolRecognizer::TryRecognizeProtocol(void)
void cProtocolRecognizer::HandleServerPing(void)
{
AString Reply;
- switch (cRoot::Get()->m_PrimaryServerVersion)
+ switch (cRoot::Get()->GetPrimaryServerVersion())
{
case PROTO_VERSION_1_2_5:
case PROTO_VERSION_1_3_2:
@@ -771,8 +771,8 @@ void cProtocolRecognizer::HandleServerPing(void)
Printf(MaxPlayers, "%d", cRoot::Get()->GetServer()->GetMaxPlayers());
AString ProtocolVersionNum;
- Printf(ProtocolVersionNum, "%d", cRoot::Get()->m_PrimaryServerVersion);
- AString ProtocolVersionTxt(GetVersionTextFromInt(cRoot::Get()->m_PrimaryServerVersion));
+ Printf(ProtocolVersionNum, "%d", cRoot::Get()->GetPrimaryServerVersion());
+ AString ProtocolVersionTxt(GetVersionTextFromInt(cRoot::Get()->GetPrimaryServerVersion()));
// Cannot use Printf() because of in-string NUL bytes.
Reply = cChatColor::Delimiter;
diff --git a/source/Root.cpp b/source/Root.cpp
index 290a5269a..1f6437784 100644
--- a/source/Root.cpp
+++ b/source/Root.cpp
@@ -140,7 +140,6 @@ void cRoot::Start(void)
}
IniFile.WriteFile();
- LOG("Initialising WebAdmin...");
m_WebAdmin = new cWebAdmin();
m_WebAdmin->Init();
@@ -172,7 +171,6 @@ void cRoot::Start(void)
LOGD("Finalising startup...");
m_Server->Start();
- LOG("Starting WebAdmin...");
m_WebAdmin->Start();
#if !defined(ANDROID_NDK)
@@ -210,7 +208,6 @@ void cRoot::Start(void)
LOGD("Freeing MonsterConfig...");
delete m_MonsterConfig; m_MonsterConfig = NULL;
- LOGD("Stopping WebAdmin...");
delete m_WebAdmin; m_WebAdmin = NULL;
LOGD("Unloading recipes...");
delete m_FurnaceRecipe; m_FurnaceRecipe = NULL;
diff --git a/source/Root.h b/source/Root.h
index 2b15d3461..c05b29d14 100644
--- a/source/Root.h
+++ b/source/Root.h
@@ -32,10 +32,7 @@ typedef cItemCallback<cWorld> cWorldListCallback;
class cRoot // tolua_export
{ // tolua_export
public:
- /// The version of the protocol that is primary for the server (reported in the server list). All versions are still supported.
- int m_PrimaryServerVersion; // tolua_export
-
- static cRoot* Get() { return s_Root; } // tolua_export
+ static cRoot * Get() { return s_Root; } // tolua_export
cRoot(void);
~cRoot();
@@ -55,7 +52,7 @@ public:
int GetPrimaryServerVersion(void) const { return m_PrimaryServerVersion; } // tolua_export
void SetPrimaryServerVersion(int a_Version) { m_PrimaryServerVersion = a_Version; } // tolua_export
- cMonsterConfig * GetMonsterConfig() { return m_MonsterConfig; }
+ cMonsterConfig * GetMonsterConfig(void) { return m_MonsterConfig; }
cGroupManager * GetGroupManager (void) { return m_GroupManager; } // tolua_export
cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export
@@ -135,6 +132,9 @@ private:
typedef std::map<AString, cWorld *> WorldMap;
typedef std::vector<cCommand> cCommandQueue;
+ /// The version of the protocol that is primary for the server (reported in the server list). All versions are still supported.
+ int m_PrimaryServerVersion;
+
cWorld * m_pDefaultWorld;
WorldMap m_WorldsByName;
diff --git a/source/WebAdmin.cpp b/source/WebAdmin.cpp
index 316513f11..882969746 100644
--- a/source/WebAdmin.cpp
+++ b/source/WebAdmin.cpp
@@ -32,7 +32,7 @@ class cPlayerAccum :
m_Contents.append("</li>");
return false;
}
-
+
public:
AString m_Contents;
@@ -53,6 +53,18 @@ cWebAdmin::cWebAdmin(void) :
+cWebAdmin::~cWebAdmin()
+{
+ if (m_IsInitialized)
+ {
+ LOG("Stopping WebAdmin...");
+ }
+}
+
+
+
+
+
void cWebAdmin::AddPlugin( cWebPlugin * a_Plugin )
{
m_Plugins.remove( a_Plugin );
@@ -78,16 +90,18 @@ bool cWebAdmin::Init(void)
{
return false;
}
-
+
+ LOG("Initialising WebAdmin...");
+
if (!m_IniFile.GetValueSetB("WebAdmin", "Enabled", true))
{
// WebAdmin is disabled, bail out faking a success
return true;
}
-
+
AString PortsIPv4 = m_IniFile.GetValueSet("WebAdmin", "Port", "8080");
AString PortsIPv6 = m_IniFile.GetValueSet("WebAdmin", "PortsIPv6", "");
-
+
if (!m_HTTPServer.Initialize(PortsIPv4, PortsIPv6))
{
return false;
@@ -107,7 +121,9 @@ bool cWebAdmin::Start(void)
// Not initialized
return false;
}
-
+
+ LOG("Starting WebAdmin...");
+
// Initialize the WebAdmin template script and load the file
m_TemplateScript.Create();
if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua"))
@@ -160,12 +176,12 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque
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)
@@ -173,14 +189,14 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque
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)
@@ -192,7 +208,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque
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)
@@ -205,7 +221,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque
} // for itr - URLParams[]
}
}
-
+
// Try to get the template from the Lua template script
if (ShouldWrapInTemplate)
{
@@ -220,7 +236,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque
a_Connection.SendStatusAndReason(500, "m_TemplateScript failed");
return;
}
-
+
AString BaseURL = GetBaseURL(URL);
AString Menu;
Template = "{CONTENT}";
@@ -254,7 +270,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque
Content += "\n<p><a href='" + BaseURL + "'>Go back</a></p>";
}
- int MemUsageKiB = GetMemoryUsage();
+ int MemUsageKiB = cRoot::GetPhysicalRAMUsage();
if (MemUsageKiB > 0)
{
ReplaceString(Template, "{MEM}", Printf("%.02f", (double)MemUsageKiB / 1024));
@@ -381,7 +397,38 @@ AString cWebAdmin::GetBaseURL( const AString& a_URL )
-AString cWebAdmin::GetBaseURL( const AStringVector& a_URLSplit )
+AString cWebAdmin::GetHTMLEscapedString(const AString & a_Input)
+{
+ AString dst;
+ dst.reserve(a_Input.length());
+
+ // Loop over input and substitute HTML characters for their alternatives:
+ size_t len = a_Input.length();
+ for (size_t i = 0; i < len; i++)
+ {
+ switch (a_Input[i])
+ {
+ case '&': dst.append("&amp;"); break;
+ case '\'': dst.append("&apos;"); break;
+ case '"': dst.append("&quot;"); break;
+ case '<': dst.append("&lt;"); break;
+ case '>': dst.append("&gt;"); break;
+ default:
+ {
+ dst.push_back(a_Input[i]);
+ break;
+ }
+ } // switch (a_Input[i])
+ } // for i - a_Input[]
+
+ return dst;
+}
+
+
+
+
+
+AString cWebAdmin::GetBaseURL(const AStringVector & a_URLSplit)
{
AString BaseURL = "./";
if (a_URLSplit.size() > 1)
@@ -399,16 +446,6 @@ AString cWebAdmin::GetBaseURL( const AStringVector& a_URLSplit )
-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)
{
const AString & URL = a_Request.GetURL();
@@ -465,7 +502,7 @@ void cWebAdmin::OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest &
{
// TODO: Handle other requests
}
-
+
// Delete any request data assigned to the request:
cRequestData * Data = (cRequestData *)(a_Request.GetUserData());
delete Data;
diff --git a/source/WebAdmin.h b/source/WebAdmin.h
index 72c77ddfb..acd81ebfa 100644
--- a/source/WebAdmin.h
+++ b/source/WebAdmin.h
@@ -51,18 +51,18 @@ struct HTTPRequest
{
typedef std::map< std::string, std::string > StringStringMap;
typedef std::map< std::string, HTTPFormData > FormDataMap;
-
+
AString Method;
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
@@ -101,15 +101,16 @@ class cWebAdmin :
{
public:
// tolua_end
-
+
typedef std::list< cWebPlugin* > PluginList;
cWebAdmin(void);
+ ~cWebAdmin();
/// 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);
@@ -117,35 +118,37 @@ public:
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 <<
+ PluginList GetPlugins() const { return m_Plugins; } // >> EXPORTED IN MANUALBINDINGS <<
// tolua_begin
-
- /// 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);
-
+ 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);
-
+
+ /// Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style)
+ AString GetBaseURL(const AString & a_URL);
+
+ /// Escapes text passed into it, so it can be embedded into html.
+ static AString GetHTMLEscapedString(const AString & a_Input);
+
// tolua_end
- AString GetBaseURL(const AStringVector& a_URLSplit);
-
+ /// Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style)
+ 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,
@@ -153,13 +156,13 @@ protected:
{
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;
@@ -168,31 +171,31 @@ protected:
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;
-
+
PluginList m_Plugins;
/// 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;
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;
diff --git a/source/World.cpp b/source/World.cpp
index 28c73f591..e2db77666 100644
--- a/source/World.cpp
+++ b/source/World.cpp
@@ -28,35 +28,9 @@
#include "Simulator/VaporizeFluidSimulator.h"
// Mobs:
-#include "Mobs/Bat.h"
-#include "Mobs/Blaze.h"
-#include "Mobs/Cavespider.h"
-#include "Mobs/Chicken.h"
-#include "Mobs/Cow.h"
-#include "Mobs/Creeper.h"
-#include "Mobs/Enderman.h"
-#include "Mobs/EnderDragon.h"
-#include "Mobs/Ghast.h"
-#include "Mobs/Giant.h"
-#include "Mobs/Horse.h"
-#include "Mobs/IronGolem.h"
-#include "Mobs/Magmacube.h"
-#include "Mobs/Mooshroom.h"
-#include "Mobs/Ocelot.h"
-#include "Mobs/Pig.h"
-#include "Mobs/Sheep.h"
-#include "Mobs/Silverfish.h"
-#include "Mobs/Skeleton.h"
-#include "Mobs/Slime.h"
-#include "Mobs/SnowGolem.h"
-#include "Mobs/Spider.h"
-#include "Mobs/Squid.h"
-#include "Mobs/Villager.h"
-#include "Mobs/Witch.h"
-#include "Mobs/Wither.h"
-#include "Mobs/Wolf.h"
-#include "Mobs/Zombie.h"
-#include "Mobs/Zombiepigman.h"
+#include "Mobs/IncludeAllMonsters.h"
+#include "MobCensus.h"
+#include "MobSpawner.h"
#include "MersenneTwister.h"
#include "Generating/Trees.h"
@@ -252,7 +226,6 @@ cWorld::cWorld(const AString & a_WorldName) :
m_WorldAge(0),
m_TimeOfDay(0),
m_LastTimeUpdate(0),
- m_LastSpawnMonster(0),
m_RSList(0),
m_Weather(eWeather_Sunny),
m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :)
@@ -515,14 +488,35 @@ void cWorld::Start(void)
m_GameMode = (eGameMode)IniFile.GetValueSetI("GameMode", "GameMode", m_GameMode);
- m_bAnimals = true;
- m_SpawnMonsterRate = 200; // 1 mob each 10 seconds
- cIniFile IniFile2("settings.ini");
- if (IniFile2.ReadFile())
+ // Load allowed mobs:
+ const char * DefaultMonsters = "";
+ switch (m_Dimension)
{
- m_bAnimals = IniFile2.GetValueB("Monsters", "AnimalsOn", true);
- m_SpawnMonsterRate = (Int64)(IniFile2.GetValueF("Monsters", "AnimalSpawnInterval", 10) * 20); // Convert from secs to ticks
-
+ case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, horse, mooshroom, ocelot, pig, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break;
+ case dimNether: DefaultMonsters = "blaze, ghast, magmacube, skeleton, zombie, zombiepigman"; break;
+ case dimEnd: DefaultMonsters = "enderman"; break;
+ default:
+ {
+ ASSERT(!"Unhandled world dimension");
+ DefaultMonsters = "wither";
+ break;
+ }
+ }
+ m_bAnimals = IniFile.GetValueB("Monsters", "AnimalsOn", true);
+ AString AllMonsters = IniFile.GetValueSet("Monsters", "Types", DefaultMonsters);
+ AStringVector SplitList = StringSplitAndTrim(AllMonsters, ",");
+ for (AStringVector::const_iterator itr = SplitList.begin(), end = SplitList.end(); itr != end; ++itr)
+ {
+ cMonster::eType ToAdd = cMonster::StringToMobType(*itr);
+ if (ToAdd != cMonster::mtInvalidType)
+ {
+ m_AllowedMobs.insert(ToAdd);
+ LOGD("Allowed mob: %s", itr->c_str());
+ }
+ else
+ {
+ LOG("World \"%s\": Unknown mob type: %s", m_WorldName.c_str(), itr->c_str());
+ }
}
m_ChunkMap = new cChunkMap(this);
@@ -553,6 +547,13 @@ void cWorld::Start(void)
m_ChunkSender.Start(this);
m_TickThread.Start();
+ // Init of the spawn monster time (as they are supposed to have different spawn rate)
+ m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfHostile, 0));
+ m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfPassive, 0));
+ m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfAmbient, 0));
+ m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfWater, 0));
+
+
// Save any changes that the defaults may have done to the ini file:
if (!IniFile.WriteFile())
{
@@ -648,7 +649,7 @@ void cWorld::Tick(float a_Dt)
UnloadUnusedChunks();
}
- TickSpawnMobs(a_Dt);
+ TickMobs(a_Dt);
std::vector<int> m_RSList_copy(m_RSList);
@@ -733,125 +734,61 @@ void cWorld::TickWeather(float a_Dt)
-void cWorld::TickSpawnMobs(float a_Dt)
+void cWorld::TickMobs(float a_Dt)
{
- if (!m_bAnimals || (m_WorldAge - m_LastSpawnMonster <= m_SpawnMonsterRate))
- {
- return;
- }
-
- m_LastSpawnMonster = m_WorldAge;
- Vector3d SpawnPos;
- {
- cCSLock Lock(m_CSPlayers);
- if (m_Players.size() <= 0)
- {
- return;
- }
- int RandomPlayerIdx = m_TickRand.randInt() & m_Players.size();
- cPlayerList::iterator itr = m_Players.begin();
- for (int i = 1; i < RandomPlayerIdx; i++)
- {
- itr++;
- }
- SpawnPos = (*itr)->GetPosition();
- }
-
- int dayRand = (m_TickRand.randInt() / 7) % 6;
- int nightRand = (m_TickRand.randInt() / 11) % 10;
+ // _X 2013_10_22: This is a quick fix for #283 - the world needs to be locked while ticking mobs
+ cWorld::cLock Lock(*this);
- SpawnPos += Vector3d((double)(m_TickRand.randInt() % 64) - 32, (double)(m_TickRand.randInt() % 64) - 32, (double)(m_TickRand.randInt() % 64) - 32);
- int Height = GetHeight((int)SpawnPos.x, (int)SpawnPos.z);
-
- int MobType = -1;
- int Biome = GetBiomeAt((int)SpawnPos.x, (int)SpawnPos.z);
- switch (Biome)
+ // before every Mob action, we have to count them depending on the distance to players, on their family ...
+ cMobCensus MobCensus;
+ m_ChunkMap->CollectMobCensus(MobCensus);
+ if (m_bAnimals)
{
- case biNether:
+ // Spawning is enabled, spawn now:
+ static const cMonster::eFamily AllFamilies[] =
{
- // Spawn nether mobs
- switch (nightRand)
- {
- case 0: MobType = cMonster::mtBlaze; break;
- case 1: MobType = cMonster::mtGhast; break;
- case 2: MobType = cMonster::mtGhast; break;
- case 3: MobType = cMonster::mtGhast; break;
- case 4: MobType = cMonster::mtZombiePigman; break;
- case 5: MobType = cMonster::mtZombiePigman; break;
- case 6: MobType = cMonster::mtZombiePigman; break;
- case 7: MobType = cMonster::mtZombiePigman; break;
- case 8: MobType = cMonster::mtZombiePigman; break;
- case 9: MobType = cMonster::mtZombiePigman; break;
- }
- break;
- }
-
- case biEnd:
+ cMonster::mfHostile,
+ cMonster::mfPassive,
+ cMonster::mfAmbient,
+ cMonster::mfWater,
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(AllFamilies); i++)
{
- // Spawn only The End mobs
- switch (nightRand)
+ cMonster::eFamily Family = AllFamilies[i];
+ int spawnrate = cMonster::GetSpawnRate(Family);
+ if (
+ (m_LastSpawnMonster[Family] > m_WorldAge - spawnrate) || // Not reached the needed tiks before the next round
+ MobCensus.IsCapped(Family)
+ )
{
- case 0: MobType = cMonster::mtEnderDragon; break;
- case 1: MobType = cMonster::mtEnderman; break;
- case 2: MobType = cMonster::mtEnderman; break;
- case 3: MobType = cMonster::mtEnderman; break;
- case 4: MobType = cMonster::mtEnderman; break;
- case 5: MobType = cMonster::mtEnderman; break;
- case 6: MobType = cMonster::mtEnderman; break;
- case 7: MobType = cMonster::mtEnderman; break;
- case 8: MobType = cMonster::mtEnderman; break;
- case 9: MobType = cMonster::mtEnderman; break;
+ continue;
}
- break;
- }
-
- case biMushroomIsland:
- case biMushroomShore:
- {
- // Mushroom land gets only mooshrooms
- MobType = cMonster::mtMooshroom;
- break;
- }
-
- default:
- {
- // Overworld biomes depend on whether it's night or day:
- if (m_TimeOfDay >= 12000 + 1000)
+ m_LastSpawnMonster[Family] = m_WorldAge;
+ cMobSpawner Spawner(Family, m_AllowedMobs);
+ if (Spawner.CanSpawnAnything())
{
- // Night mobs:
- switch (nightRand)
- {
- case 0: MobType = cMonster::mtSpider; break;
- case 1: MobType = cMonster::mtZombie; break;
- case 2: MobType = cMonster::mtEnderman; break;
- case 3: MobType = cMonster::mtCreeper; break;
- case 4: MobType = cMonster::mtCaveSpider; break;
- case 7: MobType = cMonster::mtSlime; break;
- case 8: MobType = cMonster::mtSilverfish; break;
- case 9: MobType = cMonster::mtSkeleton; break;
- }
- } // if (night)
- else
- {
- // During the day:
- switch (dayRand)
+ m_ChunkMap->SpawnMobs(Spawner);
+ // do the spawn
+ for (cMobSpawner::tSpawnedContainer::const_iterator itr2 = Spawner.getSpawned().begin(); itr2 != Spawner.getSpawned().end(); itr2++)
{
- case 0: MobType = cMonster::mtChicken; break;
- case 1: MobType = cMonster::mtCow; break;
- case 2: MobType = cMonster::mtPig; break;
- case 3: MobType = cMonster::mtSheep; break;
- case 4: MobType = cMonster::mtSquid; break;
- case 5: MobType = cMonster::mtWolf; break;
- case 6: MobType = cMonster::mtHorse; break;
+ SpawnMobFinalize(*itr2);
}
- } // else (night)
- } // case overworld biomes
- } // switch (biome)
+ }
+ } // for i - AllFamilies[]
+ } // if (Spawning enabled)
+
+ // move close mobs
+ cMobProximityCounter::sIterablePair allCloseEnoughToMoveMobs = MobCensus.GetProximityCounter().getMobWithinThosesDistances(-1, 64 * 16);// MG TODO : deal with this magic number (the 16 is the size of a block)
+ for(cMobProximityCounter::tDistanceToMonster::const_iterator itr = allCloseEnoughToMoveMobs.m_Begin; itr != allCloseEnoughToMoveMobs.m_End; itr++)
+ {
+ itr->second.m_Monster.Tick(a_Dt, itr->second.m_Chunk);
+ }
- if (MobType >= 0)
+ // remove too far mobs
+ cMobProximityCounter::sIterablePair allTooFarMobs = MobCensus.GetProximityCounter().getMobWithinThosesDistances(128 * 16, -1);// MG TODO : deal with this magic number (the 16 is the size of a block)
+ for(cMobProximityCounter::tDistanceToMonster::const_iterator itr = allTooFarMobs.m_Begin; itr != allTooFarMobs.m_End; itr++)
{
- // A proper mob type was selected, now spawn the mob:
- SpawnMob(SpawnPos.x, SpawnPos.y, SpawnPos.z, (cMonster::eType)MobType);
+ itr->second.m_Monster.Destroy(true);
}
}
@@ -2592,82 +2529,39 @@ int cWorld::SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eTyp
{
cMonster * Monster = NULL;
- int SlSize = GetTickRandomNumber(2) + 1; // 1 .. 3 - Slime
int ShColor = GetTickRandomNumber(15); // 0 .. 15 - Sheep
- bool SkType = GetDimension() == biNether; // Skeleton
+ bool SkType = GetDimension() == dimNether ; // Skeleton
- int VilType = GetTickRandomNumber(cVillager::vtMax); // 0 .. 6 - Villager
- if (VilType == 6) { VilType = 0; } // Give farmers a better chance of spawning
-
- int HseType = GetTickRandomNumber(7); // 0 .. 7 - Horse Type (donkey, zombie, etc.)
- int HseColor = GetTickRandomNumber(6); // 0 .. 6 - Horse
- int HseStyle = GetTickRandomNumber(4); // 0 .. 4 - Horse
- int HseTameTimes = GetTickRandomNumber(6) + 1; // 1 .. 7 - Horse tame amount
- if ((HseType == 5) || (HseType == 6) || (HseType == 7)) { HseType = 0; } // 5,6,7 = 0 because little chance of getting 0 with TickRand
-
- switch (a_MonsterType)
- {
- case cMonster::mtBat: Monster = new cBat(); break;
- case cMonster::mtBlaze: Monster = new cBlaze(); break;
- case cMonster::mtCaveSpider: Monster = new cCavespider(); break;
- case cMonster::mtChicken: Monster = new cChicken(); break;
- case cMonster::mtCow: Monster = new cCow(); break;
- case cMonster::mtCreeper: Monster = new cCreeper(); break;
- case cMonster::mtEnderman: Monster = new cEnderman(); break;
- case cMonster::mtEnderDragon: Monster = new cEnderDragon(); break;
- case cMonster::mtGhast: Monster = new cGhast(); break;
- case cMonster::mtGiant: Monster = new cGiant(); break;
- case cMonster::mtHorse:
- {
- Monster = new cHorse(HseType, HseColor, HseStyle, HseTameTimes); break;
- }
- case cMonster::mtIronGolem: Monster = new cIronGolem(); break;
- case cMonster::mtMagmaCube: Monster = new cMagmaCube(SlSize); break;
- case cMonster::mtMooshroom: Monster = new cMooshroom(); break;
- case cMonster::mtOcelot: Monster = new cOcelot(); break;
- case cMonster::mtPig: Monster = new cPig(); break;
- case cMonster::mtSheep: Monster = new cSheep(ShColor); break;
- case cMonster::mtSilverfish: Monster = new cSilverfish(); break;
- case cMonster::mtSkeleton: Monster = new cSkeleton(SkType); break;
- case cMonster::mtSlime: Monster = new cSlime(SlSize); break;
- case cMonster::mtSnowGolem: Monster = new cSnowGolem(); break;
- case cMonster::mtSpider: Monster = new cSpider(); break;
- case cMonster::mtSquid: Monster = new cSquid(); break;
- case cMonster::mtVillager:
- {
- Monster = new cVillager((cVillager::eVillagerType)VilType); break;
- }
- case cMonster::mtWitch: Monster = new cWitch(); break;
- case cMonster::mtWither: Monster = new cWither(); break;
- case cMonster::mtWolf: Monster = new cWolf(); break;
- case cMonster::mtZombie: Monster = new cZombie(false); break; // TODO: Villager infection
- case cMonster::mtZombiePigman: Monster = new cZombiePigman(); break;
-
- default:
- {
- LOGWARNING("%s: Unhandled monster type: %d. Not spawning.", __FUNCTION__, a_MonsterType);
- return -1;
- }
+ Monster = cMonster::NewMonsterFromType(a_MonsterType);
+ if (Monster != NULL)
+ {
+ Monster->SetPosition(a_PosX, a_PosY, a_PosZ);
}
- Monster->SetPosition(a_PosX, a_PosY, a_PosZ);
- Monster->SetHealth(Monster->GetMaxHealth());
- if (cPluginManager::Get()->CallHookSpawningMonster(*this, *Monster))
+ return SpawnMobFinalize(Monster);
+}
+
+
+
+
+int cWorld::SpawnMobFinalize(cMonster * a_Monster)
+{
+ if (!a_Monster)
+ return -1;
+ a_Monster->SetHealth(a_Monster->GetMaxHealth());
+ if (cPluginManager::Get()->CallHookSpawningMonster(*this, *a_Monster))
{
- delete Monster;
+ delete a_Monster;
return -1;
}
- if (!Monster->Initialize(this))
+ if (!a_Monster->Initialize(this))
{
- delete Monster;
+ delete a_Monster;
return -1;
}
+ BroadcastSpawnEntity(*a_Monster);
+ cPluginManager::Get()->CallHookSpawnedMonster(*this, *a_Monster);
- BroadcastSpawnEntity(*Monster);
- // Because it's logical that ALL mob spawns need spawn effects, not just spawners
- BroadcastSoundParticleEffect(2004, (int)(floor(a_PosX) * 8), (int)(floor(a_PosY) * 8), (int)(floor(a_PosZ) * 8), 0);
-
- cPluginManager::Get()->CallHookSpawnedMonster(*this, *Monster);
- return Monster->GetUniqueID();
+ return a_Monster->GetUniqueID();
}
diff --git a/source/World.h b/source/World.h
index 633ce969e..f174a1c2c 100644
--- a/source/World.h
+++ b/source/World.h
@@ -42,6 +42,7 @@ class cChunkGenerator; // The thread responsible for generating chunks
class cChestEntity;
class cDispenserEntity;
class cFurnaceEntity;
+class cMobCensus;
typedef std::list< cPlayer * > cPlayerList;
@@ -580,6 +581,7 @@ public:
/// Spawns a mob of the specified type. Returns the mob's EntityID if recognized and spawned, <0 otherwise
int SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eType a_MonsterType); // tolua_export
+ int SpawnMobFinalize(cMonster* a_Monster);
/// Creates a projectile of the specified type. Returns the projectile's EntityID if successful, <0 otherwise
int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const Vector3d * a_Speed = NULL); // tolua_export
@@ -632,7 +634,7 @@ private:
Int64 m_LastTimeUpdate; // The tick in which the last time update has been sent.
Int64 m_LastUnload; // The last WorldAge (in ticks) in which unloading was triggerred
Int64 m_LastSave; // The last WorldAge (in ticks) in which save-all was triggerred
- Int64 m_LastSpawnMonster; // The last WorldAge (in ticks) in which a monster was spawned
+ std::map<cMonster::eFamily,Int64> m_LastSpawnMonster; // The last WorldAge (in ticks) in which a monster was spawned (for each megatype of monster) // MG TODO : find a way to optimize without creating unmaintenability (if mob IDs are becoming unrowed)
eGameMode m_GameMode;
bool m_bEnabledPVP;
@@ -662,7 +664,7 @@ private:
cChunkMap * m_ChunkMap;
bool m_bAnimals;
- Int64 m_SpawnMonsterRate;
+ std::set<cMonster::eType> m_AllowedMobs;
eWeather m_Weather;
int m_WeatherInterval;
@@ -717,8 +719,8 @@ private:
/// Handles the weather in each tick
void TickWeather(float a_Dt);
- /// Handles the mob spawning each tick
- void TickSpawnMobs(float a_Dt);
+ /// Handles the mob spawning/moving/destroying each tick
+ void TickMobs(float a_Dt);
/// Executes all tasks queued onto the tick thread
void TickQueuedTasks(void);