From 9447cd20f3bff89d87bda07320c5ccbb45aa7556 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Mon, 17 Mar 2014 22:12:02 +0100 Subject: [PATCH 1/6] Fixed a crash in firework rockets. Fixes #816. --- src/WorldStorage/FireworksSerializer.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/WorldStorage/FireworksSerializer.cpp b/src/WorldStorage/FireworksSerializer.cpp index 3c97ae0a2..744fc731f 100644 --- a/src/WorldStorage/FireworksSerializer.cpp +++ b/src/WorldStorage/FireworksSerializer.cpp @@ -20,8 +20,14 @@ void cFireworkItem::WriteToNBTCompound(const cFireworkItem & a_FireworkItem, cFa a_Writer.AddByte("Flicker", a_FireworkItem.m_HasFlicker); a_Writer.AddByte("Trail", a_FireworkItem.m_HasTrail); a_Writer.AddByte("Type", a_FireworkItem.m_Type); - a_Writer.AddIntArray("Colors", &(a_FireworkItem.m_Colours[0]), a_FireworkItem.m_Colours.size()); - a_Writer.AddIntArray("FadeColors", &(a_FireworkItem.m_FadeColours[0]), a_FireworkItem.m_FadeColours.size()); + if (!a_FireworkItem.m_Colours.empty()) + { + a_Writer.AddIntArray("Colors", &(a_FireworkItem.m_Colours[0]), a_FireworkItem.m_Colours.size()); + } + if (!a_FireworkItem.m_FadeColours.empty()) + { + a_Writer.AddIntArray("FadeColors", &(a_FireworkItem.m_FadeColours[0]), a_FireworkItem.m_FadeColours.size()); + } a_Writer.EndCompound(); a_Writer.EndList(); a_Writer.EndCompound(); From 4dc5650023c234a9e82c97c795d8c39016063c51 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Tue, 18 Mar 2014 13:54:17 +0100 Subject: [PATCH 2/6] Fixed cGZipFile::ReadRestOfFile returning incorrect value. --- src/OSSupport/GZipFile.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/OSSupport/GZipFile.cpp b/src/OSSupport/GZipFile.cpp index cbf6be6c4..b13e519e0 100644 --- a/src/OSSupport/GZipFile.cpp +++ b/src/OSSupport/GZipFile.cpp @@ -73,12 +73,15 @@ int cGZipFile::ReadRestOfFile(AString & a_Contents) // Since the gzip format doesn't really support getting the uncompressed length, we need to read incrementally. Yuck! int NumBytesRead = 0; + int TotalBytes = 0; char Buffer[64 KiB]; while ((NumBytesRead = gzread(m_File, Buffer, sizeof(Buffer))) > 0) { + TotalBytes += NumBytesRead; a_Contents.append(Buffer, NumBytesRead); } - return NumBytesRead; + // NumBytesRead is < 0 on error + return (NumBytesRead >= 0) ? TotalBytes : NumBytesRead; } From 38aad32a8b92a0189483f0f61a42660ee31a835c Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Tue, 18 Mar 2014 13:54:32 +0100 Subject: [PATCH 3/6] Debuggers: Using binary file mode for .schematics. --- MCServer/Plugins/Debuggers/Debuggers.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index d2c9a2a49..fe3efa306 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -217,7 +217,7 @@ function TestBlockAreasString() return end cFile:CreateFolder("schematics") - local f = io.open("schematics/StringTest.schematic", "w") + local f = io.open("schematics/StringTest.schematic", "wb") f:write(Data) f:close() @@ -230,7 +230,7 @@ function TestBlockAreasString() BA2:Clear() -- Load another area from a string in that file: - f = io.open("schematics/StringTest.schematic", "r") + f = io.open("schematics/StringTest.schematic", "rb") Data = f:read("*all") if not(BA2:LoadFromSchematicString(Data)) then LOG("Cannot load schematic from string") From 91f64da2a6895f2dee7d010e71282b0c5fb6bf02 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Tue, 18 Mar 2014 15:45:16 +0100 Subject: [PATCH 4/6] Fixed chunkmap tree block replacing. --- src/ChunkMap.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 60fbf39d4..ffba52d54 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -1376,20 +1376,14 @@ void cChunkMap::ReplaceTreeBlocks(const sSetBlockVector & a_Blocks) break; } case E_BLOCK_LEAVES: + case E_BLOCK_NEW_LEAVES: { - if (itr->BlockType == E_BLOCK_LOG) + if ((itr->BlockType == E_BLOCK_LOG) || (itr->BlockType == E_BLOCK_NEW_LOG)) { Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); } break; } - case E_BLOCK_NEW_LEAVES: - { - if (itr->BlockType == E_BLOCK_NEW_LOG) - { - Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); - } - } } } // for itr - a_Blocks[] } From 4a67114f5654668f746f43dfa82732257571a103 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Wed, 19 Mar 2014 13:57:06 +0100 Subject: [PATCH 5/6] LuaChunkStay: Removed a debugging output. --- src/Bindings/LuaChunkStay.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Bindings/LuaChunkStay.cpp b/src/Bindings/LuaChunkStay.cpp index 0e982637f..db865cfa4 100644 --- a/src/Bindings/LuaChunkStay.cpp +++ b/src/Bindings/LuaChunkStay.cpp @@ -131,9 +131,6 @@ void cLuaChunkStay::Enable(cChunkMap & a_ChunkMap, int a_OnChunkAvailableStackPo void cLuaChunkStay::OnChunkAvailable(int a_ChunkX, int a_ChunkZ) { - // DEBUG: - LOGD("LuaChunkStay: Chunk [%d, %d] is now available, calling the callback...", a_ChunkX, a_ChunkZ); - cPluginLua::cOperation Op(m_Plugin); Op().Call((int)m_OnChunkAvailable, a_ChunkX, a_ChunkZ); } From 7c717fe6df582111efc0907f5535d32ce8d72786 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Wed, 19 Mar 2014 13:57:37 +0100 Subject: [PATCH 6/6] APIDump: Reformatted the plugin to avoid all ZBS Analyzer issues. --- MCServer/Plugins/APIDump/main_APIDump.lua | 1527 ++++++++++----------- 1 file changed, 744 insertions(+), 783 deletions(-) diff --git a/MCServer/Plugins/APIDump/main_APIDump.lua b/MCServer/Plugins/APIDump/main_APIDump.lua index 4ed692b52..6d4a6ebc5 100644 --- a/MCServer/Plugins/APIDump/main_APIDump.lua +++ b/MCServer/Plugins/APIDump/main_APIDump.lua @@ -7,92 +7,22 @@ -- Global variables: -g_Plugin = nil; -g_PluginFolder = ""; +local g_Plugin = nil +local g_PluginFolder = "" +local g_Stats = {} +local g_TrackedPages = {} -function Initialize(Plugin) - g_Plugin = Plugin; - g_PluginFolder = Plugin:GetLocalFolder(); - - LOG("Initialising " .. Plugin:GetName() .. " v." .. Plugin:GetVersion()) - - cPluginManager:BindConsoleCommand("api", HandleCmdApi, "Dumps the Lua API docs into the API/ subfolder") - g_Plugin:AddWebTab("APIDump", HandleWebAdminDump) - -- TODO: Add a WebAdmin tab that has a Dump button - return true -end - - - - - -function HandleCmdApi(a_Split) - DumpApi() - return true -end - - - - - -function DumpApi() - LOG("Dumping the API...") - - -- Load the API descriptions from the Classes and Hooks subfolders: - -- This needs to be done each time the command is invoked because the export modifies the tables' contents - dofile(g_PluginFolder .. "/APIDesc.lua") - if (g_APIDesc.Classes == nil) then - g_APIDesc.Classes = {}; - end - if (g_APIDesc.Hooks == nil) then - g_APIDesc.Hooks = {}; - end - LoadAPIFiles("/Classes/", g_APIDesc.Classes); - LoadAPIFiles("/Hooks/", g_APIDesc.Hooks); - - -- Reset the stats: - g_TrackedPages = {}; -- List of tracked pages, to be checked later whether they exist. Each item is an array of referring pagenames. - g_Stats = -- Statistics about the documentation - { - NumTotalClasses = 0, - NumUndocumentedClasses = 0, - NumTotalFunctions = 0, - NumUndocumentedFunctions = 0, - NumTotalConstants = 0, - NumUndocumentedConstants = 0, - NumTotalVariables = 0, - NumUndocumentedVariables = 0, - NumTotalHooks = 0, - NumUndocumentedHooks = 0, - NumTrackedLinks = 0, - NumInvalidLinks = 0, - } - - -- dump all available API functions and objects: - -- DumpAPITxt(); - - -- Dump all available API object in HTML format into a subfolder: - DumpAPIHtml(); - - LOG("APIDump finished"); - return true -end - - - - - -function LoadAPIFiles(a_Folder, a_DstTable) +local function LoadAPIFiles(a_Folder, a_DstTable) assert(type(a_Folder) == "string") assert(type(a_DstTable) == "table") local Folder = g_PluginFolder .. a_Folder; - for idx, fnam in ipairs(cFile:GetFolderContents(Folder)) do + for _, fnam in ipairs(cFile:GetFolderContents(Folder)) do local FileName = Folder .. fnam; -- We only want .lua files from the folder: if (cFile:IsFile(FileName) and fnam:match(".*%.lua$")) then @@ -113,45 +43,7 @@ end -function DumpAPITxt() - LOG("Dumping all available functions to API.txt..."); - function dump (prefix, a, Output) - for i, v in pairs (a) do - if (type(v) == "table") then - if (GetChar(i, 1) ~= ".") then - if (v == _G) then - -- LOG(prefix .. i .. " == _G, CYCLE, ignoring"); - elseif (v == _G.package) then - -- LOG(prefix .. i .. " == _G.package, ignoring"); - else - dump(prefix .. i .. ".", v, Output) - end - end - elseif (type(v) == "function") then - if (string.sub(i, 1, 2) ~= "__") then - table.insert(Output, prefix .. i .. "()"); - end - end - end - end - - local Output = {}; - dump("", _G, Output); - - table.sort(Output); - local f = io.open("API.txt", "w"); - for i, n in ipairs(Output) do - f:write(n, "\n"); - end - f:close(); - LOG("API.txt written."); -end - - - - - -function CreateAPITables() +local function CreateAPITables() --[[ We want an API table of the following shape: local API = { @@ -218,7 +110,7 @@ function CreateAPITables() -- 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 + for k 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 = ""}); @@ -259,7 +151,7 @@ local function WriteArticles(f)

