summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore20
-rw-r--r--MCServer/.gitignore1
-rw-r--r--MCServer/Plugins/APIDump/APIDesc.lua1337
-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.lua843
-rw-r--r--MCServer/Plugins/APIDump/prettify.css1
-rw-r--r--MCServer/Plugins/APIDump/prettify.js30
-rw-r--r--MCServer/Plugins/ChunkWorx/chunkworx_main.lua53
-rw-r--r--MCServer/Plugins/ChunkWorx/chunkworx_web.lua61
-rw-r--r--MCServer/Plugins/Debuggers/Debuggers.lua4
-rw-r--r--Tools/ProtoProxy/Connection.cpp1131
-rw-r--r--Tools/ProtoProxy/Connection.h56
-rw-r--r--Tools/ProtoProxy/Globals.h4
-rw-r--r--VC2008/.gitignore1
-rw-r--r--VC2008/MCServer.vcproj44
-rw-r--r--iniFile/iniFile.cpp215
-rw-r--r--iniFile/iniFile.h146
-rw-r--r--source/AllToLua.pkg1
-rw-r--r--source/Authenticator.cpp13
-rw-r--r--source/Authenticator.h4
-rw-r--r--source/Bindings.cpp1253
-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.cpp55
-rw-r--r--source/Blocks/BlockDirt.h4
-rw-r--r--source/ByteBuffer.cpp130
-rw-r--r--source/ByteBuffer.h22
-rw-r--r--source/Chunk.cpp519
-rw-r--r--source/Chunk.h44
-rw-r--r--source/ChunkDef.h55
-rw-r--r--source/ChunkMap.cpp81
-rw-r--r--source/ChunkMap.h15
-rw-r--r--source/ClientHandle.cpp6
-rw-r--r--source/Entities/Pickup.cpp9
-rw-r--r--source/Entities/Pickup.h9
-rw-r--r--source/Entities/Player.cpp17
-rw-r--r--source/Entities/Player.h21
-rw-r--r--source/Entities/ProjectileEntity.cpp13
-rw-r--r--source/Generating/ChunkGenerator.cpp2
-rw-r--r--source/Globals.h24
-rw-r--r--source/GroupManager.cpp4
-rw-r--r--source/Inventory.cpp2
-rw-r--r--source/LuaWindow.cpp4
-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.cpp289
-rw-r--r--source/MobSpawner.h76
-rw-r--r--source/Mobs/AggressiveMonster.cpp8
-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.cpp61
-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.cpp6
-rw-r--r--source/Mobs/Sheep.cpp7
-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/MonsterConfig.cpp8
-rw-r--r--source/PluginManager.cpp4
-rw-r--r--source/Protocol/Protocol.h21
-rw-r--r--source/Protocol/Protocol125.cpp2
-rw-r--r--source/Protocol/Protocol125.h4
-rw-r--r--source/Protocol/Protocol17x.cpp541
-rw-r--r--source/Protocol/Protocol17x.h106
-rw-r--r--source/Protocol/ProtocolRecognizer.cpp126
-rw-r--r--source/Protocol/ProtocolRecognizer.h22
-rw-r--r--source/Root.cpp18
-rw-r--r--source/Root.h12
-rw-r--r--source/UI/SlotArea.cpp4
-rw-r--r--source/UI/Window.cpp20
-rw-r--r--source/UI/Window.h22
-rw-r--r--source/WebAdmin.cpp92
-rw-r--r--source/WebAdmin.h61
-rw-r--r--source/World.cpp368
-rw-r--r--source/World.h21
-rw-r--r--source/WorldStorage/WSSAnvil.cpp2
116 files changed, 6614 insertions, 2750 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..f8e201244 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,16 +687,26 @@ 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.
-</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.
-]],
+ 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.</p>
+ <p>
+ See the {{cItem}} class for usage examples.
+ ]],
Functions =
{
constructor =
@@ -690,6 +725,29 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(),
},
Constants =
{
+ -- Only list these enchantment IDs, as they don't really need any kind of documentation:
+ enchAquaAffinity = { Notes = "" },
+ enchBaneOfArthropods = { Notes = "" },
+ enchBlastProtection = { Notes = "" },
+ enchEfficiency = { Notes = "" },
+ enchFeatherFalling = { Notes = "" },
+ enchFireAspect = { Notes = "" },
+ enchFireProtection = { Notes = "" },
+ enchFlame = { Notes = "" },
+ enchFortune = { Notes = "" },
+ enchInfinity = { Notes = "" },
+ enchKnockback = { Notes = "" },
+ enchLooting = { Notes = "" },
+ enchPower = { Notes = "" },
+ enchProjectileProtection = { Notes = "" },
+ enchProtection = { Notes = "" },
+ enchPunch = { Notes = "" },
+ enchRespiration = { Notes = "" },
+ enchSharpness = { Notes = "" },
+ enchSilkTouch = { Notes = "" },
+ enchSmite = { Notes = "" },
+ enchThorns = { Notes = "" },
+ enchUnbreaking = { Notes = "" },
},
},
@@ -750,12 +808,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 +821,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 +897,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 +911,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 +926,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 +970,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,76 +991,180 @@ cFile:Delete("/usr/bin/virus.exe");
},
},
- cIniFile =
+ cHopperEntity =
{
- 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.
-]],
+ 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 = { Return = "{{cIniFile|cIniFile}}" },
- CaseSensitive = { Return = "" },
- CaseInsensitive = { Return = "" },
- Path = { Return = "" },
- Path = { Return = "string" },
- SetPath = { Return = "" },
- ReadFile = { Return = "bool" },
- WriteFile = { Return = "bool" },
- Erase = { Return = "" },
- Clear = { Return = "" },
- Reset = { Return = "" },
- FindKey = { Notes = "long i" },
- FindValue = { Notes = "long i" },
- NumKeys = { Notes = "unsigned i" },
- GetNumKeys = { Notes = "unsigned i" },
- AddKeyName = { Notes = "unsigned int" },
- KeyName = { Notes = "Stri" },
- GetKeyName = { Notes = "Stri" },
- NumValues = { Notes = "unsigned int" },
- GetNumValues = { Notes = "unsigned int" },
- NumValues = { Notes = "unsigned int" },
- GetNumValues = { Notes = "unsigned int" },
- ValueName = { Notes = "Stri" },
- GetValueName = { Notes = "Stri" },
- ValueName = { Notes = "Stri" },
- GetValueName = { Notes = "Stri" },
- GetValue = { Notes = "Stri" },
- GetValue = { Notes = "Stri" },
- GetValueI = { Notes = "i" },
- GetValueB = { Notes = "bo" },
- GetValueF = { Notes = "doub" },
- GetValueSet = { Notes = "Stri" },
- GetValueSetI = { Notes = "i" },
- GetValueSetB = { Notes = "bo" },
- GetValueSetF = { Notes = "doub" },
- SetValue = { Return = "bool" },
- SetValue = { Return = "bool" },
- SetValueI = { Return = "bool" },
- SetValueB = { Return = "bool" },
- SetValueF = { Return = "bool" },
- DeleteValueByID = { Return = "bool" },
- DeleteValue = { Return = "bool" },
- DeleteKey = { Return = "bool" },
- NumHeaderComments = { Notes = "unsigned int" },
- HeaderComment = { Return = "" },
- HeaderComment = { Notes = "Stri" },
- DeleteHeaderComment = { Return = "bool" },
- DeleteHeaderComments = { Return = "" },
- NumKeyComments = { Notes = "unsigned i" },
- NumKeyComments = { Notes = "unsigned i" },
- KeyComment = { Return = "bool" },
- KeyComment = { Return = "bool" },
- KeyComment = { Notes = "Stri" },
- KeyComment = { Notes = "Stri" },
- DeleteKeyComment = { Return = "bool" },
- DeleteKeyComment = { Return = "bool" },
- DeleteKeyComments = { Return = "bool" },
- DeleteKeyComments = { Return = "bool" },
+ 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 = [[
+ This class implements a simple name-value storage represented on disk by an INI file. These files
+ are suitable for low-volume high-latency human-readable information storage, such as for
+ configuration. MCServer itself uses INI files for settings and options.</p>
+ <p>
+ The INI files follow this basic structure:
+<pre class="prettyprint lang-ini">
+; Header comment line
+[KeyName0]
+; Key comment line 0
+ValueName0=Value0
+ValueName1=Value1
+
+[KeyName1]
+; Key comment line 0
+; Key comment line 1
+ValueName0=SomeOtherValue
+</pre>
+ The cIniFile object stores all the objects in numbered arrays and provides access to the information
+ either based on names (KeyName, ValueName) or zero-based indices.</p>
+ <p>
+ The objects of this class are created empty. You need to either load a file using ReadFile(), or
+ insert values by hand. Then you can store the object's contents to a disk file using WriteFile(), or
+ just forget everything by destroying the object. Note that the file operations are quite slow.</p>
+ <p>
+ For storing high-volume low-latency data, use the {{sqlite3}} class. For storing
+ hierarchically-structured data, use the XML format, using the LuaExpat parser in the {{lxp}} class.
+ ]],
+ Functions =
+ {
+ constructor = { Params = "", Return = "cIniFile", Notes = "Creates a new empty cIniFile object." },
+ AddHeaderComment = { Params = "Comment", Return = "", Notes = "Adds a comment to be stored in the file header." },
+ AddKeyComment =
+ {
+ { Params = "KeyID, Comment", Return = "", Notes = "Adds a comment to be stored in the file under the specified key" },
+ { Params = "KeyName, Comment", Return = "", Notes = "Adds a comment to be stored in the file under the specified key" },
+ },
+ AddKeyName = { Params = "KeyName", Returns = "number", Notes = "Adds a new key of the specified name. Returns the KeyID of the new key." },
+ CaseInsensitive = { Params = "", Return = "", Notes = "Sets key names' and value names' comparisons to case insensitive (default)." },
+ CaseSensitive = { Params = "", Return = "", Notes = "Sets key names and value names comparisons to case sensitive." },
+ Clear = { Params = "", Return = "", Notes = "Removes all the in-memory data. Note that , like all the other operations, this doesn't affect any file data." },
+ DeleteHeaderComment = { Params = "CommentID", Return = "bool" , Notes = "Deletes the specified header comment. Returns true if successful."},
+ DeleteHeaderComments = { Params = "", Return = "", Notes = "Deletes all headers comments." },
+ DeleteKey = { Params = "KeyName", Return = "bool", Notes = "Deletes the specified key, and all values in that key. Returns true if successful." },
+ DeleteKeyComment =
+ {
+ { Params = "KeyID, CommentID", Return = "bool", Notes = "Deletes the specified key comment. Returns true if successful." },
+ { Params = "KeyName, CommentID", Return = "bool", Notes = "Deletes the specified key comment. Returns true if successful." },
+ },
+ DeleteKeyComments =
+ {
+ { Params = "KeyID", Return = "bool", Notes = "Deletes all comments for the specified key. Returns true if successful." },
+ { Params = "KeyName", Return = "bool", Notes = "Deletes all comments for the specified key. Returns true if successful." },
+ },
+ DeleteValue = { Params = "KeyName, ValueName", Return = "bool", Notes = "Deletes the specified value. Returns true if successful." },
+ DeleteValueByID = { Params = "KeyID, ValueID", Return = "bool", Notes = "Deletes the specified value. Returns true if successful." },
+ FindKey = { Params = "KeyName", Return = "number", Notes = "Returns the KeyID for the specified key name, or the noID constant if the key doesn't exist." },
+ FindValue = { Params = "KeyID, ValueName", Return = "numebr", Notes = "Returns the ValueID for the specified value name, or the noID constant if the specified key doesn't contain a value of that name." },
+ GetHeaderComment = { Params = "CommentID", Return = "string", Notes = "Returns the specified header comment, or an empty string if such comment doesn't exist" },
+ GetKeyComment =
+ {
+ { Params = "KeyID, CommentID", Return = "string", Notes = "Returns the specified key comment, or an empty string if such a comment doesn't exist" },
+ { Params = "KeyName, CommentID", Return = "string", Notes = "Returns the specified key comment, or an empty string if such a comment doesn't exist" },
+ },
+ GetKeyName = { Params = "KeyID", Return = "string", Notes = "Returns the key name for the specified key ID. Inverse for FindKey()." },
+ GetNumHeaderComments = { Params = "", Return = "number", Notes = "Retuns the number of header comments." },
+ GetNumKeyComments =
+ {
+ { Params = "KeyID", Return = "number", Notes = "Returns the number of comments under the specified key" },
+ { Params = "KeyName", Return = "number", Notes = "Returns the number of comments under the specified key" },
+ },
+ GetNumKeys = { Params = "", Return = "number", Notes = "Returns the total number of keys. This is the range for the KeyID (0 .. GetNumKeys() - 1)" },
+ GetNumValues =
+ {
+ { Params = "KeyID", Return = "number", Notes = "Returns the number of values stored under the specified key." },
+ { Params = "KeyName", Return = "number", Notes = "Returns the number of values stored under the specified key." },
+ },
+ GetValue =
+ {
+ { Params = "KeyName, ValueName", Return = "string", Notes = "Returns the value of the specified name under the specified key. Returns an empty string if the value doesn't exist." },
+ { Params = "KeyID, ValueID", Return = "string", Notes = "Returns the value of the specified name under the specified key. Returns an empty string if the value doesn't exist." },
+ },
+ GetValueB = { Params = "KeyName, ValueName", Return = "bool", Notes = "Returns the value of the specified name under the specified key, as a bool. Returns false if the value doesn't exist." },
+ GetValueF = { Params = "KeyName, ValueName", Return = "number", Notes = "Returns the value of the specified name under the specified key, as a floating-point number. Returns zero if the value doesn't exist." },
+ GetValueI = { Params = "KeyName, ValueName", Return = "number", Notes = "Returns the value of the specified name under the specified key, as an integer. Returns zero if the value doesn't exist." },
+ GetValueName =
+ {
+ { Params = "KeyID, ValueID", Return = "string", Notes = "Returns the name of the specified value Inverse for FindValue()." },
+ { Params = "KeyName, ValueID", Return = "string", Notes = "Returns the name of the specified value Inverse for FindValue()." },
+ },
+ GetValueSet = { Params = "KeyName, ValueName, Default", Return = "string", Notes = "Returns the value of the specified name under the specified key. If the value doesn't exist, creates it with the specified default." },
+ GetValueSetB = { Params = "KeyName, ValueName, Default", Return = "bool", Notes = "Returns the value of the specified name under the specified key, as a bool. If the value doesn't exist, creates it with the specified default." },
+ GetValueSetF = { Params = "KeyName, ValueName, Default", Return = "number", Notes = "Returns the value of the specified name under the specified key, as a floating-point number. If the value doesn't exist, creates it with the specified default." },
+ GetValueSetI = { Params = "KeyName, ValueName, Default", Return = "number", Notes = "Returns the value of the specified name under the specified key, as an integer. If the value doesn't exist, creates it with the specified default." },
+ ReadFile = { Params = "FileName, [AllowExampleFallback]", Return = "bool", Notes = "Reads the values from the specified file. Previous in-memory contents are lost. If the file cannot be opened, and AllowExample is true, another file, \"filename.example.ini\", is loaded and then saved as \"filename.ini\". Returns true if successful, false if not." },
+ SetValue =
+ {
+ { Params = "KeyID, ValueID, NewValue", Return = "bool", Notes = "Overwrites the specified value with a new value. If the specified value doesn't exist, returns false (doesn't add)." },
+ { Params = "KeyName, ValueName, NewValue, [CreateIfNotExists]", Return = "bool", Notes = "Overwrites the specified value with a new value. If CreateIfNotExists is true (default) and the value doesn't exist, it is first created. Returns true if the value was successfully set, false if not (didn't exists, CreateIfNotExists false)." },
+ },
+ SetValueB = { Params = "KeyName, ValueName, NewValueBool, [CreateIfNotExists]", Return = "bool", Notes = "Overwrites the specified value with a new bool value. If CreateIfNotExists is true (default) and the value doesn't exist, it is first created. Returns true if the value was successfully set, false if not (didn't exists, CreateIfNotExists false)." },
+ SetValueF = { Params = "KeyName, ValueName, NewValueFloat, [CreateIfNotExists]", Return = "bool", Notes = "Overwrites the specified value with a new floating-point number value. If CreateIfNotExists is true (default) and the value doesn't exist, it is first created. Returns true if the value was successfully set, false if not (didn't exists, CreateIfNotExists false)." },
+ SetValueI = { Params = "KeyName, ValueName, NewValueInt, [CreateIfNotExists]", Return = "bool", Notes = "Overwrites the specified value with a new integer value. If CreateIfNotExists is true (default) and the value doesn't exist, it is first created. Returns true if the value was successfully set, false if not (didn't exists, CreateIfNotExists false)." },
+ WriteFile = { Params = "FileName", Return = "bool", Notes = "Writes the current in-memory data into the specified file. Returns true if successful, false if not." },
+ },
+ Constants =
+ {
+ noID = { Notes = "" },
+ },
+ AdditionalInfo =
+ {
+ {
+ Header = "Code example: Reading a simple value",
+ Contents = [[
+ 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();
+if (IniFile:ReadFile("settings.ini")) then
+ ServerPort = IniFile:GetValueI("Server", "Port");
+end
+</pre>
+ ]],
+ },
+ {
+ Header = "Code example: Enumerating all objects in a file",
+ Contents = [[
+ To enumerate all keys in a file, you need to query the total number of keys, using GetNumKeys(),
+ and then query each key's name using GetKeyName(). Similarly, to enumerate all values under a
+ key, you need to query the total number of values using GetNumValues() and then query each
+ value's name using GetValueName().</p>
+ <p>
+ The following code logs all keynames and their valuenames into the server log:
+<pre class="prettyprint lang-lua">
+local IniFile = cIniFile();
+IniFile:ReadFile("somefile.ini")
+local NumKeys = IniFile:GetNumKeys();
+for k = 0, NumKeys do
+ local NumValues = IniFile:GetNumValues(k);
+ LOG("key \"" .. IniFile:GetKeyName(k) .. "\" has " .. NumValues .. " values:");
+ for v = 0, NumValues do
+ LOG(" value \"" .. IniFile:GetValueName(k, v) .. "\".");
+ end
+end
+</pre>
+ ]],
+ },
+ }, -- AdditionalInfo
+ }, -- cIniFile
+
cInventory =
{
Desc = [[This object is used to store the items that a {{cPlayer|cPlayer}} has. It also keeps track of what item the player has currently selected in their hotbar.
@@ -1007,8 +1172,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" },
@@ -1052,7 +1221,7 @@ These ItemGrids are available in the API and can be manipulated by the plugins,
invHotbarOffset = { Notes = "Starting slot number of the Hotbar part" },
invNumSlots = { Notes = "Total number of slots in a cInventory" },
},
- },
+ }, -- cInventory
cItem =
{
@@ -1060,9 +1229,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 +1257,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>
+]],
+ },
+ },
+ }, -- cItem
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.
@@ -1126,9 +1347,12 @@ These ItemGrids are available in the API and can be manipulated by the plugins,
{ Params = "X, Y", Return = "", Notes = "Destroys the item in the specified slot" },
},
GetFirstEmptySlot = { Params = "", Return = "number", Notes = "Returns the SlotNumber of the first empty slot, -1 if all slots are full" },
+ GetFirstUsedSlot = { Params = "", Return = "number", Notes = "Returns the SlotNumber of the first non-empty slot, -1 if all slots are empty" },
GetHeight = { Params = "", Return = "number", Notes = "Returns the Y dimension of the grid" },
GetLastEmptySlot = { Params = "", Return = "number", Notes = "Returns the SlotNumber of the last empty slot, -1 if all slots are full" },
+ GetLastUsedSlot = { Params = "", Return = "number", Notes = "Returns the SlotNumber of the last non-empty slot, -1 if all slots are empty" },
GetNextEmptySlot = { Params = "StartFrom", Return = "number", Notes = "Returns the SlotNumber of the first empty slot following StartFrom, -1 if all the following slots are full" },
+ GetNextUsedSlot = { Params = "StartFrom", Return = "number", Notes = "Returns the SlotNumber of the first non-empty slot following StartFrom, -1 if all the following slots are full" },
GetNumSlots = { Params = "", Return = "number", Notes = "Returns the total number of slots in the grid (Width * Height)" },
GetSlot =
{
@@ -1160,7 +1384,41 @@ 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
+ }, -- cItemGrid
cItems =
{
@@ -1190,7 +1448,7 @@ These ItemGrids are available in the API and can be manipulated by the plugins,
Constants =
{
},
- },
+ }, -- cItems
cLineBlockTracer =
{
@@ -1206,7 +1464,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 +1519,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 +1538,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.
@@ -1370,15 +1628,64 @@ a_Player:OpenWindow(Window);
cMonster =
{
- Desc = "",
- Functions = {},
- Constants = {},
+ Desc = [[
+ This class is the base class for all computer-controlled mobs in the game.</p>
+ <p>
+ To spawn a mob in a world, use the {{cWorld}}:SpawnMob() function.
+ ]],
+ Functions =
+ {
+ FamilyFromType = { Params = "MobType", Return = "MobFamily", Notes = "(STATIC) Returns the mob family (mfXXX constants) based on the mob type (mtXXX constants)" },
+ GetMobFamily = { Params = "", Return = "MobFamily", Notes = "Returns this mob's family (mfXXX constant)" },
+ GetMobType = { Params = "", Return = "MobType", Notes = "Returns the type of this mob (mtXXX constant)" },
+ GetSpawnDelay = { Params = "MobFamily", Return = "number", Notes = "(STATIC) Returns the spawn delay - the number of game ticks between spawn attempts - for the specified mob family." },
+ MobTypeToString = { Params = "MobType", Return = "string", Notes = "(STATIC) Returns the string representing the given mob type (mtXXX constant), or empty string if unknown type." },
+ StringToMobType = { Params = "string", Return = "MobType", Notes = "(STATIC) Returns the mob type (mtXXX constant) parsed from the string type (\"creeper\"), or mtInvalidType if unrecognized." },
+ },
+ Constants =
+ {
+ mfAmbient = { Notes = "Family: ambient (bat)" },
+ mfHostile = { Notes = "Family: hostile (blaze, cavespider, creeper, enderdragon, enderman, ghast, giant, magmacube, silverfish, skeleton, slime, spider, witch, wither, zombie, zombiepigman)" },
+ mfMaxplusone = { Notes = "The maximum family value, plus one. Returned when monster family not recognized." },
+ mfPassive = { Notes = "Family: passive (chicken, cow, horse, irongolem, mooshroom, ocelot, pig, sheep, snowgolem, villager, wolf)" },
+ mfWater = { Notes = "Family: water (squid)" },
+ mtBat = { Notes = "" },
+ mtBlaze = { Notes = "" },
+ mtCaveSpider = { Notes = "" },
+ mtChicken = { Notes = "" },
+ mtCow = { Notes = "" },
+ mtCreeper = { Notes = "" },
+ mtEnderDragon = { Notes = "" },
+ mtEnderman = { Notes = "" },
+ mtGhast = { Notes = "" },
+ mtGiant = { Notes = "" },
+ mtHorse = { Notes = "" },
+ mtInvalidType = { Notes = "Invalid monster type. Returned when monster type not recognized" },
+ mtIronGolem = { Notes = "" },
+ mtMagmaCube = { Notes = "" },
+ mtMooshroom = { Notes = "" },
+ mtOcelot = { Notes = "" },
+ mtPig = { Notes = "" },
+ mtSheep = { Notes = "" },
+ mtSilverfish = { Notes = "" },
+ mtSkeleton = { Notes = "" },
+ mtSlime = { Notes = "" },
+ mtSnowGolem = { Notes = "" },
+ mtSpider = { Notes = "" },
+ mtSquid = { Notes = "" },
+ mtVillager = { Notes = "" },
+ mtWitch = { Notes = "" },
+ mtWither = { Notes = "" },
+ mtWolf = { Notes = "" },
+ mtZombie = { Notes = "" },
+ mtZombiePigman = { Notes = "" },
+ },
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 =
{
@@ -1397,58 +1704,103 @@ a_Player:OpenWindow(Window);
cPickup =
{
- Desc = [[cPickup is a pickup object representation. It is also commonly known as "drops". With this class you could create your own "drop" or modify automatically created.
-]],
+ Desc = [[
+ This class represents a pickup entity (an item that the player or mobs can pick up). It is also
+ commonly known as "drops". With this class you could create your own "drop" or modify those
+ created automatically.
+ ]],
Functions =
{
- cPickup = { Notes = "[[cPickup}}" },
- GetItem = { Notes = "{{cItem|cItem}}" },
- CollectedBy = { Return = "bool" },
- },
- Constants =
- {
+ constructor = { Params = "PosX, PosY, PosZ, {{cItem|Item}}, IsPlayerCreated, [SpeedX, SpeedY, SpeedZ]", Return = "cPickup", Notes = "Creates a new pickup at the specified coords. If IsPlayerCreated is true, the pickup has a longer initial collection interval." },
+ CollectedBy = { Params = "{{cPlayer}}", Return = "bool", Notes = "Tries to make the player collect the pickup. Returns true if the pickup was collected, at least partially." },
+ GetAge = { Params = "", Return = "number", Notes = "Returns the number of ticks that the pickup has existed." },
+ GetItem = { Params = "", Return = "{{cItem|cItem}}", Notes = "Returns the item represented by this pickup" },
+ IsCollected = { Params = "", Return = "bool", Notes = "Returns true if this pickup has already been collected (is waiting to be destroyed)" },
+ IsPlayerCreated = { Params = "", Return = "bool", Notes = "Returns true if the pickup was created by a player" },
},
Inherits = "cEntity",
- },
+ }, -- cPickup
cPlayer =
{
- Desc = [[cPlayer describes a human player in the server. cPlayer inherits all functions and members of {{cPawn|cPawn}}
-]],
+ Desc = [[
+ This class describes a player in the server. cPlayer inherits all functions and members of
+ {{cPawn|cPawn}}. It handles all the aspects of the gameplay, such as hunger, sprinting, inventory
+ etc.
+ ]],
Functions =
{
- GetEyeHeight = { Return = "number" },
- GetEyePosition = { Return = "{{Vector3d|EyePositionVector}}" },
- GetFlying = { Return = "bool" },
- GetStance = { Return = "number" },
- GetInventory = { Return = "{{cInventory|Inventory}}" },
+ AddFoodExhaustion = { Params = "Exhaustion", Return = "", Notes = "Adds the specified number to the food exhaustion. Only positive numbers expected." },
+ AddToGroup = { Params = "GroupName", Return = "", Notes = "Temporarily adds the player to the specified group. The assignment is lost when the player disconnects." },
+ CanUseCommand = { Params = "Command", Return = "bool", Notes = "Returns true if the player is allowed to use the specified command." },
+ CloseWindow = { Params = "[CanRefuse]", Return = "", Notes = "Closes the currently open UI window. If CanRefuse is true (default), the window may refuse the closing." },
+ CloseWindowIfID = { Params = "WindowID, [CanRefuse]", Return = "", Notes = "Closes the currently open UI window if its ID matches the given ID. If CanRefuse is true (default), the window may refuse the closing." },
+ Feed = { Params = "AddFood, AddSaturation", Return = "bool", Notes = "Tries to add the specified amounts to food level and food saturation level (only positive amounts expected). Returns true if player was hungry and the food was consumed, false if too satiated." },
+ FoodPoison = { Params = "NumTicks", Return = "", Notes = "Starts the food poisoning for the specified amount of ticks; if already foodpoisoned, sets FoodPoisonedTicksRemaining to the larger of the two" },
+ GetAirLevel = { Params = "", Return = "number", Notes = "Returns the air level (number of ticks of air left)." },
+ GetClientHandle = { Params = "", Return = "{{cClientHandle}}", Notes = "Returns the client handle representing the player's connection. May be nil (AI players)." },
+ GetColor = { Return = "string", Notes = "Returns the full color code to be used for this player (based on the first group). Prefix player messages with this code." },
+ GetEquippedItem = { Params = "", Return = "{{cItem}}", Notes = "Returns the item that the player is currently holding; empty item if holding nothing." },
+ GetEyeHeight = { Return = "number", Notes = "Returns the height of the player's eyes, in absolute coords" },
+ GetEyePosition = { Return = "{{Vector3d|EyePositionVector}}", Notes = "Returns the position of the player's eyes, as a {{Vector3d}}" },
+ GetFoodExhaustionLevel = { Params = "", Return = "number", Notes = "Returns the food exhaustion level" },
+ GetFoodLevel = { Params = "", Return = "number", Notes = "Returns the food level (number of half-drumsticks on-screen)" },
+ GetFoodPoisonedTicksRemaining = { Params = "", Return = "", Notes = "Returns the number of ticks left for the food posoning effect" },
+ GetFoodSaturationLevel = { Params = "", Return = "number", Notes = "Returns the food saturation (overcharge of the food level, is depleted before food level)" },
+ GetFoodTickTimer = { Params = "", Return = "", Notes = "Returns the number of ticks past the last food-based heal or damage action; when this timer reaches 80, a new heal / damage is applied." },
GetGameMode = { Return = "{{eGameMode|GameMode}}", Notes = "Returns the player's gamemode. The player may have their gamemode unassigned, in which case they inherit the gamemode from the current {{cWorld|world}}.<br /> <b>NOTE:</b> Instead of comparing the value returned by this function to the gmXXX constants, use the IsGameModeXXX() functions. These functions handle the gamemode inheritance automatically."},
- GetIP = { Return = "string" },
- SetGameMode = { Return = "" },
- MoveTo = { Return = "" },
- GetClientHandle = { Return = "{{cClientHandle|ClientHandle}}" },
- SendMessage = { Return = "" },
- GetName = { Return = "String" },
- SetName = { Return = "" },
- AddToGroup = { Return = "" },
- CanUseCommand = { Return = "bool" },
- HasPermission = { Return = "bool" },
- IsInGroup = { Return = "bool" },
- GetColor = { Return = "string" },
- TossItem = { Return = "" },
- Heal = { Return = "" },
- TakeDamage = { Return = "" },
- KilledBy = { Return = "" },
- Respawn = { Return = "" },
- SetVisible = { Return = "" },
- IsVisible = { Return = "bool" },
- MoveToWorld = { Return = "bool" },
- LoadPermissionsFromDisk = { Return = "" },
- GetGroups = { Return = "list<{{cGroup|cGroup}}>" },
- GetResolvedPermissions = { Return = "string" },
+ GetGroups = { Return = "array-table of {{cGroup}}", Notes = "Returns all the groups that this player is member of, as a table. The groups are stored in the array part of the table, beginning with index 1."},
+ GetIP = { Return = "string", Notes = "Returns the IP address of the player, if available. Returns an empty string if there's no IP to report."},
+ GetInventory = { Return = "{{cInventory|Inventory}}", Notes = "Returns the player's inventory"},
+ GetMaxSpeed = { Params = "", Return = "number", Notes = "Returns the player's current maximum speed (as reported by the 1.6.1+ protocols)" },
+ GetName = { Return = "string", Notes = "Returns the player's name" },
+ GetNormalMaxSpeed = { Params = "", Return = "number", Notes = "Returns the player's maximum walking speed (as reported by the 1.6.1+ protocols)" },
+ GetResolvedPermissions = { Return = "array-table of string", Notes = "Returns all the player's permissions, as a table. The permissions are stored in the array part of the table, beginning with index 1." },
+ GetSprintingMaxSpeed = { Params = "", Return = "number", Notes = "Returns the player's maximum sprinting speed (as reported by the 1.6.1+ protocols)" },
+ GetStance = { Return = "number", Notes = "Returns the player's stance (Y-pos of player's eyes)" },
+ GetThrowSpeed = { Params = "SpeedCoeff", Return = "{{Vector3d}}", Notes = "Returns the speed vector for an object thrown with the specified speed coeff. Basically returns the normalized look vector multiplied by the coeff, with a slight random variation." },
+ GetThrowStartPos = { Params = "", Return = "{{Vector3d}}", Notes = "Returns the position where the projectiles should start when thrown by this player." },
+ GetWindow = { Params = "", Return = "{{cWindow}}", Notes = "Returns the currently open UI window. If the player doesn't have any UI window open, returns the inventory window." },
+ HasPermission = { Params = "PermissionString", Return = "bool", Notes = "Returns true if the player has the specified permission" },
+ Heal = { Params = "HitPoints", Return = "", Notes = "Heals the player by the specified amount of HPs. Only positive amounts are expected. Sends a health update to the client." },
+ IsEating = { Params = "", Return = "bool", Notes = "Returns true if the player is currently eating the item in their hand." },
+ IsGameModeAdventure = { Params = "", Return = "bool", Notes = "Returns true if the player is in the gmAdventure gamemode, or has their gamemode unset and the world is a gmAdventure world." },
+ IsGameModeCreative = { Params = "", Return = "bool", Notes = "Returns true if the player is in the gmCreative gamemode, or has their gamemode unset and the world is a gmCreative world." },
+ IsGameModeSurvival = { Params = "", Return = "bool", Notes = "Returns true if the player is in the gmSurvival gamemode, or has their gamemode unset and the world is a gmSurvival world." },
+ IsInGroup = { Params = "GroupNameString", Return = "bool", Notes = "Returns true if the player is a member of the specified group." },
+ IsOnGround = { Params = "", Return = "bool", Notes = "Returns true if the player is on ground (not falling, not jumping, not flying)" },
+ IsSatiated = { Params = "", Return = "bool", Notes = "Returns true if the player is satiated (cannot eat)." },
+ IsSubmerged = { Params = "", Return = "bool", Notes = "Returns true if the player is submerged in water (the player's head is in a water block)" },
+ IsSwimming = { Params = "", Return = "bool", Notes = "Returns true if the player is swimming in water (the player's feet are in a water block)" },
+ IsVisible = { Params = "", Return = "bool", Notes = "Returns true if the player is visible to other players" },
+ LoadPermissionsFromDisk = { Params = "", Return = "", Notes = "Reloads the player's permissions from the disk. This loses any temporary changes made to the player's groups." },
+ MoveTo = { Params = "{{Vector3d|NewPosition}}", Return = "Tries to move the player into the specified position." },
+ MoveToWorld = { Params = "WorldName", Return = "bool", Return = "Moves the player to the specified world. Returns true if successful." },
+ OpenWindow = { Params = "{{cWindow|Window}}", Return = "", Notes = "Opens the specified UI window for the player." },
+ RemoveFromGroup = { Params = "GroupName", Return = "", Notes = "Temporarily removes the player from the specified group. This change is lost when the player disconnects." },
+ Respawn = { Params = "", Return = "", Notes = "Restores the health, extinguishes fire, makes visible and sends the Respawn packet." },
+ SendMessage = { Params = "MessageString", Return = "", Notes = "Sends the specified message to the player." },
+ SetCrouch = { Params = "IsCrouched", Return = "", Notes = "Sets the crouch state, broadcasts the change to other players." },
+ SetFoodExhaustionLevel = { Params = "ExhaustionLevel", Return = "", Notes = "Sets the food exhaustion to the specified level." },
+ SetFoodLevel = { Params = "FoodLevel", Return = "", Notes = "Sets the food level (number of half-drumsticks on-screen)" },
+ SetFoodPoisonedTicksRemaining = { Params = "FoodPoisonedTicksRemaining", Return = "", Notes = "Sets the number of ticks remaining for food poisoning. Doesn't send foodpoisoning effect to the client, use FoodPoison() for that." },
+ SetFoodSaturationLevel = { Params = "FoodSaturationLevel", Return = "", Notes = "Sets the food saturation (overcharge of the food level)." },
+ SetFoodTickTimer = { Params = "FoodTickTimer", Return = "", Notes = "Sets the number of ticks past the last food-based heal or damage action; when this timer reaches 80, a new heal / damage is applied." },
+ SetGameMode = { Params = "{{eGameMode|NewGameMode}}", Return = "", Notes = "Sets the gamemode for the player. The new gamemode overrides the world's default gamemode, unless it is set to gmInherit." },
+ SetName = { Params = "Name", Return = "", Notes = "Sets the player name. This rename will NOT be visible to any players already in the server who are close enough to see this player." },
+ SetNormalMaxSpeed = { Params = "NormalMaxSpeed", Return = "", Notes = "Sets the normal (walking) maximum speed (as reported by the 1.6.1+ protocols)" },
+ SetSprint = { Params = "IsSprinting", Return = "", Notes = "Sets whether the player is sprinting or not." },
+ SetSprintingMaxSpeed = { Params = "SprintingMaxSpeed", Return = "", Notes = "Sets the sprinting maximum speed (as reported by the 1.6.1+ protocols)" },
+ SetVisible = { Params = "IsVisible", Return = "", Notes = "Sets the player visibility to other players" },
+ TossItem = { Params = "DraggedItem, [Amount], [CreateType], [CreateDamage]", Return = "", Notes = "FIXME: This function will be rewritten, avoid it. It tosses an item, either from the inventory, dragged in hand (while in UI window) or a newly created one." },
},
Constants =
{
+ DROWNING_TICKS = { Notes = "Number of ticks per heart of damage when drowning (zero AirLevel)" },
+ EATING_TICKS = { Notes = "Number of ticks required for consuming an item." },
+ MAX_AIR_LEVEL = { Notes = "The maximum air level value. AirLevel gets reset to this value when the player exits water." },
+ MAX_FOOD_LEVEL = { Notes = "The maximum food level value. When the food level is at this value, the player cannot eat." },
+ MAX_HEALTH = { Notes = "The maximum health value" },
},
Inherits = "cPawn",
},
@@ -1482,7 +1834,7 @@ a_Player:OpenWindow(Window);
Constants = {},
Inherits = "cPlugin",
},
-
+
cPluginManager =
{
Desc = [[
@@ -1591,18 +1943,43 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
cProjectileEntity =
{
Desc = "",
- Functions = {},
- Constants = {},
+ Functions =
+ {
+ GetCreator = { Params = "", Return = "{{cEntity}} descendant", Notes = "Returns the entity who created this projectile. May return nil." },
+ GetMCAClassName = { Params = "", Return = "string", Notes = "Returns the string that identifies the projectile type (class name) in MCA files" },
+ GetProjectileKind = { Params = "", Return = "ProjectileKind", Notes = "Returns the kind of this projectile (pkXXX constant)" },
+ IsInGround = { Params = "", Return = "bool", Notes = "Returns true if this projectile has hit the ground." },
+ },
+ Constants =
+ {
+ pkArrow = { Notes = "The projectile is an {{cArrowEntity|arrow}}" },
+ pkEgg = { Notes = "The projectile is a {{cThrownEggEntity|thrown egg}}" },
+ pkEnderPearl = { Notes = "The projectile is a {{cThrownEnderPearlEntity|thrown enderpearl}}" },
+ pkExpBottle = { Notes = "The projectile is a thrown exp bottle (NYI)" },
+ pkFireCharge = { Notes = "The projectile is a {{cFireChargeEntity|fire charge}}" },
+ pkFishingFloat = { Notes = "The projectile is a fishing float (NYI)" },
+ pkGhastFireball = { Notes = "The projectile is a {{cGhastFireballEntity|ghast fireball}}" },
+ pkSnowball = { Notes = "The projectile is a {{cThrownSnowballEntity|thrown snowball}}" },
+ pkSplashPotion = { Notes = "The projectile is a thrown splash potion (NYI)" },
+ pkWitherSkull = { Notes = "The projectile is a wither skull (NYI)" },
+ },
Inherits = "cEntity",
},
-
+
cRoot =
{
- Desc = [[There is always only one cRoot object in MCServer. cRoot manages all the important objects such as {{cServer|cServer}}
-]],
+ Desc = [[
+ This class represents the root of MCServer's object hierarchy. There is always only one cRoot
+ object. It manages and allows querying all the other objects, such as {{cServer}},
+ {{cPluginManager}}, individual {{cWorld|worlds}} etc.</p>
+ <p>
+ To get the singleton instance of this object, you call the cRoot:Get() function. Then you can call
+ the individual functions on this object. Note that some of the functions are static and don't need
+ the instance, they are to be called directly on the cRoot class, such as cRoot:GetPhysicalRAMUsage()
+ ]],
Functions =
{
- Get = { Params = "", Return = "Root object", Notes = "This function returns the cRoot object." },
+ Get = { Params = "", Return = "Root object", Notes = "(STATIC)This function returns the cRoot object." },
BroadcastChat = { Params = "Message", Return = "", Notes = "Broadcasts a message to every player in the server." },
FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "", Notes = "Calls the given callback function for the given player." },
ForEachPlayer = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each player. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|cPlayer}})</pre>" },
@@ -1611,11 +1988,13 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
GetDefaultWorld = { Params = "", Return = "{{cWorld|cWorld}}", Notes = "Returns the world object from the default world." },
GetFurnaceRecipe = { Params = "", Return = "{{cFurnaceRecipe|cFurnaceRecipe}}", Notes = "Returns the cFurnaceRecipes object." },
GetGroupManager = { Params = "", Return = "{{cGroupManager|cGroupManager}}", Notes = "Returns the cGroupManager object." },
+ GetPhysicalRAMUsage = { Params = "", Return = "number", Notes = "Returns the amount of physical RAM that the entire MCServer process is using, in KiB. Negative if the OS doesn't support this query." },
GetPluginManager = { Params = "", Return = "{{cPluginManager|cPluginManager}}", Notes = "Returns the cPluginManager object." },
GetPrimaryServerVersion = { Params = "", Return = "number", Notes = "Returns the servers primary server version." },
GetProtocolVersionTextFromInt = { Params = "Protocol Version", Return = "string", Notes = "Returns the Minecraft version from the given Protocol. If there is no version found, it returns 'Unknown protocol(Parameter)'" },
GetServer = { Params = "", Return = "{{cServer|cServer}}", Notes = "Returns the cServer object." },
GetTotalChunkCount = { Params = "", Return = "number", Notes = "Returns the amount of loaded chunks." },
+ GetVirtualRAMUsage = { Params = "", Return = "number", Notes = "Returns the amount of virtual RAM that the entire MCServer process is using, in KiB. Negative if the OS doesn't support this query." },
GetWebAdmin = { Params = "", Return = "{{cWebAdmin|cWebAdmin}}", Notes = "Returns the cWebAdmin object." },
GetWorld = { Params = "WorldName", Return = "{{cWorld|cWorld}}", Notes = "Returns the cWorld object of the given world. It returns nil if there is no world with the given name." },
QueueExecuteConsoleCommand = { Params = "Message", Return = "", Notes = "Queues a console command for execution through the cServer class. The command will be executed in the tick thread The command's output will be sent to console " .. '"stop" and "restart" commands have special handling.' },
@@ -1656,7 +2035,7 @@ Sign entities are saved and loaded from disk when the chunk they reside in is sa
Constants =
{
},
-
+
Inherits = "cBlockEntity";
},
@@ -1671,7 +2050,7 @@ Sign entities are saved and loaded from disk when the chunk they reside in is sa
{
},
},
-
+
cThrownEggEntity =
{
Desc = "",
@@ -1679,7 +2058,7 @@ Sign entities are saved and loaded from disk when the chunk they reside in is sa
Constants = {},
Inherits = "cProjectileEntity",
},
-
+
cThrownEnderPearlEntity =
{
Desc = "",
@@ -1687,7 +2066,7 @@ Sign entities are saved and loaded from disk when the chunk they reside in is sa
Constants = {},
Inherits = "cProjectileEntity",
},
-
+
cThrownSnowballEntity =
{
Desc = "",
@@ -1695,7 +2074,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,49 +2092,64 @@ 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.
-</p>
- <p>Note that one cWindow object can be used for multiple players at the same time, and therefore the slot contents are player-specific (e. g. crafting grid, or player inventory). Thus the GetSlot() and SetSlot() functions need to have the {{cPlayer|cPlayer}} parameter that specifies the player for which the contents are to be queried.
-]],
+ Desc = [[
+ This class is the common ancestor for all window classes used by MCServer. It is inherited by the
+ {{cLuaWindow|cLuaWindow}} class that plugins use for opening custom windows. It is planned to be
+ used for window-related hooks in the future. It implements the basic functionality of any
+ window.</p>
+ <p>
+ Note that one cWindow object can be used for multiple players at the same time, and therefore the
+ slot contents are player-specific (e. g. crafting grid, or player inventory). Thus the GetSlot() and
+ SetSlot() functions need to have the {{cPlayer|cPlayer}} parameter that specifies the player for
+ whom the contents are to be queried.</p>
+ <p>
+ Windows also have numeric properties, these are used to set the progressbars for furnaces or the XP
+ costs for enchantment tables.
+ ]],
Functions =
{
+ GetSlot = { Params = "{{cPlayer|Player}}, SlotNumber", Return = "{{cItem}}", Notes = "Returns the item at the specified slot for the specified player. Returns nil and logs to server console on error." },
GetWindowID = { Params = "", Return = "number", Notes = "Returns the ID of the window, as used by the network protocol" },
GetWindowTitle = { Params = "", Return = "string", Notes = "Returns the window title that will be displayed to the player" },
GetWindowType = { Params = "", Return = "number", Notes = "Returns the type of the window, one of the constants in the table above" },
- IsSlotInPlayerHotbar = { Params = "number", Return = "bool", Notes = "Returns true if the specified slot number is in the player hotbar" },
- IsSlotInPlayerInventory = { Params = "number", Return = "bool", Notes = "Returns true if the specified slot number is in the player's main inventory or in the hotbar. Note that this returns false for armor slots!" },
- IsSlotInPlayerMainInventory = { Params = "number", Return = "bool", Notes = "Returns true if the specified slot number is in the player's main inventory" },
- SetSlot = { Params = "{{cItem|cItem}}", Return = "", Notes = "Sets the contents of the specified slot for the specified player. Ignored if the slot number is invalid" },
+ IsSlotInPlayerHotbar = { Params = "SlotNum", Return = "bool", Notes = "Returns true if the specified slot number is in the player hotbar" },
+ IsSlotInPlayerInventory = { Params = "SlotNum", Return = "bool", Notes = "Returns true if the specified slot number is in the player's main inventory or in the hotbar. Note that this returns false for armor slots!" },
+ IsSlotInPlayerMainInventory = { Params = "SlotNum", Return = "bool", Notes = "Returns true if the specified slot number is in the player's main inventory" },
+ SetProperty = { Params = "PropertyID, PropartyValue, {{cPlayer|Player}}", Return = "", Notes = "Sends the UpdateWindowProperty (0x69) packet to the specified player; or to all players who are viewing this window if Player is not specified or nil." },
+ SetSlot = { Params = "{{cPlayer|Player}}, SlotNum, {{cItem|cItem}}", Return = "", Notes = "Sets the contents of the specified slot for the specified player. Ignored if the slot number is invalid" },
SetWindowTitle = { Params = "string", Return = "", Notes = "Sets the window title that will be displayed to the player" },
},
Constants =
{
- Inventory = { Notes = "" },
- Chest = { Notes = "0" },
- Workbench = { Notes = "1" },
- Furnace = { Notes = "2" },
- DropSpenser = { Notes = "3" },
- Enchantment = { Notes = "4" },
- Brewery = { Notes = "5" },
- NPCTrade = { Notes = "6" },
- Beacon = { Notes = "7" },
- Anvil = { Notes = "8" },
- Hopper = { Notes = "9" },
+ wtInventory = { Notes = "An inventory window" },
+ wtChest = { Notes = "A {{cChestEntity|chest}} or doublechest window" },
+ wtWorkbench = { Notes = "A workbench (crafting table) window" },
+ wtFurnace = { Notes = "A {{cFurnaceEntity|furnace}} window" },
+ wtDropSpenser = { Notes = "A {{cDropperEntity|dropper}} or a {{cDispenserEntity|dispenser}} window" },
+ wtEnchantment = { Notes = "An enchantment table window" },
+ wtBrewery = { Notes = "A brewing stand window" },
+ wtNPCTrade = { Notes = "A villager trade window" },
+ wtBeacon = { Notes = "A beacon window" },
+ wtAnvil = { Notes = "An anvil window" },
+ wtHopper = { Notes = "A {{cHopperEntity|hopper}} window" },
},
- },
+ }, -- cWindow
cWorld =
{
@@ -1789,7 +2183,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 +2191,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." },
@@ -1875,6 +2269,7 @@ Sign entities are saved and loaded from disk when the chunk they reside in is sa
QueueBlockForTick = { Params = "BlockX, BlockY, BlockZ, TicksToWait", Return = "", Notes = "Queues the specified block to be ticked after the specified number of gameticks." },
QueueSaveAllChunks = { Params = "", Return = "", Notes = "Queues all chunks to be saved in the world storage thread" },
QueueSetBlock = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta, TickDelay", Return = "", Notes = "Queues the block to be set to the specified blocktype and meta after the specified amount of game ticks. Uses SetBlock() for the actual setting, so simulators are woken up and block entities are handled correctly." },
+ QueueTask = { Params = "TaskFunction", Return = "", Notes = "Queues the specified function to be executed in the tick thread. This is the primary means of interaction with a cWorld from the WebAdmin page handlers (see {{WebWorldThreads}}). The function signature is <pre class=\"pretty-print lang-lua\">function()</pre>All return values from the function are ignored. Note that this function is actually called *after* the QueueTask() function returns." },
RegenerateChunk = { Params = "ChunkX, ChunkZ", Return = "", Notes = "Queues the specified chunk to be re-generated, overwriting the current data. To queue a chunk for generating only if it doesn't exist, use the GenerateChunk() instead." },
SendBlockTo = { Params = "BlockX, BlockY, BlockZ, {{cPlayer|Player}}", Return = "", Notes = "Sends the block at the specified coords to the specified player's client, as an UpdateBlock packet." },
SetBlock = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta", Return = "", Notes = "Sets the block at the specified coords, replaces the block entities for the previous block type, creates a new block entity for the new block, if appropriate, and wakes up the simulators. This is the preferred way to set blocks, as opposed to FastSetBlock(), which is only to be used under special circumstances." },
@@ -1965,7 +2360,40 @@ World:ForEachEntity(
]],
},
}, -- AdditionalInfo
- },
+ }, -- cWorld
+
+ HTTPFormData =
+ {
+ Desc = "This class stores data for one form element for a {{HTTPRequest|HTTP request}}.",
+ Variables =
+ {
+ Name = { Type = "string", Notes = "Name of the form element" },
+ Type = { Type = "string", Notes = "Type of the data (usually empty)" },
+ Value = { Type = "string", Notes = "Value of the form element. Contains the raw data as sent by the browser." },
+ },
+ }, -- HTTPFormData
+
+ HTTPRequest =
+ {
+ Desc = [[
+ This class encapsulates all the data that is sent to the WebAdmin through one HTTP request. Plugins
+ receive this class as a parameter to the function handling the web requests, as registered in the
+ FIXME: {{cPluginLua}}:AddWebPage().
+ ]],
+ Constants =
+ {
+ FormData = { Notes = "Array-table of {{HTTPFormData}}, contains the values of individual form elements submitted by the client" },
+ Params = { Notes = "Map-table of parameters given to the request in the URL (?param=value); if a form uses GET method, this is the same as FormData. For each parameter given as \"param=value\", there is an entry in the table with \"param\" as its key and \"value\" as its value." },
+ PostParams = { Notes = "Map-table of data posted through a FORM - either a GET or POST method. Logically the same as FormData, but in a map-table format (for each parameter given as \"param=value\", there is an entry in the table with \"param\" as its key and \"value\" as its value)." },
+ },
+
+ Variables =
+ {
+ Method = { Type = "string", Notes = "The HTTP method used to make the request. Usually GET or POST." },
+ Path = { Type = "string", Notes = "The Path part of the URL (excluding the parameters)" },
+ Username = { Type = "string", Notes = "Name of the logged-in user." },
+ },
+ }, -- HTTPRequest
TakeDamageInfo =
{
@@ -1977,7 +2405,7 @@ World:ForEachEntity(
Constants =
{
},
- },
+ }, -- TakeDamageInfo
Vector3d =
{
@@ -1990,7 +2418,7 @@ World:ForEachEntity(
Constants =
{
},
- },
+ }, -- Vector3d
Vector3f =
{
@@ -2002,7 +2430,7 @@ World:ForEachEntity(
Constants =
{
},
- },
+ }, -- Vector3f
Vector3i =
{
@@ -2014,7 +2442,8 @@ World:ForEachEntity(
Constants =
{
},
- },
+ }, -- Vector3i
+
Globals =
{
Desc = [[These functions are available directly, without a class instance. Any plugin cal call them at any time.]],
@@ -2054,8 +2483,8 @@ World:ForEachEntity(
},
},
},
-
-
+
+
Hooks =
{
HOOK_BLOCK_TO_PICKUPS =
@@ -2101,10 +2530,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 +2626,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 +2837,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 +2880,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
@@ -2588,7 +3017,7 @@ end;
HOOK_LOGIN =
{
- CalledWhen = "Right after player authentication. If auth is disabled, right after the player sends their name.",
+ CalledWhen = "Right before player authentication. If auth is disabled, right after the player sends their name.",
DefaultFnName = "OnLogin", -- also used as pagename
Desc = [[
This hook is called whenever a client logs in. It is called right before the client's name is sent
@@ -2894,6 +3323,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 +3730,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 +3938,10 @@ end;
"os",
"string",
"table",
+ "g_TrackedPages",
+ "g_Stats",
},
-
+
IgnoreFunctions =
{
"Globals.assert",
@@ -3076,19 +3952,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..a9cdf143b 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,13 @@ 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
+ local FnName = func.DocID or func.Name;
+ 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 +520,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 +571,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 +607,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 +656,7 @@ function ReadHooks(a_Hooks)
end
end
end -- for i, hook - a_Hooks[]
+ g_Stats.NumTotalHooks = #a_Hooks;
end
@@ -616,7 +664,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 +689,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 +730,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");
+ 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 <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 +790,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 +801,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 +937,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 +969,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/MCServer/Plugins/ChunkWorx/chunkworx_main.lua b/MCServer/Plugins/ChunkWorx/chunkworx_main.lua
index ab9962387..f74c4ea2d 100644
--- a/MCServer/Plugins/ChunkWorx/chunkworx_main.lua
+++ b/MCServer/Plugins/ChunkWorx/chunkworx_main.lua
@@ -6,20 +6,27 @@ CX = 0
CZ = 0
CURRENT = 0
TOTAL = 0
+
-- AREA Variables
AreaStartX = -10
AreaStartZ = -10
AreaEndX = 10
AreaEndZ = 10
+
-- RADIAL Variables
RadialX = 0
RadialZ = 0
-Radius = 1
+Radius = 10
+
-- WORLD
WORK_WORLD = cRoot:Get():GetDefaultWorld():GetName()
WW_instance = cRoot:Get():GetDefaultWorld()
WORLDS = {}
+
+
+
+
function Initialize(Plugin)
PLUGIN = Plugin
@@ -36,39 +43,25 @@ function Initialize(Plugin)
LOG("" .. PLUGIN:GetName() .. " v" .. PLUGIN:GetVersion() .. ": NO WORLD found :(")
end
- PLUGIN.IniFile = cIniFile("ChunkWorx.ini")
- if (PLUGIN.IniFile:ReadFile() == false) then
- PLUGIN.IniFile:HeaderComment("ChunkWorx Save")
- PLUGIN.IniFile:AddKeyName("Area data")
- PLUGIN.IniFile:SetValueI("Area data", "StartX", AreaStartX)
- PLUGIN.IniFile:SetValueI("Area data", "StartZ", AreaStartZ)
- PLUGIN.IniFile:SetValueI("Area data", "EndX", AreaEndX)
- PLUGIN.IniFile:SetValueI("Area data", "EndZ", AreaEndZ)
- PLUGIN.IniFile:AddKeyName("Radial data")
- PLUGIN.IniFile:SetValueI("Radial data", "RadialX", RadialX)
- PLUGIN.IniFile:SetValueI("Radial data", "RadialZ", RadialZ)
- PLUGIN.IniFile:SetValueI("Radial data", "Radius", Radius)
- PLUGIN.IniFile:WriteFile()
- end
-
- AreaStartX = PLUGIN.IniFile:GetValueI("Area data", "StartX")
- AreaStartZ = PLUGIN.IniFile:GetValueI("Area data", "StartZ")
- AreaEndX = PLUGIN.IniFile:GetValueI("Area data", "EndX")
- AreaEndZ = PLUGIN.IniFile:GetValueI("Area data", "EndZ")
-
- RadialX = PLUGIN.IniFile:GetValueI("Radial data", "RadialX")
- RadialZ = PLUGIN.IniFile:GetValueI("Radial data", "RadialZ")
- Radius = PLUGIN.IniFile:GetValueI("Radial data", "Radius")
+ -- Read the stored values:
+ local SettingsIni = cIniFile();
+ SettingsIni:ReadFile("ChunkWorx.ini"); -- ignore any read errors
+ AreaStartX = SettingsIni:GetValueSetI("Area data", "StartX", AreaStartX)
+ AreaStartZ = SettingsIni:GetValueSetI("Area data", "StartZ", AreaStartZ)
+ AreaEndX = SettingsIni:GetValueSetI("Area data", "EndX", AreaEndX)
+ AreaEndZ = SettingsIni:GetValueSetI("Area data", "EndZ", AreaEndZ)
+ RadialX = SettingsIni:GetValueSetI("Radial data", "RadialX", RadialX)
+ RadialZ = SettingsIni:GetValueSetI("Radial data", "RadialZ", RadialZ)
+ Radius = SettingsIni:GetValueSetI("Radial data", "Radius", Radius)
+ SettingsIni:WriteFile("ChunkWorx.ini");
LOG("Initialized " .. PLUGIN:GetName() .. " v" .. PLUGIN:GetVersion())
- --LOG("Test1: " .. math.fmod(1.5, 1)) - return fractional part!
return true
end
-function OnDisable()
- PLUGIN.IniFile:WriteFile()
- LOG(PLUGIN:GetName() .. " v" .. PLUGIN:GetVersion() .. " is shutting down...")
-end
+
+
+
function OnTick( DeltaTime )
if (GENERATION_STATE == 1 or GENERATION_STATE == 3) then
@@ -128,7 +121,7 @@ function OnTick( DeltaTime )
end
end
end
- WW_instance:SaveAllChunks()
+ WW_instance:QueueSaveAllChunks()
WW_instance:UnloadUnusedChunks()
end
end
diff --git a/MCServer/Plugins/ChunkWorx/chunkworx_web.lua b/MCServer/Plugins/ChunkWorx/chunkworx_web.lua
index e9a930c92..44993f81c 100644
--- a/MCServer/Plugins/ChunkWorx/chunkworx_web.lua
+++ b/MCServer/Plugins/ChunkWorx/chunkworx_web.lua
@@ -1,11 +1,44 @@
+
+-- chunkworx_web.lua
+
+-- WebAdmin-related functions
+
+
+
+
+
local function Buttons_Player( Name )
return "<form method='POST'><input type='hidden' name='PlayerName' value='"..Name.."'><input type='submit' name='PlayerExact' value='Exact'><input type='submit' name='Player3x3' value='3x3'></form>"
end
+
+
+
+
local function Button_World( Name )
return "<form method='POST'><input type='hidden' name='WorldName' value='"..Name.."'><input type='submit' name='SelectWorld' value='Select'></form>"
end
+
+
+
+
+local function SaveSettings()
+ local SettingsIni = cIniFile()
+ SettingsIni:SetValueI("Area data", "StartX", AreaStartX)
+ SettingsIni:SetValueI("Area data", "StartZ", AreaStartZ)
+ SettingsIni:SetValueI("Area data", "EndX", AreaEndX)
+ SettingsIni:SetValueI("Area data", "EndZ", AreaEndZ)
+ SettingsIni:SetValueI("Radial data", "RadialX", RadialX)
+ SettingsIni:SetValueI("Radial data", "RadialZ", RadialZ)
+ SettingsIni:SetValueI("Radial data", "Radius", Radius)
+ SettingsIni:WriteFile("ChunkWorx.ini")
+end
+
+
+
+
+
function HandleRequest_Generation( Request )
local Content = ""
if (Request.PostParams["AGHRRRR"] ~= nil) then
@@ -69,21 +102,12 @@ function HandleRequest_Generation( Request )
AreaStartZ = tonumber(Request.PostParams["FormAreaStartZ"])
AreaEndX = tonumber(Request.PostParams["FormAreaEndX"])
AreaEndZ = tonumber(Request.PostParams["FormAreaEndZ"])
-
- PLUGIN.IniFile:DeleteValue("Area data", "StartX")
- PLUGIN.IniFile:DeleteValue("Area data", "StartZ")
- PLUGIN.IniFile:DeleteValue("Area data", "EndX")
- PLUGIN.IniFile:DeleteValue("Area data", "EndZ")
- PLUGIN.IniFile:SetValueI("Area data", "StartX", AreaStartX)
- PLUGIN.IniFile:SetValueI("Area data", "StartZ", AreaStartZ)
- PLUGIN.IniFile:SetValueI("Area data", "EndX", AreaEndX)
- PLUGIN.IniFile:SetValueI("Area data", "EndZ", AreaEndZ)
+ SaveSettings();
if (OPERATION_CODE == 0) then
GENERATION_STATE = 1
elseif (OPERATION_CODE == 1) then
GENERATION_STATE = 3
end
- PLUGIN.IniFile:WriteFile()
Content = ProcessingContent()
return Content
end
@@ -93,26 +117,19 @@ function HandleRequest_Generation( Request )
and Request.PostParams["FormRadius"] ~= nil ) then --(Re)Generation valid!
-- COMMON (Re)gen
if( Request.PostParams["StartRadial"]) then
- RadialX = tonumber(Request.PostParams["FormRadialX"])
- RadialZ = tonumber(Request.PostParams["FormRadialZ"])
- Radius = tonumber(Request.PostParams["FormRadius"])
+ RadialX = tonumber(Request.PostParams["FormRadialX"]) or 0
+ RadialZ = tonumber(Request.PostParams["FormRadialZ"]) or 0
+ Radius = tonumber(Request.PostParams["FormRadius"]) or 10
AreaStartX = RadialX - Radius
AreaStartZ = RadialZ - Radius
AreaEndX = RadialX + Radius
AreaEndZ = RadialZ + Radius
-
- PLUGIN.IniFile:DeleteValue("Radial data", "RadialX")
- PLUGIN.IniFile:DeleteValue("Radial data", "RadialZ")
- PLUGIN.IniFile:DeleteValue("Radial data", "Radius")
- PLUGIN.IniFile:SetValueI("Radial data", "RadialX", RadialX)
- PLUGIN.IniFile:SetValueI("Radial data", "RadialZ", RadialZ)
- PLUGIN.IniFile:SetValueI("Radial data", "Radius", Radius)
+ SaveSettings()
if (OPERATION_CODE == 0) then
GENERATION_STATE = 1
elseif (OPERATION_CODE == 1) then
GENERATION_STATE = 3
end
- PLUGIN.IniFile:WriteFile()
Content = ProcessingContent()
return Content
end
@@ -214,7 +231,7 @@ function HandleRequest_Generation( Request )
Content = Content .. "</form>"
-- SELECTING RADIAL
- Content = Content .. "<h4>Radial: </h4>Center X, Center Z, Raduis (0 to any)"
+ Content = Content .. "<h4>Radial: </h4>Center X, Center Z, Radius"
Content = Content .. "<form method='POST'>"
Content = Content .. "<input type='text' name='FormRadialX' value='" .. RadialX .. "'><input type='text' name='FormRadialZ' value='" .. RadialZ .. "'><input type='text' name='FormRadius' value='" .. Radius .. "'>"
Content = Content .. "<input type='submit' name='StartRadial' value='Start'>"
diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua
index b895da05e..04a15a002 100644
--- a/MCServer/Plugins/Debuggers/Debuggers.lua
+++ b/MCServer/Plugins/Debuggers/Debuggers.lua
@@ -606,7 +606,7 @@ end
function HandleTestWndCmd(a_Split, a_Player)
- local WindowType = cWindow.Hopper;
+ local WindowType = cWindow.wtHopper;
local WindowSizeX = 5;
local WindowSizeY = 1;
if (#a_Split == 4) then
@@ -789,7 +789,7 @@ end
function HandleEnchCmd(a_Split, a_Player)
- local Wnd = cLuaWindow(cWindow.Enchantment, 1, 1, "Ench");
+ local Wnd = cLuaWindow(cWindow.wtEnchantment, 1, 1, "Ench");
a_Player:OpenWindow(Wnd);
Wnd:SetProperty(0, 10);
Wnd:SetProperty(1, 15);
diff --git a/Tools/ProtoProxy/Connection.cpp b/Tools/ProtoProxy/Connection.cpp
index c4776949e..b99415900 100644
--- a/Tools/ProtoProxy/Connection.cpp
+++ b/Tools/ProtoProxy/Connection.cpp
@@ -52,7 +52,7 @@
#define CLIENTSEND(...) SendData(m_ClientSocket, __VA_ARGS__, "Client")
#define SERVERSEND(...) SendData(m_ServerSocket, __VA_ARGS__, "Server")
-#define CLIENTENCRYPTSEND(...) SendEncryptedData(m_ClientSocket, m_ClientEncryptor, __VA_ARGS__, "Client")
+#define CLIENTENCRYPTSEND(...) SendData(m_ClientSocket, __VA_ARGS__, "Client") // The client conn is always unencrypted
#define SERVERENCRYPTSEND(...) SendEncryptedData(m_ServerSocket, m_ServerEncryptor, __VA_ARGS__, "Server")
#define COPY_TO_SERVER() \
@@ -99,19 +99,20 @@
CLIENTENCRYPTSEND(ToClient.data(), ToClient.size()); \
break; \
} \
- case csWaitingForEncryption: \
+ /* case csWaitingForEncryption: \
{ \
Log("Waiting for client encryption, queued %u bytes", ToClient.size()); \
m_ClientEncryptionBuffer.append(ToClient.data(), ToClient.size()); \
break; \
} \
+ */ \
} \
DebugSleep(50); \
}
#define HANDLE_CLIENT_READ(Proc) \
{ \
- if (!Proc()) \
+ if (!Proc) \
{ \
AString Leftover; \
m_ClientBuffer.ReadAgain(Leftover); \
@@ -123,7 +124,7 @@
#define HANDLE_SERVER_READ(Proc) \
{ \
- if (!Proc()) \
+ if (!Proc) \
{ \
m_ServerBuffer.ResetRead(); \
return true; \
@@ -144,15 +145,20 @@ typedef unsigned char Byte;
enum
{
- PACKET_KEEPALIVE = 0x00,
- PACKET_LOGIN = 0x01,
- PACKET_HANDSHAKE = 0x02,
- PACKET_CHAT_MESSAGE = 0x03,
- PACKET_TIME_UPDATE = 0x04,
- PACKET_ENTITY_EQUIPMENT = 0x05,
- PACKET_COMPASS = 0x06,
+ // client-bound packets:
+ PACKET_C_KEEPALIVE = 0x00,
+ PACKET_C_JOIN_GAME = 0x01,
+ PACKET_C_CHAT_MESSAGE = 0x02,
+
+ // server-bound packets:
+ PACKET_S_KEEPALIVE = 0x00, // Also the initial handshake, as the very first packet
+ PACKET_S_CHAT_MESSAGE = 0x01,
+
+ PACKET_TIME_UPDATE = 0x03,
+ PACKET_ENTITY_EQUIPMENT = 0x04,
+ PACKET_SPAWN_POSITION = 0x05,
+ PACKET_UPDATE_HEALTH = 0x06,
PACKET_USE_ENTITY = 0x07,
- PACKET_UPDATE_HEALTH = 0x08,
PACKET_PLAYER_ON_GROUND = 0x0a,
PACKET_PLAYER_POSITION = 0x0b,
PACKET_PLAYER_LOOK = 0x0c,
@@ -270,7 +276,10 @@ cConnection::cConnection(SOCKET a_ClientSocket, cServer & a_Server) :
m_Nonce(0),
m_ClientBuffer(1024 KiB),
m_ServerBuffer(1024 KiB),
- m_HasClientPinged(false)
+ m_HasClientPinged(false),
+ m_ServerProtocolState(-1),
+ m_ClientProtocolState(-1),
+ m_IsServerEncrypted(false)
{
// Create the Logs subfolder, if not already created:
#if defined(_WIN32)
@@ -279,7 +288,7 @@ cConnection::cConnection(SOCKET a_ClientSocket, cServer & a_Server) :
mkdir("Logs", 0777);
#endif
- Printf(m_LogNameBase, "Logs/Log_%d", (int)time(NULL));
+ Printf(m_LogNameBase, "Logs/Log_%d_%d", (int)time(NULL), a_ClientSocket);
AString fnam(m_LogNameBase);
fnam.append(".log");
m_LogFile = fopen(fnam.c_str(), "w");
@@ -453,7 +462,6 @@ bool cConnection::RelayFromServer(void)
{
m_ServerDecryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res);
DataLog(Buffer, res, "Decrypted %d bytes from the SERVER", res);
- m_ClientEncryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res);
return CLIENTSEND(Buffer, res);
}
}
@@ -486,13 +494,10 @@ bool cConnection::RelayFromClient(void)
}
case csEncryptedUnderstood:
{
- m_ClientDecryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res);
- DataLog(Buffer, res, "Decrypted %d bytes from the CLIENT", res);
return DecodeClientsPackets(Buffer, res);
}
case csEncryptedUnknown:
{
- m_ClientDecryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res);
DataLog(Buffer, res, "Decrypted %d bytes from the CLIENT", res);
m_ServerEncryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res);
return SERVERSEND(Buffer, res);
@@ -517,7 +522,7 @@ double cConnection::GetRelativeTime(void)
bool cConnection::SendData(SOCKET a_Socket, const char * a_Data, int a_Size, const char * a_Peer)
{
- DataLog(a_Data, a_Size, "Sending data to %s", a_Peer);
+ DataLog(a_Data, a_Size, "Sending data to %s, %d bytes", a_Peer, a_Size);
int res = send(a_Socket, a_Data, a_Size, 0);
if (res <= 0)
@@ -590,66 +595,97 @@ bool cConnection::DecodeClientsPackets(const char * a_Data, int a_Size)
while (m_ClientBuffer.CanReadBytes(1))
{
- unsigned char PacketType;
- m_ClientBuffer.ReadByte(PacketType);
- Log("Decoding client's packets, there are now %d bytes in the queue; next packet is 0x%02x", m_ClientBuffer.GetReadableSpace(), PacketType);
- switch (PacketType)
+ UInt32 PacketLen;
+ if (
+ !m_ClientBuffer.ReadVarInt(PacketLen) ||
+ !m_ClientBuffer.CanReadBytes(PacketLen)
+ )
{
- case PACKET_BLOCK_DIG: HANDLE_CLIENT_READ(HandleClientBlockDig); break;
- case PACKET_BLOCK_PLACE: HANDLE_CLIENT_READ(HandleClientBlockPlace); break;
- case PACKET_CHAT_MESSAGE: HANDLE_CLIENT_READ(HandleClientChatMessage); break;
- case PACKET_CLIENT_STATUSES: HANDLE_CLIENT_READ(HandleClientClientStatuses); break;
- case PACKET_CREATIVE_INVENTORY_ACTION: HANDLE_CLIENT_READ(HandleClientCreativeInventoryAction); break;
- case PACKET_DISCONNECT: HANDLE_CLIENT_READ(HandleClientDisconnect); break;
- case PACKET_ENCRYPTION_KEY_RESPONSE: HANDLE_CLIENT_READ(HandleClientEncryptionKeyResponse); break;
- case PACKET_ENTITY_ACTION: HANDLE_CLIENT_READ(HandleClientEntityAction); break;
- case PACKET_HANDSHAKE: HANDLE_CLIENT_READ(HandleClientHandshake); break;
- case PACKET_KEEPALIVE: HANDLE_CLIENT_READ(HandleClientKeepAlive); break;
- case PACKET_LOCALE_AND_VIEW: HANDLE_CLIENT_READ(HandleClientLocaleAndView); break;
- case PACKET_PING: HANDLE_CLIENT_READ(HandleClientPing); break;
- case PACKET_PLAYER_ABILITIES: HANDLE_CLIENT_READ(HandleClientPlayerAbilities); break;
- case PACKET_PLAYER_ANIMATION: HANDLE_CLIENT_READ(HandleClientAnimation); break;
- case PACKET_PLAYER_LOOK: HANDLE_CLIENT_READ(HandleClientPlayerLook); break;
- case PACKET_PLAYER_ON_GROUND: HANDLE_CLIENT_READ(HandleClientPlayerOnGround); break;
- case PACKET_PLAYER_POSITION: HANDLE_CLIENT_READ(HandleClientPlayerPosition); break;
- case PACKET_PLAYER_POSITION_LOOK: HANDLE_CLIENT_READ(HandleClientPlayerPositionLook); break;
- case PACKET_PLUGIN_MESSAGE: HANDLE_CLIENT_READ(HandleClientPluginMessage); break;
- case PACKET_SLOT_SELECT: HANDLE_CLIENT_READ(HandleClientSlotSelect); break;
- case PACKET_TAB_COMPLETION: HANDLE_CLIENT_READ(HandleClientTabCompletion); break;
- case PACKET_UPDATE_SIGN: HANDLE_CLIENT_READ(HandleClientUpdateSign); break;
- case PACKET_USE_ENTITY: HANDLE_CLIENT_READ(HandleClientUseEntity); break;
- case PACKET_WINDOW_CLICK: HANDLE_CLIENT_READ(HandleClientWindowClick); break;
- case PACKET_WINDOW_CLOSE: HANDLE_CLIENT_READ(HandleClientWindowClose); break;
- default:
+ // Not a complete packet yet
+ break;
+ }
+ UInt32 PacketType, PacketReadSoFar;
+ PacketReadSoFar = m_ClientBuffer.GetReadableSpace();
+ VERIFY(m_ClientBuffer.ReadVarInt(PacketType));
+ PacketReadSoFar -= m_ClientBuffer.GetReadableSpace();
+ Log("Decoding client's packets, there are now %d bytes in the queue; next packet is 0x%0x, %u bytes long", m_ClientBuffer.GetReadableSpace(), PacketType, PacketLen);
+ switch (m_ClientProtocolState)
+ {
+ case -1:
{
- if (m_ClientState == csEncryptedUnderstood)
+ // No initial handshake received yet
+ switch (PacketType)
{
- Log("****************** Unknown packet 0x%02x from the client while encrypted; continuing to relay blind only", PacketType);
- AString Data;
- m_ClientBuffer.ResetRead();
- m_ClientBuffer.ReadAll(Data);
- DataLog(Data.data(), Data.size(), "Current data in the client packet queue: %d bytes", Data.size());
- m_ClientState = csEncryptedUnknown;
- m_ClientBuffer.ResetRead();
- if (m_ServerState == csUnencrypted)
- {
- SERVERSEND(m_ClientBuffer);
- }
- else
- {
- SERVERENCRYPTSEND(m_ClientBuffer);
- }
- return true;
+ case 0x00: HANDLE_CLIENT_READ(HandleClientHandshake()); break;
+ default: HANDLE_CLIENT_READ(HandleClientUnknownPacket(PacketType, PacketLen, PacketReadSoFar)); break;
}
- else
+ break;
+ } // case -1
+
+ case 1:
+ {
+ // Status query
+ switch (PacketType)
{
- Log("Unknown packet 0x%02x from the client while unencrypted; aborting connection", PacketType);
- return false;
+ case 0x00: HANDLE_CLIENT_READ(HandleClientStatusRequest()); break;
+ case 0x01: HANDLE_CLIENT_READ(HandleClientStatusPing()); break;
+ default: HANDLE_CLIENT_READ(HandleClientUnknownPacket(PacketType, PacketLen, PacketReadSoFar)); break;
}
+ break;
}
- } // switch (PacketType)
+
+ case 2:
+ {
+ // Login
+ switch (PacketType)
+ {
+ case 0x00: HANDLE_CLIENT_READ(HandleClientLoginStart()); break;
+ case 0x01: HANDLE_CLIENT_READ(HandleClientLoginEncryptionKeyResponse()); break;
+ default: HANDLE_CLIENT_READ(HandleClientUnknownPacket(PacketType, PacketLen, PacketReadSoFar)); break;
+ }
+ break;
+ }
+
+ case 3:
+ {
+ // Game:
+ switch (PacketType)
+ {
+ case 0x00: HANDLE_CLIENT_READ(HandleClientKeepAlive()); break;
+ case 0x01: HANDLE_CLIENT_READ(HandleClientChatMessage()); break;
+ case 0x02: HANDLE_CLIENT_READ(HandleClientUseEntity()); break;
+ case 0x03: HANDLE_CLIENT_READ(HandleClientPlayerOnGround()); break;
+ case 0x04: HANDLE_CLIENT_READ(HandleClientPlayerPosition()); break;
+ case 0x05: HANDLE_CLIENT_READ(HandleClientPlayerLook()); break;
+ case 0x06: HANDLE_CLIENT_READ(HandleClientPlayerPositionLook()); break;
+ case 0x07: HANDLE_CLIENT_READ(HandleClientBlockDig()); break;
+ case 0x08: HANDLE_CLIENT_READ(HandleClientBlockPlace()); break;
+ case 0x09: HANDLE_CLIENT_READ(HandleClientSlotSelect()); break;
+ case 0x0a: HANDLE_CLIENT_READ(HandleClientAnimation()); break;
+ case 0x0b: HANDLE_CLIENT_READ(HandleClientEntityAction()); break;
+ case 0x0d: HANDLE_CLIENT_READ(HandleClientWindowClose()); break;
+ case 0x0e: HANDLE_CLIENT_READ(HandleClientWindowClick()); break;
+ case 0x10: HANDLE_CLIENT_READ(HandleClientCreativeInventoryAction()); break;
+ case 0x12: HANDLE_CLIENT_READ(HandleClientUpdateSign()); break;
+ case 0x13: HANDLE_CLIENT_READ(HandleClientPlayerAbilities()); break;
+ case 0x14: HANDLE_CLIENT_READ(HandleClientTabCompletion()); break;
+ case 0x15: HANDLE_CLIENT_READ(HandleClientLocaleAndView()); break;
+ case 0x16: HANDLE_CLIENT_READ(HandleClientClientStatuses()); break;
+ case 0x17: HANDLE_CLIENT_READ(HandleClientPluginMessage()); break;
+ default: HANDLE_CLIENT_READ(HandleClientUnknownPacket(PacketType, PacketLen, PacketReadSoFar)); break;
+ }
+ break;
+ } // case 3 - Game
+
+ default:
+ {
+ Log("Receiving server packets while in an unknown protocol state (%d)!", m_ClientProtocolState);
+ HANDLE_CLIENT_READ(HandleClientUnknownPacket(PacketType, PacketLen, PacketReadSoFar));
+ break;
+ }
+ } // switch (m_ProtocolState)
m_ClientBuffer.CommitRead();
- } // while (CanReadBytes(1))
+ } // while (true)
return true;
}
@@ -673,94 +709,128 @@ bool cConnection::DecodeServersPackets(const char * a_Data, int a_Size)
// Client hasn't finished encryption handshake yet, don't send them any data yet
}
- while (m_ServerBuffer.CanReadBytes(1))
+ while (true)
{
- unsigned char PacketType;
- m_ServerBuffer.ReadByte(PacketType);
- Log("Decoding server's packets, there are now %d bytes in the queue; next packet is 0x%02x", m_ServerBuffer.GetReadableSpace(), PacketType);
+ UInt32 PacketLen;
+ if (
+ !m_ServerBuffer.ReadVarInt(PacketLen) ||
+ !m_ServerBuffer.CanReadBytes(PacketLen)
+ )
+ {
+ // Not a complete packet yet
+ break;
+ }
+ UInt32 PacketType, PacketReadSoFar;
+ PacketReadSoFar = m_ServerBuffer.GetReadableSpace();
+ VERIFY(m_ServerBuffer.ReadVarInt(PacketType));
+ PacketReadSoFar -= m_ServerBuffer.GetReadableSpace();
+ Log("Decoding server's packets, there are now %d bytes in the queue; next packet is 0x%0x, %u bytes long", m_ServerBuffer.GetReadableSpace(), PacketType, PacketLen);
LogFlush();
- switch (PacketType)
+ switch (m_ServerProtocolState)
{
- case PACKET_ATTACH_ENTITY: HANDLE_SERVER_READ(HandleServerAttachEntity); break;
- case PACKET_BLOCK_ACTION: HANDLE_SERVER_READ(HandleServerBlockAction); break;
- case PACKET_BLOCK_CHANGE: HANDLE_SERVER_READ(HandleServerBlockChange); break;
- case PACKET_CHANGE_GAME_STATE: HANDLE_SERVER_READ(HandleServerChangeGameState); break;
- case PACKET_CHAT_MESSAGE: HANDLE_SERVER_READ(HandleServerChatMessage); break;
- case PACKET_COLLECT_PICKUP: HANDLE_SERVER_READ(HandleServerCollectPickup); break;
- case PACKET_COMPASS: HANDLE_SERVER_READ(HandleServerCompass); break;
- case PACKET_DESTROY_ENTITIES: HANDLE_SERVER_READ(HandleServerDestroyEntities); break;
- case PACKET_ENCRYPTION_KEY_REQUEST: HANDLE_SERVER_READ(HandleServerEncryptionKeyRequest); break;
- case PACKET_ENCRYPTION_KEY_RESPONSE: HANDLE_SERVER_READ(HandleServerEncryptionKeyResponse); break;
- case PACKET_ENTITY: HANDLE_SERVER_READ(HandleServerEntity); break;
- case PACKET_ENTITY_EQUIPMENT: HANDLE_SERVER_READ(HandleServerEntityEquipment); break;
- case PACKET_ENTITY_HEAD_LOOK: HANDLE_SERVER_READ(HandleServerEntityHeadLook); break;
- case PACKET_ENTITY_LOOK: HANDLE_SERVER_READ(HandleServerEntityLook); break;
- case PACKET_ENTITY_METADATA: HANDLE_SERVER_READ(HandleServerEntityMetadata); break;
- case PACKET_ENTITY_PROPERTIES: HANDLE_SERVER_READ(HandleServerEntityProperties); break;
- case PACKET_ENTITY_RELATIVE_MOVE: HANDLE_SERVER_READ(HandleServerEntityRelativeMove); break;
- case PACKET_ENTITY_RELATIVE_MOVE_LOOK: HANDLE_SERVER_READ(HandleServerEntityRelativeMoveLook); break;
- case PACKET_ENTITY_STATUS: HANDLE_SERVER_READ(HandleServerEntityStatus); break;
- case PACKET_ENTITY_TELEPORT: HANDLE_SERVER_READ(HandleServerEntityTeleport); break;
- case PACKET_ENTITY_VELOCITY: HANDLE_SERVER_READ(HandleServerEntityVelocity); break;
- case PACKET_EXPLOSION: HANDLE_SERVER_READ(HandleServerExplosion); break;
- case PACKET_INCREMENT_STATISTIC: HANDLE_SERVER_READ(HandleServerIncrementStatistic); break;
- case PACKET_KEEPALIVE: HANDLE_SERVER_READ(HandleServerKeepAlive); break;
- case PACKET_KICK: HANDLE_SERVER_READ(HandleServerKick); break;
- case PACKET_LOGIN: HANDLE_SERVER_READ(HandleServerLogin); break;
- case PACKET_MAP_CHUNK: HANDLE_SERVER_READ(HandleServerMapChunk); break;
- case PACKET_MAP_CHUNK_BULK: HANDLE_SERVER_READ(HandleServerMapChunkBulk); break;
- case PACKET_MULTI_BLOCK_CHANGE: HANDLE_SERVER_READ(HandleServerMultiBlockChange); break;
- case PACKET_NAMED_SOUND_EFFECT: HANDLE_SERVER_READ(HandleServerNamedSoundEffect); break;
- case PACKET_PLAYER_ABILITIES: HANDLE_SERVER_READ(HandleServerPlayerAbilities); break;
- case PACKET_PLAYER_ANIMATION: HANDLE_SERVER_READ(HandleServerPlayerAnimation); break;
- case PACKET_PLAYER_LIST_ITEM: HANDLE_SERVER_READ(HandleServerPlayerListItem); break;
- case PACKET_PLAYER_POSITION_LOOK: HANDLE_SERVER_READ(HandleServerPlayerPositionLook); break;
- case PACKET_PLUGIN_MESSAGE: HANDLE_SERVER_READ(HandleServerPluginMessage); break;
- case PACKET_SET_EXPERIENCE: HANDLE_SERVER_READ(HandleServerSetExperience); break;
- case PACKET_SET_SLOT: HANDLE_SERVER_READ(HandleServerSetSlot); break;
- case PACKET_SLOT_SELECT: HANDLE_SERVER_READ(HandleServerSlotSelect); break;
- case PACKET_SOUND_EFFECT: HANDLE_SERVER_READ(HandleServerSoundEffect); break;
- case PACKET_SPAWN_MOB: HANDLE_SERVER_READ(HandleServerSpawnMob); break;
- case PACKET_SPAWN_NAMED_ENTITY: HANDLE_SERVER_READ(HandleServerSpawnNamedEntity); break;
- case PACKET_SPAWN_OBJECT_VEHICLE: HANDLE_SERVER_READ(HandleServerSpawnObjectVehicle); break;
- case PACKET_SPAWN_PAINTING: HANDLE_SERVER_READ(HandleServerSpawnPainting); break;
- case PACKET_SPAWN_PICKUP: HANDLE_SERVER_READ(HandleServerSpawnPickup); break;
- case PACKET_TAB_COMPLETION: HANDLE_SERVER_READ(HandleServerTabCompletion); break;
- case PACKET_TIME_UPDATE: HANDLE_SERVER_READ(HandleServerTimeUpdate); break;
- case PACKET_UPDATE_HEALTH: HANDLE_SERVER_READ(HandleServerUpdateHealth); break;
- case PACKET_UPDATE_SIGN: HANDLE_SERVER_READ(HandleServerUpdateSign); break;
- case PACKET_UPDATE_TILE_ENTITY: HANDLE_SERVER_READ(HandleServerUpdateTileEntity); break;
- case PACKET_WINDOW_CLOSE: HANDLE_SERVER_READ(HandleServerWindowClose); break;
- case PACKET_WINDOW_CONTENTS: HANDLE_SERVER_READ(HandleServerWindowContents); break;
- case PACKET_WINDOW_OPEN: HANDLE_SERVER_READ(HandleServerWindowOpen); break;
- default:
+ case -1:
+ {
+ Log("Receiving data from the server without an initial handshake message!");
+ HANDLE_SERVER_READ(HandleServerUnknownPacket(PacketType, PacketLen, PacketReadSoFar));
+ break;
+ }
+
+ case 1:
{
- if (m_ServerState == csEncryptedUnderstood)
+ // Status query:
+ switch (PacketType)
{
- Log("********************** Unknown packet 0x%02x from the server while encrypted; continuing to relay blind only", PacketType);
- AString Data;
- m_ServerBuffer.ResetRead();
- m_ServerBuffer.ReadAll(Data);
- DataLog(Data.data(), Data.size(), "Current data in the server packet queue: %d bytes", Data.size());
- m_ServerState = csEncryptedUnknown;
- m_ServerBuffer.ResetRead();
- if (m_ClientState == csUnencrypted)
- {
- CLIENTSEND(m_ServerBuffer);
- }
- else
- {
- CLIENTENCRYPTSEND(m_ServerBuffer);
- }
- return true;
+ case 0x00: HANDLE_SERVER_READ(HandleServerStatusResponse()); break;
+ case 0x01: HANDLE_SERVER_READ(HandleServerStatusPing()); break;
+ default: HANDLE_SERVER_READ(HandleServerUnknownPacket(PacketType, PacketLen, PacketReadSoFar)); break;
}
- else
+ break;
+ }
+
+ case 2:
+ {
+ // Login:
+ switch (PacketType)
{
- Log("Unknown packet 0x%02x from the server while unencrypted; aborting connection", PacketType);
- return false;
+ case 0x00: HANDLE_SERVER_READ(HandleServerLoginDisconnect()); break;
+ case 0x01: HANDLE_SERVER_READ(HandleServerLoginEncryptionKeyRequest()); break;
+ case 0x02: HANDLE_SERVER_READ(HandleServerLoginSuccess()); break;
+ default: HANDLE_SERVER_READ(HandleServerUnknownPacket(PacketType, PacketLen, PacketReadSoFar)); break;
}
+ break;
+ }
+
+ case 3:
+ {
+ // Game:
+ switch (PacketType)
+ {
+ case 0x00: HANDLE_SERVER_READ(HandleServerKeepAlive()); break;
+ case 0x01: HANDLE_SERVER_READ(HandleServerJoinGame()); break;
+ case 0x02: HANDLE_SERVER_READ(HandleServerChatMessage()); break;
+ case 0x03: HANDLE_SERVER_READ(HandleServerTimeUpdate()); break;
+ case 0x04: HANDLE_SERVER_READ(HandleServerEntityEquipment()); break;
+ case 0x05: HANDLE_SERVER_READ(HandleServerCompass()); break;
+ case 0x06: HANDLE_SERVER_READ(HandleServerUpdateHealth()); break;
+ case 0x07: HANDLE_SERVER_READ(HandleServerRespawn()); break;
+ case 0x08: HANDLE_SERVER_READ(HandleServerPlayerPositionLook()); break;
+ case 0x09: HANDLE_SERVER_READ(HandleServerSlotSelect()); break;
+ case 0x0a: HANDLE_SERVER_READ(HandleServerUseBed()); break;
+ case 0x0b: HANDLE_SERVER_READ(HandleServerPlayerAnimation()); break;
+ case 0x0c: HANDLE_SERVER_READ(HandleServerSpawnNamedEntity()); break;
+ case 0x0d: HANDLE_SERVER_READ(HandleServerCollectPickup()); break;
+ case 0x0e: HANDLE_SERVER_READ(HandleServerSpawnObjectVehicle()); break;
+ case 0x0f: HANDLE_SERVER_READ(HandleServerSpawnMob()); break;
+ case 0x10: HANDLE_SERVER_READ(HandleServerSpawnPainting()); break;
+ case 0x11: HANDLE_SERVER_READ(HandleServerSpawnExperienceOrbs()); break;
+ case 0x12: HANDLE_SERVER_READ(HandleServerEntityVelocity()); break;
+ case 0x13: HANDLE_SERVER_READ(HandleServerDestroyEntities()); break;
+ case 0x14: HANDLE_SERVER_READ(HandleServerEntity()); break;
+ case 0x15: HANDLE_SERVER_READ(HandleServerEntityRelativeMove()); break;
+ case 0x16: HANDLE_SERVER_READ(HandleServerEntityLook()); break;
+ case 0x17: HANDLE_SERVER_READ(HandleServerEntityRelativeMoveLook()); break;
+ case 0x18: HANDLE_SERVER_READ(HandleServerEntityTeleport()); break;
+ case 0x19: HANDLE_SERVER_READ(HandleServerEntityHeadLook()); break;
+ case 0x1a: HANDLE_SERVER_READ(HandleServerEntityStatus()); break;
+ case 0x1b: HANDLE_SERVER_READ(HandleServerAttachEntity()); break;
+ case 0x1c: HANDLE_SERVER_READ(HandleServerEntityMetadata()); break;
+ case 0x1f: HANDLE_SERVER_READ(HandleServerSetExperience()); break;
+ case 0x20: HANDLE_SERVER_READ(HandleServerEntityProperties()); break;
+ case 0x21: HANDLE_SERVER_READ(HandleServerMapChunk()); break;
+ case 0x22: HANDLE_SERVER_READ(HandleServerMultiBlockChange()); break;
+ case 0x23: HANDLE_SERVER_READ(HandleServerBlockChange()); break;
+ case 0x24: HANDLE_SERVER_READ(HandleServerBlockAction()); break;
+ case 0x26: HANDLE_SERVER_READ(HandleServerMapChunkBulk()); break;
+ case 0x27: HANDLE_SERVER_READ(HandleServerExplosion()); break;
+ case 0x28: HANDLE_SERVER_READ(HandleServerSoundEffect()); break;
+ case 0x29: HANDLE_SERVER_READ(HandleServerNamedSoundEffect()); break;
+ case 0x2b: HANDLE_SERVER_READ(HandleServerChangeGameState()); break;
+ case 0x2d: HANDLE_SERVER_READ(HandleServerWindowOpen()); break;
+ case 0x2e: HANDLE_SERVER_READ(HandleServerWindowClose()); break;
+ case 0x2f: HANDLE_SERVER_READ(HandleServerSetSlot()); break;
+ case 0x30: HANDLE_SERVER_READ(HandleServerWindowContents()); break;
+ case 0x33: HANDLE_SERVER_READ(HandleServerUpdateSign()); break;
+ case 0x35: HANDLE_SERVER_READ(HandleServerUpdateTileEntity()); break;
+ case 0x37: HANDLE_SERVER_READ(HandleServerStatistics()); break;
+ case 0x38: HANDLE_SERVER_READ(HandleServerPlayerListItem()); break;
+ case 0x39: HANDLE_SERVER_READ(HandleServerPlayerAbilities()); break;
+ case 0x3a: HANDLE_SERVER_READ(HandleServerTabCompletion()); break;
+ case 0x3f: HANDLE_SERVER_READ(HandleServerPluginMessage()); break;
+ case 0x40: HANDLE_SERVER_READ(HandleServerKick()); break;
+ default: HANDLE_SERVER_READ(HandleServerUnknownPacket(PacketType, PacketLen, PacketReadSoFar)); break;
+ } // switch (PacketType)
+ break;
+ } // case 3 - Game
+
+ // TODO: Move this elsewhere
+ default:
+ {
+ Log("Received a packet from the server while in an unknown state: %d", m_ServerProtocolState);
+ HANDLE_SERVER_READ(HandleServerUnknownPacket(PacketType, PacketLen, PacketReadSoFar));
+ break;
}
- } // switch (PacketType)
+ } // switch (m_ProtocolState)
+
m_ServerBuffer.CommitRead();
} // while (CanReadBytes(1))
return true;
@@ -770,6 +840,76 @@ bool cConnection::DecodeServersPackets(const char * a_Data, int a_Size)
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// packet handling, client-side, initial handshake:
+
+bool cConnection::HandleClientHandshake(void)
+{
+ // Read the packet from the client:
+ HANDLE_CLIENT_PACKET_READ(ReadVarInt, UInt32, ProtocolVersion);
+ HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, ServerHost);
+ HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, ServerPort);
+ HANDLE_CLIENT_PACKET_READ(ReadVarInt, UInt32, NextState);
+ m_ClientBuffer.CommitRead();
+
+ Log("Received an initial handshake packet from the client:");
+ Log(" ProtocolVersion = %u", ProtocolVersion);
+ Log(" ServerHost = \"%s\"", ServerHost.c_str());
+ Log(" ServerPort = %d", ServerPort);
+ Log(" NextState = %u", NextState);
+
+ // Send the same packet to the server, but with our port:
+ cByteBuffer Packet(512);
+ Packet.WriteVarInt(0); // Packet type - initial handshake
+ Packet.WriteVarInt(ProtocolVersion);
+ Packet.WriteVarUTF8String(ServerHost);
+ Packet.WriteBEShort(m_Server.GetConnectPort());
+ Packet.WriteVarInt(NextState);
+ AString Pkt;
+ Packet.ReadAll(Pkt);
+ cByteBuffer ToServer(512);
+ ToServer.WriteVarUTF8String(Pkt);
+ SERVERSEND(ToServer);
+
+ m_ClientProtocolState = (int)NextState;
+ m_ServerProtocolState = (int)NextState;
+
+ return true;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// packet handling, client-side, login:
+
+bool cConnection::HandleClientLoginEncryptionKeyResponse(void)
+{
+ Log("Client: Unexpected packet: encryption key response");
+ return true;
+}
+
+
+
+
+
+bool cConnection::HandleClientLoginStart(void)
+{
+ HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, UserName);
+ Log("Received a login start packet from the client:");
+ Log(" Username = \"%s\"", UserName.c_str());
+ COPY_TO_SERVER();
+ return true;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// packet handling, client-side, game:
+
bool cConnection::HandleClientAnimation(void)
{
HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, EntityID);
@@ -833,7 +973,7 @@ bool cConnection::HandleClientBlockPlace(void)
bool cConnection::HandleClientChatMessage(void)
{
- HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Message);
+ HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, Message);
Log("Received a PACKET_CHAT_MESSAGE from the client:");
Log(" Message = \"%s\"", Message.c_str());
COPY_TO_SERVER();
@@ -879,7 +1019,7 @@ bool cConnection::HandleClientCreativeInventoryAction(void)
bool cConnection::HandleClientDisconnect(void)
{
- HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Reason);
+ HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, Reason);
Log("Received a PACKET_DISCONNECT from the client:");
Log(" Reason = \"%s\"", Reason.c_str());
COPY_TO_SERVER();
@@ -890,42 +1030,15 @@ bool cConnection::HandleClientDisconnect(void)
-bool cConnection::HandleClientEncryptionKeyResponse(void)
-{
- HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, EncKeyLength);
- AString EncKey;
- if (!m_ClientBuffer.ReadString(EncKey, EncKeyLength))
- {
- return true;
- }
- HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, EncNonceLength);
- AString EncNonce;
- if (!m_ClientBuffer.ReadString(EncNonce, EncNonceLength))
- {
- return true;
- }
- if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN))
- {
- Log("Client: Too long encryption params");
- return true;
- }
- StartClientEncryption(EncKey, EncNonce);
- return true;
-}
-
-
-
-
-
bool cConnection::HandleClientEntityAction(void)
{
HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, PlayerID);
HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, ActionType);
- HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, UnknownHorseVal);
+ HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, HorseJumpBoost);
Log("Received a PACKET_ENTITY_ACTION from the client:");
Log(" PlayerID = %d", PlayerID);
Log(" ActionType = %d", ActionType);
- Log(" UnknownHorseVal = %d (0x%08x)", UnknownHorseVal, UnknownHorseVal);
+ Log(" HorseJumpBoost = %d (0x%08x)", HorseJumpBoost, HorseJumpBoost);
COPY_TO_SERVER();
return true;
}
@@ -934,37 +1047,6 @@ bool cConnection::HandleClientEntityAction(void)
-bool cConnection::HandleClientHandshake(void)
-{
- // Read the packet from the client:
- HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, ProtocolVersion);
- HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Username);
- HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, ServerHost);
- HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, ServerPort);
- m_ClientBuffer.CommitRead();
-
- Log("Received a PACKET_HANDSHAKE from the client:");
- Log(" ProtocolVersion = %d", ProtocolVersion);
- Log(" Username = \"%s\"", Username.c_str());
- Log(" ServerHost = \"%s\"", ServerHost.c_str());
- Log(" ServerPort = %d", ServerPort);
-
- // Send the same packet to the server, but with our port:
- cByteBuffer ToServer(512);
- ToServer.WriteByte (PACKET_HANDSHAKE);
- ToServer.WriteByte (ProtocolVersion);
- ToServer.WriteBEUTF16String16(Username);
- ToServer.WriteBEUTF16String16(ServerHost);
- ToServer.WriteBEInt (m_Server.GetConnectPort());
- SERVERSEND(ToServer);
-
- return true;
-}
-
-
-
-
-
bool cConnection::HandleClientKeepAlive(void)
{
HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, ID);
@@ -979,11 +1061,12 @@ bool cConnection::HandleClientKeepAlive(void)
bool cConnection::HandleClientLocaleAndView(void)
{
- HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Locale);
- HANDLE_CLIENT_PACKET_READ(ReadChar, char, ViewDistance);
- HANDLE_CLIENT_PACKET_READ(ReadChar, char, ChatFlags);
- HANDLE_CLIENT_PACKET_READ(ReadChar, char, Difficulty);
- HANDLE_CLIENT_PACKET_READ(ReadChar, char, ShowCape);
+ HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, Locale);
+ HANDLE_CLIENT_PACKET_READ(ReadChar, char, ViewDistance);
+ HANDLE_CLIENT_PACKET_READ(ReadChar, char, ChatFlags);
+ HANDLE_CLIENT_PACKET_READ(ReadChar, char, Unused);
+ HANDLE_CLIENT_PACKET_READ(ReadChar, char, Difficulty);
+ HANDLE_CLIENT_PACKET_READ(ReadChar, char, ShowCape);
Log("Received a PACKET_LOCALE_AND_VIEW from the client");
COPY_TO_SERVER();
return true;
@@ -1008,7 +1091,7 @@ bool cConnection::HandleClientPing(void)
bool cConnection::HandleClientPlayerAbilities(void)
{
- HANDLE_CLIENT_PACKET_READ(ReadChar, char, Flags);
+ HANDLE_CLIENT_PACKET_READ(ReadChar, char, Flags);
HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, FlyingSpeed);
HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, WalkingSpeed);
Log("Receives a PACKET_PLAYER_ABILITIES from the client:");
@@ -1039,7 +1122,7 @@ bool cConnection::HandleClientPlayerLook(void)
bool cConnection::HandleClientPlayerOnGround(void)
{
- HANDLE_CLIENT_PACKET_READ(ReadChar, char, OnGround);
+ HANDLE_CLIENT_PACKET_READ(ReadChar, char, OnGround);
Log("Received a PACKET_PLAYER_ON_GROUND from the client");
COPY_TO_SERVER();
return true;
@@ -1052,8 +1135,8 @@ bool cConnection::HandleClientPlayerOnGround(void)
bool cConnection::HandleClientPlayerPosition(void)
{
HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosX);
- HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, Stance);
HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosY);
+ HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, Stance);
HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosZ);
HANDLE_CLIENT_PACKET_READ(ReadChar, char, IsOnGround);
Log("Received a PACKET_PLAYER_POSITION from the client");
@@ -1071,8 +1154,8 @@ bool cConnection::HandleClientPlayerPosition(void)
bool cConnection::HandleClientPlayerPositionLook(void)
{
HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosX);
- HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, Stance);
HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosY);
+ HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, Stance);
HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosZ);
HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Yaw);
HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Pitch);
@@ -1080,7 +1163,7 @@ bool cConnection::HandleClientPlayerPositionLook(void)
Log("Received a PACKET_PLAYER_POSITION_LOOK from the client");
Log(" Pos = {%.03f, %.03f, %.03f}", PosX, PosY, PosZ);
Log(" Stance = %.03f", Stance);
- Log(" Y, P = %.03f, %.03f", Yaw, Pitch);
+ Log(" Yaw, Pitch = <%.03f, %.03f>", Yaw, Pitch);
Log(" IsOnGround = %s", IsOnGround ? "true" : "false");
COPY_TO_SERVER();
@@ -1093,7 +1176,7 @@ bool cConnection::HandleClientPlayerPositionLook(void)
bool cConnection::HandleClientPluginMessage(void)
{
- HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, ChannelName);
+ HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, ChannelName);
HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, Length);
AString Data;
if (!m_ClientBuffer.ReadString(Data, Length))
@@ -1124,9 +1207,33 @@ bool cConnection::HandleClientSlotSelect(void)
+bool cConnection::HandleClientStatusPing(void)
+{
+ HANDLE_CLIENT_PACKET_READ(ReadBEInt64, Int64, Time);
+ Log("Received the status ping packet from the client:");
+ Log(" Time = %lld", Time);
+ COPY_TO_SERVER();
+ return true;
+}
+
+
+
+
+
+bool cConnection::HandleClientStatusRequest(void)
+{
+ Log("Received the status request packet from the client");
+ COPY_TO_SERVER();
+ return true;
+}
+
+
+
+
+
bool cConnection::HandleClientTabCompletion(void)
{
- HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Query);
+ HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, Query);
Log("Received a PACKET_TAB_COMPLETION query from the client");
Log(" Query = \"%s\"", Query.c_str());
COPY_TO_SERVER();
@@ -1139,13 +1246,13 @@ bool cConnection::HandleClientTabCompletion(void)
bool cConnection::HandleClientUpdateSign(void)
{
- HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockX);
- HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, BlockY);
- HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockZ);
- HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Line1);
- HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Line2);
- HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Line3);
- HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Line4);
+ HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockX);
+ HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, BlockY);
+ HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockZ);
+ HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, Line1);
+ HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, Line2);
+ HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, Line3);
+ HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, Line4);
Log("Received a PACKET_UPDATE_SIGN from the client:");
Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ);
Log(" Lines = \"%s\", \"%s\", \"%s\", \"%s\"", Line1.c_str(), Line2.c_str(), Line3.c_str(), Line4.c_str());
@@ -1159,11 +1266,9 @@ bool cConnection::HandleClientUpdateSign(void)
bool cConnection::HandleClientUseEntity(void)
{
- HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, PlayerID);
HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, EntityID);
HANDLE_CLIENT_PACKET_READ(ReadChar, char, MouseButton);
Log("Received a PACKET_USE_ENTITY from the client:");
- Log(" PlayerID = %d", PlayerID);
Log(" EntityID = %d", EntityID);
Log(" MouseButton = %d", MouseButton);
COPY_TO_SERVER();
@@ -1178,9 +1283,9 @@ bool cConnection::HandleClientWindowClick(void)
{
HANDLE_CLIENT_PACKET_READ(ReadChar, char, WindowID);
HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, SlotNum);
- HANDLE_CLIENT_PACKET_READ(ReadChar, char, IsRightClick);
+ HANDLE_CLIENT_PACKET_READ(ReadChar, char, Button);
HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, TransactionID);
- HANDLE_CLIENT_PACKET_READ(ReadChar, char, IsShiftClick);
+ HANDLE_CLIENT_PACKET_READ(ReadChar, char, Mode);
AString Item;
if (!ParseSlot(m_ClientBuffer, Item))
{
@@ -1189,7 +1294,7 @@ bool cConnection::HandleClientWindowClick(void)
Log("Received a PACKET_WINDOW_CLICK from the client");
Log(" WindowID = %d", WindowID);
Log(" SlotNum = %d", SlotNum);
- Log(" IsRclk = %d, IsShift = %d", IsRightClick, IsShiftClick);
+ Log(" Button = %d, Mode = %d", Button, Mode);
Log(" TransactionID = 0x%x", TransactionID);
Log(" ClickedItem = %s", Item.c_str());
COPY_TO_SERVER();
@@ -1213,6 +1318,100 @@ bool cConnection::HandleClientWindowClose(void)
+bool cConnection::HandleClientUnknownPacket(UInt32 a_PacketType, UInt32 a_PacketLen, UInt32 a_PacketReadSoFar)
+{
+ AString Data;
+ if (!m_ClientBuffer.ReadString(Data, a_PacketLen - a_PacketReadSoFar))
+ {
+ return false;
+ }
+ DataLog(Data.data(), Data.size(), "****************** Unknown packet 0x%x from the client; relaying and ignoring", a_PacketType);
+ COPY_TO_SERVER();
+ return true;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// packet handling, server-side, login:
+
+bool cConnection::HandleServerLoginDisconnect(void)
+{
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Reason);
+ Log("Received a login-disconnect packet from the server:");
+ Log(" Reason = \"%s\"", Reason.c_str());
+ COPY_TO_SERVER();
+ return true;
+}
+
+
+
+
+
+bool cConnection::HandleServerLoginEncryptionKeyRequest(void)
+{
+ // Read the packet from the server:
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, ServerID);
+ HANDLE_SERVER_PACKET_READ(ReadBEShort, short, PublicKeyLength);
+ AString PublicKey;
+ if (!m_ServerBuffer.ReadString(PublicKey, PublicKeyLength))
+ {
+ return false;
+ }
+ HANDLE_SERVER_PACKET_READ(ReadBEShort, short, NonceLength);
+ AString Nonce;
+ if (!m_ServerBuffer.ReadString(Nonce, NonceLength))
+ {
+ return false;
+ }
+ Log("Got PACKET_ENCRYPTION_KEY_REQUEST from the SERVER:");
+ Log(" ServerID = %s", ServerID.c_str());
+
+ // Reply to the server:
+ SendEncryptionKeyResponse(PublicKey, Nonce);
+
+ // Do not send to client - we want the client connection open
+ return true;
+}
+
+
+
+
+
+bool cConnection::HandleServerLoginSuccess(void)
+{
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, UUID);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Username);
+ Log("Received a login success packet from the server:");
+ Log(" UUID = \"%s\"", UUID.c_str());
+ Log(" Username = \"%s\"", Username.c_str());
+
+ Log("Server is now in protocol state Game.");
+ m_ServerProtocolState = 3;
+
+ if (m_IsServerEncrypted)
+ {
+ Log("Server communication is now encrypted");
+ m_ServerState = csEncryptedUnderstood;
+ DataLog(m_ServerEncryptionBuffer.data(), m_ServerEncryptionBuffer.size(), "Sending the queued data to server (%u bytes):", m_ServerEncryptionBuffer.size());
+ SERVERENCRYPTSEND(m_ServerEncryptionBuffer.data(), m_ServerEncryptionBuffer.size());
+ m_ServerEncryptionBuffer.clear();
+ }
+ COPY_TO_CLIENT();
+ Log("Client is now in protocol state Game.");
+ m_ClientProtocolState = 3;
+ return true;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// packet handling, server-side, game:
+
bool cConnection::HandleServerAttachEntity(void)
{
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID);
@@ -1232,16 +1431,16 @@ bool cConnection::HandleServerAttachEntity(void)
bool cConnection::HandleServerBlockAction(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX);
- HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockY);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ);
- HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Byte1);
- HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Byte2);
- HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockID);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX);
+ HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockY);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ);
+ HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Byte1);
+ HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Byte2);
+ HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, BlockID);
Log("Received a PACKET_BLOCK_ACTION from the server:");
Log(" Pos = {%d, %d, %d}", BlockX, BlockY, BlockZ);
Log(" Bytes = (%d, %d) == (0x%x, 0x%x)", Byte1, Byte2, Byte1, Byte2);
- Log(" BlockID = %d", BlockID);
+ Log(" BlockID = %u", BlockID);
COPY_TO_CLIENT();
return true;
}
@@ -1252,11 +1451,11 @@ bool cConnection::HandleServerBlockAction(void)
bool cConnection::HandleServerBlockChange(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX);
- HANDLE_SERVER_PACKET_READ(ReadByte, Byte, BlockY);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ);
- HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockType);
- HANDLE_SERVER_PACKET_READ(ReadChar, char, BlockMeta);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX);
+ HANDLE_SERVER_PACKET_READ(ReadByte, Byte, BlockY);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ);
+ HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, BlockType);
+ HANDLE_SERVER_PACKET_READ(ReadChar, char, BlockMeta);
Log("Received a PACKET_BLOCK_CHANGE from the server");
COPY_TO_CLIENT();
return true;
@@ -1283,7 +1482,7 @@ bool cConnection::HandleServerChangeGameState(void)
bool cConnection::HandleServerChatMessage(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Message);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Message);
Log("Received a PACKET_CHAT_MESSAGE from the server:");
Log(" Message = \"%s\"", Message.c_str());
COPY_TO_CLIENT();
@@ -1341,48 +1540,6 @@ bool cConnection::HandleServerDestroyEntities(void)
-bool cConnection::HandleServerEncryptionKeyRequest(void)
-{
- // Read the packet from the server:
- HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, ServerID);
- HANDLE_SERVER_PACKET_READ(ReadBEShort, short, PublicKeyLength);
- AString PublicKey;
- if (!m_ServerBuffer.ReadString(PublicKey, PublicKeyLength))
- {
- return false;
- }
- HANDLE_SERVER_PACKET_READ(ReadBEShort, short, NonceLength);
- AString Nonce;
- if (!m_ServerBuffer.ReadString(Nonce, NonceLength))
- {
- return false;
- }
- Log("Got PACKET_ENCRYPTION_KEY_REQUEST from the SERVER:");
- Log(" ServerID = %s", ServerID.c_str());
-
- // Reply to the server:
- SendEncryptionKeyResponse(PublicKey, Nonce);
-
- // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD to the client, using our own key:
- Log("Sending PACKET_ENCRYPTION_KEY_REQUEST to the CLIENT");
- AString key;
- StringSink sink(key); // GCC won't allow inline instantiation in the following line, damned temporary refs
- m_Server.GetPublicKey().Save(sink);
- cByteBuffer ToClient(512);
- ToClient.WriteByte (PACKET_ENCRYPTION_KEY_REQUEST);
- ToClient.WriteBEUTF16String16(ServerID);
- ToClient.WriteBEShort ((short)key.size());
- ToClient.WriteBuf (key.data(), key.size());
- ToClient.WriteBEShort (4);
- ToClient.WriteBEInt (m_Nonce); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
- CLIENTSEND(ToClient);
- return true;
-}
-
-
-
-
-
bool cConnection::HandleServerEntity(void)
{
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID);
@@ -1481,20 +1638,18 @@ bool cConnection::HandleServerEntityProperties(void)
for (int i = 0; i < Count; i++)
{
- HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Key);
- HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, Value);
- Log(" \"%s\" = %f", Key.c_str(), Value);
- } // for i
-
- HANDLE_SERVER_PACKET_READ(ReadBEShort, short, ListLength);
- Log(" ListLength = %d", ListLength);
- for (int i = 0; i < ListLength; i++)
- {
- HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, UUIDHi);
- HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, UUIDLo);
- HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, DblVal);
- HANDLE_SERVER_PACKET_READ(ReadByte, Byte, ByteVal);
- Log(" [%d] = {0x%08llx%08llx, %f, %i}", i, UUIDHi, UUIDLo, DblVal, ByteVal);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Key);
+ HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, Value);
+ HANDLE_SERVER_PACKET_READ(ReadBEShort, short, ListLength);
+ Log(" \"%s\" = %f; %d modifiers", Key.c_str(), Value, ListLength);
+ for (short j = 0; j < ListLength; j++)
+ {
+ HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, UUIDHi);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, UUIDLo);
+ HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, DblVal);
+ HANDLE_SERVER_PACKET_READ(ReadByte, Byte, ByteVal);
+ Log(" [%d] = {0x%08llx%08llx, %f, %u}", j, UUIDHi, UUIDLo, DblVal, ByteVal);
+ }
} // for i
COPY_TO_CLIENT();
return true;
@@ -1559,7 +1714,7 @@ bool cConnection::HandleServerEntityStatus(void)
bool cConnection::HandleServerEntityTeleport(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID);
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, AbsX);
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, AbsY);
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, AbsZ);
@@ -1567,7 +1722,7 @@ bool cConnection::HandleServerEntityTeleport(void)
HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch);
Log("Received a PACKET_ENTITY_TELEPORT from the server:");
Log(" EntityID = %d", EntityID);
- Log(" Pos = (%d, %d, %d) ~ {%.02f, %.02f, %.02f}", AbsX, AbsY, AbsZ, (double)AbsX / 32, (double)AbsY / 32, (double)AbsZ / 32);
+ Log(" Pos = %s", PrintableAbsIntTriplet(AbsX, AbsY, AbsZ).c_str());
Log(" Yaw = %d", Yaw);
Log(" Pitch = %d", Pitch);
COPY_TO_CLIENT();
@@ -1597,11 +1752,11 @@ bool cConnection::HandleServerEntityVelocity(void)
bool cConnection::HandleServerExplosion(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosX);
- HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosY);
- HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosZ);
- HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Force);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, NumRecords);
+ HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PosX);
+ HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PosY);
+ HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PosZ);
+ HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Force);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, NumRecords);
struct sCoords
{
int x, y, z;
@@ -1654,12 +1809,22 @@ bool cConnection::HandleServerIncrementStatistic(void)
-bool cConnection::HandleServerKeepAlive(void)
+bool cConnection::HandleServerJoinGame(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PingID);
- Log("Received a PACKET_KEEP_ALIVE from the server:");
- Log(" ID = %d", PingID);
- COPY_TO_CLIENT()
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID);
+ HANDLE_SERVER_PACKET_READ(ReadChar, char, GameMode);
+ HANDLE_SERVER_PACKET_READ(ReadChar, char, Dimension);
+ HANDLE_SERVER_PACKET_READ(ReadChar, char, Difficulty);
+ HANDLE_SERVER_PACKET_READ(ReadChar, char, MaxPlayers);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, LevelType);
+ Log("Received a PACKET_LOGIN from the server:");
+ Log(" EntityID = %d", EntityID);
+ Log(" GameMode = %d", GameMode);
+ Log(" Dimension = %d", Dimension);
+ Log(" Difficulty = %d", Difficulty);
+ Log(" MaxPlayers = %d", MaxPlayers);
+ Log(" LevelType = \"%s\"", LevelType.c_str());
+ COPY_TO_CLIENT();
return true;
}
@@ -1667,19 +1832,12 @@ bool cConnection::HandleServerKeepAlive(void)
-bool cConnection::HandleServerEncryptionKeyResponse(void)
+bool cConnection::HandleServerKeepAlive(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Lengths);
- if (Lengths != 0)
- {
- Log("Lengths are not zero!");
- return true;
- }
- Log("Server communication is now encrypted");
- m_ServerState = csEncryptedUnderstood;
- DataLog(m_ServerEncryptionBuffer.data(), m_ServerEncryptionBuffer.size(), "Sending the queued data to server (%u bytes):", m_ServerEncryptionBuffer.size());
- SERVERENCRYPTSEND(m_ServerEncryptionBuffer.data(), m_ServerEncryptionBuffer.size());
- m_ServerEncryptionBuffer.clear();
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PingID);
+ Log("Received a PACKET_KEEP_ALIVE from the server:");
+ Log(" ID = %d", PingID);
+ COPY_TO_CLIENT()
return true;
}
@@ -1689,7 +1847,7 @@ bool cConnection::HandleServerEncryptionKeyResponse(void)
bool cConnection::HandleServerKick(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Reason);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Reason);
Log("Received PACKET_KICK from the SERVER:");
if (m_HasClientPinged)
{
@@ -1759,31 +1917,6 @@ bool cConnection::HandleServerKick(void)
-bool cConnection::HandleServerLogin(void)
-{
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID);
- HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, LevelType);
- HANDLE_SERVER_PACKET_READ(ReadChar, char, GameMode);
- HANDLE_SERVER_PACKET_READ(ReadChar, char, Dimension);
- HANDLE_SERVER_PACKET_READ(ReadChar, char, Difficulty);
- HANDLE_SERVER_PACKET_READ(ReadChar, char, Unused);
- HANDLE_SERVER_PACKET_READ(ReadChar, char, MaxPlayers);
- Log("Received a PACKET_LOGIN from the server:");
- Log(" EntityID = %d", EntityID);
- Log(" LevelType = \"%s\"", LevelType.c_str());
- Log(" GameMode = %d", GameMode);
- Log(" Dimension = %d", Dimension);
- Log(" Difficulty = %d", Difficulty);
- Log(" Unused = %d", Unused);
- Log(" MaxPlayers = %d", MaxPlayers);
- COPY_TO_CLIENT();
- return true;
-}
-
-
-
-
-
bool cConnection::HandleServerMapChunk(void)
{
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkX);
@@ -1894,15 +2027,15 @@ bool cConnection::HandleServerMultiBlockChange(void)
bool cConnection::HandleServerNamedSoundEffect(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, SoundName);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ);
- HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Volume);
- HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, SoundName);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ);
+ HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Volume);
+ HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch);
Log("Received a PACKET_NAMED_SOUND_EFFECT from the server:");
Log(" SoundName = \"%s\"", SoundName.c_str());
- Log(" Pos = (%d, %d, %d) ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 8, PosY / 8, PosZ / 8);
+ Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ, 8).c_str());
Log(" Volume = %f", Volume);
Log(" Pitch = %d", Pitch);
COPY_TO_CLIENT();
@@ -1932,10 +2065,10 @@ bool cConnection::HandleServerPlayerAbilities(void)
bool cConnection::HandleServerPlayerAnimation(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PlayerID);
- HANDLE_SERVER_PACKET_READ(ReadByte, Byte, AnimationID);
+ HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, PlayerID);
+ HANDLE_SERVER_PACKET_READ(ReadByte, Byte, AnimationID);
Log("Received a PACKET_PLAYER_ANIMATION from the server:");
- Log(" PlayerID: %d (0x%x)", PlayerID, PlayerID);
+ Log(" PlayerID: %u (0x%x)", PlayerID, PlayerID);
Log(" Animation: %d", AnimationID);
COPY_TO_CLIENT();
return true;
@@ -1947,7 +2080,7 @@ bool cConnection::HandleServerPlayerAnimation(void)
bool cConnection::HandleServerPlayerListItem(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, PlayerName);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, PlayerName);
HANDLE_SERVER_PACKET_READ(ReadChar, char, IsOnline);
HANDLE_SERVER_PACKET_READ(ReadBEShort, short, Ping);
Log("Received a PACKET_PLAYERLIST_ITEM from the server:");
@@ -1964,7 +2097,6 @@ bool cConnection::HandleServerPlayerListItem(void)
bool cConnection::HandleServerPlayerPositionLook(void)
{
HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosX);
- HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, Stance);
HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosY);
HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosZ);
HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Yaw);
@@ -1984,7 +2116,7 @@ bool cConnection::HandleServerPlayerPositionLook(void)
bool cConnection::HandleServerPluginMessage(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, ChannelName);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, ChannelName);
HANDLE_SERVER_PACKET_READ(ReadBEShort, short, Length);
AString Data;
if (!m_ServerBuffer.ReadString(Data, Length))
@@ -1994,7 +2126,26 @@ bool cConnection::HandleServerPluginMessage(void)
Log("Received a PACKET_PLUGIN_MESSAGE from the server");
Log(" ChannelName = \"%s\"", ChannelName.c_str());
DataLog(Data.data(), Length, " Data: %d bytes", Length);
- COPY_TO_SERVER();
+ COPY_TO_CLIENT();
+ return true;
+}
+
+
+
+
+
+bool cConnection::HandleServerRespawn(void)
+{
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Dimension);
+ HANDLE_SERVER_PACKET_READ(ReadChar, char, Difficulty);
+ HANDLE_SERVER_PACKET_READ(ReadChar, char, GameMode);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, LevelType);
+ Log("Received a respawn packet from the server:");
+ Log(" Dimension = %d", Dimension);
+ Log(" Difficulty = %d", Difficulty);
+ Log(" GameMode = %d", GameMode);
+ Log(" LevelType = \"%s\"", LevelType.c_str());
+ COPY_TO_CLIENT();
return true;
}
@@ -2042,7 +2193,7 @@ bool cConnection::HandleServerSetSlot(void)
bool cConnection::HandleServerSlotSelect(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SlotNum);
+ HANDLE_SERVER_PACKET_READ(ReadByte, Byte, SlotNum);
Log("Received a PACKET_SLOT_SELECT from the server:");
Log(" SlotNum = %d", SlotNum);
COPY_TO_CLIENT();
@@ -2074,6 +2225,25 @@ bool cConnection::HandleServerSoundEffect(void)
+bool cConnection::HandleServerSpawnExperienceOrbs(void)
+{
+ HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, EntityID);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ);
+ HANDLE_SERVER_PACKET_READ(ReadBEShort, short, Count);
+ Log("Received a SPAWN_EXPERIENCE_ORBS packet from the server:");
+ Log(" EntityID = %u (0x%x)", EntityID, EntityID);
+ Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str());
+ Log(" Count = %d", Count);
+ COPY_TO_CLIENT();
+ return true;
+}
+
+
+
+
+
bool cConnection::HandleServerSpawnMob(void)
{
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID);
@@ -2112,14 +2282,15 @@ bool cConnection::HandleServerSpawnMob(void)
bool cConnection::HandleServerSpawnNamedEntity(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID);
- HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, EntityName);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ);
- HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw);
- HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch);
- HANDLE_SERVER_PACKET_READ(ReadBEShort, short, CurrentItem);
+ HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, EntityID);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, EntityUUID);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, EntityName);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ);
+ HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw);
+ HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch);
+ HANDLE_SERVER_PACKET_READ(ReadBEShort, short, CurrentItem);
AString Metadata;
if (!ParseMetadata(m_ServerBuffer, Metadata))
{
@@ -2128,7 +2299,8 @@ bool cConnection::HandleServerSpawnNamedEntity(void)
AString HexDump;
CreateHexDump(HexDump, Metadata.data(), Metadata.size(), 32);
Log("Received a PACKET_SPAWN_NAMED_ENTITY from the server:");
- Log(" EntityID = %d (0x%x)", EntityID, EntityID);
+ Log(" EntityID = %u (0x%x)", EntityID, EntityID);
+ Log(" UUID = %s", EntityUUID.c_str());
Log(" Name = %s", EntityName.c_str());
Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str());
Log(" Rotation = <yaw %d, pitch %d>", Yaw, Pitch);
@@ -2161,14 +2333,14 @@ bool cConnection::HandleServerSpawnObjectVehicle(void)
DataLog(Buffer.data(), Buffer.size(), "Buffer while parsing the PACKET_SPAWN_OBJECT_VEHICLE packet (%d bytes):", Buffer.size());
#endif // _DEBUG
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID);
- HANDLE_SERVER_PACKET_READ(ReadChar, char, ObjType);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ);
- HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw);
- HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, DataIndicator);
+ HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, EntityID);
+ HANDLE_SERVER_PACKET_READ(ReadByte, Byte, ObjType);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ);
+ HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch);
+ HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, DataIndicator);
AString ExtraData;
short VelocityX, VelocityY, VelocityZ;
if (DataIndicator != 0)
@@ -2201,7 +2373,7 @@ bool cConnection::HandleServerSpawnObjectVehicle(void)
*/
}
Log("Received a PACKET_SPAWN_OBJECT_VEHICLE from the server:");
- Log(" EntityID = %d (0x%x)", EntityID, EntityID);
+ Log(" EntityID = %u (0x%x)", EntityID, EntityID);
Log(" ObjType = %d (0x%x)", ObjType, ObjType);
Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str());
Log(" Rotation = <yaw %d, pitch %d>", Yaw, Pitch);
@@ -2221,14 +2393,14 @@ bool cConnection::HandleServerSpawnObjectVehicle(void)
bool cConnection::HandleServerSpawnPainting(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID);
- HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, ImageName);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ);
- HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Direction);
+ HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, EntityID);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, ImageName);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Direction);
Log("Received a PACKET_SPAWN_PAINTING from the server:");
- Log(" EntityID = %d", EntityID);
+ Log(" EntityID = %u", EntityID);
Log(" ImageName = \"%s\"", ImageName.c_str());
Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str());
Log(" Direction = %d", Direction);
@@ -2267,9 +2439,65 @@ bool cConnection::HandleServerSpawnPickup(void)
+bool cConnection::HandleServerStatistics(void)
+{
+ HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, NumEntries);
+ for (UInt32 i = 0; i < NumEntries; i++)
+ {
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, StatName);
+ HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, StatValue);
+ }
+ COPY_TO_CLIENT();
+ return true;
+}
+
+
+
+
+bool cConnection::HandleServerStatusPing(void)
+{
+ HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, Time);
+ Log("Received server's ping response:");
+ Log(" Time = %lld", Time);
+ COPY_TO_CLIENT();
+ return true;
+}
+
+
+
+
+
+bool cConnection::HandleServerStatusResponse(void)
+{
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Response);
+ Log("Received server's status response:");
+ Log(" Response: %s", Response.c_str());
+
+ // Modify the response to show that it's being proto-proxied:
+ size_t idx = Response.find("\"description\":\"");
+ if (idx != AString::npos)
+ {
+ Response.assign(Response.substr(0, idx + 15) + "ProtoProxy: " + Response.substr(idx + 15));
+ }
+ cByteBuffer Packet(1000);
+ Packet.WriteVarInt(0); // Packet type - status response
+ Packet.WriteVarUTF8String(Response);
+ AString Pkt;
+ Packet.ReadAll(Pkt);
+ cByteBuffer ToClient(1000);
+ ToClient.WriteVarUTF8String(Pkt);
+ CLIENTSEND(ToClient);
+ return true;
+}
+
+
+
+
+
bool cConnection::HandleServerTabCompletion(void)
{
- HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Results);
+ HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, NumResults);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Results);
Log("Received a PACKET_TAB_COMPLETION from the server, results given:");
// Parse the zero-terminated list of results:
@@ -2327,10 +2555,10 @@ bool cConnection::HandleServerUpdateSign(void)
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX);
HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockY);
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ);
- HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Line1);
- HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Line2);
- HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Line3);
- HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Line4);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Line1);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Line2);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Line3);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Line4);
Log("Received a PACKET_UPDATE_SIGN from the server:");
Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ);
Log(" Lines = \"%s\", \"%s\", \"%s\", \"%s\"", Line1.c_str(), Line2.c_str(), Line3.c_str(), Line4.c_str());
@@ -2357,7 +2585,24 @@ bool cConnection::HandleServerUpdateTileEntity(void)
Log("Received a PACKET_UPDATE_TILE_ENTITY from the server:");
Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ);
Log(" Action = %d", Action);
- DataLog(Data.data(), Data.size(), " Data (%d bytes)", Data.size());
+ DataLog(Data.data(), Data.size(), " Data (%u bytes)", Data.size());
+ COPY_TO_CLIENT();
+ return true;
+}
+
+
+
+
+
+bool cConnection::HandleServerUseBed(void)
+{
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BedX);
+ HANDLE_SERVER_PACKET_READ(ReadByte, Byte, BedY);
+ HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BedZ);
+ Log("Received a use bed packet from the server:");
+ Log(" EntityID = %d", EntityID);
+ Log(" Bed = {%d, %d, %d}", BedX, BedY, BedZ);
COPY_TO_CLIENT();
return true;
}
@@ -2409,14 +2654,14 @@ bool cConnection::HandleServerWindowOpen(void)
{
HANDLE_SERVER_PACKET_READ(ReadChar, char, WindowID);
HANDLE_SERVER_PACKET_READ(ReadChar, char, WindowType);
- HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Title);
+ HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Title);
HANDLE_SERVER_PACKET_READ(ReadByte, Byte, NumSlots);
HANDLE_SERVER_PACKET_READ(ReadByte, Byte, UseProvidedTitle);
- int HorseInt = 0;
+ int HorseEntityID = 0;
if (WindowType == 11) // Horse / Donkey / Mule
{
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, intHorseInt);
- HorseInt = intHorseInt;
+ HorseEntityID = intHorseInt;
}
Log("Received a PACKET_WINDOW_OPEN from the server:");
Log(" WindowID = %d", WindowID);
@@ -2425,8 +2670,24 @@ bool cConnection::HandleServerWindowOpen(void)
Log(" NumSlots = %d", NumSlots);
if (WindowType == 11)
{
- Log(" HorseInt = %d (0x%08x)", HorseInt, HorseInt);
+ Log(" HorseEntityID = %d (0x%08x)", HorseEntityID, HorseEntityID);
+ }
+ COPY_TO_CLIENT();
+ return true;
+}
+
+
+
+
+
+bool cConnection::HandleServerUnknownPacket(UInt32 a_PacketType, UInt32 a_PacketLen, UInt32 a_PacketReadSoFar)
+{
+ AString Data;
+ if (!m_ServerBuffer.ReadString(Data, a_PacketLen - a_PacketReadSoFar))
+ {
+ return false;
}
+ DataLog(Data.data(), Data.size(), "****************** Unknown packet 0x%x from the server; relaying and ignoring", a_PacketType);
COPY_TO_CLIENT();
return true;
}
@@ -2674,68 +2935,14 @@ void cConnection::SendEncryptionKeyResponse(const AString & a_ServerPublicKey, c
// Send the packet to the server:
Log("Sending PACKET_ENCRYPTION_KEY_RESPONSE to the SERVER");
cByteBuffer ToServer(1024);
- ToServer.WriteByte(PACKET_ENCRYPTION_KEY_RESPONSE);
+ ToServer.WriteByte(0x01); // To server: Encryption key response
ToServer.WriteBEShort(EncryptedLength);
ToServer.WriteBuf(EncryptedSecret, EncryptedLength);
ToServer.WriteBEShort(EncryptedLength);
ToServer.WriteBuf(EncryptedNonce, EncryptedLength);
SERVERSEND(ToServer);
- m_ServerState = csWaitingForEncryption;
-}
-
-
-
-
-
-void cConnection::StartClientEncryption(const AString & a_EncKey, const AString & a_EncNonce)
-{
- // Decrypt EncNonce using privkey
- RSAES<PKCS1v15>::Decryptor rsaDecryptor(m_Server.GetPrivateKey());
- time_t CurTime = time(NULL);
- RandomPool rng;
- rng.Put((const byte *)&CurTime, sizeof(CurTime));
- byte DecryptedNonce[MAX_ENC_LEN];
- DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), DecryptedNonce);
- if (!res.isValidCoding || (res.messageLength != 4))
- {
- Log("Client: Bad nonce length");
- return;
- }
- if (ntohl(*((int *)DecryptedNonce)) != m_Nonce)
- {
- Log("Bad nonce value");
- return;
- }
-
- // Decrypt the symmetric encryption key using privkey:
- byte SharedSecret[MAX_ENC_LEN];
- res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncKey.data(), a_EncKey.size(), SharedSecret);
- if (!res.isValidCoding || (res.messageLength != 16))
- {
- Log("Bad key length");
- return;
- }
-
- // Send encryption key response:
- cByteBuffer ToClient(6);
- ToClient.WriteByte((char)0xfc);
- ToClient.WriteBEShort(0);
- ToClient.WriteBEShort(0);
- CLIENTSEND(ToClient);
-
- // Start the encryption:
- m_ClientEncryptor.SetKey(SharedSecret, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(SharedSecret, 16))(Name::FeedbackSize(), 1));
- m_ClientDecryptor.SetKey(SharedSecret, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(SharedSecret, 16))(Name::FeedbackSize(), 1));
- Log("Client connection is now encrypted");
- m_ClientState = csEncryptedUnderstood;
-
- // Send the queued data:
- DataLog(m_ClientEncryptionBuffer.data(), m_ClientEncryptionBuffer.size(), "Sending the queued data to client (%u bytes):", m_ClientEncryptionBuffer.size());
- CLIENTENCRYPTSEND(m_ClientEncryptionBuffer.data(), m_ClientEncryptionBuffer.size());
- m_ClientEncryptionBuffer.clear();
-
- // Handle all postponed server data
- DecodeServersPackets(NULL, 0);
+ m_ServerState = csEncryptedUnderstood;
+ m_IsServerEncrypted = true;
}
diff --git a/Tools/ProtoProxy/Connection.h b/Tools/ProtoProxy/Connection.h
index 6093408d6..c2bce30fb 100644
--- a/Tools/ProtoProxy/Connection.h
+++ b/Tools/ProtoProxy/Connection.h
@@ -71,14 +71,27 @@ protected:
Decryptor m_ServerDecryptor;
Encryptor m_ServerEncryptor;
- Decryptor m_ClientDecryptor;
- Encryptor m_ClientEncryptor;
-
- AString m_ClientEncryptionBuffer; // Buffer for the data to be sent to the client once encryption is established
AString m_ServerEncryptionBuffer; // Buffer for the data to be sent to the server once encryption is established
/// Set to true when PACKET_PING is received from the client; will cause special parsing for server kick
bool m_HasClientPinged;
+
+ /*
+ The protocol states can be one of:
+ -1: no initial handshake received yet
+ 1: status
+ 2: login
+ 3: game
+ */
+ /// State the to-server protocol is in (as defined by the initial handshake / login), -1 if no initial handshake received yet
+ int m_ServerProtocolState;
+
+ /// State the to-client protocol is in (as defined by the initial handshake / login), -1 if no initial handshake received yet
+ int m_ClientProtocolState;
+
+ /// True if the server connection has provided encryption keys
+ bool m_IsServerEncrypted;
+
bool ConnectToServer(void);
@@ -109,7 +122,18 @@ protected:
/// Decodes packets coming from the server, sends appropriate counterparts to the client; returns false if the connection is to be dropped
bool DecodeServersPackets(const char * a_Data, int a_Size);
- // Packet handling, client-side:
+ // Packet handling, client-side, initial:
+ bool HandleClientHandshake(void);
+
+ // Packet handling, client-side, status:
+ bool HandleClientStatusPing(void);
+ bool HandleClientStatusRequest(void);
+
+ // Packet handling, client-side, login:
+ bool HandleClientLoginEncryptionKeyResponse(void);
+ bool HandleClientLoginStart(void);
+
+ // Packet handling, client-side, game:
bool HandleClientAnimation(void);
bool HandleClientBlockDig(void);
bool HandleClientBlockPlace(void);
@@ -117,9 +141,7 @@ protected:
bool HandleClientClientStatuses(void);
bool HandleClientCreativeInventoryAction(void);
bool HandleClientDisconnect(void);
- bool HandleClientEncryptionKeyResponse(void);
bool HandleClientEntityAction(void);
- bool HandleClientHandshake(void);
bool HandleClientKeepAlive(void);
bool HandleClientLocaleAndView(void);
bool HandleClientPing(void);
@@ -135,8 +157,15 @@ protected:
bool HandleClientUseEntity(void);
bool HandleClientWindowClick(void);
bool HandleClientWindowClose(void);
+
+ bool HandleClientUnknownPacket(UInt32 a_PacketType, UInt32 a_PacketLen, UInt32 a_PacketReadSoFar);
+
+ // Packet handling, server-side, login:
+ bool HandleServerLoginDisconnect(void);
+ bool HandleServerLoginEncryptionKeyRequest(void);
+ bool HandleServerLoginSuccess(void);
- // Packet handling, server-side:
+ // Packet handling, server-side, game:
bool HandleServerAttachEntity(void);
bool HandleServerBlockAction(void);
bool HandleServerBlockChange(void);
@@ -145,8 +174,6 @@ protected:
bool HandleServerCollectPickup(void);
bool HandleServerCompass(void);
bool HandleServerDestroyEntities(void);
- bool HandleServerEncryptionKeyRequest(void);
- bool HandleServerEncryptionKeyResponse(void);
bool HandleServerEntity(void);
bool HandleServerEntityEquipment(void);
bool HandleServerEntityHeadLook(void);
@@ -160,6 +187,7 @@ protected:
bool HandleServerEntityVelocity(void);
bool HandleServerExplosion(void);
bool HandleServerIncrementStatistic(void);
+ bool HandleServerJoinGame(void);
bool HandleServerKeepAlive(void);
bool HandleServerKick(void);
bool HandleServerLogin(void);
@@ -172,24 +200,32 @@ protected:
bool HandleServerPlayerListItem(void);
bool HandleServerPlayerPositionLook(void);
bool HandleServerPluginMessage(void);
+ bool HandleServerRespawn(void);
bool HandleServerSetExperience(void);
bool HandleServerSetSlot(void);
bool HandleServerSlotSelect(void);
bool HandleServerSoundEffect(void);
+ bool HandleServerSpawnExperienceOrbs(void);
bool HandleServerSpawnMob(void);
bool HandleServerSpawnNamedEntity(void);
bool HandleServerSpawnObjectVehicle(void);
bool HandleServerSpawnPainting(void);
bool HandleServerSpawnPickup(void);
+ bool HandleServerStatistics(void);
+ bool HandleServerStatusPing(void);
+ bool HandleServerStatusResponse(void);
bool HandleServerTabCompletion(void);
bool HandleServerTimeUpdate(void);
bool HandleServerUpdateHealth(void);
bool HandleServerUpdateSign(void);
bool HandleServerUpdateTileEntity(void);
+ bool HandleServerUseBed(void);
bool HandleServerWindowClose(void);
bool HandleServerWindowContents(void);
bool HandleServerWindowOpen(void);
+ bool HandleServerUnknownPacket(UInt32 a_PacketType, UInt32 a_PacketLen, UInt32 a_PacketReadSoFar);
+
/// Parses the slot data in a_Buffer into item description; returns true if successful, false if not enough data
bool ParseSlot(cByteBuffer & a_Buffer, AString & a_ItemDesc);
diff --git a/Tools/ProtoProxy/Globals.h b/Tools/ProtoProxy/Globals.h
index 3b154b866..8424aca81 100644
--- a/Tools/ProtoProxy/Globals.h
+++ b/Tools/ProtoProxy/Globals.h
@@ -70,6 +70,10 @@ typedef long long Int64;
typedef int Int32;
typedef short Int16;
+typedef unsigned long long UInt64;
+typedef unsigned int UInt32;
+typedef unsigned short UInt16;
+
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..cb8cf9f81 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>
@@ -2492,6 +2528,14 @@
>
</File>
<File
+ RelativePath="..\source\Protocol\Protocol17x.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\Protocol\Protocol17x.h"
+ >
+ </File>
+ <File
RelativePath="..\source\Protocol\ProtocolRecognizer.cpp"
>
</File>
diff --git a/iniFile/iniFile.cpp b/iniFile/iniFile.cpp
index 5ecbc5e06..da523e783 100644
--- a/iniFile/iniFile.cpp
+++ b/iniFile/iniFile.cpp
@@ -42,34 +42,33 @@ using namespace std;
-cIniFile::cIniFile(const string & a_Path) :
+cIniFile::cIniFile(void) :
m_IsCaseInsensitive(true)
{
- Path(a_Path);
}
-bool cIniFile::ReadFile(bool a_AllowExampleRedirect)
+bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect)
{
// Normally you would use ifstream, but the SGI CC compiler has
// a few bugs with ifstream. So ... fstream used.
fstream f;
- string line;
- string keyname, valuename, value;
- string::size_type pLeft, pRight;
+ AString line;
+ AString keyname, valuename, value;
+ AString::size_type pLeft, pRight;
bool IsFromExampleRedirect = false;
- f.open((FILE_IO_PREFIX + m_Path).c_str(), ios::in);
+ f.open((FILE_IO_PREFIX + a_FileName).c_str(), ios::in);
if (f.fail())
{
f.clear();
if (a_AllowExampleRedirect)
{
// Retry with the .example.ini file instead of .ini:
- string ExPath(m_Path.substr(0, m_Path.length() - 4));
+ AString ExPath(a_FileName.substr(0, a_FileName.length() - 4));
ExPath.append(".example.ini");
f.open((FILE_IO_PREFIX + ExPath).c_str(), ios::in);
if (f.fail())
@@ -91,7 +90,7 @@ bool cIniFile::ReadFile(bool a_AllowExampleRedirect)
// Note that the '\r' will be written to INI files from
// Unix so that the created INI file can be read under Win32
// without change.
- unsigned int lineLength = line.length();
+ size_t lineLength = line.length();
if (lineLength == 0)
{
continue;
@@ -114,7 +113,7 @@ bool cIniFile::ReadFile(bool a_AllowExampleRedirect)
f.close();
return false;
}
- if ((pLeft = line.find_first_of(";#[=")) == string::npos)
+ if ((pLeft = line.find_first_of(";#[=")) == AString::npos)
{
continue;
}
@@ -124,7 +123,7 @@ bool cIniFile::ReadFile(bool a_AllowExampleRedirect)
case '[':
{
if (
- ((pRight = line.find_last_of("]")) != string::npos) &&
+ ((pRight = line.find_last_of("]")) != AString::npos) &&
(pRight > pLeft)
)
{
@@ -147,11 +146,11 @@ bool cIniFile::ReadFile(bool a_AllowExampleRedirect)
{
if (names.size() == 0)
{
- HeaderComment(line.substr(pLeft + 1));
+ AddHeaderComment(line.substr(pLeft + 1));
}
else
{
- KeyComment(keyname, line.substr(pLeft + 1));
+ AddKeyComment(keyname, line.substr(pLeft + 1));
}
break;
}
@@ -166,7 +165,7 @@ bool cIniFile::ReadFile(bool a_AllowExampleRedirect)
if (IsFromExampleRedirect)
{
- WriteFile();
+ WriteFile(FILE_IO_PREFIX + a_FileName);
}
return true;
}
@@ -175,42 +174,42 @@ bool cIniFile::ReadFile(bool a_AllowExampleRedirect)
-bool cIniFile::WriteFile(void) const
+bool cIniFile::WriteFile(const AString & a_FileName) const
{
- unsigned commentID, keyID, valueID;
// Normally you would use ofstream, but the SGI CC compiler has
// a few bugs with ofstream. So ... fstream used.
fstream f;
- f.open((FILE_IO_PREFIX + m_Path).c_str(), ios::out);
+ f.open((FILE_IO_PREFIX + a_FileName).c_str(), ios::out);
if (f.fail())
{
return false;
}
// Write header comments.
- for (commentID = 0; commentID < comments.size(); ++commentID)
+ size_t NumComments = comments.size();
+ for (size_t commentID = 0; commentID < NumComments; ++commentID)
{
f << ';' << comments[commentID] << iniEOL;
}
- if (comments.size())
+ if (NumComments > 0)
{
f << iniEOL;
}
// Write keys and values.
- for (keyID = 0; keyID < keys.size(); ++keyID)
+ for (size_t keyID = 0; keyID < keys.size(); ++keyID)
{
f << '[' << names[keyID] << ']' << iniEOL;
// Comments.
- for (commentID = 0; commentID < keys[keyID].comments.size(); ++commentID)
+ for (size_t commentID = 0; commentID < keys[keyID].comments.size(); ++commentID)
{
f << ';' << keys[keyID].comments[commentID] << iniEOL;
}
// Values.
- for (valueID = 0; valueID < keys[keyID].names.size(); ++valueID)
+ for (size_t valueID = 0; valueID < keys[keyID].names.size(); ++valueID)
{
f << keys[keyID].names[valueID] << '=' << keys[keyID].values[valueID] << iniEOL;
}
@@ -225,14 +224,14 @@ bool cIniFile::WriteFile(void) const
-long cIniFile::FindKey(const string & a_KeyName) const
+int cIniFile::FindKey(const AString & a_KeyName) const
{
- string CaseKeyName = CheckCase(a_KeyName);
- for (unsigned keyID = 0; keyID < names.size(); ++keyID)
+ AString CaseKeyName = CheckCase(a_KeyName);
+ for (size_t keyID = 0; keyID < names.size(); ++keyID)
{
if (CheckCase(names[keyID]) == CaseKeyName)
{
- return long(keyID);
+ return keyID;
}
}
return noID;
@@ -242,19 +241,19 @@ long cIniFile::FindKey(const string & a_KeyName) const
-long cIniFile::FindValue(unsigned const keyID, const string & a_ValueName) const
+int cIniFile::FindValue(const int keyID, const AString & a_ValueName) const
{
- if (!keys.size() || (keyID >= keys.size()))
+ if (!keys.size() || (keyID >= (int)keys.size()))
{
return noID;
}
- string CaseValueName = CheckCase(a_ValueName);
- for (unsigned valueID = 0; valueID < keys[keyID].names.size(); ++valueID)
+ AString CaseValueName = CheckCase(a_ValueName);
+ for (size_t valueID = 0; valueID < keys[keyID].names.size(); ++valueID)
{
if (CheckCase(keys[keyID].names[valueID]) == CaseValueName)
{
- return long(valueID);
+ return int(valueID);
}
}
return noID;
@@ -264,7 +263,7 @@ long cIniFile::FindValue(unsigned const keyID, const string & a_ValueName) const
-unsigned cIniFile::AddKeyName(const string & keyname)
+int cIniFile::AddKeyName(const AString & keyname)
{
names.resize(names.size() + 1, keyname);
keys.resize(keys.size() + 1);
@@ -275,9 +274,9 @@ unsigned cIniFile::AddKeyName(const string & keyname)
-string cIniFile::KeyName(unsigned const keyID) const
+AString cIniFile::GetKeyName(const int keyID) const
{
- if (keyID < names.size())
+ if (keyID < (int)names.size())
{
return names[keyID];
}
@@ -291,11 +290,11 @@ string cIniFile::KeyName(unsigned const keyID) const
-unsigned cIniFile::NumValues(unsigned const keyID)
+int cIniFile::GetNumValues(const int keyID) const
{
- if (keyID < keys.size())
+ if (keyID < (int)keys.size())
{
- return keys[keyID].names.size();
+ return (int)keys[keyID].names.size();
}
return 0;
}
@@ -304,23 +303,23 @@ unsigned cIniFile::NumValues(unsigned const keyID)
-unsigned cIniFile::NumValues(const string & keyname)
+int cIniFile::GetNumValues(const AString & keyname) const
{
- long keyID = FindKey(keyname);
+ int keyID = FindKey(keyname);
if (keyID == noID)
{
return 0;
}
- return keys[keyID].names.size();
+ return (int)keys[keyID].names.size();
}
-string cIniFile::ValueName(unsigned const keyID, unsigned const valueID) const
+AString cIniFile::GetValueName(const int keyID, const int valueID) const
{
- if (keyID < keys.size() && valueID < keys[keyID].names.size())
+ if ((keyID < (int)keys.size()) && (valueID < (int)keys[keyID].names.size()))
{
return keys[keyID].names[valueID];
}
@@ -331,23 +330,23 @@ string cIniFile::ValueName(unsigned const keyID, unsigned const valueID) const
-string cIniFile::ValueName(const string & keyname, unsigned const valueID) const
+AString cIniFile::GetValueName(const AString & keyname, const int valueID) const
{
- long keyID = FindKey(keyname);
+ int keyID = FindKey(keyname);
if (keyID == noID)
{
return "";
}
- return ValueName(keyID, valueID);
+ return GetValueName(keyID, valueID);
}
-bool cIniFile::SetValue(unsigned const keyID, unsigned const valueID, const string & value)
+bool cIniFile::SetValue(const int keyID, const int valueID, const AString & value)
{
- if ((keyID < keys.size()) && (valueID < keys[keyID].names.size()))
+ if ((keyID < (int)keys.size()) && (valueID < (int)keys[keyID].names.size()))
{
keys[keyID].values[valueID] = value;
}
@@ -358,14 +357,14 @@ bool cIniFile::SetValue(unsigned const keyID, unsigned const valueID, const stri
-bool cIniFile::SetValue(const string & keyname, const string & valuename, const string & value, bool const create)
+bool cIniFile::SetValue(const AString & keyname, const AString & valuename, const AString & value, bool const create)
{
- long keyID = FindKey(keyname);
+ int keyID = FindKey(keyname);
if (keyID == noID)
{
if (create)
{
- keyID = long(AddKeyName(keyname));
+ keyID = int(AddKeyName(keyname));
}
else
{
@@ -373,7 +372,7 @@ bool cIniFile::SetValue(const string & keyname, const string & valuename, const
}
}
- long valueID = FindValue(unsigned(keyID), valuename);
+ int valueID = FindValue(int(keyID), valuename);
if (valueID == noID)
{
if (!create)
@@ -403,7 +402,7 @@ bool cIniFile::SetValue(const string & keyname, const string & valuename, const
-bool cIniFile::SetValueI(const string & keyname, const string & valuename, int const value, bool const create)
+bool cIniFile::SetValueI(const AString & keyname, const AString & valuename, const int value, bool const create)
{
AString Data;
Printf(Data, "%d", value);
@@ -414,7 +413,7 @@ bool cIniFile::SetValueI(const string & keyname, const string & valuename, int c
-bool cIniFile::SetValueF(const string & keyname, const string & valuename, double const value, bool const create)
+bool cIniFile::SetValueF(const AString & keyname, const AString & valuename, double const value, bool const create)
{
AString Data;
Printf(Data, "%f", value);
@@ -425,7 +424,7 @@ bool cIniFile::SetValueF(const string & keyname, const string & valuename, doubl
-bool cIniFile::SetValueV(const string & keyname, const string & valuename, char * format, ...)
+bool cIniFile::SetValueV(const AString & keyname, const AString & valuename, char * format, ...)
{
va_list args;
va_start(args, format);
@@ -440,9 +439,9 @@ bool cIniFile::SetValueV(const string & keyname, const string & valuename, char
-string cIniFile::GetValue(unsigned const keyID, unsigned const valueID, const string & defValue) const
+AString cIniFile::GetValue(const int keyID, const int valueID, const AString & defValue) const
{
- if ((keyID < keys.size()) && (valueID < keys[keyID].names.size()))
+ if ((keyID < (int)keys.size()) && (valueID < (int)keys[keyID].names.size()))
{
return keys[keyID].values[valueID];
}
@@ -453,15 +452,15 @@ string cIniFile::GetValue(unsigned const keyID, unsigned const valueID, const st
-string cIniFile::GetValue(const string & keyname, const string & valuename, const string & defValue) const
+AString cIniFile::GetValue(const AString & keyname, const AString & valuename, const AString & defValue) const
{
- long keyID = FindKey(keyname);
+ int keyID = FindKey(keyname);
if (keyID == noID)
{
return defValue;
}
- long valueID = FindValue(unsigned(keyID), valuename);
+ int valueID = FindValue(int(keyID), valuename);
if (valueID == noID)
{
return defValue;
@@ -474,7 +473,7 @@ string cIniFile::GetValue(const string & keyname, const string & valuename, cons
-int cIniFile::GetValueI(const string & keyname, const string & valuename, int const defValue) const
+int cIniFile::GetValueI(const AString & keyname, const AString & valuename, const int defValue) const
{
AString Data;
Printf(Data, "%d", defValue);
@@ -485,7 +484,7 @@ int cIniFile::GetValueI(const string & keyname, const string & valuename, int co
-double cIniFile::GetValueF(const string & keyname, const string & valuename, double const defValue) const
+double cIniFile::GetValueF(const AString & keyname, const AString & valuename, double const defValue) const
{
AString Data;
Printf(Data, "%f", defValue);
@@ -498,14 +497,14 @@ double cIniFile::GetValueF(const string & keyname, const string & valuename, dou
AString cIniFile::GetValueSet(const AString & keyname, const AString & valuename, const AString & defValue)
{
- long keyID = FindKey(keyname);
+ int keyID = FindKey(keyname);
if (keyID == noID)
{
SetValue(keyname, valuename, defValue);
return defValue;
}
- long valueID = FindValue(unsigned(keyID), valuename);
+ int valueID = FindValue(int(keyID), valuename);
if (valueID == noID)
{
SetValue(keyname, valuename, defValue);
@@ -541,13 +540,13 @@ int cIniFile::GetValueSetI(const AString & keyname, const AString & valuename, c
-bool cIniFile::DeleteValueByID(const unsigned keyID, const unsigned valueID)
+bool cIniFile::DeleteValueByID(const int keyID, const int valueID)
{
- if (keyID < keys.size() && valueID < keys[keyID].names.size())
+ if ((keyID < (int)keys.size()) && (valueID < (int)keys[keyID].names.size()))
{
// This looks strange, but is neccessary.
- vector<string>::iterator npos = keys[keyID].names.begin() + valueID;
- vector<string>::iterator vpos = keys[keyID].values.begin() + valueID;
+ vector<AString>::iterator npos = keys[keyID].names.begin() + valueID;
+ vector<AString>::iterator vpos = keys[keyID].values.begin() + valueID;
keys[keyID].names.erase(npos, npos + 1);
keys[keyID].values.erase(vpos, vpos + 1);
return true;
@@ -559,15 +558,15 @@ bool cIniFile::DeleteValueByID(const unsigned keyID, const unsigned valueID)
-bool cIniFile::DeleteValue(const string & keyname, const string & valuename)
+bool cIniFile::DeleteValue(const AString & keyname, const AString & valuename)
{
- long keyID = FindKey(keyname);
+ int keyID = FindKey(keyname);
if (keyID == noID)
{
return false;
}
- long valueID = FindValue(unsigned(keyID), valuename);
+ int valueID = FindValue(int(keyID), valuename);
if (valueID == noID)
{
return false;
@@ -580,15 +579,15 @@ bool cIniFile::DeleteValue(const string & keyname, const string & valuename)
-bool cIniFile::DeleteKey(const string & keyname)
+bool cIniFile::DeleteKey(const AString & keyname)
{
- long keyID = FindKey(keyname);
+ int keyID = FindKey(keyname);
if (keyID == noID)
{
return false;
}
- vector<string>::iterator npos = names.begin() + keyID;
+ vector<AString>::iterator npos = names.begin() + keyID;
vector<key>::iterator kpos = keys.begin() + keyID;
names.erase(npos, npos + 1);
keys.erase(kpos, kpos + 1);
@@ -611,7 +610,7 @@ void cIniFile::Clear(void)
-void cIniFile::HeaderComment(const string & comment)
+void cIniFile::AddHeaderComment(const AString & comment)
{
comments.push_back(comment);
// comments.resize(comments.size() + 1, comment);
@@ -621,9 +620,9 @@ void cIniFile::HeaderComment(const string & comment)
-string cIniFile::HeaderComment(unsigned const commentID) const
+AString cIniFile::GetHeaderComment(const int commentID) const
{
- if (commentID < comments.size())
+ if (commentID < (int)comments.size())
{
return comments[commentID];
}
@@ -634,11 +633,11 @@ string cIniFile::HeaderComment(unsigned const commentID) const
-bool cIniFile::DeleteHeaderComment(unsigned commentID)
+bool cIniFile::DeleteHeaderComment(int commentID)
{
- if (commentID < comments.size())
+ if (commentID < (int)comments.size())
{
- vector<string>::iterator cpos = comments.begin() + commentID;
+ vector<AString>::iterator cpos = comments.begin() + commentID;
comments.erase(cpos, cpos + 1);
return true;
}
@@ -649,9 +648,9 @@ bool cIniFile::DeleteHeaderComment(unsigned commentID)
-unsigned cIniFile::NumKeyComments(unsigned const keyID) const
+int cIniFile::GetNumKeyComments(const int keyID) const
{
- if (keyID < keys.size())
+ if (keyID < (int)keys.size())
{
return keys[keyID].comments.size();
}
@@ -662,21 +661,23 @@ unsigned cIniFile::NumKeyComments(unsigned const keyID) const
-unsigned cIniFile::NumKeyComments(const string & keyname) const
+int cIniFile::GetNumKeyComments(const AString & keyname) const
{
- long keyID = FindKey(keyname);
+ int keyID = FindKey(keyname);
if (keyID == noID)
+ {
return 0;
- return keys[keyID].comments.size();
+ }
+ return (int)keys[keyID].comments.size();
}
-bool cIniFile::KeyComment(unsigned const keyID, const string & comment)
+bool cIniFile::AddKeyComment(const int keyID, const AString & comment)
{
- if (keyID < keys.size())
+ if (keyID < (int)keys.size())
{
keys[keyID].comments.resize(keys[keyID].comments.size() + 1, comment);
return true;
@@ -688,23 +689,23 @@ bool cIniFile::KeyComment(unsigned const keyID, const string & comment)
-bool cIniFile::KeyComment(const string & keyname, const string & comment)
+bool cIniFile::AddKeyComment(const AString & keyname, const AString & comment)
{
- long keyID = FindKey(keyname);
+ int keyID = FindKey(keyname);
if (keyID == noID)
{
return false;
}
- return KeyComment(unsigned(keyID), comment);
+ return AddKeyComment(keyID, comment);
}
-string cIniFile::KeyComment(unsigned const keyID, unsigned const commentID) const
+AString cIniFile::GetKeyComment(const int keyID, const int commentID) const
{
- if ((keyID < keys.size()) && (commentID < keys[keyID].comments.size()))
+ if ((keyID < (int)keys.size()) && (commentID < (int)keys[keyID].comments.size()))
{
return keys[keyID].comments[commentID];
}
@@ -715,25 +716,25 @@ string cIniFile::KeyComment(unsigned const keyID, unsigned const commentID) cons
-string cIniFile::KeyComment(const string & keyname, unsigned const commentID) const
+AString cIniFile::GetKeyComment(const AString & keyname, const int commentID) const
{
- long keyID = FindKey(keyname);
+ int keyID = FindKey(keyname);
if (keyID == noID)
{
return "";
}
- return KeyComment(unsigned(keyID), commentID);
+ return GetKeyComment(int(keyID), commentID);
}
-bool cIniFile::DeleteKeyComment(unsigned const keyID, unsigned const commentID)
+bool cIniFile::DeleteKeyComment(const int keyID, const int commentID)
{
- if ((keyID < keys.size()) && (commentID < keys[keyID].comments.size()))
+ if ((keyID < (int)keys.size()) && (commentID < (int)keys[keyID].comments.size()))
{
- vector<string>::iterator cpos = keys[keyID].comments.begin() + commentID;
+ vector<AString>::iterator cpos = keys[keyID].comments.begin() + commentID;
keys[keyID].comments.erase(cpos, cpos + 1);
return true;
}
@@ -744,23 +745,23 @@ bool cIniFile::DeleteKeyComment(unsigned const keyID, unsigned const commentID)
-bool cIniFile::DeleteKeyComment(const string & keyname, unsigned const commentID)
+bool cIniFile::DeleteKeyComment(const AString & keyname, const int commentID)
{
- long keyID = FindKey(keyname);
+ int keyID = FindKey(keyname);
if (keyID == noID)
{
return false;
}
- return DeleteKeyComment(unsigned(keyID), commentID);
+ return DeleteKeyComment(int(keyID), commentID);
}
-bool cIniFile::DeleteKeyComments(unsigned const keyID)
+bool cIniFile::DeleteKeyComments(const int keyID)
{
- if (keyID < keys.size())
+ if (keyID < (int)keys.size())
{
keys[keyID].comments.clear();
return true;
@@ -772,27 +773,27 @@ bool cIniFile::DeleteKeyComments(unsigned const keyID)
-bool cIniFile::DeleteKeyComments(const string & keyname)
+bool cIniFile::DeleteKeyComments(const AString & keyname)
{
- long keyID = FindKey(keyname);
+ int keyID = FindKey(keyname);
if (keyID == noID)
{
return false;
}
- return DeleteKeyComments(unsigned(keyID));
+ return DeleteKeyComments(int(keyID));
}
-string cIniFile::CheckCase(const string & s) const
+AString cIniFile::CheckCase(const AString & s) const
{
if (!m_IsCaseInsensitive)
{
return s;
}
- string res(s);
+ AString res(s);
size_t len = res.length();
for (size_t i = 0; i < len; i++)
{
diff --git a/iniFile/iniFile.h b/iniFile/iniFile.h
index aef45c094..83d961fc6 100644
--- a/iniFile/iniFile.h
+++ b/iniFile/iniFile.h
@@ -36,21 +36,20 @@ class cIniFile
{
private:
bool m_IsCaseInsensitive;
- std::string m_Path;
struct key
{
- std::vector<std::string> names;
- std::vector<std::string> values;
- std::vector<std::string> comments;
+ std::vector<AString> names;
+ std::vector<AString> values;
+ std::vector<AString> comments;
} ;
- std::vector<key> keys;
- std::vector<std::string> names;
- std::vector<std::string> comments;
+ std::vector<key> keys;
+ std::vector<AString> names;
+ std::vector<AString> comments;
/// If the object is case-insensitive, returns s as lowercase; otherwise returns s as-is
- std::string CheckCase(const std::string & s) const;
+ AString CheckCase(const AString & s) const;
public:
enum errors
@@ -58,79 +57,60 @@ public:
noID = -1,
};
- /// Creates a new instance; sets m_Path to a_Path, but doesn't read the file
- cIniFile(const std::string & a_Path = "");
+ /// Creates a new instance with no data
+ cIniFile(void);
// Sets whether or not keynames and valuenames should be case sensitive.
// The default is case insensitive.
void CaseSensitive (void) { m_IsCaseInsensitive = false; }
void CaseInsensitive(void) { m_IsCaseInsensitive = true; }
- // Sets path of ini file to read and write from.
- void Path(const std::string & newPath) {m_Path = newPath;}
- const std::string & Path(void) const {return m_Path;}
- void SetPath(const std::string & newPath) {Path(newPath);}
-
- /** Reads the ini file specified in m_Path
+ /** Reads the contents of the specified ini file
If the file doesn't exist and a_AllowExampleRedirect is true, tries to read <basename>.example.ini, and
writes its contents as <basename>.ini, if successful.
Returns true if successful, false otherwise.
*/
- bool ReadFile(bool a_AllowExampleRedirect = true);
+ bool ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect = true);
- /// Writes data stored in class to ini file specified in m_Path
- bool WriteFile(void) const;
+ /// Writes data stored in class to the specified ini file
+ bool WriteFile(const AString & a_FileName) const;
/// Deletes all stored ini data (but doesn't touch the file)
void Clear(void);
- void Reset(void) { Clear(); }
- void Erase(void) { Clear(); } // OBSOLETE, this name is misguiding and will be removed from the interface
/// Returns index of specified key, or noID if not found
- long FindKey(const std::string & keyname) const;
+ int FindKey(const AString & keyname) const;
/// Returns index of specified value, in the specified key, or noID if not found
- long FindValue(const unsigned keyID, const std::string & valuename) const;
+ int FindValue(const int keyID, const AString & valuename) const;
/// Returns number of keys currently in the ini
- unsigned NumKeys (void) const {return names.size();}
- unsigned GetNumKeys(void) const {return NumKeys();}
+ int GetNumKeys(void) const { return (int)keys.size(); }
/// Add a key name
- unsigned AddKeyName(const std::string & keyname);
+ int AddKeyName(const AString & keyname);
// Returns key names by index.
- std::string KeyName(const unsigned keyID) const;
- std::string GetKeyName(const unsigned keyID) const {return KeyName(keyID);}
+ AString GetKeyName(const int keyID) const;
// Returns number of values stored for specified key.
- unsigned NumValues (const std::string & keyname);
- unsigned GetNumValues(const std::string & keyname) {return NumValues(keyname);}
- unsigned NumValues (const unsigned keyID);
- unsigned GetNumValues(const unsigned keyID) {return NumValues(keyID);}
+ int GetNumValues(const AString & keyname) const;
+ int GetNumValues(const int keyID) const;
// Returns value name by index for a given keyname or keyID.
- std::string ValueName( const std::string & keyname, const unsigned valueID) const;
- std::string GetValueName( const std::string & keyname, const unsigned valueID) const
- {
- return ValueName(keyname, valueID);
- }
- std::string ValueName (const unsigned keyID, const unsigned valueID) const;
- std::string GetValueName(const unsigned keyID, const unsigned valueID) const
- {
- return ValueName(keyID, valueID);
- }
+ AString GetValueName(const AString & keyname, const int valueID) const;
+ AString GetValueName(const int keyID, const int valueID) const;
// Gets value of [keyname] valuename =.
// Overloaded to return string, int, and double.
// Returns defValue if key/value not found.
AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const;
- AString GetValue (const unsigned keyID, const unsigned valueID, const AString & defValue = "") const;
+ AString GetValue (const int keyID, const int valueID, const AString & defValue = "") const;
double GetValueF(const AString & keyname, const AString & valuename, const double defValue = 0) const;
int GetValueI(const AString & keyname, const AString & valuename, const int defValue = 0) const;
bool GetValueB(const AString & keyname, const AString & valuename, const bool defValue = false) const
{
- return (GetValueI(keyname, valuename, int(defValue)) > 0);
+ return (GetValueI(keyname, valuename, defValue ? 1 : 0) != 0);
}
// Gets the value; if not found, write the default to the INI file
@@ -139,50 +119,54 @@ public:
int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0);
bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false)
{
- return (GetValueSetI(keyname, valuename, defValue ? 1 : 0) > 0);
+ return (GetValueSetI(keyname, valuename, defValue ? 1 : 0) != 0);
}
// Sets value of [keyname] valuename =.
// Specify the optional paramter as false (0) if you do not want it to create
// the key if it doesn't exist. Returns true if data entered, false otherwise.
// Overloaded to accept string, int, and double.
- bool SetValue( const unsigned keyID, const unsigned valueID, const std::string & value);
- bool SetValue( const std::string & keyname, const std::string & valuename, const std::string & value, const bool create = true);
- bool SetValueI( const std::string & keyname, const std::string & valuename, const int value, const bool create = true);
- bool SetValueB( const std::string & keyname, const std::string & valuename, const bool value, const bool create = true)
+ bool SetValue( const int keyID, const int valueID, const AString & value);
+ bool SetValue( const AString & keyname, const AString & valuename, const AString & value, const bool create = true);
+ bool SetValueI( const AString & keyname, const AString & valuename, const int value, const bool create = true);
+ bool SetValueB( const AString & keyname, const AString & valuename, const bool value, const bool create = true)
{
return SetValueI( keyname, valuename, int(value), create);
}
- bool SetValueF( const std::string & keyname, const std::string & valuename, const double value, const bool create = true);
+ bool SetValueF( const AString & keyname, const AString & valuename, const double value, const bool create = true);
// tolua_end
- bool SetValueV( const std::string & keyname, const std::string & valuename, char *format, ...);
+ bool SetValueV( const AString & keyname, const AString & valuename, char *format, ...);
// tolua_begin
// Deletes specified value.
// Returns true if value existed and deleted, false otherwise.
- bool DeleteValueByID( const unsigned keyID, const unsigned valueID );
- bool DeleteValue( const std::string & keyname, const std::string & valuename);
+ bool DeleteValueByID(const int keyID, const int valueID);
+ bool DeleteValue(const AString & keyname, const AString & valuename);
// Deletes specified key and all values contained within.
// Returns true if key existed and deleted, false otherwise.
- bool DeleteKey(const std::string & keyname);
+ bool DeleteKey(const AString & keyname);
// Header comment functions.
// Header comments are those comments before the first key.
- //
- // Number of header comments.
- unsigned NumHeaderComments(void) {return comments.size();}
- // Add a header comment.
- void HeaderComment(const std::string & comment);
- // Return a header comment.
- std::string HeaderComment(const unsigned commentID) const;
- // Delete a header comment.
- bool DeleteHeaderComment(unsigned commentID);
- // Delete all header comments.
- void DeleteHeaderComments(void) {comments.clear();}
+
+ /// Returns the number of header comments
+ int GetNumHeaderComments(void) {return (int)comments.size();}
+
+ /// Adds a header comment
+ void AddHeaderComment(const AString & comment);
+
+ /// Returns a header comment, or empty string if out of range
+ AString GetHeaderComment(const int commentID) const;
+
+ /// Deletes a header comment. Returns true if successful
+ bool DeleteHeaderComment(int commentID);
+
+ /// Deletes all header comments
+ void DeleteHeaderComments(void) {comments.clear();}
// Key comment functions.
@@ -190,26 +174,30 @@ public:
// defined within value names will be added to this list. Therefore,
// these comments will be moved to the top of the key definition when
// the CIniFile::WriteFile() is called.
- //
- // Number of key comments.
- unsigned NumKeyComments( const unsigned keyID) const;
- unsigned NumKeyComments( const std::string & keyname) const;
+
+ /// Get number of key comments
+ int GetNumKeyComments(const int keyID) const;
+
+ /// Get number of key comments
+ int GetNumKeyComments(const AString & keyname) const;
- // Add a key comment.
- bool KeyComment(const unsigned keyID, const std::string & comment);
- bool KeyComment(const std::string & keyname, const std::string & comment);
+ /// Add a key comment
+ bool AddKeyComment(const int keyID, const AString & comment);
+
+ /// Add a key comment
+ bool AddKeyComment(const AString & keyname, const AString & comment);
- // Return a key comment.
- std::string KeyComment(const unsigned keyID, const unsigned commentID) const;
- std::string KeyComment(const std::string & keyname, const unsigned commentID) const;
+ /// Return a key comment
+ AString GetKeyComment(const int keyID, const int commentID) const;
+ AString GetKeyComment(const AString & keyname, const int commentID) const;
// Delete a key comment.
- bool DeleteKeyComment(const unsigned keyID, const unsigned commentID);
- bool DeleteKeyComment(const std::string & keyname, const unsigned commentID);
+ bool DeleteKeyComment(const int keyID, const int commentID);
+ bool DeleteKeyComment(const AString & keyname, const int commentID);
// Delete all comments for a key.
- bool DeleteKeyComments(const unsigned keyID);
- bool DeleteKeyComments(const std::string & keyname);
+ bool DeleteKeyComments(const int keyID);
+ bool DeleteKeyComments(const AString & keyname);
};
// tolua_end
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/Authenticator.cpp b/source/Authenticator.cpp
index a45617f93..e09fd0871 100644
--- a/source/Authenticator.cpp
+++ b/source/Authenticator.cpp
@@ -28,7 +28,6 @@ cAuthenticator::cAuthenticator(void) :
m_Address(DEFAULT_AUTH_ADDRESS),
m_ShouldAuthenticate(true)
{
- ReadINI();
}
@@ -45,14 +44,8 @@ cAuthenticator::~cAuthenticator()
/// Read custom values from INI
-void cAuthenticator::ReadINI(void)
+void cAuthenticator::ReadINI(cIniFile & IniFile)
{
- cIniFile IniFile("settings.ini");
- if (!IniFile.ReadFile())
- {
- return;
- }
-
m_Server = IniFile.GetValue("Authentication", "Server");
m_Address = IniFile.GetValue("Authentication", "Address");
m_ShouldAuthenticate = IniFile.GetValueB("Authentication", "Authenticate", true);
@@ -74,7 +67,6 @@ void cAuthenticator::ReadINI(void)
if (bSave)
{
IniFile.SetValueB("Authentication", "Authenticate", m_ShouldAuthenticate);
- IniFile.WriteFile();
}
}
@@ -100,8 +92,9 @@ void cAuthenticator::Authenticate(int a_ClientID, const AString & a_UserName, co
-void cAuthenticator::Start(void)
+void cAuthenticator::Start(cIniFile & IniFile)
{
+ ReadINI(IniFile);
m_ShouldTerminate = false;
super::Start();
}
diff --git a/source/Authenticator.h b/source/Authenticator.h
index 868476d80..02cd6f4c5 100644
--- a/source/Authenticator.h
+++ b/source/Authenticator.h
@@ -37,13 +37,13 @@ public:
~cAuthenticator();
/// (Re-)read server and address from INI:
- void ReadINI(void);
+ void ReadINI(cIniFile & IniFile);
/// Queues a request for authenticating a user. If the auth fails, the user is kicked
void Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash);
/// Starts the authenticator thread. The thread may be started and stopped repeatedly
- void Start(void);
+ void Start(cIniFile & IniFile);
/// Stops the authenticator thread. The thread may be started and stopped repeatedly
void Stop(void);
diff --git a/source/Bindings.cpp b/source/Bindings.cpp
index c241bad75..8259eda81 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/28/13 13:11:03.
*/
#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;
}
@@ -222,7 +230,7 @@ static void tolua_reg_types (lua_State* tolua_S)
tolua_usertype(tolua_S,"cRoot");
tolua_usertype(tolua_S,"std::vector<cIniFile::key>");
tolua_usertype(tolua_S,"cPickup");
- tolua_usertype(tolua_S,"cItems");
+ tolua_usertype(tolua_S,"sWebAdminPage");
tolua_usertype(tolua_S,"cFireChargeEntity");
tolua_usertype(tolua_S,"cWorld");
tolua_usertype(tolua_S,"cChunkDesc");
@@ -244,40 +252,40 @@ 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,"std::vector<AString>");
tolua_usertype(tolua_S,"cBlockEntityWithItems");
tolua_usertype(tolua_S,"cWindow");
- tolua_usertype(tolua_S,"cGroup");
tolua_usertype(tolua_S,"HTTPFormData");
- tolua_usertype(tolua_S,"cCraftingGrid");
+ tolua_usertype(tolua_S,"cGroup");
tolua_usertype(tolua_S,"cArrowEntity");
tolua_usertype(tolua_S,"cDropSpenserEntity");
+ tolua_usertype(tolua_S,"cCraftingGrid");
+ tolua_usertype(tolua_S,"cPlayer");
tolua_usertype(tolua_S,"cBlockArea");
tolua_usertype(tolua_S,"cTracer");
tolua_usertype(tolua_S,"cStringMap");
- tolua_usertype(tolua_S,"cServer");
- tolua_usertype(tolua_S,"Vector3i");
tolua_usertype(tolua_S,"cBlockEntity");
tolua_usertype(tolua_S,"cCriticalSection");
tolua_usertype(tolua_S,"HTTPTemplateRequest");
+ tolua_usertype(tolua_S,"cBoundingBox");
+ tolua_usertype(tolua_S,"cServer");
+ tolua_usertype(tolua_S,"Vector3i");
tolua_usertype(tolua_S,"cFile");
- tolua_usertype(tolua_S,"std::vector<std::string>");
+ tolua_usertype(tolua_S,"cItems");
tolua_usertype(tolua_S,"cClientHandle");
+ tolua_usertype(tolua_S,"cIniFile");
tolua_usertype(tolua_S,"cChatColor");
tolua_usertype(tolua_S,"cWebPlugin");
- tolua_usertype(tolua_S,"cWebAdmin");
- tolua_usertype(tolua_S,"cIniFile");
- tolua_usertype(tolua_S,"sWebAdminPage");
- tolua_usertype(tolua_S,"cItem");
tolua_usertype(tolua_S,"cPawn");
- tolua_usertype(tolua_S,"cPlayer");
+ tolua_usertype(tolua_S,"cThrownEggEntity");
tolua_usertype(tolua_S,"cGroupManager");
- tolua_usertype(tolua_S,"cBlockEntityWindowOwner");
- tolua_usertype(tolua_S,"HTTPRequest");
+ tolua_usertype(tolua_S,"cWebAdmin");
+ tolua_usertype(tolua_S,"cItem");
tolua_usertype(tolua_S,"cProjectileEntity");
+ tolua_usertype(tolua_S,"HTTPRequest");
tolua_usertype(tolua_S,"cItemGrid::cListener");
tolua_usertype(tolua_S,"cDropperEntity");
- tolua_usertype(tolua_S,"cThrownEggEntity");
}
/* method: new of class cIniFile */
@@ -337,59 +345,6 @@ static int tolua_AllToLua_cIniFile_new00_local(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* method: new of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_new01
-static int tolua_AllToLua_cIniFile_new01(lua_State* tolua_S)
-{
- tolua_Error tolua_err;
- if (
- !tolua_isusertable(tolua_S,1,"cIniFile",0,&tolua_err) ||
- !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,3,&tolua_err)
- )
- goto tolua_lerror;
- else
- {
- const std::string a_Path = ((const std::string) tolua_tocppstring(tolua_S,2,0));
- {
- cIniFile* tolua_ret = (cIniFile*) Mtolua_new((cIniFile)(a_Path));
- tolua_pushusertype(tolua_S,(void*)tolua_ret,"cIniFile");
- tolua_pushcppstring(tolua_S,(const char*)a_Path);
- }
- }
- return 2;
-tolua_lerror:
- return tolua_AllToLua_cIniFile_new00(tolua_S);
-}
-#endif //#ifndef TOLUA_DISABLE
-
-/* method: new_local of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_new01_local
-static int tolua_AllToLua_cIniFile_new01_local(lua_State* tolua_S)
-{
- tolua_Error tolua_err;
- if (
- !tolua_isusertable(tolua_S,1,"cIniFile",0,&tolua_err) ||
- !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,3,&tolua_err)
- )
- goto tolua_lerror;
- else
- {
- const std::string a_Path = ((const std::string) tolua_tocppstring(tolua_S,2,0));
- {
- cIniFile* tolua_ret = (cIniFile*) Mtolua_new((cIniFile)(a_Path));
- tolua_pushusertype(tolua_S,(void*)tolua_ret,"cIniFile");
- tolua_register_gc(tolua_S,lua_gettop(tolua_S));
- tolua_pushcppstring(tolua_S,(const char*)a_Path);
- }
- }
- return 2;
-tolua_lerror:
- return tolua_AllToLua_cIniFile_new00_local(tolua_S);
-}
-#endif //#ifndef TOLUA_DISABLE
-
/* method: CaseSensitive of class cIniFile */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_CaseSensitive00
static int tolua_AllToLua_cIniFile_CaseSensitive00(lua_State* tolua_S)
@@ -452,101 +407,6 @@ static int tolua_AllToLua_cIniFile_CaseInsensitive00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* method: Path of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_Path00
-static int tolua_AllToLua_cIniFile_Path00(lua_State* tolua_S)
-{
-#ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
- !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,3,&tolua_err)
- )
- goto tolua_lerror;
- else
-#endif
- {
- cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string newPath = ((const std::string) tolua_tocppstring(tolua_S,2,0));
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Path'", NULL);
-#endif
- {
- self->Path(newPath);
- tolua_pushcppstring(tolua_S,(const char*)newPath);
- }
- }
- return 1;
-#ifndef TOLUA_RELEASE
- tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'Path'.",&tolua_err);
- return 0;
-#endif
-}
-#endif //#ifndef TOLUA_DISABLE
-
-/* method: Path of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_Path01
-static int tolua_AllToLua_cIniFile_Path01(lua_State* tolua_S)
-{
- tolua_Error tolua_err;
- if (
- !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,2,&tolua_err)
- )
- goto tolua_lerror;
- else
- {
- const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Path'", NULL);
-#endif
- {
- const std::string tolua_ret = (const std::string) self->Path();
- tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
- }
- }
- return 1;
-tolua_lerror:
- return tolua_AllToLua_cIniFile_Path00(tolua_S);
-}
-#endif //#ifndef TOLUA_DISABLE
-
-/* method: SetPath of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetPath00
-static int tolua_AllToLua_cIniFile_SetPath00(lua_State* tolua_S)
-{
-#ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
- !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,3,&tolua_err)
- )
- goto tolua_lerror;
- else
-#endif
- {
- cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string newPath = ((const std::string) tolua_tocppstring(tolua_S,2,0));
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPath'", NULL);
-#endif
- {
- self->SetPath(newPath);
- tolua_pushcppstring(tolua_S,(const char*)newPath);
- }
- }
- return 1;
-#ifndef TOLUA_RELEASE
- tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'SetPath'.",&tolua_err);
- return 0;
-#endif
-}
-#endif //#ifndef TOLUA_DISABLE
-
/* method: ReadFile of class cIniFile */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_ReadFile00
static int tolua_AllToLua_cIniFile_ReadFile00(lua_State* tolua_S)
@@ -555,24 +415,27 @@ static int tolua_AllToLua_cIniFile_ReadFile00(lua_State* tolua_S)
tolua_Error tolua_err;
if (
!tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
- !tolua_isboolean(tolua_S,2,1,&tolua_err) ||
- !tolua_isnoobj(tolua_S,3,&tolua_err)
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,3,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- bool a_AllowExampleRedirect = ((bool) tolua_toboolean(tolua_S,2,true));
+ const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ bool a_AllowExampleRedirect = ((bool) tolua_toboolean(tolua_S,3,true));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReadFile'", NULL);
#endif
{
- bool tolua_ret = (bool) self->ReadFile(a_AllowExampleRedirect);
+ bool tolua_ret = (bool) self->ReadFile(a_FileName,a_AllowExampleRedirect);
tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_FileName);
}
}
- return 1;
+ return 2;
#ifndef TOLUA_RELEASE
tolua_lerror:
tolua_error(tolua_S,"#ferror in function 'ReadFile'.",&tolua_err);
@@ -589,22 +452,25 @@ static int tolua_AllToLua_cIniFile_WriteFile00(lua_State* tolua_S)
tolua_Error tolua_err;
if (
!tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,2,&tolua_err)
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WriteFile'", NULL);
#endif
{
- bool tolua_ret = (bool) self->WriteFile();
+ bool tolua_ret = (bool) self->WriteFile(a_FileName);
tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_FileName);
}
}
- return 1;
+ return 2;
#ifndef TOLUA_RELEASE
tolua_lerror:
tolua_error(tolua_S,"#ferror in function 'WriteFile'.",&tolua_err);
@@ -644,68 +510,6 @@ static int tolua_AllToLua_cIniFile_Clear00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* method: Reset of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_Reset00
-static int tolua_AllToLua_cIniFile_Reset00(lua_State* tolua_S)
-{
-#ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,2,&tolua_err)
- )
- goto tolua_lerror;
- else
-#endif
- {
- cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Reset'", NULL);
-#endif
- {
- self->Reset();
- }
- }
- return 0;
-#ifndef TOLUA_RELEASE
- tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'Reset'.",&tolua_err);
- return 0;
-#endif
-}
-#endif //#ifndef TOLUA_DISABLE
-
-/* method: Erase of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_Erase00
-static int tolua_AllToLua_cIniFile_Erase00(lua_State* tolua_S)
-{
-#ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,2,&tolua_err)
- )
- goto tolua_lerror;
- else
-#endif
- {
- cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Erase'", NULL);
-#endif
- {
- self->Erase();
- }
- }
- return 0;
-#ifndef TOLUA_RELEASE
- tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'Erase'.",&tolua_err);
- return 0;
-#endif
-}
-#endif //#ifndef TOLUA_DISABLE
-
/* method: FindKey of class cIniFile */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_FindKey00
static int tolua_AllToLua_cIniFile_FindKey00(lua_State* tolua_S)
@@ -722,12 +526,12 @@ static int tolua_AllToLua_cIniFile_FindKey00(lua_State* tolua_S)
#endif
{
const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string keyname = ((const std::string) tolua_tocppstring(tolua_S,2,0));
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FindKey'", NULL);
#endif
{
- long tolua_ret = (long) self->FindKey(keyname);
+ int tolua_ret = (int) self->FindKey(keyname);
tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
tolua_pushcppstring(tolua_S,(const char*)keyname);
}
@@ -758,13 +562,13 @@ static int tolua_AllToLua_cIniFile_FindValue00(lua_State* tolua_S)
#endif
{
const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
- const unsigned keyID = ((const unsigned) tolua_tonumber(tolua_S,2,0));
- const std::string valuename = ((const std::string) tolua_tocppstring(tolua_S,3,0));
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FindValue'", NULL);
#endif
{
- long tolua_ret = (long) self->FindValue(keyID,valuename);
+ int tolua_ret = (int) self->FindValue(keyID,valuename);
tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
tolua_pushcppstring(tolua_S,(const char*)valuename);
}
@@ -778,38 +582,6 @@ static int tolua_AllToLua_cIniFile_FindValue00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* method: NumKeys of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_NumKeys00
-static int tolua_AllToLua_cIniFile_NumKeys00(lua_State* tolua_S)
-{
-#ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,2,&tolua_err)
- )
- goto tolua_lerror;
- else
-#endif
- {
- const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NumKeys'", NULL);
-#endif
- {
- unsigned tolua_ret = (unsigned) self->NumKeys();
- tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
- }
- }
- return 1;
-#ifndef TOLUA_RELEASE
- tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'NumKeys'.",&tolua_err);
- return 0;
-#endif
-}
-#endif //#ifndef TOLUA_DISABLE
-
/* method: GetNumKeys of class cIniFile */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumKeys00
static int tolua_AllToLua_cIniFile_GetNumKeys00(lua_State* tolua_S)
@@ -829,7 +601,7 @@ static int tolua_AllToLua_cIniFile_GetNumKeys00(lua_State* tolua_S)
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumKeys'", NULL);
#endif
{
- unsigned tolua_ret = (unsigned) self->GetNumKeys();
+ int tolua_ret = (int) self->GetNumKeys();
tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
}
}
@@ -858,12 +630,12 @@ static int tolua_AllToLua_cIniFile_AddKeyName00(lua_State* tolua_S)
#endif
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string keyname = ((const std::string) tolua_tocppstring(tolua_S,2,0));
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddKeyName'", NULL);
#endif
{
- unsigned tolua_ret = (unsigned) self->AddKeyName(keyname);
+ int tolua_ret = (int) self->AddKeyName(keyname);
tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
tolua_pushcppstring(tolua_S,(const char*)keyname);
}
@@ -877,40 +649,6 @@ static int tolua_AllToLua_cIniFile_AddKeyName00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* method: KeyName of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_KeyName00
-static int tolua_AllToLua_cIniFile_KeyName00(lua_State* tolua_S)
-{
-#ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
- !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,3,&tolua_err)
- )
- goto tolua_lerror;
- else
-#endif
- {
- const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
- const unsigned keyID = ((const unsigned) tolua_tonumber(tolua_S,2,0));
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'KeyName'", NULL);
-#endif
- {
- std::string tolua_ret = (std::string) self->KeyName(keyID);
- tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
- }
- }
- return 1;
-#ifndef TOLUA_RELEASE
- tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'KeyName'.",&tolua_err);
- return 0;
-#endif
-}
-#endif //#ifndef TOLUA_DISABLE
-
/* method: GetKeyName of class cIniFile */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetKeyName00
static int tolua_AllToLua_cIniFile_GetKeyName00(lua_State* tolua_S)
@@ -927,12 +665,12 @@ static int tolua_AllToLua_cIniFile_GetKeyName00(lua_State* tolua_S)
#endif
{
const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
- const unsigned keyID = ((const unsigned) tolua_tonumber(tolua_S,2,0));
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKeyName'", NULL);
#endif
{
- std::string tolua_ret = (std::string) self->GetKeyName(keyID);
+ AString tolua_ret = (AString) self->GetKeyName(keyID);
tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
}
}
@@ -945,41 +683,6 @@ static int tolua_AllToLua_cIniFile_GetKeyName00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* method: NumValues of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_NumValues00
-static int tolua_AllToLua_cIniFile_NumValues00(lua_State* tolua_S)
-{
-#ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
- !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,3,&tolua_err)
- )
- goto tolua_lerror;
- else
-#endif
- {
- cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string keyname = ((const std::string) tolua_tocppstring(tolua_S,2,0));
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NumValues'", NULL);
-#endif
- {
- unsigned tolua_ret = (unsigned) self->NumValues(keyname);
- tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
- tolua_pushcppstring(tolua_S,(const char*)keyname);
- }
- }
- return 2;
-#ifndef TOLUA_RELEASE
- tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'NumValues'.",&tolua_err);
- return 0;
-#endif
-}
-#endif //#ifndef TOLUA_DISABLE
-
/* method: GetNumValues of class cIniFile */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumValues00
static int tolua_AllToLua_cIniFile_GetNumValues00(lua_State* tolua_S)
@@ -987,7 +690,7 @@ static int tolua_AllToLua_cIniFile_GetNumValues00(lua_State* tolua_S)
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
- !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
!tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
!tolua_isnoobj(tolua_S,3,&tolua_err)
)
@@ -995,13 +698,13 @@ static int tolua_AllToLua_cIniFile_GetNumValues00(lua_State* tolua_S)
else
#endif
{
- cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string keyname = ((const std::string) tolua_tocppstring(tolua_S,2,0));
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumValues'", NULL);
#endif
{
- unsigned tolua_ret = (unsigned) self->GetNumValues(keyname);
+ int tolua_ret = (int) self->GetNumValues(keyname);
tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
tolua_pushcppstring(tolua_S,(const char*)keyname);
}
@@ -1015,55 +718,26 @@ static int tolua_AllToLua_cIniFile_GetNumValues00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* method: NumValues of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_NumValues01
-static int tolua_AllToLua_cIniFile_NumValues01(lua_State* tolua_S)
-{
- tolua_Error tolua_err;
- if (
- !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
- !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,3,&tolua_err)
- )
- goto tolua_lerror;
- else
- {
- cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const unsigned keyID = ((const unsigned) tolua_tonumber(tolua_S,2,0));
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NumValues'", NULL);
-#endif
- {
- unsigned tolua_ret = (unsigned) self->NumValues(keyID);
- tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
- }
- }
- return 1;
-tolua_lerror:
- return tolua_AllToLua_cIniFile_NumValues00(tolua_S);
-}
-#endif //#ifndef TOLUA_DISABLE
-
/* method: GetNumValues of class cIniFile */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumValues01
static int tolua_AllToLua_cIniFile_GetNumValues01(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (
- !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
!tolua_isnumber(tolua_S,2,0,&tolua_err) ||
!tolua_isnoobj(tolua_S,3,&tolua_err)
)
goto tolua_lerror;
else
{
- cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const unsigned keyID = ((const unsigned) tolua_tonumber(tolua_S,2,0));
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumValues'", NULL);
#endif
{
- unsigned tolua_ret = (unsigned) self->GetNumValues(keyID);
+ int tolua_ret = (int) self->GetNumValues(keyID);
tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
}
}
@@ -1073,43 +747,6 @@ tolua_lerror:
}
#endif //#ifndef TOLUA_DISABLE
-/* method: ValueName of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_ValueName00
-static int tolua_AllToLua_cIniFile_ValueName00(lua_State* tolua_S)
-{
-#ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
- !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
- !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,4,&tolua_err)
- )
- goto tolua_lerror;
- else
-#endif
- {
- const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string keyname = ((const std::string) tolua_tocppstring(tolua_S,2,0));
- const unsigned valueID = ((const unsigned) tolua_tonumber(tolua_S,3,0));
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ValueName'", NULL);
-#endif
- {
- std::string tolua_ret = (std::string) self->ValueName(keyname,valueID);
- tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
- tolua_pushcppstring(tolua_S,(const char*)keyname);
- }
- }
- return 2;
-#ifndef TOLUA_RELEASE
- tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'ValueName'.",&tolua_err);
- return 0;
-#endif
-}
-#endif //#ifndef TOLUA_DISABLE
-
/* method: GetValueName of class cIniFile */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueName00
static int tolua_AllToLua_cIniFile_GetValueName00(lua_State* tolua_S)
@@ -1127,13 +764,13 @@ static int tolua_AllToLua_cIniFile_GetValueName00(lua_State* tolua_S)
#endif
{
const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string keyname = ((const std::string) tolua_tocppstring(tolua_S,2,0));
- const unsigned valueID = ((const unsigned) tolua_tonumber(tolua_S,3,0));
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const int valueID = ((const int) tolua_tonumber(tolua_S,3,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueName'", NULL);
#endif
{
- std::string tolua_ret = (std::string) self->GetValueName(keyname,valueID);
+ AString tolua_ret = (AString) self->GetValueName(keyname,valueID);
tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
tolua_pushcppstring(tolua_S,(const char*)keyname);
}
@@ -1147,37 +784,6 @@ static int tolua_AllToLua_cIniFile_GetValueName00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* method: ValueName of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_ValueName01
-static int tolua_AllToLua_cIniFile_ValueName01(lua_State* tolua_S)
-{
- tolua_Error tolua_err;
- if (
- !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
- !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
- !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,4,&tolua_err)
- )
- goto tolua_lerror;
- else
- {
- const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
- const unsigned keyID = ((const unsigned) tolua_tonumber(tolua_S,2,0));
- const unsigned valueID = ((const unsigned) tolua_tonumber(tolua_S,3,0));
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ValueName'", NULL);
-#endif
- {
- std::string tolua_ret = (std::string) self->ValueName(keyID,valueID);
- tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
- }
- }
- return 1;
-tolua_lerror:
- return tolua_AllToLua_cIniFile_ValueName00(tolua_S);
-}
-#endif //#ifndef TOLUA_DISABLE
-
/* method: GetValueName of class cIniFile */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueName01
static int tolua_AllToLua_cIniFile_GetValueName01(lua_State* tolua_S)
@@ -1193,13 +799,13 @@ static int tolua_AllToLua_cIniFile_GetValueName01(lua_State* tolua_S)
else
{
const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
- const unsigned keyID = ((const unsigned) tolua_tonumber(tolua_S,2,0));
- const unsigned valueID = ((const unsigned) tolua_tonumber(tolua_S,3,0));
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const int valueID = ((const int) tolua_tonumber(tolua_S,3,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueName'", NULL);
#endif
{
- std::string tolua_ret = (std::string) self->GetValueName(keyID,valueID);
+ AString tolua_ret = (AString) self->GetValueName(keyID,valueID);
tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
}
}
@@ -1298,8 +904,8 @@ static int tolua_AllToLua_cIniFile_GetValue02(lua_State* tolua_S)
else
{
const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
- const unsigned keyID = ((const unsigned) tolua_tonumber(tolua_S,2,0));
- const unsigned valueID = ((const unsigned) tolua_tonumber(tolua_S,3,0));
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const int valueID = ((const int) tolua_tonumber(tolua_S,3,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL);
#endif
@@ -1330,8 +936,8 @@ static int tolua_AllToLua_cIniFile_GetValue03(lua_State* tolua_S)
else
{
const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
- const unsigned keyID = ((const unsigned) tolua_tonumber(tolua_S,2,0));
- const unsigned valueID = ((const unsigned) tolua_tonumber(tolua_S,3,0));
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const int valueID = ((const int) tolua_tonumber(tolua_S,3,0));
const AString defValue = ((const AString) tolua_tocppstring(tolua_S,4,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL);
@@ -1680,9 +1286,9 @@ static int tolua_AllToLua_cIniFile_SetValue00(lua_State* tolua_S)
#endif
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const unsigned keyID = ((const unsigned) tolua_tonumber(tolua_S,2,0));
- const unsigned valueID = ((const unsigned) tolua_tonumber(tolua_S,3,0));
- const std::string value = ((const std::string) tolua_tocppstring(tolua_S,4,0));
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const int valueID = ((const int) tolua_tonumber(tolua_S,3,0));
+ const AString value = ((const AString) tolua_tocppstring(tolua_S,4,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValue'", NULL);
#endif
@@ -1718,9 +1324,9 @@ static int tolua_AllToLua_cIniFile_SetValue01(lua_State* tolua_S)
else
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string keyname = ((const std::string) tolua_tocppstring(tolua_S,2,0));
- const std::string valuename = ((const std::string) tolua_tocppstring(tolua_S,3,0));
- const std::string value = ((const std::string) tolua_tocppstring(tolua_S,4,0));
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
+ const AString value = ((const AString) tolua_tocppstring(tolua_S,4,0));
const bool create = ((const bool) tolua_toboolean(tolua_S,5,true));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValue'", NULL);
@@ -1758,8 +1364,8 @@ static int tolua_AllToLua_cIniFile_SetValueI00(lua_State* tolua_S)
#endif
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string keyname = ((const std::string) tolua_tocppstring(tolua_S,2,0));
- const std::string valuename = ((const std::string) tolua_tocppstring(tolua_S,3,0));
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
const int value = ((const int) tolua_tonumber(tolua_S,4,0));
const bool create = ((const bool) tolua_toboolean(tolua_S,5,true));
#ifndef TOLUA_RELEASE
@@ -1800,8 +1406,8 @@ static int tolua_AllToLua_cIniFile_SetValueB00(lua_State* tolua_S)
#endif
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string keyname = ((const std::string) tolua_tocppstring(tolua_S,2,0));
- const std::string valuename = ((const std::string) tolua_tocppstring(tolua_S,3,0));
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
const bool value = ((const bool) tolua_toboolean(tolua_S,4,0));
const bool create = ((const bool) tolua_toboolean(tolua_S,5,true));
#ifndef TOLUA_RELEASE
@@ -1842,8 +1448,8 @@ static int tolua_AllToLua_cIniFile_SetValueF00(lua_State* tolua_S)
#endif
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string keyname = ((const std::string) tolua_tocppstring(tolua_S,2,0));
- const std::string valuename = ((const std::string) tolua_tocppstring(tolua_S,3,0));
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
const double value = ((const double) tolua_tonumber(tolua_S,4,0));
const bool create = ((const bool) tolua_toboolean(tolua_S,5,true));
#ifndef TOLUA_RELEASE
@@ -1882,8 +1488,8 @@ static int tolua_AllToLua_cIniFile_DeleteValueByID00(lua_State* tolua_S)
#endif
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const unsigned keyID = ((const unsigned) tolua_tonumber(tolua_S,2,0));
- const unsigned valueID = ((const unsigned) tolua_tonumber(tolua_S,3,0));
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const int valueID = ((const int) tolua_tonumber(tolua_S,3,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteValueByID'", NULL);
#endif
@@ -1918,8 +1524,8 @@ static int tolua_AllToLua_cIniFile_DeleteValue00(lua_State* tolua_S)
#endif
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string keyname = ((const std::string) tolua_tocppstring(tolua_S,2,0));
- const std::string valuename = ((const std::string) tolua_tocppstring(tolua_S,3,0));
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteValue'", NULL);
#endif
@@ -1955,7 +1561,7 @@ static int tolua_AllToLua_cIniFile_DeleteKey00(lua_State* tolua_S)
#endif
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string keyname = ((const std::string) tolua_tocppstring(tolua_S,2,0));
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKey'", NULL);
#endif
@@ -1974,9 +1580,9 @@ static int tolua_AllToLua_cIniFile_DeleteKey00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* method: NumHeaderComments of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_NumHeaderComments00
-static int tolua_AllToLua_cIniFile_NumHeaderComments00(lua_State* tolua_S)
+/* method: GetNumHeaderComments of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumHeaderComments00
+static int tolua_AllToLua_cIniFile_GetNumHeaderComments00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
@@ -1990,25 +1596,25 @@ static int tolua_AllToLua_cIniFile_NumHeaderComments00(lua_State* tolua_S)
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NumHeaderComments'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumHeaderComments'", NULL);
#endif
{
- unsigned tolua_ret = (unsigned) self->NumHeaderComments();
+ int tolua_ret = (int) self->GetNumHeaderComments();
tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
}
}
return 1;
#ifndef TOLUA_RELEASE
tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'NumHeaderComments'.",&tolua_err);
+ tolua_error(tolua_S,"#ferror in function 'GetNumHeaderComments'.",&tolua_err);
return 0;
#endif
}
#endif //#ifndef TOLUA_DISABLE
-/* method: HeaderComment of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_HeaderComment00
-static int tolua_AllToLua_cIniFile_HeaderComment00(lua_State* tolua_S)
+/* method: AddHeaderComment of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddHeaderComment00
+static int tolua_AllToLua_cIniFile_AddHeaderComment00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
@@ -2022,28 +1628,29 @@ static int tolua_AllToLua_cIniFile_HeaderComment00(lua_State* tolua_S)
#endif
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string comment = ((const std::string) tolua_tocppstring(tolua_S,2,0));
+ const AString comment = ((const AString) tolua_tocppstring(tolua_S,2,0));
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HeaderComment'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddHeaderComment'", NULL);
#endif
{
- self->HeaderComment(comment);
+ self->AddHeaderComment(comment);
tolua_pushcppstring(tolua_S,(const char*)comment);
}
}
return 1;
#ifndef TOLUA_RELEASE
tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'HeaderComment'.",&tolua_err);
+ tolua_error(tolua_S,"#ferror in function 'AddHeaderComment'.",&tolua_err);
return 0;
#endif
}
#endif //#ifndef TOLUA_DISABLE
-/* method: HeaderComment of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_HeaderComment01
-static int tolua_AllToLua_cIniFile_HeaderComment01(lua_State* tolua_S)
+/* method: GetHeaderComment of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetHeaderComment00
+static int tolua_AllToLua_cIniFile_GetHeaderComment00(lua_State* tolua_S)
{
+#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
!tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
@@ -2052,20 +1659,24 @@ static int tolua_AllToLua_cIniFile_HeaderComment01(lua_State* tolua_S)
)
goto tolua_lerror;
else
+#endif
{
const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
- const unsigned commentID = ((const unsigned) tolua_tonumber(tolua_S,2,0));
+ const int commentID = ((const int) tolua_tonumber(tolua_S,2,0));
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HeaderComment'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeaderComment'", NULL);
#endif
{
- std::string tolua_ret = (std::string) self->HeaderComment(commentID);
+ AString tolua_ret = (AString) self->GetHeaderComment(commentID);
tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
}
}
return 1;
-tolua_lerror:
- return tolua_AllToLua_cIniFile_HeaderComment00(tolua_S);
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetHeaderComment'.",&tolua_err);
+ return 0;
+#endif
}
#endif //#ifndef TOLUA_DISABLE
@@ -2085,7 +1696,7 @@ static int tolua_AllToLua_cIniFile_DeleteHeaderComment00(lua_State* tolua_S)
#endif
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- unsigned commentID = ((unsigned) tolua_tonumber(tolua_S,2,0));
+ int commentID = ((int) tolua_tonumber(tolua_S,2,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteHeaderComment'", NULL);
#endif
@@ -2134,9 +1745,9 @@ static int tolua_AllToLua_cIniFile_DeleteHeaderComments00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* method: NumKeyComments of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_NumKeyComments00
-static int tolua_AllToLua_cIniFile_NumKeyComments00(lua_State* tolua_S)
+/* method: GetNumKeyComments of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumKeyComments00
+static int tolua_AllToLua_cIniFile_GetNumKeyComments00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
@@ -2150,27 +1761,27 @@ static int tolua_AllToLua_cIniFile_NumKeyComments00(lua_State* tolua_S)
#endif
{
const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
- const unsigned keyID = ((const unsigned) tolua_tonumber(tolua_S,2,0));
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NumKeyComments'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumKeyComments'", NULL);
#endif
{
- unsigned tolua_ret = (unsigned) self->NumKeyComments(keyID);
+ int tolua_ret = (int) self->GetNumKeyComments(keyID);
tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
}
}
return 1;
#ifndef TOLUA_RELEASE
tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'NumKeyComments'.",&tolua_err);
+ tolua_error(tolua_S,"#ferror in function 'GetNumKeyComments'.",&tolua_err);
return 0;
#endif
}
#endif //#ifndef TOLUA_DISABLE
-/* method: NumKeyComments of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_NumKeyComments01
-static int tolua_AllToLua_cIniFile_NumKeyComments01(lua_State* tolua_S)
+/* method: GetNumKeyComments of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumKeyComments01
+static int tolua_AllToLua_cIniFile_GetNumKeyComments01(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (
@@ -2182,25 +1793,25 @@ static int tolua_AllToLua_cIniFile_NumKeyComments01(lua_State* tolua_S)
else
{
const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string keyname = ((const std::string) tolua_tocppstring(tolua_S,2,0));
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NumKeyComments'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumKeyComments'", NULL);
#endif
{
- unsigned tolua_ret = (unsigned) self->NumKeyComments(keyname);
+ int tolua_ret = (int) self->GetNumKeyComments(keyname);
tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
tolua_pushcppstring(tolua_S,(const char*)keyname);
}
}
return 2;
tolua_lerror:
- return tolua_AllToLua_cIniFile_NumKeyComments00(tolua_S);
+ return tolua_AllToLua_cIniFile_GetNumKeyComments00(tolua_S);
}
#endif //#ifndef TOLUA_DISABLE
-/* method: KeyComment of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_KeyComment00
-static int tolua_AllToLua_cIniFile_KeyComment00(lua_State* tolua_S)
+/* method: AddKeyComment of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddKeyComment00
+static int tolua_AllToLua_cIniFile_AddKeyComment00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
@@ -2215,13 +1826,13 @@ static int tolua_AllToLua_cIniFile_KeyComment00(lua_State* tolua_S)
#endif
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const unsigned keyID = ((const unsigned) tolua_tonumber(tolua_S,2,0));
- const std::string comment = ((const std::string) tolua_tocppstring(tolua_S,3,0));
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const AString comment = ((const AString) tolua_tocppstring(tolua_S,3,0));
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'KeyComment'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddKeyComment'", NULL);
#endif
{
- bool tolua_ret = (bool) self->KeyComment(keyID,comment);
+ bool tolua_ret = (bool) self->AddKeyComment(keyID,comment);
tolua_pushboolean(tolua_S,(bool)tolua_ret);
tolua_pushcppstring(tolua_S,(const char*)comment);
}
@@ -2229,15 +1840,15 @@ static int tolua_AllToLua_cIniFile_KeyComment00(lua_State* tolua_S)
return 2;
#ifndef TOLUA_RELEASE
tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'KeyComment'.",&tolua_err);
+ tolua_error(tolua_S,"#ferror in function 'AddKeyComment'.",&tolua_err);
return 0;
#endif
}
#endif //#ifndef TOLUA_DISABLE
-/* method: KeyComment of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_KeyComment01
-static int tolua_AllToLua_cIniFile_KeyComment01(lua_State* tolua_S)
+/* method: AddKeyComment of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddKeyComment01
+static int tolua_AllToLua_cIniFile_AddKeyComment01(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (
@@ -2250,13 +1861,13 @@ static int tolua_AllToLua_cIniFile_KeyComment01(lua_State* tolua_S)
else
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string keyname = ((const std::string) tolua_tocppstring(tolua_S,2,0));
- const std::string comment = ((const std::string) tolua_tocppstring(tolua_S,3,0));
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString comment = ((const AString) tolua_tocppstring(tolua_S,3,0));
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'KeyComment'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddKeyComment'", NULL);
#endif
{
- bool tolua_ret = (bool) self->KeyComment(keyname,comment);
+ bool tolua_ret = (bool) self->AddKeyComment(keyname,comment);
tolua_pushboolean(tolua_S,(bool)tolua_ret);
tolua_pushcppstring(tolua_S,(const char*)keyname);
tolua_pushcppstring(tolua_S,(const char*)comment);
@@ -2264,14 +1875,15 @@ static int tolua_AllToLua_cIniFile_KeyComment01(lua_State* tolua_S)
}
return 3;
tolua_lerror:
- return tolua_AllToLua_cIniFile_KeyComment00(tolua_S);
+ return tolua_AllToLua_cIniFile_AddKeyComment00(tolua_S);
}
#endif //#ifndef TOLUA_DISABLE
-/* method: KeyComment of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_KeyComment02
-static int tolua_AllToLua_cIniFile_KeyComment02(lua_State* tolua_S)
+/* method: GetKeyComment of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetKeyComment00
+static int tolua_AllToLua_cIniFile_GetKeyComment00(lua_State* tolua_S)
{
+#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
!tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
@@ -2281,27 +1893,31 @@ static int tolua_AllToLua_cIniFile_KeyComment02(lua_State* tolua_S)
)
goto tolua_lerror;
else
+#endif
{
const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
- const unsigned keyID = ((const unsigned) tolua_tonumber(tolua_S,2,0));
- const unsigned commentID = ((const unsigned) tolua_tonumber(tolua_S,3,0));
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const int commentID = ((const int) tolua_tonumber(tolua_S,3,0));
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'KeyComment'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKeyComment'", NULL);
#endif
{
- std::string tolua_ret = (std::string) self->KeyComment(keyID,commentID);
+ AString tolua_ret = (AString) self->GetKeyComment(keyID,commentID);
tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
}
}
return 1;
-tolua_lerror:
- return tolua_AllToLua_cIniFile_KeyComment01(tolua_S);
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetKeyComment'.",&tolua_err);
+ return 0;
+#endif
}
#endif //#ifndef TOLUA_DISABLE
-/* method: KeyComment of class cIniFile */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_KeyComment03
-static int tolua_AllToLua_cIniFile_KeyComment03(lua_State* tolua_S)
+/* method: GetKeyComment of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetKeyComment01
+static int tolua_AllToLua_cIniFile_GetKeyComment01(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (
@@ -2314,20 +1930,20 @@ static int tolua_AllToLua_cIniFile_KeyComment03(lua_State* tolua_S)
else
{
const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string keyname = ((const std::string) tolua_tocppstring(tolua_S,2,0));
- const unsigned commentID = ((const unsigned) tolua_tonumber(tolua_S,3,0));
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const int commentID = ((const int) tolua_tonumber(tolua_S,3,0));
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'KeyComment'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKeyComment'", NULL);
#endif
{
- std::string tolua_ret = (std::string) self->KeyComment(keyname,commentID);
+ AString tolua_ret = (AString) self->GetKeyComment(keyname,commentID);
tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
tolua_pushcppstring(tolua_S,(const char*)keyname);
}
}
return 2;
tolua_lerror:
- return tolua_AllToLua_cIniFile_KeyComment02(tolua_S);
+ return tolua_AllToLua_cIniFile_GetKeyComment00(tolua_S);
}
#endif //#ifndef TOLUA_DISABLE
@@ -2348,8 +1964,8 @@ static int tolua_AllToLua_cIniFile_DeleteKeyComment00(lua_State* tolua_S)
#endif
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const unsigned keyID = ((const unsigned) tolua_tonumber(tolua_S,2,0));
- const unsigned commentID = ((const unsigned) tolua_tonumber(tolua_S,3,0));
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const int commentID = ((const int) tolua_tonumber(tolua_S,3,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComment'", NULL);
#endif
@@ -2382,8 +1998,8 @@ static int tolua_AllToLua_cIniFile_DeleteKeyComment01(lua_State* tolua_S)
else
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string keyname = ((const std::string) tolua_tocppstring(tolua_S,2,0));
- const unsigned commentID = ((const unsigned) tolua_tonumber(tolua_S,3,0));
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const int commentID = ((const int) tolua_tonumber(tolua_S,3,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComment'", NULL);
#endif
@@ -2415,7 +2031,7 @@ static int tolua_AllToLua_cIniFile_DeleteKeyComments00(lua_State* tolua_S)
#endif
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const unsigned keyID = ((const unsigned) tolua_tonumber(tolua_S,2,0));
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComments'", NULL);
#endif
@@ -2447,7 +2063,7 @@ static int tolua_AllToLua_cIniFile_DeleteKeyComments01(lua_State* tolua_S)
else
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
- const std::string keyname = ((const std::string) tolua_tocppstring(tolua_S,2,0));
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComments'", NULL);
#endif
@@ -10065,24 +9681,26 @@ static int tolua_AllToLua_cPickup_new00(lua_State* tolua_S)
!tolua_isnumber(tolua_S,3,0,&tolua_err) ||
!tolua_isnumber(tolua_S,4,0,&tolua_err) ||
(tolua_isvaluenil(tolua_S,5,&tolua_err) || !tolua_isusertype(tolua_S,5,"const cItem",0,&tolua_err)) ||
- !tolua_isnumber(tolua_S,6,1,&tolua_err) ||
+ !tolua_isboolean(tolua_S,6,0,&tolua_err) ||
!tolua_isnumber(tolua_S,7,1,&tolua_err) ||
!tolua_isnumber(tolua_S,8,1,&tolua_err) ||
- !tolua_isnoobj(tolua_S,9,&tolua_err)
+ !tolua_isnumber(tolua_S,9,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,10,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
- double a_X = ((double) tolua_tonumber(tolua_S,2,0));
- double a_Y = ((double) tolua_tonumber(tolua_S,3,0));
- double a_Z = ((double) tolua_tonumber(tolua_S,4,0));
+ double a_PosX = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_PosY = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0));
const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,5,0));
- float a_SpeedX = ((float) tolua_tonumber(tolua_S,6,0.f));
- float a_SpeedY = ((float) tolua_tonumber(tolua_S,7,0.f));
- float a_SpeedZ = ((float) tolua_tonumber(tolua_S,8,0.f));
+ bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,6,0));
+ float a_SpeedX = ((float) tolua_tonumber(tolua_S,7,0.f));
+ float a_SpeedY = ((float) tolua_tonumber(tolua_S,8,0.f));
+ float a_SpeedZ = ((float) tolua_tonumber(tolua_S,9,0.f));
{
- cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_X,a_Y,a_Z,*a_Item,a_SpeedX,a_SpeedY,a_SpeedZ));
+ cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_PosX,a_PosY,a_PosZ,*a_Item,IsPlayerCreated,a_SpeedX,a_SpeedY,a_SpeedZ));
tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPickup");
}
}
@@ -10107,24 +9725,26 @@ static int tolua_AllToLua_cPickup_new00_local(lua_State* tolua_S)
!tolua_isnumber(tolua_S,3,0,&tolua_err) ||
!tolua_isnumber(tolua_S,4,0,&tolua_err) ||
(tolua_isvaluenil(tolua_S,5,&tolua_err) || !tolua_isusertype(tolua_S,5,"const cItem",0,&tolua_err)) ||
- !tolua_isnumber(tolua_S,6,1,&tolua_err) ||
+ !tolua_isboolean(tolua_S,6,0,&tolua_err) ||
!tolua_isnumber(tolua_S,7,1,&tolua_err) ||
!tolua_isnumber(tolua_S,8,1,&tolua_err) ||
- !tolua_isnoobj(tolua_S,9,&tolua_err)
+ !tolua_isnumber(tolua_S,9,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,10,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
- double a_X = ((double) tolua_tonumber(tolua_S,2,0));
- double a_Y = ((double) tolua_tonumber(tolua_S,3,0));
- double a_Z = ((double) tolua_tonumber(tolua_S,4,0));
+ double a_PosX = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_PosY = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0));
const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,5,0));
- float a_SpeedX = ((float) tolua_tonumber(tolua_S,6,0.f));
- float a_SpeedY = ((float) tolua_tonumber(tolua_S,7,0.f));
- float a_SpeedZ = ((float) tolua_tonumber(tolua_S,8,0.f));
+ bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,6,0));
+ float a_SpeedX = ((float) tolua_tonumber(tolua_S,7,0.f));
+ float a_SpeedY = ((float) tolua_tonumber(tolua_S,8,0.f));
+ float a_SpeedZ = ((float) tolua_tonumber(tolua_S,9,0.f));
{
- cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_X,a_Y,a_Z,*a_Item,a_SpeedX,a_SpeedY,a_SpeedZ));
+ cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_PosX,a_PosY,a_PosZ,*a_Item,IsPlayerCreated,a_SpeedX,a_SpeedY,a_SpeedZ));
tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPickup");
tolua_register_gc(tolua_S,lua_gettop(tolua_S));
}
@@ -10268,6 +9888,38 @@ static int tolua_AllToLua_cPickup_IsCollected00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
+/* method: IsPlayerCreated of class cPickup */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_IsPlayerCreated00
+static int tolua_AllToLua_cPickup_IsPlayerCreated00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPickup",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPickup* self = (const cPickup*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsPlayerCreated'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsPlayerCreated();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsPlayerCreated'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* method: GetProjectileKind of class cProjectileEntity */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cProjectileEntity_GetProjectileKind00
static int tolua_AllToLua_cProjectileEntity_GetProjectileKind00(lua_State* tolua_S)
@@ -12533,7 +12185,8 @@ static int tolua_AllToLua_cWorld_SpawnItemPickups00(lua_State* tolua_S)
!tolua_isnumber(tolua_S,4,0,&tolua_err) ||
!tolua_isnumber(tolua_S,5,0,&tolua_err) ||
!tolua_isnumber(tolua_S,6,1,&tolua_err) ||
- !tolua_isnoobj(tolua_S,7,&tolua_err)
+ !tolua_isboolean(tolua_S,7,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,8,&tolua_err)
)
goto tolua_lerror;
else
@@ -12545,11 +12198,12 @@ static int tolua_AllToLua_cWorld_SpawnItemPickups00(lua_State* tolua_S)
double a_BlockY = ((double) tolua_tonumber(tolua_S,4,0));
double a_BlockZ = ((double) tolua_tonumber(tolua_S,5,0));
double a_FlyAwaySpeed = ((double) tolua_tonumber(tolua_S,6,1.0));
+ bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,7,false));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnItemPickups'", NULL);
#endif
{
- self->SpawnItemPickups(*a_Pickups,a_BlockX,a_BlockY,a_BlockZ,a_FlyAwaySpeed);
+ self->SpawnItemPickups(*a_Pickups,a_BlockX,a_BlockY,a_BlockZ,a_FlyAwaySpeed,IsPlayerCreated);
}
}
return 0;
@@ -12575,7 +12229,8 @@ static int tolua_AllToLua_cWorld_SpawnItemPickups01(lua_State* tolua_S)
!tolua_isnumber(tolua_S,6,0,&tolua_err) ||
!tolua_isnumber(tolua_S,7,0,&tolua_err) ||
!tolua_isnumber(tolua_S,8,0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,9,&tolua_err)
+ !tolua_isboolean(tolua_S,9,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,10,&tolua_err)
)
goto tolua_lerror;
else
@@ -12588,11 +12243,12 @@ static int tolua_AllToLua_cWorld_SpawnItemPickups01(lua_State* tolua_S)
double a_SpeedX = ((double) tolua_tonumber(tolua_S,6,0));
double a_SpeedY = ((double) tolua_tonumber(tolua_S,7,0));
double a_SpeedZ = ((double) tolua_tonumber(tolua_S,8,0));
+ bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,9,false));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnItemPickups'", NULL);
#endif
{
- self->SpawnItemPickups(*a_Pickups,a_BlockX,a_BlockY,a_BlockZ,a_SpeedX,a_SpeedY,a_SpeedZ);
+ self->SpawnItemPickups(*a_Pickups,a_BlockX,a_BlockY,a_BlockZ,a_SpeedX,a_SpeedY,a_SpeedZ,IsPlayerCreated);
}
}
return 0;
@@ -18309,23 +17965,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 +18503,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 +18872,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 +18983,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 +19111,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 +28826,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: GetSpawnDelay of class cMonster */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetSpawnDelay00
+static int tolua_AllToLua_cMonster_GetSpawnDelay00(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::GetSpawnDelay(a_MobFamily);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSpawnDelay'.",&tolua_err);
return 0;
#endif
}
@@ -29278,33 +29095,19 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"new",tolua_AllToLua_cIniFile_new00);
tolua_function(tolua_S,"new_local",tolua_AllToLua_cIniFile_new00_local);
tolua_function(tolua_S,".call",tolua_AllToLua_cIniFile_new00_local);
- tolua_function(tolua_S,"new",tolua_AllToLua_cIniFile_new01);
- tolua_function(tolua_S,"new_local",tolua_AllToLua_cIniFile_new01_local);
- tolua_function(tolua_S,".call",tolua_AllToLua_cIniFile_new01_local);
tolua_function(tolua_S,"CaseSensitive",tolua_AllToLua_cIniFile_CaseSensitive00);
tolua_function(tolua_S,"CaseInsensitive",tolua_AllToLua_cIniFile_CaseInsensitive00);
- tolua_function(tolua_S,"Path",tolua_AllToLua_cIniFile_Path00);
- tolua_function(tolua_S,"Path",tolua_AllToLua_cIniFile_Path01);
- tolua_function(tolua_S,"SetPath",tolua_AllToLua_cIniFile_SetPath00);
tolua_function(tolua_S,"ReadFile",tolua_AllToLua_cIniFile_ReadFile00);
tolua_function(tolua_S,"WriteFile",tolua_AllToLua_cIniFile_WriteFile00);
tolua_function(tolua_S,"Clear",tolua_AllToLua_cIniFile_Clear00);
- tolua_function(tolua_S,"Reset",tolua_AllToLua_cIniFile_Reset00);
- tolua_function(tolua_S,"Erase",tolua_AllToLua_cIniFile_Erase00);
tolua_function(tolua_S,"FindKey",tolua_AllToLua_cIniFile_FindKey00);
tolua_function(tolua_S,"FindValue",tolua_AllToLua_cIniFile_FindValue00);
- tolua_function(tolua_S,"NumKeys",tolua_AllToLua_cIniFile_NumKeys00);
tolua_function(tolua_S,"GetNumKeys",tolua_AllToLua_cIniFile_GetNumKeys00);
tolua_function(tolua_S,"AddKeyName",tolua_AllToLua_cIniFile_AddKeyName00);
- tolua_function(tolua_S,"KeyName",tolua_AllToLua_cIniFile_KeyName00);
tolua_function(tolua_S,"GetKeyName",tolua_AllToLua_cIniFile_GetKeyName00);
- tolua_function(tolua_S,"NumValues",tolua_AllToLua_cIniFile_NumValues00);
tolua_function(tolua_S,"GetNumValues",tolua_AllToLua_cIniFile_GetNumValues00);
- tolua_function(tolua_S,"NumValues",tolua_AllToLua_cIniFile_NumValues01);
tolua_function(tolua_S,"GetNumValues",tolua_AllToLua_cIniFile_GetNumValues01);
- tolua_function(tolua_S,"ValueName",tolua_AllToLua_cIniFile_ValueName00);
tolua_function(tolua_S,"GetValueName",tolua_AllToLua_cIniFile_GetValueName00);
- tolua_function(tolua_S,"ValueName",tolua_AllToLua_cIniFile_ValueName01);
tolua_function(tolua_S,"GetValueName",tolua_AllToLua_cIniFile_GetValueName01);
tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue00);
tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue01);
@@ -29326,17 +29129,17 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"DeleteValueByID",tolua_AllToLua_cIniFile_DeleteValueByID00);
tolua_function(tolua_S,"DeleteValue",tolua_AllToLua_cIniFile_DeleteValue00);
tolua_function(tolua_S,"DeleteKey",tolua_AllToLua_cIniFile_DeleteKey00);
- tolua_function(tolua_S,"NumHeaderComments",tolua_AllToLua_cIniFile_NumHeaderComments00);
- tolua_function(tolua_S,"HeaderComment",tolua_AllToLua_cIniFile_HeaderComment00);
- tolua_function(tolua_S,"HeaderComment",tolua_AllToLua_cIniFile_HeaderComment01);
+ tolua_function(tolua_S,"GetNumHeaderComments",tolua_AllToLua_cIniFile_GetNumHeaderComments00);
+ tolua_function(tolua_S,"AddHeaderComment",tolua_AllToLua_cIniFile_AddHeaderComment00);
+ tolua_function(tolua_S,"GetHeaderComment",tolua_AllToLua_cIniFile_GetHeaderComment00);
tolua_function(tolua_S,"DeleteHeaderComment",tolua_AllToLua_cIniFile_DeleteHeaderComment00);
tolua_function(tolua_S,"DeleteHeaderComments",tolua_AllToLua_cIniFile_DeleteHeaderComments00);
- tolua_function(tolua_S,"NumKeyComments",tolua_AllToLua_cIniFile_NumKeyComments00);
- tolua_function(tolua_S,"NumKeyComments",tolua_AllToLua_cIniFile_NumKeyComments01);
- tolua_function(tolua_S,"KeyComment",tolua_AllToLua_cIniFile_KeyComment00);
- tolua_function(tolua_S,"KeyComment",tolua_AllToLua_cIniFile_KeyComment01);
- tolua_function(tolua_S,"KeyComment",tolua_AllToLua_cIniFile_KeyComment02);
- tolua_function(tolua_S,"KeyComment",tolua_AllToLua_cIniFile_KeyComment03);
+ tolua_function(tolua_S,"GetNumKeyComments",tolua_AllToLua_cIniFile_GetNumKeyComments00);
+ tolua_function(tolua_S,"GetNumKeyComments",tolua_AllToLua_cIniFile_GetNumKeyComments01);
+ tolua_function(tolua_S,"AddKeyComment",tolua_AllToLua_cIniFile_AddKeyComment00);
+ tolua_function(tolua_S,"AddKeyComment",tolua_AllToLua_cIniFile_AddKeyComment01);
+ tolua_function(tolua_S,"GetKeyComment",tolua_AllToLua_cIniFile_GetKeyComment00);
+ tolua_function(tolua_S,"GetKeyComment",tolua_AllToLua_cIniFile_GetKeyComment01);
tolua_function(tolua_S,"DeleteKeyComment",tolua_AllToLua_cIniFile_DeleteKeyComment00);
tolua_function(tolua_S,"DeleteKeyComment",tolua_AllToLua_cIniFile_DeleteKeyComment01);
tolua_function(tolua_S,"DeleteKeyComments",tolua_AllToLua_cIniFile_DeleteKeyComments00);
@@ -30320,6 +30123,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"CollectedBy",tolua_AllToLua_cPickup_CollectedBy00);
tolua_function(tolua_S,"GetAge",tolua_AllToLua_cPickup_GetAge00);
tolua_function(tolua_S,"IsCollected",tolua_AllToLua_cPickup_IsCollected00);
+ tolua_function(tolua_S,"IsPlayerCreated",tolua_AllToLua_cPickup_IsPlayerCreated00);
tolua_endmodule(tolua_S);
tolua_cclass(tolua_S,"cProjectileEntity","cProjectileEntity","cEntity",NULL);
tolua_beginmodule(tolua_S,"cProjectileEntity");
@@ -30747,7 +30551,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 +30597,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 +30638,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 +30651,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);
@@ -31210,17 +31025,17 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_endmodule(tolua_S);
tolua_cclass(tolua_S,"cWindow","cWindow","",NULL);
tolua_beginmodule(tolua_S,"cWindow");
- tolua_constant(tolua_S,"Inventory",cWindow::Inventory);
- tolua_constant(tolua_S,"Chest",cWindow::Chest);
- tolua_constant(tolua_S,"Workbench",cWindow::Workbench);
- tolua_constant(tolua_S,"Furnace",cWindow::Furnace);
- tolua_constant(tolua_S,"DropSpenser",cWindow::DropSpenser);
- tolua_constant(tolua_S,"Enchantment",cWindow::Enchantment);
- tolua_constant(tolua_S,"Brewery",cWindow::Brewery);
- tolua_constant(tolua_S,"NPCTrade",cWindow::NPCTrade);
- tolua_constant(tolua_S,"Beacon",cWindow::Beacon);
- tolua_constant(tolua_S,"Anvil",cWindow::Anvil);
- tolua_constant(tolua_S,"Hopper",cWindow::Hopper);
+ tolua_constant(tolua_S,"wtInventory",cWindow::wtInventory);
+ tolua_constant(tolua_S,"wtChest",cWindow::wtChest);
+ tolua_constant(tolua_S,"wtWorkbench",cWindow::wtWorkbench);
+ tolua_constant(tolua_S,"wtFurnace",cWindow::wtFurnace);
+ tolua_constant(tolua_S,"wtDropSpenser",cWindow::wtDropSpenser);
+ tolua_constant(tolua_S,"wtEnchantment",cWindow::wtEnchantment);
+ tolua_constant(tolua_S,"wtBrewery",cWindow::wtBrewery);
+ tolua_constant(tolua_S,"wtNPCTrade",cWindow::wtNPCTrade);
+ tolua_constant(tolua_S,"wtBeacon",cWindow::wtBeacon);
+ tolua_constant(tolua_S,"wtAnvil",cWindow::wtAnvil);
+ tolua_constant(tolua_S,"wtHopper",cWindow::wtHopper);
tolua_function(tolua_S,"GetWindowID",tolua_AllToLua_cWindow_GetWindowID00);
tolua_function(tolua_S,"GetWindowType",tolua_AllToLua_cWindow_GetWindowType00);
tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cWindow_GetSlot00);
@@ -31244,10 +31059,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 +31092,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,"GetSpawnDelay",tolua_AllToLua_cMonster_GetSpawnDelay00);
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..411e608d9 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/28/13 13:11:04.
*/
/* 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..7193094d8 100644
--- a/source/BlockID.cpp
+++ b/source/BlockID.cpp
@@ -42,20 +42,20 @@ class cBlockIDMap
public:
cBlockIDMap(void)
{
- cIniFile Ini("items.ini");
- if (!Ini.ReadFile())
+ cIniFile Ini;
+ if (!Ini.ReadFile("items.ini"))
{
return;
}
- long KeyID = Ini.FindKey("Items");
+ int KeyID = Ini.FindKey("Items");
if (KeyID == cIniFile::noID)
{
return;
}
- unsigned NumValues = Ini.GetNumValues(KeyID);
- for (unsigned i = 0; i < NumValues; i++)
+ int NumValues = Ini.GetNumValues(KeyID);
+ for (int i = 0; i < NumValues; i++)
{
- AString Name = Ini.ValueName(KeyID, i);
+ AString Name = Ini.GetValueName(KeyID, i);
if (Name.empty())
{
continue;
@@ -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/Blocks/BlockDirt.h b/source/Blocks/BlockDirt.h
index b2bc4756c..c694d79f6 100644
--- a/source/Blocks/BlockDirt.h
+++ b/source/Blocks/BlockDirt.h
@@ -37,7 +37,7 @@ public:
if (a_BlockY < cChunkDef::Height - 1)
{
BLOCKTYPE Above = a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ);
- if (!g_BlockTransparent[Above] && !g_BlockOneHitDig[Above])
+ if ((!g_BlockTransparent[Above] && !g_BlockOneHitDig[Above]) || IsBlockWater(Above))
{
a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DIRT, 0);
return;
@@ -69,7 +69,7 @@ public:
NIBBLETYPE AboveMeta;
IsValid = a_World->GetBlockTypeMeta(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ, AboveDest, AboveMeta);
ASSERT(IsValid); // WTF - how did we get the DestBlock if AboveBlock is not valid?
- if (g_BlockOneHitDig[AboveDest] || g_BlockTransparent[AboveDest])
+ if ((g_BlockOneHitDig[AboveDest] || g_BlockTransparent[AboveDest]) && !IsBlockWater(AboveDest))
{
a_World->FastSetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ, E_BLOCK_GRASS, 0);
}
diff --git a/source/ByteBuffer.cpp b/source/ByteBuffer.cpp
index c82951271..bccd1c48b 100644
--- a/source/ByteBuffer.cpp
+++ b/source/ByteBuffer.cpp
@@ -13,8 +13,54 @@
-#define NEEDBYTES(Num) if (!CanReadBytes(Num)) return false;
-#define PUTBYTES(Num) if (!CanWriteBytes(Num)) return false;
+// If a string sent over the protocol is larger than this, a warning is emitted to the console
+#define MAX_STRING_SIZE (512 KiB)
+
+#define NEEDBYTES(Num) if (!CanReadBytes(Num)) return false; // Check if at least Num bytes can be read from the buffer, return false if not
+#define PUTBYTES(Num) if (!CanWriteBytes(Num)) return false; // Check if at least Num bytes can be written to the buffer, return false if not
+
+
+
+
+
+#if 0
+
+/// Self-test of the VarInt-reading and writing code
+class cByteBufferSelfTest
+{
+public:
+ cByteBufferSelfTest(void)
+ {
+ TestRead();
+ TestWrite();
+ }
+
+ void TestRead(void)
+ {
+ cByteBuffer buf(50);
+ buf.Write("\x05\xac\x02\x00", 4);
+ UInt32 v1;
+ ASSERT(buf.ReadVarInt(v1) && (v1 == 5));
+ UInt32 v2;
+ ASSERT(buf.ReadVarInt(v2) && (v2 == 300));
+ UInt32 v3;
+ ASSERT(buf.ReadVarInt(v3) && (v3 == 0));
+ }
+
+ void TestWrite(void)
+ {
+ cByteBuffer buf(50);
+ buf.WriteVarInt(5);
+ buf.WriteVarInt(300);
+ buf.WriteVarInt(0);
+ AString All;
+ buf.ReadAll(All);
+ ASSERT(All.size() == 4);
+ ASSERT(memcmp(All.data(), "\x05\xac\x02\x00", All.size()) == 0);
+ }
+} g_ByteBufferTest;
+
+#endif
@@ -328,6 +374,48 @@ bool cByteBuffer::ReadBEUTF16String16(AString & a_Value)
+bool cByteBuffer::ReadVarInt(UInt32 & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ UInt32 Value = 0;
+ int Shift = 0;
+ unsigned char b = 0;
+ do
+ {
+ NEEDBYTES(1);
+ ReadBuf(&b, 1);
+ Value = Value | (((Int64)(b & 0x7f)) << Shift);
+ Shift += 7;
+ } while ((b & 0x80) != 0);
+ a_Value = Value;
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadVarUTF8String(AString & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ UInt32 Size = 0;
+ if (!ReadVarInt(Size))
+ {
+ return false;
+ }
+ if (Size > MAX_STRING_SIZE)
+ {
+ LOGWARNING("%s: String too large: %llu (%llu KiB)", __FUNCTION__, Size, Size / 1024);
+ }
+ return ReadString(a_Value, (int)Size);
+}
+
+
+
+
+
bool cByteBuffer::WriteChar(char a_Value)
{
CHECK_THREAD;
@@ -446,6 +534,44 @@ bool cByteBuffer::WriteBEUTF16String16(const AString & a_Value)
+bool cByteBuffer::WriteVarInt(UInt32 a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+
+ // A 32-bit integer can be encoded by at most 5 bytes:
+ unsigned char b[5];
+ int idx = 0;
+ do
+ {
+ b[idx] = (a_Value & 0x7f) | ((a_Value > 0x7f) ? 0x80 : 0x00);
+ a_Value = a_Value >> 7;
+ idx++;
+ } while (a_Value > 0);
+
+ return WriteBuf(b, idx);
+}
+
+
+
+
+bool cByteBuffer::WriteVarUTF8String(const AString & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(a_Value.size() + 1); // This is a lower-bound on the bytes that will be actually written. Fail early.
+ bool res = WriteVarInt(a_Value.size());
+ if (!res)
+ {
+ return false;
+ }
+ return WriteBuf(a_Value.data(), a_Value.size());
+}
+
+
+
+
+
bool cByteBuffer::ReadBuf(void * a_Buffer, int a_Count)
{
CHECK_THREAD;
diff --git a/source/ByteBuffer.h b/source/ByteBuffer.h
index 650eda5b0..21abb0377 100644
--- a/source/ByteBuffer.h
+++ b/source/ByteBuffer.h
@@ -57,8 +57,22 @@ public:
bool ReadBEFloat (float & a_Value);
bool ReadBEDouble (double & a_Value);
bool ReadBool (bool & a_Value);
- bool ReadBEUTF16String16(AString & a_Value);
-
+ bool ReadBEUTF16String16(AString & a_Value); // string length as BE short, then string as UTF-16BE
+ bool ReadVarInt (UInt32 & a_Value);
+ bool ReadVarUTF8String (AString & a_Value); // string length as VarInt, then string as UTF-8
+
+ /// Reads VarInt, assigns it to anything that can be assigned from an UInt32 (unsigned short, char, Byte, double, ...)
+ template <typename T> bool ReadVarInt(T & a_Value)
+ {
+ UInt32 v;
+ bool res = ReadVarInt(v);
+ if (res)
+ {
+ a_Value = v;
+ }
+ return res;
+ }
+
// Write the specified datatype; return true if successfully written
bool WriteChar (char a_Value);
bool WriteByte (unsigned char a_Value);
@@ -68,7 +82,9 @@ public:
bool WriteBEFloat (float a_Value);
bool WriteBEDouble (double a_Value);
bool WriteBool (bool a_Value);
- bool WriteBEUTF16String16(const AString & a_Value);
+ bool WriteBEUTF16String16(const AString & a_Value); // string length as BE short, then string as UTF-16BE
+ bool WriteVarInt (UInt32 a_Value);
+ bool WriteVarUTF8String (const AString & a_Value); // string length as VarInt, then string as UTF-8
/// Reads a_Count bytes into a_Buffer; returns true if successful
bool ReadBuf(void * a_Buffer, int a_Count);
diff --git a/source/Chunk.cpp b/source/Chunk.cpp
index db533f642..be75eae41 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,129 @@ 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);
+
+ 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 : check that "Level" really means Y
+
+ NIBBLETYPE SkyLight = 0;
+
+ NIBBLETYPE BlockLight = 0;
+
+ if (IsLightValid())
+ {
+ cEntity* newMob = a_MobSpawner.TryToSpawnHere(this, Try_X, Try_Y, Try_Z, Biome, MaxNbOfSuccess);
+ if (newMob)
+ {
+ int WorldX, WorldY, WorldZ;
+ PositionToWorldPosition(Try_X, Try_Y, Try_Z, WorldX, WorldY, WorldZ);
+ double ActualX = WorldX + 0.5;
+ double ActualZ = WorldZ + 0.5;
+ newMob->SetPosition(ActualX, WorldY, ActualZ);
+ 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 +583,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 +1053,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))
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
{
- 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))
- {
- 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,95 +1074,100 @@ 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))
+ 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_BlockType = GetBlock(BlockIdx);
- return true;
+ // The chunk is not available, bail out
+ return false;
}
+ a_BlockType = Chunk->GetBlock(a_RelX, a_RelY, a_RelZ);
+ 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))
- {
- return m_NeighborXP->UnboundedRelGetBlockType(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockType);
- }
- if ((a_RelZ < 0) && (m_NeighborZM != NULL))
+
+
+
+
+bool cChunk::UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockMeta) const
+{
+ if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height))
{
- return m_NeighborZM->UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ + cChunkDef::Width, a_BlockType);
+ 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->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_BlockMeta = Chunk->GetMeta(a_RelX, a_RelY, a_RelZ);
+ return true;
}
-bool cChunk::UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockMeta) const
+bool cChunk::UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockBlockLight) const
{
if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height))
{
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_BlockBlockLight = Chunk->GetBlockLight(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::UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockSkyLight) 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_BlockSkyLight = Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ);
+ return true;
+}
+
+
+
+
+
+bool cChunk::UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight, NIBBLETYPE & a_SkyLight) 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
- );
+ int idx = Chunk->MakeIndex(a_RelX, a_RelY, a_RelZ);
+ a_BlockLight = Chunk->GetBlockLight(idx);
+ a_SkyLight = Chunk->GetSkyLight(idx);
+ return true;
}
@@ -1077,44 +1181,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 +1202,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))
- {
- return m_NeighborXP->UnboundedRelFastSetBlock(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
- }
- if ((a_RelZ < 0) && (m_NeighborZM != NULL))
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
{
- 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 +1223,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 +2360,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;
}
@@ -2774,6 +2786,17 @@ Vector3i cChunk::PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ)
+NIBBLETYPE cChunk::GetTimeAlteredLight(NIBBLETYPE a_Skylight) const
+{
+ a_Skylight -= m_World->GetSkyDarkness();
+ // Because NIBBLETYPE is unsigned, we clamp it to 0 .. 15 by checking for values above 15
+ return (a_Skylight < 16)? a_Skylight : 0;
+}
+
+
+
+
+
#if !C_CHUNK_USE_INLINE
# include "cChunk.inl.h"
#endif
diff --git a/source/Chunk.h b/source/Chunk.h
index c979b7928..63a8f75cd 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); }
@@ -290,25 +299,40 @@ 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); }
+ inline NIBBLETYPE GetBlockLight(int a_Idx) const {return cChunkDef::GetNibble(m_BlockLight, a_Idx); }
+ inline NIBBLETYPE GetSkyLight (int a_Idx) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_Idx); }
- /// 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;
+
+ /// Queries both BlockLight and SkyLight, relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ bool UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight, 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
void UnboundedQueueTickBlock(int a_RelX, int a_RelY, int a_RelZ);
+ /// Light alterations based on time
+ NIBBLETYPE GetTimeAlteredLight(NIBBLETYPE a_Skylight) const;
+
+
// Simulator data:
cFireSimulatorChunkData & GetFireSimulatorData (void) { return m_FireSimulatorData; }
cFluidSimulatorData * GetWaterSimulatorData(void) { return m_WaterSimulatorData; }
@@ -385,6 +409,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 8f451fce9..d9de24cff 100644
--- a/source/ChunkMap.cpp
+++ b/source/ChunkMap.cpp
@@ -13,6 +13,8 @@
#include "PluginManager.h"
#include "Entities/TNTEntity.h"
#include "Blocks/BlockHandler.h"
+#include "MobCensus.h"
+#include "MobSpawner.h"
#ifndef _WIN32
#include <cstdlib> // abs
@@ -1645,18 +1647,21 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
break;
}
+ case E_BLOCK_AIR:
+ {
+ // No pickups for air
+ break;
+ }
+
default:
{
- if (Block != E_BLOCK_AIR) // No pickups for air
+ if (m_World->GetTickRandomNumber(10) == 5)
{
- if (m_World->GetTickRandomNumber(10) == 5)
- {
- cItems Drops;
- cBlockHandler * Handler = BlockHandler(Block);
-
- Handler->ConvertToPickups(Drops, area.GetBlockMeta(bx + x, by + y, bz + z)); // Saves us from a massive switch
- m_World->SpawnItemPickups(Drops, bx + x, by + y, bz + z);
- }
+ cItems Drops;
+ cBlockHandler * Handler = BlockHandler(Block);
+
+ Handler->ConvertToPickups(Drops, area.GetBlockMeta(bx + x, by + y, bz + z));
+ m_World->SpawnItemPickups(Drops, bx + x, by + y, bz + z);
}
area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_AIR);
a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z));
@@ -2166,6 +2171,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)
{
@@ -2324,6 +2355,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/ClientHandle.cpp b/source/ClientHandle.cpp
index f67a546fd..ea8b48f9d 100644
--- a/source/ClientHandle.cpp
+++ b/source/ClientHandle.cpp
@@ -469,12 +469,12 @@ bool cClientHandle::HandleLogin(int a_ProtocolVersion, const AString & a_Usernam
void cClientHandle::HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem)
{
// This is for creative Inventory changes
- if (m_Player->GetGameMode() != eGameMode_Creative)
+ if (!m_Player->IsGameModeCreative())
{
LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in creative mode. Ignoring.", m_Username.c_str());
return;
}
- if (m_Player->GetWindow()->GetWindowType() != cWindow::Inventory)
+ if (m_Player->GetWindow()->GetWindowType() != cWindow::wtInventory)
{
LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in the inventory window. Ignoring.", m_Username.c_str());
return;
@@ -650,7 +650,7 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc
m_LastDigBlockZ = a_BlockZ;
if (
- (m_Player->GetGameMode() == eGameMode_Creative) || // In creative mode, digging is done immediately
+ (m_Player->IsGameModeCreative()) || // In creative mode, digging is done immediately
g_BlockOneHitDig[a_OldBlock] // One-hit blocks get destroyed immediately, too
)
{
diff --git a/source/Entities/Pickup.cpp b/source/Entities/Pickup.cpp
index 075f93449..bc8abd204 100644
--- a/source/Entities/Pickup.cpp
+++ b/source/Entities/Pickup.cpp
@@ -24,11 +24,12 @@
-cPickup::cPickup(double a_X, double a_Y, double a_Z, const cItem & a_Item, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */)
- : cEntity(etPickup, a_X, a_Y, a_Z, 0.2, 0.2)
+cPickup::cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_Item, bool IsPlayerCreated, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */)
+ : cEntity(etPickup, a_PosX, a_PosY, a_PosZ, 0.2, 0.2)
, m_Timer( 0.f )
, m_Item(a_Item)
, m_bCollected( false )
+ , m_bIsPlayerCreated( IsPlayerCreated )
{
m_MaxHealth = 5;
m_Health = 5;
@@ -126,8 +127,8 @@ bool cPickup::CollectedBy(cPlayer * a_Dest)
return false; // It's already collected!
}
- // 800 is to long
- if (m_Timer < 500.f)
+ // Two seconds if player created the pickup (vomiting), half a second if anything else
+ if (m_Timer < (m_bIsPlayerCreated ? 2000.f : 500.f))
{
// LOG("Pickup %d cannot be collected by \"%s\", because it is not old enough.", m_UniqueID, a_Dest->GetName().c_str());
return false; // Not old enough
diff --git a/source/Entities/Pickup.h b/source/Entities/Pickup.h
index 488f91fb2..d39eda298 100644
--- a/source/Entities/Pickup.h
+++ b/source/Entities/Pickup.h
@@ -24,14 +24,14 @@ class cPickup :
public:
CLASS_PROTODEF(cPickup);
- cPickup(double a_X, double a_Y, double a_Z, const cItem & a_Item, float a_SpeedX = 0.f, float a_SpeedY = 0.f, float a_SpeedZ = 0.f); // tolua_export
+ cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_Item, bool IsPlayerCreated, float a_SpeedX = 0.f, float a_SpeedY = 0.f, float a_SpeedZ = 0.f); // tolua_export
cItem & GetItem(void) {return m_Item; } // tolua_export
const cItem & GetItem(void) const {return m_Item; }
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
- virtual bool CollectedBy(cPlayer * a_Dest); // tolua_export
+ bool CollectedBy(cPlayer * a_Dest); // tolua_export
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
@@ -40,6 +40,9 @@ public:
/// Returns true if the pickup has already been collected
bool IsCollected(void) const { return m_bCollected; } // tolua_export
+
+ /// Returns true if created by player (i.e. vomiting), used for determining picking-up delay time
+ bool IsPlayerCreated(void) const { return m_bIsPlayerCreated; } // tolua_export
private:
Vector3d m_ResultingSpeed; //Can be used to modify the resulting speed for the current tick ;)
@@ -52,6 +55,8 @@ private:
cItem m_Item;
bool m_bCollected;
+
+ bool m_bIsPlayerCreated;
}; // tolua_export
diff --git a/source/Entities/Player.cpp b/source/Entities/Player.cpp
index d93b45614..d94bc944c 100644
--- a/source/Entities/Player.cpp
+++ b/source/Entities/Player.cpp
@@ -349,11 +349,8 @@ void cPlayer::SetTouchGround(bool a_bTouchGround)
void cPlayer::Heal(int a_Health)
{
- if (m_Health < GetMaxHealth())
- {
- m_Health = (short)std::min((int)a_Health + m_Health, (int)GetMaxHealth());
- SendHealth();
- }
+ super::Heal(a_Health);
+ SendHealth();
}
@@ -1183,7 +1180,7 @@ void cPlayer::TossItem(
double vX = 0, vY = 0, vZ = 0;
EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY);
vY = -vY * 2 + 1.f;
- m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 3, vY * 3, vZ * 3);
+ m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player
}
@@ -1227,11 +1224,11 @@ void cPlayer::LoadPermissionsFromDisk()
m_Groups.clear();
m_Permissions.clear();
- cIniFile IniFile("users.ini");
- if( IniFile.ReadFile() )
+ cIniFile IniFile;
+ if (IniFile.ReadFile("users.ini"))
{
std::string Groups = IniFile.GetValue(m_PlayerName, "Groups", "");
- if( Groups.size() > 0 )
+ if (!Groups.empty())
{
AStringVector Split = StringSplit( Groups, "," );
for( unsigned int i = 0; i < Split.size(); i++ )
@@ -1248,7 +1245,7 @@ void cPlayer::LoadPermissionsFromDisk()
}
else
{
- LOGWARN("WARNING: Failed to read ini file users.ini");
+ LOGWARN("Failed to read the users.ini file. The player will be added only to the Default group.");
AddToGroup("Default");
}
ResolvePermissions();
diff --git a/source/Entities/Player.h b/source/Entities/Player.h
index 82ff48954..449a63231 100644
--- a/source/Entities/Player.h
+++ b/source/Entities/Player.h
@@ -128,8 +128,8 @@ public:
// Sets the current gamemode, doesn't check validity, doesn't send update packets to client
void LoginSetGameMode(eGameMode a_GameMode);
- /// Tries to move to a new position, with collision checks and stuff
- virtual void MoveTo( const Vector3d & a_NewPos ); // tolua_export
+ /// Tries to move to a new position, with attachment-related checks (y == -999)
+ void MoveTo(const Vector3d & a_NewPos); // tolua_export
cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export
const cWindow * GetWindow(void) const { return m_CurrentWindow; }
@@ -159,21 +159,25 @@ public:
/// Adds a player to existing group or creates a new group when it doesn't exist
void AddToGroup( const AString & a_GroupName ); // tolua_export
+
/// Removes a player from the group, resolves permissions and group inheritance (case sensitive)
void RemoveFromGroup( const AString & a_GroupName ); // tolua_export
+
bool CanUseCommand( const AString & a_Command ); // tolua_export
bool HasPermission( const AString & a_Permission ); // tolua_export
const GroupList & GetGroups() { return m_Groups; } // >> EXPORTED IN MANUALBINDINGS <<
StringList GetResolvedPermissions(); // >> EXPORTED IN MANUALBINDINGS <<
bool IsInGroup( const AString & a_Group ); // tolua_export
- AString GetColor(void) const; // tolua_export
+ // tolua_begin
+
+ /// Returns the full color code to use for this player, based on their primary group or set in m_Color
+ AString GetColor(void) const;
- void TossItem(bool a_bDraggingItem, char a_Amount = 1, short a_CreateType = 0, short a_CreateHealth = 0); // tolua_export
+ void TossItem(bool a_bDraggingItem, char a_Amount = 1, short a_CreateType = 0, short a_CreateHealth = 0);
- void Heal( int a_Health ); // tolua_export
-
- // tolua_begin
+ /// Heals the player by the specified amount of HPs (positive only); sends health update
+ void Heal(int a_Health);
int GetFoodLevel (void) const { return m_FoodLevel; }
double GetFoodSaturationLevel (void) const { return m_FoodSaturationLevel; }
@@ -181,7 +185,7 @@ public:
double GetFoodExhaustionLevel (void) const { return m_FoodExhaustionLevel; }
int GetFoodPoisonedTicksRemaining(void) const { return m_FoodPoisonedTicksRemaining; }
- int GetAirLevel (void) const { return m_AirLevel; }
+ int GetAirLevel (void) const { return m_AirLevel; }
/// Returns true if the player is satiated, i. e. their foodlevel is at the max and they cannot eat anymore
bool IsSatiated(void) const { return (m_FoodLevel >= MAX_FOOD_LEVEL); }
@@ -302,6 +306,7 @@ protected:
/// Player's air level (for swimming)
int m_AirLevel;
+
/// used to time ticks between damage taken via drowning/suffocation
int m_AirTickTimer;
diff --git a/source/Entities/ProjectileEntity.cpp b/source/Entities/ProjectileEntity.cpp
index 4c8e680d0..1d5532718 100644
--- a/source/Entities/ProjectileEntity.cpp
+++ b/source/Entities/ProjectileEntity.cpp
@@ -474,8 +474,17 @@ cThrownEggEntity::cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y,
void cThrownEggEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
{
- // TODO: Random-spawn a chicken or four
-
+ if (m_World->GetTickRandomNumber(7) == 1)
+ {
+ m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
+ }
+ else if (m_World->GetTickRandomNumber(32) == 1)
+ {
+ m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
+ m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
+ m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
+ m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
+ }
Destroy();
}
diff --git a/source/Generating/ChunkGenerator.cpp b/source/Generating/ChunkGenerator.cpp
index d35b30460..59a00b540 100644
--- a/source/Generating/ChunkGenerator.cpp
+++ b/source/Generating/ChunkGenerator.cpp
@@ -75,8 +75,6 @@ bool cChunkGenerator::Start(cWorld * a_World, cIniFile & a_IniFile)
m_Generator->Initialize(a_World, a_IniFile);
- a_IniFile.WriteFile();
-
return super::Start();
}
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/GroupManager.cpp b/source/GroupManager.cpp
index b79fde9dc..d7332fd0a 100644
--- a/source/GroupManager.cpp
+++ b/source/GroupManager.cpp
@@ -44,8 +44,8 @@ cGroupManager::cGroupManager()
: m_pState( new sGroupManagerState )
{
LOGD("-- Loading Groups --");
- cIniFile IniFile("groups.ini");
- if (!IniFile.ReadFile())
+ cIniFile IniFile;
+ if (!IniFile.ReadFile("groups.ini"))
{
LOGWARNING("groups.ini inaccessible, no groups are defined");
return;
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.cpp b/source/LuaWindow.cpp
index a0609f746..9011d668c 100644
--- a/source/LuaWindow.cpp
+++ b/source/LuaWindow.cpp
@@ -31,8 +31,8 @@ cLuaWindow::cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_Slo
// If appropriate, add an Armor slot area:
switch (a_WindowType)
{
- case cWindow::Inventory:
- case cWindow::Workbench:
+ case cWindow::wtInventory:
+ case cWindow::wtWorkbench:
{
m_SlotAreas.push_back(new cSlotAreaArmor(*this));
break;
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..d4926bbe5
--- /dev/null
+++ b/source/MobSpawner.cpp
@@ -0,0 +1,289 @@
+
+#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(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, cMonster::eType a_MobType, EMCSBiome a_Biome)
+{
+ BLOCKTYPE TargetBlock;
+ if (m_AllowedTypes.find(a_MobType) != m_AllowedTypes.end() && a_Chunk->UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, TargetBlock))
+ {
+ NIBBLETYPE BlockLight = a_Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ);
+ NIBBLETYPE SkyLight = a_Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ);
+ BLOCKTYPE BlockAbove = a_Chunk->GetBlock(a_RelX, a_RelY + 1, a_RelZ);
+ BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ);
+
+ SkyLight = a_Chunk->GetTimeAlteredLight(SkyLight);
+
+ switch(a_MobType)
+ {
+ case cMonster::mtSquid:
+ return IsBlockWater(TargetBlock) && (a_RelY >= 45) && (a_RelY <= 62);
+
+ case cMonster::mtBat:
+ return (a_RelY <= 63) && (BlockLight <= 4) && (SkyLight <= 4) && (TargetBlock == E_BLOCK_AIR) && (!g_BlockTransparent[BlockAbove]);
+
+ case cMonster::mtChicken:
+ case cMonster::mtCow:
+ case cMonster::mtPig:
+ case cMonster::mtHorse:
+ case cMonster::mtSheep:
+ {
+ return (TargetBlock == E_BLOCK_AIR) && (BlockAbove == E_BLOCK_AIR) && (!g_BlockTransparent[BlockBelow]) &&
+ (BlockBelow == E_BLOCK_GRASS) && (SkyLight >= 9);
+ }
+
+ case cMonster::mtOcelot:
+ {
+ return (TargetBlock == E_BLOCK_AIR) && (BlockAbove == E_BLOCK_AIR) &&
+ ((BlockBelow == E_BLOCK_GRASS) || (BlockBelow == E_BLOCK_LEAVES)) && (a_RelY >= 62) && (m_Random.NextInt(3,a_Biome) != 0);
+ }
+ case cMonster::mtEnderman:
+ {
+ if (a_RelY < 250)
+ {
+ BLOCKTYPE BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 2, a_RelZ);
+ if (BlockTop == E_BLOCK_AIR)
+ {
+ BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 3, a_RelZ);
+ return (TargetBlock == E_BLOCK_AIR) && (BlockAbove == E_BLOCK_AIR) && (BlockTop == E_BLOCK_AIR) && (!g_BlockTransparent[BlockBelow]) &&
+ (SkyLight <= 7) && (BlockLight <= 7);
+ }
+ }
+ break;
+ }
+ case cMonster::mtSpider:
+ {
+ bool CanSpawn = true;
+ bool HaveFloor = false;
+ for (int x = 0; x < 2; ++x)
+ {
+ for(int z = 0; z < 2; ++z)
+ {
+ CanSpawn = a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY, a_RelZ + z, TargetBlock);
+ CanSpawn = CanSpawn && (TargetBlock == E_BLOCK_AIR);
+ if (!CanSpawn)
+ {
+ return false;
+ }
+ if (!HaveFloor)
+ {
+ a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY - 1, a_RelZ + z, TargetBlock);
+ HaveFloor = HaveFloor || !g_BlockTransparent[TargetBlock];
+ }
+ }
+ }
+ return CanSpawn && HaveFloor && (SkyLight <= 7) && (BlockLight <= 7);
+
+ }
+ case cMonster::mtCreeper:
+ case cMonster::mtZombie:
+ return (TargetBlock == E_BLOCK_AIR) && (BlockAbove == E_BLOCK_AIR) && (!g_BlockTransparent[BlockBelow]) &&
+ (SkyLight <= 7) && (BlockLight <= 7) && (m_Random.NextInt(2,a_Biome) == 0);
+
+ case cMonster::mtSlime:
+ return (TargetBlock == E_BLOCK_AIR) && (BlockAbove == E_BLOCK_AIR) && (!g_BlockTransparent[BlockBelow]) &&
+ ((a_RelY <= 40) || a_Biome == biSwampland);
+ case cMonster::mtGhast:
+ case cMonster::mtZombiePigman:
+ return (TargetBlock == E_BLOCK_AIR) && (BlockAbove == E_BLOCK_AIR) && (!g_BlockTransparent[BlockBelow]) &&
+ (m_Random.NextInt(20,a_Biome) == 0);
+ default:
+ LOGD("MG TODO : check I've got a Rule to write for type %d",a_MobType);
+ return false;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+cMonster* cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, 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;
+ }
+
+ // Make sure we are looking at the right chunk to spawn in
+ a_Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+
+ if (CanSpawnHere(a_Chunk, a_RelX, a_RelY, a_RelZ, m_MobType, a_Biome))
+ {
+ 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..ea6636310
--- /dev/null
+++ b/source/MobSpawner.h
@@ -0,0 +1,76 @@
+
+#pragma once
+
+#include <set>
+#include "BlockID.h"
+#include "ChunkDef.h"
+#include "Chunk.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(cChunk * a_Chunk, int A_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, 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(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, cMonster::eType a_MobType, EMCSBiome a_Biome);
+
+ // 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..88bd2743a 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;
@@ -33,7 +33,7 @@ void cAggressiveMonster::InStateChasing(float a_Dt)
if (m_Target->IsPlayer())
{
cPlayer * Player = (cPlayer *) m_Target;
- if (Player->GetGameMode() == 1)
+ if (Player->IsGameModeCreative())
{
m_EMState = IDLE;
return;
@@ -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..d18887ea4 100644
--- a/source/Mobs/Horse.cpp
+++ b/source/Mobs/Horse.cpp
@@ -1,4 +1,3 @@
-
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Horse.h"
@@ -10,7 +9,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 +75,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 +92,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);
}
}
@@ -128,6 +141,10 @@ void cHorse::OnRightClicked(cPlayer & a_Player)
void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER);
+ if (m_bIsSaddled)
+ {
+ a_Drops.push_back(cItem(E_ITEM_SADDLE, 1));
+ }
}
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..72dfb2583 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::GetSpawnDelay(cMonster::eFamily a_MobFamily)
+{
+ switch (a_MobFamily)
+ {
+ case mfHostile: return 40;
+ case mfPassive: return 40;
+ case mfAmbient: return 40;
+ 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..a0002bf4f 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, empty string if unknown
+ static AString MobTypeToString(eType a_MobType);
+
+ /// Translates MobType string to the enum, mtInvalidType if not recognized
+ static eType StringToMobType(const AString & a_MobTypeName);
+ /// Returns the mob family based on the type
+ static eFamily FamilyFromType(eType a_MobType);
+
+ /// Returns the spawn delay (number of game ticks between spawn attempts) for the given mob family
+ static int GetSpawnDelay(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..0871a38a9 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)
{
}
@@ -22,6 +22,10 @@ cPig::cPig(void) :
void cPig::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_COOKED_PORKCHOP : E_ITEM_RAW_PORKCHOP);
+ if (m_bIsSaddled)
+ {
+ a_Drops.push_back(cItem(E_ITEM_SADDLE, 1));
+ }
}
diff --git a/source/Mobs/Sheep.cpp b/source/Mobs/Sheep.cpp
index 440c5c2b9..703482ddb 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)
{
@@ -33,6 +33,7 @@ void cSheep::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+
void cSheep::OnRightClicked(cPlayer & a_Player)
{
if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_SHEARS) && (!m_IsSheared))
@@ -46,7 +47,8 @@ void cSheep::OnRightClicked(cPlayer & a_Player)
}
cItems Drops;
- Drops.push_back(cItem(E_BLOCK_WOOL, 4, m_WoolColor));
+ int NumDrops = m_World->GetTickRandomNumber(2) + 1;
+ Drops.push_back(cItem(E_BLOCK_WOOL, NumDrops, m_WoolColor));
m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10);
}
}
@@ -54,3 +56,4 @@ void cSheep::OnRightClicked(cPlayer & a_Player)
+
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/MonsterConfig.cpp b/source/MonsterConfig.cpp
index 37c7431b0..a5a1ebd49 100644
--- a/source/MonsterConfig.cpp
+++ b/source/MonsterConfig.cpp
@@ -55,18 +55,18 @@ cMonsterConfig::~cMonsterConfig()
void cMonsterConfig::Initialize()
{
- cIniFile MonstersIniFile("monsters.ini");
+ cIniFile MonstersIniFile;
- if (!MonstersIniFile.ReadFile())
+ if (!MonstersIniFile.ReadFile("monsters.ini"))
{
LOGWARNING("%s: Cannot read monsters.ini file, monster attributes not available", __FUNCTION__);
return;
}
- for (int i = (int)MonstersIniFile.NumKeys(); i >= 0; i--)
+ for (int i = (int)MonstersIniFile.GetNumKeys(); i >= 0; i--)
{
sAttributesStruct Attributes;
- AString Name = MonstersIniFile.KeyName(i);
+ AString Name = MonstersIniFile.GetKeyName(i);
Attributes.m_Name = Name;
Attributes.m_AttackDamage = MonstersIniFile.GetValueF(Name, "AttackDamage", 0);
Attributes.m_AttackRange = MonstersIniFile.GetValueF(Name, "AttackRange", 0);
diff --git a/source/PluginManager.cpp b/source/PluginManager.cpp
index a557bdc03..5ae70d48d 100644
--- a/source/PluginManager.cpp
+++ b/source/PluginManager.cpp
@@ -103,8 +103,8 @@ void cPluginManager::ReloadPluginsNow(void)
cServer::BindBuiltInConsoleCommands();
- cIniFile IniFile("settings.ini");
- if (!IniFile.ReadFile())
+ cIniFile IniFile;
+ if (!IniFile.ReadFile("settings.ini"))
{
LOGWARNING("cPluginManager: Can't find settings.ini, so can't load any plugins.");
}
diff --git a/source/Protocol/Protocol.h b/source/Protocol/Protocol.h
index 5071f5961..466cf874b 100644
--- a/source/Protocol/Protocol.h
+++ b/source/Protocol/Protocol.h
@@ -186,6 +186,27 @@ protected:
WriteInt(a_Vector.y);
WriteInt(a_Vector.z);
}
+
+ void WriteVarInt(UInt32 a_Value)
+ {
+ // A 32-bit integer can be encoded by at most 5 bytes:
+ unsigned char b[5];
+ int idx = 0;
+ do
+ {
+ b[idx] = (a_Value & 0x7f) | ((a_Value > 0x7f) ? 0x80 : 0x00);
+ a_Value = a_Value >> 7;
+ idx++;
+ } while (a_Value > 0);
+
+ SendData((const char *)b, idx);
+ }
+
+ void WriteVarUTF8String(const AString & a_String)
+ {
+ WriteVarInt(a_String.size());
+ SendData(a_String.data(), a_String.size());
+ }
} ;
diff --git a/source/Protocol/Protocol125.cpp b/source/Protocol/Protocol125.cpp
index fb7315468..ef40f265a 100644
--- a/source/Protocol/Protocol125.cpp
+++ b/source/Protocol/Protocol125.cpp
@@ -963,7 +963,7 @@ void cProtocol125::SendWholeInventory(const cWindow & a_Window)
void cProtocol125::SendWindowClose(const cWindow & a_Window)
{
- if (a_Window.GetWindowType() == cWindow::Inventory)
+ if (a_Window.GetWindowType() == cWindow::wtInventory)
{
// Do not send inventory-window-close
return;
diff --git a/source/Protocol/Protocol125.h b/source/Protocol/Protocol125.h
index ae198780c..da3f81444 100644
--- a/source/Protocol/Protocol125.h
+++ b/source/Protocol/Protocol125.h
@@ -92,9 +92,9 @@ protected:
PARSE_INCOMPLETE = -3,
} ;
- cByteBuffer m_ReceivedData; //< Buffer for the received data
+ cByteBuffer m_ReceivedData; ///< Buffer for the received data
- AString m_Username; //< Stored in ParseHandshake(), compared to Login username
+ AString m_Username; ///< Stored in ParseHandshake(), compared to Login username
virtual void SendData(const char * a_Data, int a_Size) override;
diff --git a/source/Protocol/Protocol17x.cpp b/source/Protocol/Protocol17x.cpp
new file mode 100644
index 000000000..b3e4540c7
--- /dev/null
+++ b/source/Protocol/Protocol17x.cpp
@@ -0,0 +1,541 @@
+
+// Protocol17x.cpp
+
+/*
+Implements the 1.7.x protocol classes:
+ - cProtocol172
+ - release 1.7.2 protocol (#4)
+(others may be added later in the future for the 1.7 release series)
+*/
+
+#include "Globals.h"
+#include "Protocol17x.h"
+#include "../ClientHandle.h"
+#include "../Root.h"
+#include "../Server.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+#define HANDLE_PACKET_READ(Proc, Type, Var) \
+ Type Var; \
+ m_ReceivedData.Proc(Var);
+
+
+
+
+
+cProtocol172::cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State) :
+ super(a_Client),
+ m_ServerAddress(a_ServerAddress),
+ m_ServerPort(a_ServerPort),
+ m_State(a_State),
+ m_ReceivedData(32 KiB),
+ m_IsEncrypted(false)
+{
+}
+
+
+
+
+
+void cProtocol172::DataReceived(const char * a_Data, int a_Size)
+{
+ if (m_IsEncrypted)
+ {
+ byte Decrypted[512];
+ while (a_Size > 0)
+ {
+ int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size;
+ m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes);
+ AddReceivedData((const char *)Decrypted, NumBytes);
+ a_Size -= NumBytes;
+ a_Data += NumBytes;
+ }
+ }
+ else
+ {
+ AddReceivedData(a_Data, a_Size);
+ }
+}
+
+
+
+
+void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
+{
+ if (!m_ReceivedData.Write(a_Data, a_Size))
+ {
+ // Too much data in the incoming queue, report to caller:
+ m_Client->PacketBufferFull();
+ return;
+ }
+
+ // Handle all complete packets:
+ while (true)
+ {
+ UInt32 PacketLen;
+ if (!m_ReceivedData.ReadVarInt(PacketLen))
+ {
+ // Not enough data
+ return;
+ }
+ if (!m_ReceivedData.CanReadBytes(PacketLen))
+ {
+ // The full packet hasn't been received yet
+ return;
+ }
+ UInt32 PacketType;
+ UInt32 Mark1 = m_ReceivedData.GetReadableSpace();
+ if (!m_ReceivedData.ReadVarInt(PacketType))
+ {
+ // Not enough data
+ return;
+ }
+ UInt32 NumBytesRead = Mark1 - m_ReceivedData.GetReadableSpace();
+ HandlePacket(PacketType, PacketLen - NumBytesRead);
+
+ if (Mark1 - m_ReceivedData.GetReadableSpace() > PacketLen)
+ {
+ // Read more than packet length, report as error
+ m_Client->PacketError(PacketType);
+ }
+
+ // Go to packet end in any case:
+ m_ReceivedData.ResetRead();
+ m_ReceivedData.SkipRead(PacketLen);
+ m_ReceivedData.CommitRead();
+ } // while (true)
+}
+
+
+
+
+void cProtocol172::HandlePacket(UInt32 a_PacketType, UInt32 a_RemainingBytes)
+{
+ switch (m_State)
+ {
+ case 1:
+ {
+ // Status
+ switch (a_PacketType)
+ {
+ case 0x00: HandlePacketStatusRequest(a_RemainingBytes); return;
+ case 0x01: HandlePacketStatusPing (a_RemainingBytes); return;
+ }
+ break;
+ }
+
+ case 2:
+ {
+ // Login
+ switch (a_PacketType)
+ {
+ case 0x00: HandlePacketLoginStart(a_RemainingBytes); return;
+ case 0x01: HandlePacketLoginEncryptionResponse(a_RemainingBytes); return;
+ }
+ break;
+ }
+
+ case 3:
+ {
+ // Game
+ switch (a_PacketType)
+ {
+ case 0x00: HandlePacketKeepAlive (a_RemainingBytes); break;
+ case 0x01: HandlePacketChatMessage (a_RemainingBytes); break;
+ case 0x02: HandlePacketUseEntity (a_RemainingBytes); break;
+ case 0x03: HandlePacketPlayer (a_RemainingBytes); break;
+ case 0x04: HandlePacketPlayerPos (a_RemainingBytes); break;
+ case 0x05: HandlePacketPlayerLook (a_RemainingBytes); break;
+ case 0x06: HandlePacketPlayerPosLook (a_RemainingBytes); break;
+ case 0x07: HandlePacketBlockDig (a_RemainingBytes); break;
+ case 0x08: HandlePacketBlockPlace (a_RemainingBytes); break;
+ case 0x09: HandlePacketSlotSelect (a_RemainingBytes); break;
+ case 0x0a: HandlePacketAnimation (a_RemainingBytes); break;
+ case 0x0b: HandlePacketEntityAction (a_RemainingBytes); break;
+ case 0x0c: HandlePacketSteerVehicle (a_RemainingBytes); break;
+ case 0x0d: HandlePacketWindowClose (a_RemainingBytes); break;
+ case 0x0e: HandlePacketWindowClick (a_RemainingBytes); break;
+ case 0x0f: // Confirm transaction - not used in MCS
+ case 0x10: HandlePacketCreativeInventoryAction(a_RemainingBytes); break;
+ case 0x12: HandlePacketUpdateSign (a_RemainingBytes); break;
+ case 0x13: HandlePacketPlayerAbilities (a_RemainingBytes); break;
+ case 0x14: HandlePacketTabComplete (a_RemainingBytes); break;
+ case 0x15: HandlePacketClientSettings (a_RemainingBytes); break;
+ case 0x16: HandlePacketClientStatus (a_RemainingBytes); break;
+ case 0x17: HandlePacketPluginMessage (a_RemainingBytes); break;
+ }
+ break;
+ }
+ } // switch (m_State)
+
+ // Unknown packet type, report to the client:
+ m_Client->PacketUnknown(a_PacketType);
+ m_ReceivedData.SkipRead(a_RemainingBytes);
+ m_ReceivedData.CommitRead();
+}
+
+
+
+
+
+void cProtocol172::HandlePacketStatusPing(UInt32 a_RemainingBytes)
+{
+ ASSERT(a_RemainingBytes == 8);
+ if (a_RemainingBytes != 8)
+ {
+ m_Client->PacketError(0x01);
+ m_ReceivedData.SkipRead(a_RemainingBytes);
+ m_ReceivedData.CommitRead();
+ return;
+ }
+ Int64 Timestamp;
+ m_ReceivedData.ReadBEInt64(Timestamp);
+ m_ReceivedData.CommitRead();
+
+ cByteBuffer Packet(18);
+ Packet.WriteVarInt(0x01);
+ Packet.WriteBEInt64(Timestamp);
+ WritePacket(Packet);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketStatusRequest(UInt32 a_RemainingBytes)
+{
+ // No more bytes in this packet
+ ASSERT(a_RemainingBytes == 0);
+ m_ReceivedData.CommitRead();
+
+ // Send the response:
+ AString Response = "{\"version\":{\"name\":\"1.7.2\",\"protocol\":4},\"players\":{";
+ AppendPrintf(Response, "\"max\":%u,\"online\":%u,\"sample\":[]},",
+ cRoot::Get()->GetServer()->GetMaxPlayers(),
+ cRoot::Get()->GetServer()->GetNumPlayers()
+ );
+ AppendPrintf(Response, "\"description\":{\"text\":\"%s\"}",
+ cRoot::Get()->GetServer()->GetDescription().c_str()
+ );
+ Response.append("}");
+
+ cByteBuffer Packet(Response.size() + 10);
+ Packet.WriteVarInt(0x00); // Response packet
+ Packet.WriteVarUTF8String(Response);
+ WritePacket(Packet);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketLoginEncryptionResponse(UInt32 a_RemainingBytes)
+{
+ // TODO: Add protocol encryption
+}
+
+
+
+
+
+void cProtocol172::HandlePacketLoginStart(UInt32 a_RemainingBytes)
+{
+ AString Username;
+ m_ReceivedData.ReadVarUTF8String(Username);
+
+ // TODO: Protocol encryption should be set up here if not localhost / auth
+
+ // Send login success:
+ cByteBuffer Packet(Username.size() + 30);
+ Packet.WriteVarInt(0x02); // Login success packet
+ Packet.WriteVarUTF8String(Printf("%d", m_Client->GetUniqueID())); // TODO: UUID
+ Packet.WriteVarUTF8String(Username);
+ WritePacket(Packet);
+
+ m_Client->HandleLogin(4, Username);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketAnimation(UInt32 a_RemainingBytes)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
+ HANDLE_PACKET_READ(ReadByte, Byte, Animation);
+ m_Client->HandleAnimation(Animation);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketBlockDig(UInt32 a_RemainingBytes)
+{
+ HANDLE_PACKET_READ(ReadByte, Byte, Status);
+ HANDLE_PACKET_READ(ReadBEInt, int, BlockX);
+ HANDLE_PACKET_READ(ReadByte, Byte, BlockY);
+ HANDLE_PACKET_READ(ReadBEInt, int, BlockZ);
+ HANDLE_PACKET_READ(ReadByte, Byte, Face);
+ m_Client->HandleLeftClick(BlockX, BlockY, BlockZ, Face, Status);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketBlockPlace(UInt32 a_RemainingBytes)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, BlockX);
+ HANDLE_PACKET_READ(ReadByte, Byte, BlockY);
+ HANDLE_PACKET_READ(ReadBEInt, int, BlockZ);
+ HANDLE_PACKET_READ(ReadByte, Byte, Face);
+ HANDLE_PACKET_READ(ReadByte, Byte, CursorX);
+ HANDLE_PACKET_READ(ReadByte, Byte, CursorY);
+ HANDLE_PACKET_READ(ReadByte, Byte, CursorZ);
+ m_Client->HandleRightClick(BlockX, BlockY, BlockZ, Face, CursorX, CursorY, CursorZ, m_Client->GetPlayer()->GetEquippedItem());
+}
+
+
+
+
+
+void cProtocol172::HandlePacketChatMessage(UInt32 a_RemainingBytes)
+{
+ HANDLE_PACKET_READ(ReadVarUTF8String, AString, Message);
+ m_Client->HandleChat(Message);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketClientSettings(UInt32 a_RemainingBytes)
+{
+ HANDLE_PACKET_READ(ReadVarUTF8String, AString, Locale);
+ HANDLE_PACKET_READ(ReadByte, Byte, ViewDistance);
+ HANDLE_PACKET_READ(ReadByte, Byte, ChatFlags);
+ HANDLE_PACKET_READ(ReadByte, Byte, Unused);
+ HANDLE_PACKET_READ(ReadByte, Byte, Difficulty);
+ HANDLE_PACKET_READ(ReadByte, Byte, ShowCape);
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::HandlePacketClientStatus(UInt32 a_RemainingBytes)
+{
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::HandlePacketCreativeInventoryAction(UInt32 a_RemainingBytes)
+{
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::HandlePacketEntityAction(UInt32 a_RemainingBytes)
+{
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::HandlePacketKeepAlive(UInt32 a_RemainingBytes)
+{
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::HandlePacketPlayer(UInt32 a_RemainingBytes)
+{
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::HandlePacketPlayerAbilities(UInt32 a_RemainingBytes)
+{
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::HandlePacketPlayerLook(UInt32 a_RemainingBytes)
+{
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::HandlePacketPlayerPos(UInt32 a_RemainingBytes)
+{
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::HandlePacketPlayerPosLook(UInt32 a_RemainingBytes)
+{
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::HandlePacketPluginMessage(UInt32 a_RemainingBytes)
+{
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::HandlePacketSlotSelect(UInt32 a_RemainingBytes)
+{
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::HandlePacketSteerVehicle(UInt32 a_RemainingBytes)
+{
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::HandlePacketTabComplete(UInt32 a_RemainingBytes)
+{
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::HandlePacketUpdateSign(UInt32 a_RemainingBytes)
+{
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::HandlePacketUseEntity(UInt32 a_RemainingBytes)
+{
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::HandlePacketWindowClick(UInt32 a_RemainingBytes)
+{
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::HandlePacketWindowClose(UInt32 a_RemainingBytes)
+{
+ // TODO
+}
+
+
+
+
+
+void cProtocol172::WritePacket(cByteBuffer & a_Packet)
+{
+ cCSLock Lock(m_CSPacket);
+ AString Pkt;
+ a_Packet.ReadAll(Pkt);
+ WriteVarInt(Pkt.size());
+ SendData(Pkt.data(), Pkt.size());
+ Flush();
+}
+
+
+
+
+
+void cProtocol172::SendData(const char * a_Data, int a_Size)
+{
+ m_DataToSend.append(a_Data, a_Size);
+}
+
+
+
+
+
+void cProtocol172::Flush(void)
+{
+ ASSERT(m_CSPacket.IsLockedByCurrentThread()); // Did all packets lock the CS properly?
+
+ if (m_DataToSend.empty())
+ {
+ LOGD("Flushing empty");
+ return;
+ }
+ const char * a_Data = m_DataToSend.data();
+ int a_Size = m_DataToSend.size();
+ if (m_IsEncrypted)
+ {
+ byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks)
+ while (a_Size > 0)
+ {
+ int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size;
+ m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes);
+ m_Client->SendData((const char *)Encrypted, NumBytes);
+ a_Size -= NumBytes;
+ a_Data += NumBytes;
+ }
+ }
+ else
+ {
+ m_Client->SendData(a_Data, a_Size);
+ }
+ m_DataToSend.clear();
+}
+
+
+
+
+
diff --git a/source/Protocol/Protocol17x.h b/source/Protocol/Protocol17x.h
new file mode 100644
index 000000000..bc197235b
--- /dev/null
+++ b/source/Protocol/Protocol17x.h
@@ -0,0 +1,106 @@
+
+// Protocol17x.h
+
+/*
+Declares the 1.7.x protocol classes:
+ - cProtocol172
+ - release 1.7.2 protocol (#4)
+(others may be added later in the future for the 1.7 release series)
+*/
+
+
+
+
+
+#pragma once
+
+#include "Protocol16x.h"
+
+
+
+
+
+class cProtocol172 :
+ public cProtocol162 // TODO
+{
+ typedef cProtocol162 super; // TODO
+
+public:
+
+ cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State);
+
+ /// Called when client sends some data:
+ virtual void DataReceived(const char * a_Data, int a_Size) override;
+
+protected:
+
+ AString m_ServerAddress;
+
+ UInt16 m_ServerPort;
+
+ /// State of the protocol. 1 = status, 2 = login, 3 = game
+ UInt32 m_State;
+
+ /// Buffer for the received data
+ cByteBuffer m_ReceivedData;
+
+ bool m_IsEncrypted;
+ CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption m_Decryptor;
+ CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption m_Encryptor;
+
+ /// (Unencrypted) data to be sent to the client. Written by SendData, cleared by Flush()
+ AString m_DataToSend;
+
+
+ /// Adds the received (unencrypted) data to m_ReceivedData, parses complete packets
+ void AddReceivedData(const char * a_Data, int a_Size);
+
+ /// Reads and handles the packet. The packet length and type have already been read.
+ void HandlePacket(UInt32 a_PacketType, UInt32 a_RemainingBytes);
+
+ // Packet handlers while in the Status state (m_State == 1):
+ void HandlePacketStatusPing (UInt32 a_RemainingBytes);
+ void HandlePacketStatusRequest(UInt32 a_RemainingBytes);
+
+ // Packet handlers while in the Login state (m_State == 2):
+ void HandlePacketLoginEncryptionResponse(UInt32 a_RemainingBytes);
+ void HandlePacketLoginStart (UInt32 a_RemainingBytes);
+
+ // Packet handlers while in the Game state (m_State == 3):
+ void HandlePacketAnimation (UInt32 a_RemainingBytes);
+ void HandlePacketBlockDig (UInt32 a_RemainingBytes);
+ void HandlePacketBlockPlace (UInt32 a_RemainingBytes);
+ void HandlePacketChatMessage (UInt32 a_RemainingBytes);
+ void HandlePacketClientSettings (UInt32 a_RemainingBytes);
+ void HandlePacketClientStatus (UInt32 a_RemainingBytes);
+ void HandlePacketCreativeInventoryAction(UInt32 a_RemainingBytes);
+ void HandlePacketEntityAction (UInt32 a_RemainingBytes);
+ void HandlePacketKeepAlive (UInt32 a_RemainingBytes);
+ void HandlePacketPlayer (UInt32 a_RemainingBytes);
+ void HandlePacketPlayerAbilities (UInt32 a_RemainingBytes);
+ void HandlePacketPlayerLook (UInt32 a_RemainingBytes);
+ void HandlePacketPlayerPos (UInt32 a_RemainingBytes);
+ void HandlePacketPlayerPosLook (UInt32 a_RemainingBytes);
+ void HandlePacketPluginMessage (UInt32 a_RemainingBytes);
+ void HandlePacketSlotSelect (UInt32 a_RemainingBytes);
+ void HandlePacketSteerVehicle (UInt32 a_RemainingBytes);
+ void HandlePacketTabComplete (UInt32 a_RemainingBytes);
+ void HandlePacketUpdateSign (UInt32 a_RemainingBytes);
+ void HandlePacketUseEntity (UInt32 a_RemainingBytes);
+ void HandlePacketWindowClick (UInt32 a_RemainingBytes);
+ void HandlePacketWindowClose (UInt32 a_RemainingBytes);
+
+
+ /// Writes an entire packet into the output stream. a_Packet is expected to start with the packet type; data length is prepended here.
+ void WritePacket(cByteBuffer & a_Packet);
+
+ /// Adds unencrypted data to the outgoing data buffer
+ virtual void SendData(const char * a_Data, int a_Size) override;
+
+ /// Flushes m_DataToSend through the optional encryption into the outgoing socket data
+ virtual void Flush(void) override;
+} ;
+
+
+
+
diff --git a/source/Protocol/ProtocolRecognizer.cpp b/source/Protocol/ProtocolRecognizer.cpp
index fe99b22e1..18e9186b2 100644
--- a/source/Protocol/ProtocolRecognizer.cpp
+++ b/source/Protocol/ProtocolRecognizer.cpp
@@ -12,6 +12,7 @@
#include "Protocol14x.h"
#include "Protocol15x.h"
#include "Protocol16x.h"
+#include "Protocol17x.h"
#include "../ClientHandle.h"
#include "../Root.h"
#include "../Server.h"
@@ -667,11 +668,65 @@ bool cProtocolRecognizer::TryRecognizeProtocol(void)
}
switch (PacketType)
{
- case 0x02: break; // Handshake, continue recognizing
- case 0xfe: HandleServerPing(); return false;
- default: return false;
+ case 0x02: return TryRecognizeLengthlessProtocol(); // Handshake, continue recognizing
+ case 0xfe:
+ {
+ // This may be either a packet length or the length-less Ping packet
+ Byte NextByte;
+ if (!m_Buffer.ReadByte(NextByte))
+ {
+ // Not enough data for either protocol
+ // This could actually happen with the 1.2 / 1.3 client, but their support is fading out anyway
+ return false;
+ }
+ if (NextByte != 0x01)
+ {
+ // This is definitely NOT a length-less Ping packet, handle as lengthed protocol:
+ break;
+ }
+ if (!m_Buffer.ReadByte(NextByte))
+ {
+ // There is no more data. Although this *could* mean TCP fragmentation, it is highly unlikely
+ // and rather this is a 1.4 client sending a regular Ping packet (without the following Plugin message)
+ SendLengthlessServerPing();
+ return false;
+ }
+ if (NextByte == 0xfa)
+ {
+ // Definitely a length-less Ping followed by a Plugin message
+ SendLengthlessServerPing();
+ return false;
+ }
+ // Definitely a lengthed Initial handshake, handle below:
+ break;
+ }
+ } // switch (PacketType)
+
+ // This must be a lengthed protocol, try if it has the entire initial handshake packet:
+ m_Buffer.ResetRead();
+ UInt32 PacketLen;
+ UInt32 ReadSoFar = m_Buffer.GetReadableSpace();
+ if (!m_Buffer.ReadVarInt(PacketLen))
+ {
+ // Not enough bytes for the packet length, keep waiting
+ return false;
}
-
+ ReadSoFar -= m_Buffer.GetReadableSpace();
+ if (!m_Buffer.CanReadBytes(PacketLen))
+ {
+ // Not enough bytes for the packet, keep waiting
+ return false;
+ }
+ return TryRecognizeLengthedProtocol(PacketLen - ReadSoFar);
+}
+
+
+
+
+
+bool cProtocolRecognizer::TryRecognizeLengthlessProtocol(void)
+{
+ // The comm started with 0x02, which is a Handshake packet in the length-less protocol family
// 1.3.2 starts with 0x02 0x39 <name-length-short>
// 1.2.5 starts with 0x02 <name-length-short> and name is expected to less than 0x3900 long :)
char ch;
@@ -724,10 +779,59 @@ bool cProtocolRecognizer::TryRecognizeProtocol(void)
-void cProtocolRecognizer::HandleServerPing(void)
+bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining)
+{
+ UInt32 PacketType;
+ UInt32 NumBytesRead = m_Buffer.GetReadableSpace();
+ if (!m_Buffer.ReadVarInt(PacketType))
+ {
+ return false;
+ }
+ if (PacketType != 0x00)
+ {
+ // Not an initial handshake packet, we don't know how to talk to them
+ LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, initial packet %u)",
+ m_Client->GetIPString().c_str(), PacketType
+ );
+ m_Client->Kick("Unsupported protocol version");
+ return false;
+ }
+ UInt32 ProtocolVersion;
+ if (!m_Buffer.ReadVarInt(ProtocolVersion))
+ {
+ return false;
+ }
+ NumBytesRead -= m_Buffer.GetReadableSpace();
+ switch (ProtocolVersion)
+ {
+ case PROTO_VERSION_1_7_2:
+ {
+ AString ServerAddress;
+ short ServerPort;
+ UInt32 NextState;
+ m_Buffer.ReadVarUTF8String(ServerAddress);
+ m_Buffer.ReadBEShort(ServerPort);
+ m_Buffer.ReadVarInt(NextState);
+ m_Buffer.CommitRead();
+ m_Protocol = new cProtocol172(m_Client, ServerAddress, ServerPort, NextState);
+ return true;
+ }
+ }
+ LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, version %u)",
+ m_Client->GetIPString().c_str(), ProtocolVersion
+ );
+ m_Client->Kick("Unsupported protocol version");
+ return false;
+}
+
+
+
+
+
+void cProtocolRecognizer::SendLengthlessServerPing(void)
{
AString Reply;
- switch (cRoot::Get()->m_PrimaryServerVersion)
+ switch (cRoot::Get()->GetPrimaryServerVersion())
{
case PROTO_VERSION_1_2_5:
case PROTO_VERSION_1_3_2:
@@ -757,10 +861,12 @@ void cProtocolRecognizer::HandleServerPing(void)
// http://wiki.vg/wiki/index.php?title=Protocol&oldid=3101#Server_List_Ping_.280xFE.29
// _X 2012_10_31: I know that this needn't eat the byte, since it still may be in transit.
// Who cares? We're disconnecting anyway.
- if (m_Buffer.CanReadBytes(1))
+ m_Buffer.ResetRead();
+ if (m_Buffer.CanReadBytes(2))
{
byte val;
- m_Buffer.ReadByte(val);
+ m_Buffer.ReadByte(val); // Packet type - Serverlist ping
+ m_Buffer.ReadByte(val); // 0x01 magic value
ASSERT(val == 0x01);
}
@@ -771,8 +877,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/Protocol/ProtocolRecognizer.h b/source/Protocol/ProtocolRecognizer.h
index c53288230..4c473a269 100644
--- a/source/Protocol/ProtocolRecognizer.h
+++ b/source/Protocol/ProtocolRecognizer.h
@@ -47,6 +47,9 @@ public:
PROTO_VERSION_NEXT,
PROTO_VERSION_LATEST = PROTO_VERSION_NEXT - 1, ///< Automatically assigned to the last protocol version, this serves as the default for PrimaryServerVersion
+
+ // These will be kept "under" the next / latest, because the next and latest are only needed for previous protocols
+ PROTO_VERSION_1_7_2 = 4,
} ;
cProtocolRecognizer(cClientHandle * a_Client);
@@ -124,8 +127,23 @@ protected:
/// Tries to recognize protocol based on m_Buffer contents; returns true if recognized
bool TryRecognizeProtocol(void);
- /// Called when the recognizer gets a server ping packet; responds with server stats and destroys the client
- void HandleServerPing(void);
+ /** Tries to recognize a protocol in the length-less family, based on m_Buffer; returns true if recognized.
+ Handles protocols before release 1.7, that didn't include packet lengths, and started with a 0x02 handshake packet
+ Note that length-less server ping is handled directly in TryRecognizeProtocol(), this function is called only
+ when the 0x02 Handshake packet has been received
+ */
+ bool TryRecognizeLengthlessProtocol(void);
+
+ /** Tries to recognize a protocol in the leghted family (1.7+), based on m_Buffer; returns true if recognized.
+ The packet length and type have already been read, type is 0
+ The number of bytes remaining in the packet is passed as a_PacketLengthRemaining
+ **/
+ bool TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining);
+
+ /** Called when the recognizer gets a length-less protocol's server ping packet
+ Responds with server stats and destroys the client.
+ */
+ void SendLengthlessServerPing(void);
} ;
diff --git a/source/Root.cpp b/source/Root.cpp
index 290a5269a..e992ff614 100644
--- a/source/Root.cpp
+++ b/source/Root.cpp
@@ -116,8 +116,8 @@ void cRoot::Start(void)
m_Server = new cServer();
LOG("Reading server config...");
- cIniFile IniFile("settings.ini");
- if (!IniFile.ReadFile())
+ cIniFile IniFile;
+ if (!IniFile.ReadFile("settings.ini"))
{
LOGWARNING("settings.ini inaccessible, all settings are reset to default values");
}
@@ -138,9 +138,7 @@ void cRoot::Start(void)
LOGERROR("Failure starting server, aborting...");
return;
}
- IniFile.WriteFile();
- LOG("Initialising WebAdmin...");
m_WebAdmin = new cWebAdmin();
m_WebAdmin->Init();
@@ -150,7 +148,7 @@ void cRoot::Start(void)
m_FurnaceRecipe = new cFurnaceRecipe();
LOGD("Loading worlds...");
- LoadWorlds();
+ LoadWorlds(IniFile);
LOGD("Loading plugin manager...");
m_PluginManager = new cPluginManager();
@@ -161,8 +159,10 @@ void cRoot::Start(void)
// This sets stuff in motion
LOGD("Starting Authenticator...");
- m_Authenticator.Start();
+ m_Authenticator.Start(IniFile);
+ IniFile.WriteFile("settings.ini");
+
LOGD("Starting worlds...");
StartWorlds();
@@ -172,7 +172,6 @@ void cRoot::Start(void)
LOGD("Finalising startup...");
m_Server->Start();
- LOG("Starting WebAdmin...");
m_WebAdmin->Start();
#if !defined(ANDROID_NDK)
@@ -210,7 +209,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;
@@ -248,10 +246,8 @@ void cRoot::LoadGlobalSettings()
-void cRoot::LoadWorlds(void)
+void cRoot::LoadWorlds(cIniFile & IniFile)
{
- cIniFile IniFile("settings.ini"); IniFile.ReadFile();
-
// First get the default world
AString DefaultWorldName = IniFile.GetValueSet("Worlds", "DefaultWorld", "world");
m_pDefaultWorld = new cWorld( DefaultWorldName.c_str() );
diff --git a/source/Root.h b/source/Root.h
index 2b15d3461..175084c53 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;
@@ -162,7 +162,7 @@ private:
void LoadGlobalSettings();
/// Loads the worlds from settings.ini, creates the worldmap
- void LoadWorlds(void);
+ void LoadWorlds(cIniFile & IniFile);
/// Starts each world's life
void StartWorlds(void);
diff --git a/source/UI/SlotArea.cpp b/source/UI/SlotArea.cpp
index 0a37e82b0..82e87e126 100644
--- a/source/UI/SlotArea.cpp
+++ b/source/UI/SlotArea.cpp
@@ -559,7 +559,7 @@ cSlotAreaInventoryBase::cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset,
void cSlotAreaInventoryBase::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
{
- if ((a_Player.GetGameMode() == eGameMode_Creative) && (m_ParentWindow.GetWindowType() == cWindow::Inventory))
+ if (a_Player.IsGameModeCreative() && (m_ParentWindow.GetWindowType() == cWindow::wtInventory))
{
// Creative inventory must treat a_ClickedItem as a DraggedItem instead, replacing the inventory slot with it
SetSlot(a_SlotNum, a_Player, a_ClickedItem);
@@ -793,7 +793,7 @@ void cSlotAreaTemporary::TossItems(cPlayer & a_Player, int a_Begin, int a_End)
double vX = 0, vY = 0, vZ = 0;
EulerToVector(-a_Player.GetRotation(), a_Player.GetPitch(), vZ, vX, vY);
vY = -vY * 2 + 1.f;
- a_Player.GetWorld()->SpawnItemPickups(Drops, a_Player.GetPosX(), a_Player.GetPosY() + 1.6f, a_Player.GetPosZ(), vX * 3, vY * 3, vZ * 3);
+ a_Player.GetWorld()->SpawnItemPickups(Drops, a_Player.GetPosX(), a_Player.GetPosY() + 1.6f, a_Player.GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because player created
}
diff --git a/source/UI/Window.cpp b/source/UI/Window.cpp
index 2794abe22..1318cbca8 100644
--- a/source/UI/Window.cpp
+++ b/source/UI/Window.cpp
@@ -23,7 +23,7 @@ char cWindow::m_WindowIDCounter = 1;
-cWindow::cWindow(cWindow::WindowType a_WindowType, const AString & a_WindowTitle) :
+cWindow::cWindow(WindowType a_WindowType, const AString & a_WindowTitle) :
m_WindowID((++m_WindowIDCounter) % 127),
m_WindowType(a_WindowType),
m_WindowTitle(a_WindowTitle),
@@ -31,7 +31,7 @@ cWindow::cWindow(cWindow::WindowType a_WindowType, const AString & a_WindowTitle
m_IsDestroyed(false),
m_ShouldDistributeToHotbarFirst(true)
{
- if (a_WindowType == Inventory)
+ if (a_WindowType == wtInventory)
{
m_WindowID = 0;
}
@@ -277,7 +277,7 @@ bool cWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse)
m_OpenedBy.remove(&a_Player);
- if ((m_WindowType != Inventory) && m_OpenedBy.empty())
+ if ((m_WindowType != wtInventory) && m_OpenedBy.empty())
{
Destroy();
}
@@ -703,7 +703,7 @@ void cWindow::SetProperty(int a_Property, int a_Value, cPlayer & a_Player)
// cInventoryWindow:
cInventoryWindow::cInventoryWindow(cPlayer & a_Player) :
- cWindow(cWindow::Inventory, "Inventory"),
+ cWindow(wtInventory, "Inventory"),
m_Player(a_Player)
{
m_SlotAreas.push_back(new cSlotAreaCrafting(2, *this)); // The creative inventory doesn't display it, but it's still counted into slot numbers
@@ -720,7 +720,7 @@ cInventoryWindow::cInventoryWindow(cPlayer & a_Player) :
// cCraftingWindow:
cCraftingWindow::cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) :
- cWindow(cWindow::Workbench, "Crafting Table")
+ cWindow(wtWorkbench, "Crafting Table")
{
m_SlotAreas.push_back(new cSlotAreaCrafting(3, *this));
m_SlotAreas.push_back(new cSlotAreaInventory(*this));
@@ -735,7 +735,7 @@ cCraftingWindow::cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) :
// cChestWindow:
cChestWindow::cChestWindow(cChestEntity * a_Chest) :
- cWindow(cWindow::Chest, "Chest"),
+ cWindow(wtChest, "Chest"),
m_World(a_Chest->GetWorld()),
m_BlockX(a_Chest->GetPosX()),
m_BlockY(a_Chest->GetPosY()),
@@ -757,7 +757,7 @@ cChestWindow::cChestWindow(cChestEntity * a_Chest) :
cChestWindow::cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest) :
- cWindow(cWindow::Chest, "Double Chest"),
+ cWindow(wtChest, "Double Chest"),
m_World(a_PrimaryChest->GetWorld()),
m_BlockX(a_PrimaryChest->GetPosX()),
m_BlockY(a_PrimaryChest->GetPosY()),
@@ -796,7 +796,7 @@ cChestWindow::~cChestWindow()
// cDropSpenserWindow:
cDropSpenserWindow::cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_DropSpenser) :
- cWindow(cWindow::DropSpenser, "Dropspenser")
+ cWindow(wtDropSpenser, "Dropspenser")
{
m_ShouldDistributeToHotbarFirst = false;
m_SlotAreas.push_back(new cSlotAreaItemGrid(a_DropSpenser->GetContents(), *this));
@@ -812,7 +812,7 @@ cDropSpenserWindow::cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ,
// cHopperWindow:
cHopperWindow::cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper) :
- super(cWindow::Hopper, "Hopper")
+ super(wtHopper, "Hopper")
{
m_ShouldDistributeToHotbarFirst = false;
m_SlotAreas.push_back(new cSlotAreaItemGrid(a_Hopper->GetContents(), *this));
@@ -828,7 +828,7 @@ cHopperWindow::cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEn
// cFurnaceWindow:
cFurnaceWindow::cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace) :
- cWindow(cWindow::Furnace, "Furnace")
+ cWindow(wtFurnace, "Furnace")
{
m_ShouldDistributeToHotbarFirst = false;
m_SlotAreas.push_back(new cSlotAreaFurnace(a_Furnace, *this));
diff --git a/source/UI/Window.h b/source/UI/Window.h
index aa7e9d0d0..2d5e81e9e 100644
--- a/source/UI/Window.h
+++ b/source/UI/Window.h
@@ -49,17 +49,17 @@ class cWindow
public:
enum WindowType
{
- Inventory = -1, // This value is never actually sent to a client
- Chest = 0,
- Workbench = 1,
- Furnace = 2,
- DropSpenser = 3, // Dropper or Dispenser
- Enchantment = 4,
- Brewery = 5,
- NPCTrade = 6,
- Beacon = 7,
- Anvil = 8,
- Hopper = 9,
+ wtInventory = -1, // This value is never actually sent to a client
+ wtChest = 0,
+ wtWorkbench = 1,
+ wtFurnace = 2,
+ wtDropSpenser = 3, // Dropper or Dispenser
+ wtEnchantment = 4,
+ wtBrewery = 5,
+ wtNPCTrade = 6,
+ wtBeacon = 7,
+ wtAnvil = 8,
+ wtHopper = 9,
};
// tolua_end
diff --git a/source/WebAdmin.cpp b/source/WebAdmin.cpp
index 316513f11..8c95e4e21 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;
@@ -44,9 +44,20 @@ public:
cWebAdmin::cWebAdmin(void) :
m_IsInitialized(false),
- m_TemplateScript("<webadmin_template>"),
- m_IniFile("webadmin.ini")
+ m_TemplateScript("<webadmin_template>")
+{
+}
+
+
+
+
+
+cWebAdmin::~cWebAdmin()
{
+ if (m_IsInitialized)
+ {
+ LOG("Stopping WebAdmin...");
+ }
}
@@ -74,20 +85,22 @@ void cWebAdmin::RemovePlugin( cWebPlugin * a_Plugin )
bool cWebAdmin::Init(void)
{
- if (!m_IniFile.ReadFile())
+ if (!m_IniFile.ReadFile("webadmin.ini"))
{
return false;
}
-
+
if (!m_IniFile.GetValueSetB("WebAdmin", "Enabled", true))
{
// WebAdmin is disabled, bail out faking a success
return true;
}
-
+
+ LOG("Initialising WebAdmin...");
+
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 +120,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 +175,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 +188,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 +207,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 +220,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 +235,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 +269,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 +396,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 +445,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 +501,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 ef56e7fe9..dd3965e3d 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"
@@ -81,6 +55,12 @@
/// Up to this many m_SpreadQueue elements are handled each world tick
const int MAX_LIGHTING_SPREAD_PER_TICK = 10;
+const int TIME_SUNSET = 12000;
+const int TIME_NIGHT_START = 13187;
+const int TIME_NIGHT_END = 22812;
+const int TIME_SUNRISE = 23999;
+const int TIME_SPAWN_DIVISOR = 148;
+
@@ -252,11 +232,11 @@ 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 :)
- m_TickThread(*this)
+ m_TickThread(*this),
+ m_SkyDarkness(0)
{
LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str());
@@ -471,8 +451,8 @@ void cWorld::Start(void)
m_SpawnZ = (double)((m_TickRand.randInt() % 1000) - 500);
m_GameMode = eGameMode_Creative;
- cIniFile IniFile(m_IniFileName);
- if (!IniFile.ReadFile())
+ cIniFile IniFile;
+ if (!IniFile.ReadFile(m_IniFileName))
{
LOGWARNING("Cannot read world settings from \"%s\", defaults will be used.", m_IniFileName.c_str());
}
@@ -515,14 +495,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,8 +554,15 @@ 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())
+ if (!IniFile.WriteFile(m_IniFileName))
{
LOGWARNING("Could not write world config to %s", m_IniFileName.c_str());
}
@@ -607,6 +615,9 @@ void cWorld::Tick(float a_Dt)
m_WorldAge = (Int64)(m_WorldAgeSecs * 20.0);
m_TimeOfDay = (Int64)(m_TimeOfDaySecs * 20.0);
+ // Updates the sky darkness based on current time of day
+ UpdateSkyDarkness();
+
// Broadcast time update every 40 ticks (2 seconds)
if (m_LastTimeUpdate < m_WorldAge - 40)
{
@@ -648,7 +659,7 @@ void cWorld::Tick(float a_Dt)
UnloadUnusedChunks();
}
- TickSpawnMobs(a_Dt);
+ TickMobs(a_Dt);
std::vector<int> m_RSList_copy(m_RSList);
@@ -733,125 +744,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();
- }
+ // _X 2013_10_22: This is a quick fix for #283 - the world needs to be locked while ticking mobs
+ cWorld::cLock Lock(*this);
- int dayRand = (m_TickRand.randInt() / 7) % 6;
- int nightRand = (m_TickRand.randInt() / 11) % 10;
-
- 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 SpawnDelay = cMonster::GetSpawnDelay(Family);
+ if (
+ (m_LastSpawnMonster[Family] > m_WorldAge - SpawnDelay) || // Not reached the needed ticks 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)
- {
- // 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
+ m_LastSpawnMonster[Family] = m_WorldAge;
+ cMobSpawner Spawner(Family, m_AllowedMobs);
+ if (Spawner.CanSpawnAnything())
{
- // 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)
- if (MobType >= 0)
+ // 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++)
{
- // A proper mob type was selected, now spawn the mob:
- SpawnMob(SpawnPos.x, SpawnPos.y, SpawnPos.z, (cMonster::eType)MobType);
+ itr->second.m_Monster.Tick(a_Dt, itr->second.m_Chunk);
+ }
+
+ // 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++)
+ {
+ itr->second.m_Monster.Destroy(true);
}
}
@@ -931,6 +878,31 @@ void cWorld::TickClients(float a_Dt)
+void cWorld::UpdateSkyDarkness(void)
+{
+ int TempTime = (int)m_TimeOfDay;
+ if (TempTime <= TIME_SUNSET)
+ {
+ m_SkyDarkness = 0;
+ }
+ else if (TempTime <= TIME_NIGHT_START)
+ {
+ m_SkyDarkness = (TIME_NIGHT_START - TempTime) / TIME_SPAWN_DIVISOR;
+ }
+ else if (TempTime <= TIME_NIGHT_END)
+ {
+ m_SkyDarkness = 8;
+ }
+ else
+ {
+ m_SkyDarkness = (TIME_SUNRISE - TempTime) / TIME_SPAWN_DIVISOR;
+ }
+}
+
+
+
+
+
void cWorld::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ)
{
return m_ChunkMap->WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ);
@@ -1533,7 +1505,7 @@ bool cWorld::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlock
-void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed)
+void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed, bool IsPlayerCreated)
{
MTRand r1;
a_FlyAwaySpeed /= 1000; // Pre-divide, so that we don't have to divide each time inside the loop
@@ -1545,7 +1517,7 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double
cPickup * Pickup = new cPickup(
a_BlockX, a_BlockY, a_BlockZ,
- *itr, SpeedX, SpeedY, SpeedZ
+ *itr, IsPlayerCreated, SpeedX, SpeedY, SpeedZ
);
Pickup->Initialize(this);
}
@@ -1555,13 +1527,13 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double
-void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ)
+void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ, bool IsPlayerCreated)
{
for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr)
{
cPickup * Pickup = new cPickup(
a_BlockX, a_BlockY, a_BlockZ,
- *itr, (float)a_SpeedX, (float)a_SpeedY, (float)a_SpeedZ
+ *itr, IsPlayerCreated, (float)a_SpeedX, (float)a_SpeedY, (float)a_SpeedZ
);
Pickup->Initialize(this);
}
@@ -2592,82 +2564,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
-
- int VilType = GetTickRandomNumber(cVillager::vtMax); // 0 .. 6 - Villager
- if (VilType == 6) { VilType = 0; } // Give farmers a better chance of spawning
+ bool SkType = GetDimension() == dimNether ; // Skeleton
- 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();
}
@@ -2782,3 +2711,4 @@ void cWorld::cTaskSaveAllChunks::Run(cWorld & a_World)
+
diff --git a/source/World.h b/source/World.h
index 25bc0b338..c4fd06d0b 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;
@@ -347,10 +348,10 @@ public:
// tolua_begin
/// Spawns item pickups for each item in the list. May compress pickups if too many entities:
- void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed = 1.0);
+ void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed = 1.0, bool IsPlayerCreated = false);
/// Spawns item pickups for each item in the list. May compress pickups if too many entities. All pickups get the speed specified:
- void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ);
+ void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ, bool IsPlayerCreated = false);
/// Spawns a new primed TNT entity at the specified block coords and specified fuse duration. Initial velocity is given based on the relative coefficient provided
void SpawnPrimedTNT(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec, double a_InitialVelocityCoeff = 1);
@@ -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
@@ -590,6 +592,9 @@ public:
/// Appends all usernames starting with a_Text (case-insensitive) into Results
void TabCompleteUserName(const AString & a_Text, AStringVector & a_Results);
+ /// Get the current darkness level based on the time
+ NIBBLETYPE GetSkyDarkness() { return m_SkyDarkness; }
+
private:
friend class cRoot;
@@ -632,7 +637,9 @@ 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)
+
+ NIBBLETYPE m_SkyDarkness;
eGameMode m_GameMode;
bool m_bEnabledPVP;
@@ -662,7 +669,7 @@ private:
cChunkMap * m_ChunkMap;
bool m_bAnimals;
- Int64 m_SpawnMonsterRate;
+ std::set<cMonster::eType> m_AllowedMobs;
eWeather m_Weather;
int m_WeatherInterval;
@@ -717,14 +724,16 @@ 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);
/// Ticks all clients that are in this world
void TickClients(float a_Dt);
+
+ void UpdateSkyDarkness();
/// Creates a new fluid simulator, loads its settings from the inifile (a_FluidName section)
cFluidSimulator * InitializeFluidSimulator(cIniFile & a_IniFile, const char * a_FluidName, BLOCKTYPE a_SimulateBlock, BLOCKTYPE a_StationaryBlock);
diff --git a/source/WorldStorage/WSSAnvil.cpp b/source/WorldStorage/WSSAnvil.cpp
index 537e2f723..b2e104a78 100644
--- a/source/WorldStorage/WSSAnvil.cpp
+++ b/source/WorldStorage/WSSAnvil.cpp
@@ -1123,7 +1123,7 @@ void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
return;
}
- std::auto_ptr<cPickup> Pickup(new cPickup(0, 0, 0, Item));
+ std::auto_ptr<cPickup> Pickup(new cPickup(0, 0, 0, Item, false)); // Pickup delay doesn't matter, just say false
if (!LoadEntityBaseFromNBT(*Pickup.get(), a_NBT, a_TagIdx))
{
return;