diff options
Diffstat (limited to 'MCServer/Plugins')
-rw-r--r-- | MCServer/Plugins/APIDump/APIDesc.lua | 1337 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/LICENSE-prettify.txt | 191 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/WebWorldThreads.html | 121 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/lang-lua.js | 2 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/main.css | 30 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/main.lua | 843 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/prettify.css | 1 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/prettify.js | 30 | ||||
-rw-r--r-- | MCServer/Plugins/ChunkWorx/chunkworx_main.lua | 53 | ||||
-rw-r--r-- | MCServer/Plugins/ChunkWorx/chunkworx_web.lua | 61 | ||||
-rw-r--r-- | MCServer/Plugins/Debuggers/Debuggers.lua | 4 |
11 files changed, 2112 insertions, 561 deletions
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); |