The following articles provide various extra information on plugin development

    ]]); - for i, extra in ipairs(g_APIDesc.ExtraPages) do + for _, extra in ipairs(g_APIDesc.ExtraPages) do local SrcFileName = g_PluginFolder .. "/" .. extra.FileName; if (cFile:Exists(SrcFileName)) then local DstFileName = "API/" .. extra.FileName; @@ -279,590 +171,8 @@ end -local function WriteClasses(f, a_API, a_ClassMenu) - f:write([[ -

    Class index

    -

    The following classes are available in the MCServer Lua scripting language: -

      - ]]); - for i, cls in ipairs(a_API) do - f:write("
    • ", cls.Name, "
    • \n"); - WriteHtmlClass(cls, a_API, a_ClassMenu); - end - f:write([[ -

    -
    - ]]); -end - - - - - -local function WriteHooks(f, a_Hooks, a_UndocumentedHooks, a_HookNav) - f:write([[ -

    Hooks

    -

    - A plugin can register to be called whenever an "interesting event" occurs. It does so by calling - cPluginManager's AddHook() function and implementing a callback - function to handle the event.

    -

    - 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.

    - - - - - - ]]); - for i, hook in ipairs(a_Hooks) do - if (hook.DefaultFnName == nil) then - -- The hook is not documented yet - f:write(" \n \n \n \n"); - table.insert(a_UndocumentedHooks, hook.Name); - else - f:write(" \n \n \n \n"); - WriteHtmlHook(hook, a_HookNav); - end - end - f:write([[ -
    Hook nameCalled when
    " .. hook.Name .. "(No documentation yet)
    " .. hook.Name .. "" .. LinkifyString(hook.CalledWhen, hook.Name) .. "
    -
    - ]]); -end - - - - - -function DumpAPIHtml() - LOG("Dumping all available functions and constants to API subfolder..."); - - -- Create the output folder - if not(cFile:IsFolder("API")) then - cFile:CreateFolder("API"); - end - - LOG("Copying static files.."); - cFile:CreateFolder("API/Static"); - local localFolder = g_Plugin:GetLocalFolder(); - for idx, fnam in ipairs(cFile:GetFolderContents(localFolder .. "/Static")) do - cFile:Delete("API/Static/" .. fnam); - cFile:Copy(localFolder .. "/Static/" .. fnam, "API/Static/" .. fnam); - end - - LOG("Creating API tables..."); - local API, Globals = CreateAPITables(); - local Hooks = {}; - local UndocumentedHooks = {}; - - -- Sort the classes by name: - LOG("Sorting..."); - table.sort(API, - function (c1, c2) - return (string.lower(c1.Name) < string.lower(c2.Name)); - end - ); - - g_Stats.NumTotalClasses = #API; - - -- Add Globals into the API: - Globals.Name = "Globals"; - table.insert(API, Globals); - - -- Extract hook constants: - for name, obj in pairs(cPluginManager) do - if ( - (type(obj) == "number") and - name:match("HOOK_.*") and - (name ~= "HOOK_MAX") and - (name ~= "HOOK_NUM_HOOKS") - ) then - table.insert(Hooks, { Name = name }); - end - end - table.sort(Hooks, - function(Hook1, Hook2) - return (Hook1.Name < Hook2.Name); - end - ); - - -- Read in the descriptions: - LOG("Reading descriptions..."); - ReadDescriptions(API); - ReadHooks(Hooks); - - -- Create a "class index" file, write each class as a link to that file, - -- then dump class contents into class-specific file - LOG("Writing HTML files..."); - local f = io.open("API/index.html", "w"); - if (f == nil) then - LOGINFO("Cannot output HTML API: " .. err); - return; - end - - -- Create a class navigation menu that will be inserted into each class file for faster navigation (#403) - local ClassMenuTab = {}; - for idx, cls in ipairs(API) do - table.insert(ClassMenuTab, ""); - table.insert(ClassMenuTab, cls.Name); - table.insert(ClassMenuTab, "
    "); - end - local ClassMenu = table.concat(ClassMenuTab, ""); - - -- Create a hook navigation menu that will be inserted into each hook file for faster navigation(#403) - local HookNavTab = {}; - for idx, hook in ipairs(Hooks) do - table.insert(HookNavTab, ""); - table.insert(HookNavTab, (hook.Name:gsub("^HOOK_", ""))); -- remove the "HOOK_" part of the name - table.insert(HookNavTab, "
    "); - end - local HookNav = table.concat(HookNavTab, ""); - - -- Write the HTML file: - f:write([[ - - - MCServer API - Index - - - -
    -
    -

    MCServer API - Index

    -
    -
    -

    The API reference is divided into the following sections:

    - -
    - ]]); - - WriteArticles(f); - WriteClasses(f, API, ClassMenu); - WriteHooks(f, Hooks, UndocumentedHooks, HookNav); - - -- Copy the static files to the output folder: - local StaticFiles = - { - "main.css", - "prettify.js", - "prettify.css", - "lang-lua.js", - }; - for idx, fnam in ipairs(StaticFiles) do - cFile:Delete("API/" .. fnam); - cFile:Copy(g_Plugin:GetLocalFolder() .. "/" .. fnam, "API/" .. fnam); - end - - -- List the documentation problems: - LOG("Listing leftovers..."); - ListUndocumentedObjects(API, UndocumentedHooks); - ListUnexportedObjects(); - ListMissingPages(); - - WriteStats(f); - - f:write([[
- - -]]); - f:close(); - - LOG("API subfolder written"); -end - - - - - -function ReadDescriptions(a_API) - -- Returns true if the class of the specified name is to be ignored - local function IsClassIgnored(a_ClsName) - if (g_APIDesc.IgnoreClasses == nil) then - return false; - end - for i, name in ipairs(g_APIDesc.IgnoreClasses) do - if (a_ClsName:match(name)) then - return true; - end - end - return false; - end - - -- Returns true if the function is to be ignored - local function IsFunctionIgnored(a_ClassName, a_FnName) - if (g_APIDesc.IgnoreFunctions == nil) then - return false; - end - if (((g_APIDesc.Classes[a_ClassName] or {}).Functions or {})[a_FnName] ~= nil) then - -- The function is documented, don't ignore - return false; - end - local FnName = a_ClassName .. "." .. a_FnName; - for i, name in ipairs(g_APIDesc.IgnoreFunctions) do - if (FnName:match(name)) then - return true; - end - end - return false; - end - - -- Returns true if the constant (specified by its fully qualified name) is to be ignored - local function IsConstantIgnored(a_CnName) - if (g_APIDesc.IgnoreConstants == nil) then - return false; - end; - for i, name in ipairs(g_APIDesc.IgnoreConstants) do - if (a_CnName:match(name)) then - return true; - end - end - return false; - end - - -- 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 - if not(IsClassIgnored(cls.Name)) then - table.insert(APICopy, cls); - end - end - for i = 1, #a_API do - a_API[i] = APICopy[i]; - end; - - -- Process the documentation for each class: - for i, cls in ipairs(a_API) do - -- Initialize default values for each class: - cls.ConstantGroups = {}; - cls.NumConstantsInGroups = 0; - cls.NumConstantsInGroupsForDescendants = 0; - - -- Rename special functions: - for j, fn in ipairs(cls.Functions) do - if (fn.Name == ".call") then - fn.DocID = "constructor"; - fn.Name = "() (constructor)"; - elseif (fn.Name == ".add") then - fn.DocID = "operator_plus"; - fn.Name = "operator +"; - elseif (fn.Name == ".div") then - fn.DocID = "operator_div"; - fn.Name = "operator /"; - elseif (fn.Name == ".mul") then - fn.DocID = "operator_mul"; - fn.Name = "operator *"; - elseif (fn.Name == ".sub") then - fn.DocID = "operator_sub"; - fn.Name = "operator -"; - elseif (fn.Name == ".eq") then - fn.DocID = "operator_eq"; - fn.Name = "operator =="; - end - end - - local APIDesc = g_APIDesc.Classes[cls.Name]; - if (APIDesc ~= nil) then - APIDesc.IsExported = true; - cls.Desc = APIDesc.Desc; - cls.AdditionalInfo = APIDesc.AdditionalInfo; - - -- Process inheritance: - if (APIDesc.Inherits ~= nil) then - for j, icls in ipairs(a_API) do - if (icls.Name == APIDesc.Inherits) then - table.insert(icls.Descendants, cls); - cls.Inherits = icls; - end - end - end - - cls.UndocumentedFunctions = {}; -- This will contain names of all the functions that are not documented - cls.UndocumentedConstants = {}; -- This will contain names of all the constants that are not documented - 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 - - local function AddFunction(a_Name, a_Params, a_Return, a_Notes) - table.insert(DoxyFunctions, {Name = a_Name, Params = a_Params, Return = a_Return, Notes = a_Notes}); - end - - if (APIDesc.Functions ~= nil) then - -- Assign function descriptions: - for j, func in ipairs(cls.Functions) do - local FnName = func.DocID or func.Name; - local FnDesc = APIDesc.Functions[FnName]; - if (FnDesc == nil) then - -- No description for this API function - AddFunction(func.Name); - if not(IsFunctionIgnored(cls.Name, FnName)) then - table.insert(cls.UndocumentedFunctions, FnName); - end - else - -- Description is available - if (FnDesc[1] == nil) then - -- Single function definition - AddFunction(func.Name, FnDesc.Params, FnDesc.Return, FnDesc.Notes); - else - -- Multiple function overloads - for k, desc in ipairs(FnDesc) do - AddFunction(func.Name, desc.Params, desc.Return, desc.Notes); - end -- for k, desc - FnDesc[] - end - FnDesc.IsExported = true; - end - end -- for j, func - - -- Replace functions with their described and overload-expanded versions: - cls.Functions = DoxyFunctions; - 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 - -- Assign constant descriptions: - for j, cons in ipairs(cls.Constants) do - local CnDesc = APIDesc.Constants[cons.Name]; - if (CnDesc == nil) then - -- Not documented - if not(IsConstantIgnored(cls.Name .. "." .. cons.Name)) then - table.insert(cls.UndocumentedConstants, cons.Name); - end - else - cons.Notes = CnDesc.Notes; - CnDesc.IsExported = true; - end - end -- for j, cons - 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) - - if (APIDesc.ConstantGroups ~= nil) then - -- Create links between the constants and the groups: - local NumInGroups = 0; - local NumInDescendantGroups = 0; - for j, group in pairs(APIDesc.ConstantGroups) do - group.Name = j; - group.Constants = {}; - if (type(group.Include) == "string") then - group.Include = { group.Include }; - end - local NumInGroup = 0; - for idx, incl in ipairs(group.Include or {}) do - for cidx, cons in ipairs(cls.Constants) do - if ((cons.Group == nil) and cons.Name:match(incl)) then - cons.Group = group; - table.insert(group.Constants, cons); - NumInGroup = NumInGroup + 1; - end - end -- for cidx - cls.Constants[] - end -- for idx - group.Include[] - NumInGroups = NumInGroups + NumInGroup; - if (group.ShowInDescendants) then - NumInDescendantGroups = NumInDescendantGroups + NumInGroup; - end - - -- Sort the constants: - table.sort(group.Constants, - function(c1, c2) - return (c1.Name < c2.Name); - end - ); - end -- for j - APIDesc.ConstantGroups[] - cls.ConstantGroups = APIDesc.ConstantGroups; - cls.NumConstantsInGroups = NumInGroups; - cls.NumConstantsInGroupsForDescendants = NumInDescendantGroups; - - -- Remove grouped constants from the normal list: - local NewConstants = {}; - for idx, cons in ipairs(cls.Constants) do - if (cons.Group == nil) then - table.insert(NewConstants, cons); - end - end - cls.Constants = NewConstants; - end -- if (ConstantGroups ~= 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 - table.insert(cls.UndocumentedFunctions, FnName); - end - end -- for j, func - cls.Functions[] - for j, cons in ipairs(cls.Constants) do - if not(IsConstantIgnored(cls.Name .. "." .. cons.Name)) then - table.insert(cls.UndocumentedConstants, cons.Name); - end - end -- for j, cons - cls.Constants[] - 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: - local NewFunctions = {}; - for j, fn in ipairs(cls.Functions) do - if (not(IsFunctionIgnored(cls.Name, fn.Name))) then - table.insert(NewFunctions, fn); - end - end -- for j, fn - cls.Functions = NewFunctions; - - -- Sort the functions (they may have been renamed): - table.sort(cls.Functions, - function(f1, f2) - if (f1.Name == f2.Name) then - -- Same name, either comparing the same function to itself, or two overloads, in which case compare the params - if ((f1.Params == nil) or (f2.Params == nil)) then - return 0; - end - return (f1.Params < f2.Params); - end - return (f1.Name < f2.Name); - end - ); - - -- Remove ignored constants: - local NewConstants = {}; - for j, cn in ipairs(cls.Constants) do - if (not(IsFunctionIgnored(cls.Name, cn.Name))) then - table.insert(NewConstants, cn); - end - end -- for j, cn - cls.Constants = NewConstants; - - -- Sort the constants: - table.sort(cls.Constants, - function(c1, c2) - return (c1.Name < c2.Name); - end - ); - - -- Remove ignored member variables: - 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: - for i, cls in ipairs(a_API) do - table.sort(cls.Descendants, - function(c1, c2) - return (c1.Name < c2.Name); - end - ); - end -- for i, cls -end - - - - - -function ReadHooks(a_Hooks) - --[[ - a_Hooks = { - { Name = "HOOK_1"}, - { Name = "HOOK_2"}, - ... - }; - We want to add hook descriptions to each hook in this array - --]] - for i, hook in ipairs(a_Hooks) do - local HookDesc = g_APIDesc.Hooks[hook.Name]; - if (HookDesc ~= nil) then - for key, val in pairs(HookDesc) do - hook[key] = val; - end - end - end -- for i, hook - a_Hooks[] - g_Stats.NumTotalHooks = #a_Hooks; -end - - - - - -- Make a link out of anything with the special linkifying syntax {{link|title}} -function LinkifyString(a_String, a_Referrer) +local function LinkifyString(a_String, a_Referrer) assert(a_Referrer ~= nil); assert(a_Referrer ~= ""); @@ -915,9 +225,494 @@ end -function WriteHtmlClass(a_ClassAPI, a_AllAPI, a_ClassMenu) +local function WriteHtmlHook(a_Hook, a_HookNav) + local fnam = "API/" .. a_Hook.DefaultFnName .. ".html"; + local f, error = io.open(fnam, "w"); + if (f == nil) then + LOG("Cannot write \"" .. fnam .. "\": \"" .. error .. "\"."); + return; + end + local HookName = a_Hook.DefaultFnName; + + f:write([[ + + MCServer API - ]], HookName, [[ Hook + + + + + + +
+
+

]], a_Hook.Name, [[

+
+
+
+ Index:
+ Articles
+ Classes
+ Hooks
+
+ Quick navigation:
+ ]]); + f:write(a_HookNav); + f:write([[ +

+ ]]); + f:write(LinkifyString(a_Hook.Desc, HookName)); + f:write("

\n

Callback function

\n

The default name for the callback function is "); + f:write(a_Hook.DefaultFnName, ". It has the following signature:\n"); + f:write("

function ", HookName, "(");
+	if (a_Hook.Params == nil) then
+		a_Hook.Params = {};
+	end
+	for i, param in ipairs(a_Hook.Params) do
+		if (i > 1) then
+			f:write(", ");
+		end
+		f:write(param.Name);
+	end
+	f:write(")
\n

Parameters:

\n\n"); + for _, param in ipairs(a_Hook.Params) do + f:write("\n"); + end + f:write("
NameTypeNotes
", param.Name, "", LinkifyString(param.Type, HookName), "", LinkifyString(param.Notes, HookName), "
\n

" .. (a_Hook.Returns or "") .. "

\n\n"); + f:write([[

Code examples

Registering the callback

]]); + f:write("
\n");
+	f:write([[cPluginManager:AddHook(cPluginManager.]] .. a_Hook.Name .. ", My" .. a_Hook.DefaultFnName .. [[);]]);
+	f:write("
\n\n"); + local Examples = a_Hook.CodeExamples or {}; + for _, example in ipairs(Examples) do + f:write("

", (example.Title or "missing Title"), "

\n"); + f:write("

", (example.Desc or "missing Desc"), "

\n"); + f:write("
", (example.Code or "missing Code"), "\n
\n\n"); + end + f:write([[
]]); + f:close(); +end + + + + + +local function WriteHooks(f, a_Hooks, a_UndocumentedHooks, a_HookNav) + f:write([[ +

Hooks

+

+ A plugin can register to be called whenever an "interesting event" occurs. It does so by calling + cPluginManager's AddHook() function and implementing a callback + function to handle the event.

+

+ 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.

+ + + + + + ]]); + for _, hook in ipairs(a_Hooks) do + if (hook.DefaultFnName == nil) then + -- The hook is not documented yet + f:write(" \n \n \n \n"); + table.insert(a_UndocumentedHooks, hook.Name); + else + f:write(" \n \n \n \n"); + WriteHtmlHook(hook, a_HookNav); + end + end + f:write([[ +
Hook nameCalled when
" .. hook.Name .. "(No documentation yet)
" .. hook.Name .. "" .. LinkifyString(hook.CalledWhen, hook.Name) .. "
+
+ ]]); +end + + + + + +local function ReadDescriptions(a_API) + -- Returns true if the class of the specified name is to be ignored + local function IsClassIgnored(a_ClsName) + if (g_APIDesc.IgnoreClasses == nil) then + return false; + end + for _, name in ipairs(g_APIDesc.IgnoreClasses) do + if (a_ClsName:match(name)) then + return true; + end + end + return false; + end + + -- Returns true if the function is to be ignored + local function IsFunctionIgnored(a_ClassName, a_FnName) + if (g_APIDesc.IgnoreFunctions == nil) then + return false; + end + if (((g_APIDesc.Classes[a_ClassName] or {}).Functions or {})[a_FnName] ~= nil) then + -- The function is documented, don't ignore + return false; + end + local FnName = a_ClassName .. "." .. a_FnName; + for _, name in ipairs(g_APIDesc.IgnoreFunctions) do + if (FnName:match(name)) then + return true; + end + end + return false; + end + + -- Returns true if the constant (specified by its fully qualified name) is to be ignored + local function IsConstantIgnored(a_CnName) + if (g_APIDesc.IgnoreConstants == nil) then + return false; + end; + for _, name in ipairs(g_APIDesc.IgnoreConstants) do + if (a_CnName:match(name)) then + return true; + end + end + 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 _, 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 _, cls in ipairs(a_API) do + if not(IsClassIgnored(cls.Name)) then + table.insert(APICopy, cls); + end + end + for i = 1, #a_API do + a_API[i] = APICopy[i]; + end; + + -- Process the documentation for each class: + for _, cls in ipairs(a_API) do + -- Initialize default values for each class: + cls.ConstantGroups = {}; + cls.NumConstantsInGroups = 0; + cls.NumConstantsInGroupsForDescendants = 0; + + -- Rename special functions: + for _, fn in ipairs(cls.Functions) do + if (fn.Name == ".call") then + fn.DocID = "constructor"; + fn.Name = "() (constructor)"; + elseif (fn.Name == ".add") then + fn.DocID = "operator_plus"; + fn.Name = "operator +"; + elseif (fn.Name == ".div") then + fn.DocID = "operator_div"; + fn.Name = "operator /"; + elseif (fn.Name == ".mul") then + fn.DocID = "operator_mul"; + fn.Name = "operator *"; + elseif (fn.Name == ".sub") then + fn.DocID = "operator_sub"; + fn.Name = "operator -"; + elseif (fn.Name == ".eq") then + fn.DocID = "operator_eq"; + fn.Name = "operator =="; + end + end + + local APIDesc = g_APIDesc.Classes[cls.Name]; + if (APIDesc ~= nil) then + APIDesc.IsExported = true; + cls.Desc = APIDesc.Desc; + cls.AdditionalInfo = APIDesc.AdditionalInfo; + + -- Process inheritance: + if (APIDesc.Inherits ~= nil) then + for _, icls in ipairs(a_API) do + if (icls.Name == APIDesc.Inherits) then + table.insert(icls.Descendants, cls); + cls.Inherits = icls; + end + end + end + + cls.UndocumentedFunctions = {}; -- This will contain names of all the functions that are not documented + cls.UndocumentedConstants = {}; -- This will contain names of all the constants that are not documented + 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 + + local function AddFunction(a_Name, a_Params, a_Return, a_Notes) + table.insert(DoxyFunctions, {Name = a_Name, Params = a_Params, Return = a_Return, Notes = a_Notes}); + end + + if (APIDesc.Functions ~= nil) then + -- Assign function descriptions: + for _, func in ipairs(cls.Functions) do + local FnName = func.DocID or func.Name; + local FnDesc = APIDesc.Functions[FnName]; + if (FnDesc == nil) then + -- No description for this API function + AddFunction(func.Name); + if not(IsFunctionIgnored(cls.Name, FnName)) then + table.insert(cls.UndocumentedFunctions, FnName); + end + else + -- Description is available + if (FnDesc[1] == nil) then + -- Single function definition + AddFunction(func.Name, FnDesc.Params, FnDesc.Return, FnDesc.Notes); + else + -- Multiple function overloads + for _, desc in ipairs(FnDesc) do + AddFunction(func.Name, desc.Params, desc.Return, desc.Notes); + end -- for k, desc - FnDesc[] + end + FnDesc.IsExported = true; + end + end -- for j, func + + -- Replace functions with their described and overload-expanded versions: + cls.Functions = DoxyFunctions; + else -- if (APIDesc.Functions ~= nil) + for _, 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 + -- Assign constant descriptions: + for _, cons in ipairs(cls.Constants) do + local CnDesc = APIDesc.Constants[cons.Name]; + if (CnDesc == nil) then + -- Not documented + if not(IsConstantIgnored(cls.Name .. "." .. cons.Name)) then + table.insert(cls.UndocumentedConstants, cons.Name); + end + else + cons.Notes = CnDesc.Notes; + CnDesc.IsExported = true; + end + end -- for j, cons + else -- if (APIDesc.Constants ~= nil) + for _, 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 _, 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 _, 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) + + if (APIDesc.ConstantGroups ~= nil) then + -- Create links between the constants and the groups: + local NumInGroups = 0; + local NumInDescendantGroups = 0; + for j, group in pairs(APIDesc.ConstantGroups) do + group.Name = j; + group.Constants = {}; + if (type(group.Include) == "string") then + group.Include = { group.Include }; + end + local NumInGroup = 0; + for _, incl in ipairs(group.Include or {}) do + for _, cons in ipairs(cls.Constants) do + if ((cons.Group == nil) and cons.Name:match(incl)) then + cons.Group = group; + table.insert(group.Constants, cons); + NumInGroup = NumInGroup + 1; + end + end -- for cidx - cls.Constants[] + end -- for idx - group.Include[] + NumInGroups = NumInGroups + NumInGroup; + if (group.ShowInDescendants) then + NumInDescendantGroups = NumInDescendantGroups + NumInGroup; + end + + -- Sort the constants: + table.sort(group.Constants, + function(c1, c2) + return (c1.Name < c2.Name); + end + ); + end -- for j - APIDesc.ConstantGroups[] + cls.ConstantGroups = APIDesc.ConstantGroups; + cls.NumConstantsInGroups = NumInGroups; + cls.NumConstantsInGroupsForDescendants = NumInDescendantGroups; + + -- Remove grouped constants from the normal list: + local NewConstants = {}; + for _, cons in ipairs(cls.Constants) do + if (cons.Group == nil) then + table.insert(NewConstants, cons); + end + end + cls.Constants = NewConstants; + end -- if (ConstantGroups ~= 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 _, func in ipairs(cls.Functions) do + local FnName = func.DocID or func.Name; + if not(IsFunctionIgnored(cls.Name, FnName)) then + table.insert(cls.UndocumentedFunctions, FnName); + end + end -- for j, func - cls.Functions[] + for _, cons in ipairs(cls.Constants) do + if not(IsConstantIgnored(cls.Name .. "." .. cons.Name)) then + table.insert(cls.UndocumentedConstants, cons.Name); + end + end -- for j, cons - cls.Constants[] + for _, 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: + local NewFunctions = {}; + for _, fn in ipairs(cls.Functions) do + if (not(IsFunctionIgnored(cls.Name, fn.Name))) then + table.insert(NewFunctions, fn); + end + end -- for j, fn + cls.Functions = NewFunctions; + + -- Sort the functions (they may have been renamed): + table.sort(cls.Functions, + function(f1, f2) + if (f1.Name == f2.Name) then + -- Same name, either comparing the same function to itself, or two overloads, in which case compare the params + if ((f1.Params == nil) or (f2.Params == nil)) then + return 0; + end + return (f1.Params < f2.Params); + end + return (f1.Name < f2.Name); + end + ); + + -- Remove ignored constants: + local NewConstants = {}; + for _, cn in ipairs(cls.Constants) do + if (not(IsFunctionIgnored(cls.Name, cn.Name))) then + table.insert(NewConstants, cn); + end + end -- for j, cn + cls.Constants = NewConstants; + + -- Sort the constants: + table.sort(cls.Constants, + function(c1, c2) + return (c1.Name < c2.Name); + end + ); + + -- Remove ignored member variables: + local NewVariables = {}; + for _, 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: + for _, cls in ipairs(a_API) do + table.sort(cls.Descendants, + function(c1, c2) + return (c1.Name < c2.Name); + end + ); + end -- for i, cls +end + + + + + +local function ReadHooks(a_Hooks) + --[[ + a_Hooks = { + { Name = "HOOK_1"}, + { Name = "HOOK_2"}, + ... + }; + We want to add hook descriptions to each hook in this array + --]] + for _, hook in ipairs(a_Hooks) do + local HookDesc = g_APIDesc.Hooks[hook.Name]; + if (HookDesc ~= nil) then + for key, val in pairs(HookDesc) do + hook[key] = val; + end + end + end -- for i, hook - a_Hooks[] + g_Stats.NumTotalHooks = #a_Hooks; +end + + + + + +local function WriteHtmlClass(a_ClassAPI, a_ClassMenu) local cf, err = io.open("API/" .. a_ClassAPI.Name .. ".html", "w"); if (cf == nil) then + LOGINFO("Cannot write HTML API for class " .. a_ClassAPI.Name .. ": " .. err) return; end @@ -931,7 +726,7 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI, a_ClassMenu) cf:write("

Functions inherited from ", a_InheritedName, "

\n"); end cf:write("\n\n"); - for i, func in ipairs(a_Functions) do + for _, func in ipairs(a_Functions) do cf:write("\n"); cf:write("\n"); cf:write("\n"); @@ -942,7 +737,7 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI, a_ClassMenu) local function WriteConstantTable(a_Constants, a_Source) cf:write("
NameParametersReturn valueNotes
", func.Name, "", LinkifyString(func.Params or "", (a_InheritedName or a_ClassAPI.Name)), "", LinkifyString(func.Return or "", (a_InheritedName or a_ClassAPI.Name)), "
\n\n"); - for i, cons in ipairs(a_Constants) do + for _, cons in ipairs(a_Constants) do cf:write("\n"); cf:write("\n"); cf:write("\n"); @@ -965,7 +760,7 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI, a_ClassMenu) WriteConstantTable(a_Constants, Source); end - for k, group in pairs(a_ConstantGroups) do + for _, group in pairs(a_ConstantGroups) do if ((a_InheritedName == nil) or group.ShowInDescendants) then cf:write("

"); cf:write(LinkifyString(group.TextBefore or "", Source)); @@ -985,7 +780,7 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI, a_ClassMenu) end cf:write("

NameValueNotes
", cons.Name, "", cons.Value, "", LinkifyString(cons.Notes or "", a_Source), "
\n"); - for i, var in ipairs(a_Variables) do + for _, var in ipairs(a_Variables) do cf:write("\n"); cf:write("\n"); cf:write("\n \n"); @@ -998,7 +793,7 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI, a_ClassMenu) return; end cf:write("
    "); - for i, desc in ipairs(a_Descendants) do + for _, desc in ipairs(a_Descendants) do cf:write("
  • ", desc.Name, ""); WriteDescendants(desc.Descendants); cf:write("
  • \n"); @@ -1049,7 +844,7 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI, a_ClassMenu) local HasConstants = (#a_ClassAPI.Constants > 0) or (a_ClassAPI.NumConstantsInGroups > 0); local HasFunctions = (#a_ClassAPI.Functions > 0); local HasVariables = (#a_ClassAPI.Variables > 0); - for idx, cls in ipairs(InheritanceChain) do + for _, cls in ipairs(InheritanceChain) do HasConstants = HasConstants or (#cls.Constants > 0) or (cls.NumConstantsInGroupsForDescendants > 0); HasFunctions = HasFunctions or (#cls.Functions > 0); HasVariables = HasVariables or (#cls.Variables > 0); @@ -1088,7 +883,7 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI, a_ClassMenu) cf:write("

    Inheritance

    \n"); if (#InheritanceChain > 0) then cf:write("

    This class inherits from the following parent classes:

      \n"); - for i, cls in ipairs(InheritanceChain) do + for _, cls in ipairs(InheritanceChain) do cf:write("
    • ", cls.Name, "
    • \n"); end cf:write("

    \n"); @@ -1105,7 +900,7 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI, a_ClassMenu) cf:write("

    Constants

    \n"); WriteConstants(a_ClassAPI.Constants, a_ClassAPI.ConstantGroups, a_ClassAPI.NumConstantsInGroups, nil); g_Stats.NumTotalConstants = g_Stats.NumTotalConstants + #a_ClassAPI.Constants + (a_ClassAPI.NumConstantsInGroups or 0); - for i, cls in ipairs(InheritanceChain) do + for _, cls in ipairs(InheritanceChain) do WriteConstants(cls.Constants, cls.ConstantGroups, cls.NumConstantsInGroupsForDescendants, cls.Name); end; end; @@ -1115,7 +910,7 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI, a_ClassMenu) cf:write("

    Member variables

    \n"); WriteVariables(a_ClassAPI.Variables, nil); g_Stats.NumTotalVariables = g_Stats.NumTotalVariables + #a_ClassAPI.Variables; - for i, cls in ipairs(InheritanceChain) do + for _, cls in ipairs(InheritanceChain) do WriteVariables(cls.Variables, cls.Name); end; end @@ -1125,7 +920,7 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI, a_ClassMenu) cf:write("

    Functions

    \n"); WriteFunctions(a_ClassAPI.Functions, nil); g_Stats.NumTotalFunctions = g_Stats.NumTotalFunctions + #a_ClassAPI.Functions; - for i, cls in ipairs(InheritanceChain) do + for _, cls in ipairs(InheritanceChain) do WriteFunctions(cls.Functions, cls.Name); end end @@ -1146,71 +941,20 @@ end -function WriteHtmlHook(a_Hook, a_HookNav) - local fnam = "API/" .. a_Hook.DefaultFnName .. ".html"; - local f, error = io.open(fnam, "w"); - if (f == nil) then - LOG("Cannot write \"" .. fnam .. "\": \"" .. error .. "\"."); - return; - end - local HookName = a_Hook.DefaultFnName; - - f:write([[ - - MCServer API - ]], HookName, [[ Hook - - - - - - -
    -
    -

    ]], a_Hook.Name, [[

    -
    -
    -
NameTypeNotes
", var.Name, "", LinkifyString(var.Type or "(undocumented)", a_InheritedName or a_ClassAPI.Name), "", LinkifyString(var.Notes or "", a_InheritedName or a_ClassAPI.Name), "
- Index:
- Articles
- Classes
- Hooks
-
- Quick navigation:
- ]]); - f:write(a_HookNav); +local function WriteClasses(f, a_API, a_ClassMenu) f:write([[ -

+

Class index

+

The following classes are available in the MCServer Lua scripting language: +

    ]]); - f:write(LinkifyString(a_Hook.Desc, HookName)); - f:write("

    \n

    Callback function

    \n

    The default name for the callback function is "); - f:write(a_Hook.DefaultFnName, ". It has the following signature:\n"); - f:write("

    function ", HookName, "(");
    -	if (a_Hook.Params == nil) then
    -		a_Hook.Params = {};
    +	for _, cls in ipairs(a_API) do
    +		f:write("
  • ", cls.Name, "
  • \n"); + WriteHtmlClass(cls, a_ClassMenu); end - for i, param in ipairs(a_Hook.Params) do - if (i > 1) then - f:write(", "); - end - f:write(param.Name); - end - f:write(")
    \n

    Parameters:

    \n\n"); - for i, param in ipairs(a_Hook.Params) do - f:write("\n"); - end - f:write("
    NameTypeNotes
    ", param.Name, "", LinkifyString(param.Type, HookName), "", LinkifyString(param.Notes, HookName), "
    \n

    " .. (a_Hook.Returns or "") .. "

    \n\n"); - f:write([[

    Code examples

    Registering the callback

    ]]); - f:write("
    \n");
    -	f:write([[cPluginManager:AddHook(cPluginManager.]] .. a_Hook.Name .. ", My" .. a_Hook.DefaultFnName .. [[);]]);
    -	f:write("
    \n\n"); - local Examples = a_Hook.CodeExamples or {}; - for i, example in ipairs(Examples) do - f:write("

    ", (example.Title or "missing Title"), "

    \n"); - f:write("

    ", (example.Desc or "missing Desc"), "

    \n"); - f:write("
    ", (example.Code or "missing Code"), "\n
    \n\n"); - end - f:write([[
]]); - f:close(); + f:write([[ +

+
+ ]]); end @@ -1218,12 +962,12 @@ end --- Writes a list of undocumented objects into a file -function ListUndocumentedObjects(API, UndocumentedHooks) +local 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 + for _, 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)); @@ -1240,7 +984,7 @@ function ListUndocumentedObjects(API, UndocumentedHooks) 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 + for _, 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"); @@ -1249,7 +993,7 @@ function ListUndocumentedObjects(API, UndocumentedHooks) 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 + for _, 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"); @@ -1258,7 +1002,7 @@ function ListUndocumentedObjects(API, UndocumentedHooks) 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 + for _, 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"); @@ -1306,7 +1050,7 @@ end --- Lists the API objects that are documented but not available in the API: -function ListUnexportedObjects() +local function ListUnexportedObjects() f = io.open("API/_unexported-documented.txt", "w"); if (f ~= nil) then for clsname, cls in pairs(g_APIDesc.Classes) do @@ -1338,7 +1082,7 @@ end -function ListMissingPages() +local function ListMissingPages() local MissingPages = {}; local NumLinks = 0; for PageName, Referrers in pairs(g_TrackedPages) do @@ -1368,7 +1112,7 @@ function ListMissingPages() LOGWARNING("Cannot open _missingPages.txt for writing: '" .. err .. "'. There are " .. #MissingPages .. " pages missing."); return; end - for idx, pg in ipairs(MissingPages) do + for _, pg in ipairs(MissingPages) do f:write(pg.Name .. ":\n"); -- Sort and output the referrers: table.sort(pg.Refs); @@ -1384,7 +1128,7 @@ end --- Writes the documentation statistics (in g_Stats) into the given HTML file -function WriteStats(f) +local function WriteStats(f) local function ExportMeter(a_Percent) local Color; if (a_Percent > 99) then @@ -1453,7 +1197,198 @@ end -function HandleWebAdminDump(a_Request) +local function DumpAPIHtml(a_API) + LOG("Dumping all available functions and constants to API subfolder..."); + + -- Create the output folder + if not(cFile:IsFolder("API")) then + cFile:CreateFolder("API"); + end + + LOG("Copying static files.."); + cFile:CreateFolder("API/Static"); + local localFolder = g_Plugin:GetLocalFolder(); + for _, fnam in ipairs(cFile:GetFolderContents(localFolder .. "/Static")) do + cFile:Delete("API/Static/" .. fnam); + cFile:Copy(localFolder .. "/Static/" .. fnam, "API/Static/" .. fnam); + end + + -- Extract hook constants: + local Hooks = {}; + local UndocumentedHooks = {}; + for name, obj in pairs(cPluginManager) do + if ( + (type(obj) == "number") and + name:match("HOOK_.*") and + (name ~= "HOOK_MAX") and + (name ~= "HOOK_NUM_HOOKS") + ) then + table.insert(Hooks, { Name = name }); + end + end + table.sort(Hooks, + function(Hook1, Hook2) + return (Hook1.Name < Hook2.Name); + end + ); + + -- Read in the descriptions: + LOG("Reading descriptions..."); + ReadDescriptions(a_API); + ReadHooks(Hooks); + + -- Create a "class index" file, write each class as a link to that file, + -- then dump class contents into class-specific file + LOG("Writing HTML files..."); + local f, err = io.open("API/index.html", "w"); + if (f == nil) then + LOGINFO("Cannot output HTML API: " .. err); + return; + end + + -- Create a class navigation menu that will be inserted into each class file for faster navigation (#403) + local ClassMenuTab = {}; + for _, cls in ipairs(a_API) do + table.insert(ClassMenuTab, ""); + table.insert(ClassMenuTab, cls.Name); + table.insert(ClassMenuTab, "
"); + end + local ClassMenu = table.concat(ClassMenuTab, ""); + + -- Create a hook navigation menu that will be inserted into each hook file for faster navigation(#403) + local HookNavTab = {}; + for _, hook in ipairs(Hooks) do + table.insert(HookNavTab, ""); + table.insert(HookNavTab, (hook.Name:gsub("^HOOK_", ""))); -- remove the "HOOK_" part of the name + table.insert(HookNavTab, "
"); + end + local HookNav = table.concat(HookNavTab, ""); + + -- Write the HTML file: + f:write([[ + + + MCServer API - Index + + + +
+
+

MCServer API - Index

+
+
+

The API reference is divided into the following sections:

+ +
+ ]]); + + WriteArticles(f); + WriteClasses(f, a_API, ClassMenu); + WriteHooks(f, Hooks, UndocumentedHooks, HookNav); + + -- Copy the static files to the output folder: + local StaticFiles = + { + "main.css", + "prettify.js", + "prettify.css", + "lang-lua.js", + }; + for _, fnam in ipairs(StaticFiles) do + cFile:Delete("API/" .. fnam); + cFile:Copy(g_Plugin:GetLocalFolder() .. "/" .. fnam, "API/" .. fnam); + end + + -- List the documentation problems: + LOG("Listing leftovers..."); + ListUndocumentedObjects(a_API, UndocumentedHooks); + ListUnexportedObjects(); + ListMissingPages(); + + WriteStats(f); + + f:write([[ +
+ +]]); + f:close(); + + LOG("API subfolder written"); +end + + + + + +local function DumpApi() + LOG("Dumping the API...") + + -- Load the API descriptions from the Classes and Hooks subfolders: + -- This needs to be done each time the command is invoked because the export modifies the tables' contents + dofile(g_PluginFolder .. "/APIDesc.lua") + if (g_APIDesc.Classes == nil) then + g_APIDesc.Classes = {}; + end + if (g_APIDesc.Hooks == nil) then + g_APIDesc.Hooks = {}; + end + LoadAPIFiles("/Classes/", g_APIDesc.Classes); + LoadAPIFiles("/Hooks/", g_APIDesc.Hooks); + + -- Reset the stats: + g_TrackedPages = {}; -- List of tracked pages, to be checked later whether they exist. Each item is an array of referring pagenames. + g_Stats = -- Statistics about the documentation + { + NumTotalClasses = 0, + NumUndocumentedClasses = 0, + NumTotalFunctions = 0, + NumUndocumentedFunctions = 0, + NumTotalConstants = 0, + NumUndocumentedConstants = 0, + NumTotalVariables = 0, + NumUndocumentedVariables = 0, + NumTotalHooks = 0, + NumUndocumentedHooks = 0, + NumTrackedLinks = 0, + NumInvalidLinks = 0, + } + + -- Create the API tables: + local API, Globals = CreateAPITables(); + + -- Sort the classes by name: + table.sort(API, + function (c1, c2) + return (string.lower(c1.Name) < string.lower(c2.Name)); + end + ); + g_Stats.NumTotalClasses = #API; + + -- Add Globals into the API: + Globals.Name = "Globals"; + table.insert(API, Globals); + + -- Dump all available API object in HTML format into a subfolder: + DumpAPIHtml(API); + + LOG("APIDump finished"); + return true +end + + + + + +local function HandleWebAdminDump(a_Request) if (a_Request.PostParams["Dump"] ~= nil) then DumpApi() end @@ -1467,3 +1402,29 @@ end + +local function HandleCmdApi(a_Split) + DumpApi() + return true +end + + + + + +function Initialize(Plugin) + g_Plugin = Plugin; + g_PluginFolder = Plugin:GetLocalFolder(); + + LOG("Initialising " .. Plugin:GetName() .. " v." .. Plugin:GetVersion()) + + cPluginManager:BindConsoleCommand("api", HandleCmdApi, "Dumps the Lua API docs into the API/ subfolder") + g_Plugin:AddWebTab("APIDump", HandleWebAdminDump) + -- TODO: Add a WebAdmin tab that has a Dump button + return true +end + + + + +