1
0

Merge branch 'master' into cmake-win

This commit is contained in:
madmaxoft 2013-12-28 14:43:22 +01:00
commit 3cec77ced9
10 changed files with 298 additions and 163 deletions

1
.gitignore vendored
View File

@ -11,6 +11,7 @@ cloc.xsl
*.user *.user
*.suo *.suo
/EveryNight.cmd /EveryNight.cmd
/UploadLuaAPI.cmd
# IDE Stuff # IDE Stuff
## Sublime Text ## Sublime Text

View File

@ -2760,6 +2760,8 @@ end
ExtraPages = ExtraPages =
{ {
-- No sorting is provided for these, they will be output in the same order as defined here -- No sorting is provided for these, they will be output in the same order as defined here
{ FileName = "Writing-a-MCServer-plugin.html", Title = "Writing a MCServer plugin" },
{ FileName = "SettingUpDecoda.html", Title = "Setting up the Decoda Lua IDE" },
{ FileName = "WebWorldThreads.html", Title = "Webserver vs World threads" }, { FileName = "WebWorldThreads.html", Title = "Webserver vs World threads" },
} }
} ; } ;

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html>
<head>
<title>MCServer - Setting up Decoda</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>
<meta charset="UTF-8">
</head>
<body>
<div id="content">
<h1>Setting up the Decoda IDE</h1>
<p>
This article will explain how to set up Decoda, an IDE for writing Lua code, so that you can develop MCServer plugins with the comfort of an IDE.</p>
<h2><img src="Static/decoda_logo.png" /> About Decoda</h2>
<p>To quickly introduce Decoda, it is an IDE for writing Lua code. It has the basic features expected of an IDE - you can group files into project, you can edit multiple files in a tabbed editor, the code is syntax-highlighted. Code completion, symbol browsing, and more. It also features a Lua debugger that allows you to debug your Lua code within any application that embeds the Lua runtime or uses Lua as a dynamic-link library (DLL). Although it is written using the multiplatform WxWidgets toolkit, it hasn't yet been ported to any platform other than 32-bit Windows. This unfortunately means that Linux users will not be able to use it. It can be used on 64-bit Windows, but the debugger only works for 32-bit programs.</p>
<p>Here's a screenshot of a default Decoda window with the debugger stepping through the code (scaled down):<br />
<img src="Static/decoda_workspace.png" /></p>
<p>As you can see, you can set breakpoints in the code, inspect variables' values, view both the Lua and native (C++) call-stacks. Decoda also breaks program execution when a faulty Lua script is executed, providing a detailed error message and pointing you directly to the faulting code. It is even possible to attach a C++ debugger to a process that is being debugged by Decoda, this way you can trap both C++ and Lua errors.</p>
<p>Decoda is open-source, the sources are on GitHub: <a href="https://github.com/unknownworlds/decoda">https://github.com/unknownworlds/decoda</a>. You can download a compiled binary from the creators' site, <a href="http://unknownworlds.com/decoda/">http://unknownworlds.com/decoda/</a>.
<h2><img src="Static/decoda_logo.png" /> Project management</h2>
<p>To begin using Decoda, you need to create a project, or load an existing one. Decoda projects have a .deproj extension, and are simply a list of Lua files that are to be opened. You can create a project through menu Project -> New Project. Save your project first, so that Decoda knows what relative paths to use for the files. Then either add existing Lua files or create new one, through menu Project -> Add Add New File / Add Existing File.</p>
<p>Next you need to set up the executable that Decoda will run when debugging your files. Select menu Project -> Settings. A new dialog will open:<br />
<img src="Static/decoda_debug_settings.png" /></p>
<p>In the debugging section, fill in the full path to MCServer.exe, or click the triple-dot button to browse for the file. Note that the Working directory will be automatically filled in for you with the folder where the executable is (until the last backslash). This is how it's supposed to work, don't change it; if it for some reason doesn't update, copy and paste the folder name from the Command edit box. All done, you can close this dialog now.</p>
<h2><img src="Static/decoda_logo.png" /> Debugging</h2>
<p>You are now ready to debug your code. Before doing that, though, don't forget to save your project file. If you haven't done so already, enable your plugin in the settings.ini file. If you want the program to break at a certain line, it is best to set the breakpoint before starting the program. Set the cursor on the line and hit F9 (or use menu Debug -> Toggle Breakpoint) to toggle a breakpoint on that line. Finally, hit F5, or select menu Debug -> Start to launch MCServer under the debugger. The MCServer window comes up and loads your plugin. If Decoda displays the Project Settings dialog instead, you haven't set up the executable to run, see the Project management section for instructions.</p>
<p>At this point you will see that Decoda starts adding new items to your project. All the files for all plugins are added temporarily. Don't worry, they are only temporary, they will be removed again once the debugging session finishes. You can tell the temporary files from the regular files by their icon, it's faded out. Decoda handles all the files that MCServer loads, so you can actually debug any of those faded files, too.</p>
<p>If there's an error in the code, the Decoda window will flash and a dialog box will come up, describing the error and taking you to the line where it occured. Note that the execution is paused in the thread executing the plugin, so until you select Debug -> Continue from the menu (F5), MCServer won't be fully running. You can fix the error and issue a "reload" command in MCServer console to reload the plugin code anew (MCServer doesn't detect changes in plugin code automatically).</p>
<p>If the execution hits a breakpoint, the Decoda window will flash and a yellow arrow is displayed next to the line. You can step through the code using F10 and F11, just like in MSVS. You can also use the Watch window to inspect variable values, or simply hover your mouse over a variable to display its value in the tooltip.</p>
<h2><img src="Static/decoda_logo.png" /> Limitations</h2>
<p>So far everything seems wonderful. Unfortunately, it doesn't always go as easy. There are several limits to what Decoda can do:</p>
<ul>
<li>When the program encounters a logical bug (using a nil value, calling a non-existent function etc.), Decoda will break, but usually the Watch window and the tooltips are showing nonsense values. You shouldn't trust them in such a case, if you kep running into such a problem regularly, put console logging functions (LOG) in the code to print out any info you need prior to the failure.</li>
<li>Sometimes breakpoints just don't work. This is especially true if there are multiple plugins that have files of the same name. Breakpoints will never work after changing the code and reloading the plugin in MCServer; you need to stop the server and start again to reenable breakpoints.</li>
<li>Most weirdly, sometimes Decoda reports an error, but instead of opening the current version of the file, opens up another window with old contents of the file. Watch for this, because you could overwrite your new code if you saved this file over your newer file. Fortunately enough, Decoda will always ask you for a filename before saving such a file.</li>
<li>Decoda stores the project in two files. The .deproj file has the file list, and should be put into version control systems. The .deuser file has the settings (debugged application etc.) and is per-user specific. This file shouldn't go to version control systems, because each user may have different paths for the debuggee.</li>
<li>Unfortunately for us Windows users, the Decoda project file uses Unix-lineends (CR only). This makes it problematic when checking the file into a version control system, since those usually expect windows (CRLF) lineends; I personally convert the lineends each time I edit the project file using <a href="http://jsimlo.sk/notepad/">TED Notepad</a>.</li>
</ul>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View File

@ -6,6 +6,7 @@
<link rel="stylesheet" type="text/css" href="prettify.css" /> <link rel="stylesheet" type="text/css" href="prettify.css" />
<script src="prettify.js"></script> <script src="prettify.js"></script>
<script src="lang-lua.js"></script> <script src="lang-lua.js"></script>
<meta charset="UTF-8">
</head> </head>
<body> <body>
<div id="content"> <div id="content">

View File

@ -2,12 +2,12 @@
<html> <html>
<head> <head>
<!--MCServer, a divison of McDonalds Enterprises-->
<title>MCS Plugin Tutorial</title> <title>MCS Plugin Tutorial</title>
<link rel="stylesheet" type="text/css" href="main.css" /> <link rel="stylesheet" type="text/css" href="main.css" />
<link rel="stylesheet" type="text/css" href="prettify.css" /> <link rel="stylesheet" type="text/css" href="prettify.css" />
<script src="prettify.js"></script> <script src="prettify.js"></script>
<script src="lang-lua.js"></script> <script src="lang-lua.js"></script>
<meta charset="UTF-8">
</head> </head>
<body> <body>
<div id="content"> <div id="content">
@ -35,26 +35,26 @@
Format it like so: Format it like so:
</p> </p>
<pre class="prettyprint lang-lua"> <pre class="prettyprint lang-lua">
local PLUGIN local PLUGIN
function Initialize( Plugin )
Plugin:SetName( "DerpyPlugin" )
Plugin:SetVersion( 1 )
PLUGIN = Plugin
-- Hooks function Initialize(Plugin)
Plugin:SetName("DerpyPlugin")
local PluginManager = cPluginManager:Get() Plugin:SetVersion(1)
-- Command bindings
PLUGIN = Plugin
LOG( "Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion() ) -- Hooks
return true
end local PluginManager = cPluginManager:Get()
-- Command bindings
function OnDisable()
LOG(PLUGIN:GetName() .. " is shutting down...") LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
end return true
end
function OnDisable()
LOG(PLUGIN:GetName() .. " is shutting down...")
end
</pre> </pre>
<p> <p>
Now for an explanation of the basics. Now for an explanation of the basics.
@ -72,7 +72,7 @@
<h2>Registering hooks</h2> <h2>Registering hooks</h2>
<p> <p>
Hooks are things that MCServer calls when an internal event occurs. For example, a hook is fired when a player places a block, moves, Hooks are things that MCServer calls when an internal event occurs. For example, a hook is fired when a player places a block, moves,
logs on, eats, and many other things. For a full list, see <a href="http://mc-server.xoft.cz/LuaAPI/">the API documentation</a>. logs on, eats, and many other things. For a full list, see <a href="index.html">the API documentation</a>.
</p> </p>
<p> <p>
A hook can be either informative or overridable. In any case, returning false will not trigger a response, but returning true will cancel A hook can be either informative or overridable. In any case, returning false will not trigger a response, but returning true will cancel
@ -84,7 +84,7 @@
To register a hook, insert the following code template into the "-- Hooks" area in the previous code example. To register a hook, insert the following code template into the "-- Hooks" area in the previous code example.
</p> </p>
<pre class="prettyprint lang-lua"> <pre class="prettyprint lang-lua">
cPluginManager.AddHook(cPluginManager.HOOK_NAME_HERE, FunctionNameToBeCalled) cPluginManager.AddHook(cPluginManager.HOOK_NAME_HERE, FunctionNameToBeCalled)
</pre> </pre>
<p> <p>
What does this code do? What does this code do?
@ -98,22 +98,22 @@
So in total, this is a working representation of what we have so far covered. So in total, this is a working representation of what we have so far covered.
</p> </p>
<pre class="prettyprint lang-lua"> <pre class="prettyprint lang-lua">
function Initialize( Plugin ) function Initialize(Plugin)
Plugin:SetName( "DerpyPlugin" ) Plugin:SetName("DerpyPlugin")
Plugin:SetVersion( 1 ) Plugin:SetVersion(1)
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_MOVING, OnPlayerMoving) cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_MOVING, OnPlayerMoving)
local PluginManager = cPluginManager:Get()
-- Command bindings
LOG( "Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion() ) local PluginManager = cPluginManager:Get()
return true -- Command bindings
end
LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
function OnPlayerMoving(Player) -- See API docs for parameters of all hooks return true
return true -- Prohibit player movement, see docs for whether a hook is cancellable end
end
function OnPlayerMoving(Player) -- See API docs for parameters of all hooks
return true -- Prohibit player movement, see docs for whether a hook is cancellable
end
</pre> </pre>
<p> <p>
So, that code stops the player from moving. Not particularly helpful, but yes :P. Note that ALL documentation is available So, that code stops the player from moving. Not particularly helpful, but yes :P. Note that ALL documentation is available
@ -126,11 +126,11 @@
We firstly add this template to the "-- Command bindings" section of the initial example: We firstly add this template to the "-- Command bindings" section of the initial example:
</p> </p>
<pre class="prettyprint lang-lua"> <pre class="prettyprint lang-lua">
-- ADD THIS IF COMMAND DOES NOT REQUIRE A PARAMETER (/explode) -- ADD THIS IF COMMAND DOES NOT REQUIRE A PARAMETER (/explode)
PluginManager:BindCommand("/commandname", "permissionnode", FunctionToCall, " - Description of command") PluginManager:BindCommand("/commandname", "permissionnode", FunctionToCall, " - Description of command")
-- ADD THIS IF COMMAND DOES REQUIRE A PARAMETER (/explode Notch) -- ADD THIS IF COMMAND DOES REQUIRE A PARAMETER (/explode Notch)
PluginManager:BindCommand("/commandname", "permissionnode", FunctionToCall, " ~ Description of command and parameter(s)") PluginManager:BindCommand("/commandname", "permissionnode", FunctionToCall, " ~ Description of command and parameter(s)")
</pre> </pre>
<p> <p>
What does it do, and why are there two? What does it do, and why are there two?
@ -171,17 +171,17 @@
your code in. Since MCS brings all the files together on JIT compile, we don't need to worry about requiring any files or such. Simply follow the below examples: your code in. Since MCS brings all the files together on JIT compile, we don't need to worry about requiring any files or such. Simply follow the below examples:
</p> </p>
<pre class="prettyprint lang-lua"> <pre class="prettyprint lang-lua">
-- Format: §yellow[INFO] §white%text% (yellow [INFO], white text following it) -- Format: §yellow[INFO] §white%text% (yellow [INFO], white text following it)
-- Use: Informational message, such as instructions for usage of a command -- Use: Informational message, such as instructions for usage of a command
SendMessage(Player, "Usage: /explode [player]") SendMessage(Player, "Usage: /explode [player]")
-- Format: §green[INFO] §white%text% (green [INFO] etc.) -- Format: §green[INFO] §white%text% (green [INFO] etc.)
-- Use: Success message, like when a command executes successfully -- Use: Success message, like when a command executes successfully
SendMessageSuccess(Player, "Notch was blown up!") SendMessageSuccess(Player, "Notch was blown up!")
-- Format: §rose[INFO] §white%text% (rose coloured [INFO] etc.) -- Format: §rose[INFO] §white%text% (rose coloured [INFO] etc.)
-- Use: Failure message, like when a command was entered correctly but failed to run, such as when the destination player wasn't found in a /tp command -- Use: Failure message, like when a command was entered correctly but failed to run, such as when the destination player wasn't found in a /tp command
SendMessageFailure(Player, "Player Salted was not found") SendMessageFailure(Player, "Player Salted was not found")
</pre> </pre>
<p> <p>
Those are the basics. If you want to output text to the player for a reason other than the three listed above, and you want to colour the text, simply concatenate Those are the basics. If you want to output text to the player for a reason other than the three listed above, and you want to colour the text, simply concatenate
@ -193,51 +193,63 @@
So, a working example that checks the validity of a command, and blows up a player, and also refuses pickup collection to players with >100ms ping. So, a working example that checks the validity of a command, and blows up a player, and also refuses pickup collection to players with >100ms ping.
</p> </p>
<pre class="prettyprint lang-lua"> <pre class="prettyprint lang-lua">
function Initialize( Plugin ) function Initialize(Plugin)
Plugin:SetName( "DerpyPluginThatBlowsPeopleUp" ) Plugin:SetName("DerpyPluginThatBlowsPeopleUp")
Plugin:SetVersion( 9001 ) Plugin:SetVersion(9001)
local PluginManager = cPluginManager:Get()
PluginManager:BindCommand("/explode", "derpyplugin.explode", Explode, " ~ Explode a player");
cPluginManager.AddHook(cPluginManager.HOOK_COLLECTING_PICKUP, OnCollectingPickup) local PluginManager = cPluginManager:Get()
PluginManager:BindCommand("/explode", "derpyplugin.explode", Explode, " ~ Explode a player");
LOG( "Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion() ) cPluginManager.AddHook(cPluginManager.HOOK_COLLECTING_PICKUP, OnCollectingPickup)
return true
end LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
return true
function Explode(Split, Player) end
if #Split ~= 2
SendMessage(Player, "Usage: /explode [playername]") -- There was more or less than one argument (excluding the /explode bit) function Explode(Split, Player)
else if (#Split ~= 2) then
local ExplodePlayer = function(Explodee) -- Create a callback ExplodePlayer with parameter Explodee, which MCS calls for every player on the server -- There was more or less than one argument (excluding the "/explode" bit)
if (Explodee:GetName() == Split[2] then -- If the player we are currently at is the one we specified as the parameter... -- Send the proper usage to the player and exit
Player:GetWorld():DoExplosionAt(Explodee:GetPosX(), Explodee:GetPosY(), Explodee:GetPosZ(), false, esPlugin) -- Explode 'em; see API docs for further details of this function SendMessage(Player, "Usage: /explode [playername]")
SendMessageSuccess(Player, Split[2] .. " was successfully exploded") -- Success! return true
return true -- Break out end
end
end -- Create a callback ExplodePlayer with parameter Explodee, which MCS calls for every player on the server
local HasExploded = false
cRoot:Get():FindAndDoWithPlayer(Split[2], ExplodePlayer) -- Tells MCS to loop through all players and call the callback above with the Player object it has found local ExplodePlayer = function(Explodee)
-- If the player we are currently at is the one we specified as the parameter
SendMessageFailure(Player, Split[2] .. " was not found") -- We have not broken out so far, therefore, the player must not exist, send failure if (Explodee:GetName() == Split[2]) then
end -- Create an explosion at the same position as they are; see <a href="cWorld.html">API docs</a> for further details of this function
Player:GetWorld():DoExplosionAt(Explodee:GetPosX(), Explodee:GetPosY(), Explodee:GetPosZ(), false, esPlugin)
return true -- Concluding return SendMessageSuccess(Player, Split[2] .. " was successfully exploded")
end HasExploded = true;
return true -- Signalize to MCS that we do not need to call this callback for any more players
function OnCollectingPickup(Player, Pickup) -- Again, see the API docs for parameters of all hooks. In this case, it is a Player and Pickup object end
if (Player:GetClientHandle():GetPing() > 100) then -- Get ping of player, in milliseconds end
return true -- Discriminate against high latency - you don't get drops :D
else -- Tell MCS to loop through all players and call the callback above with the Player object it has found
return false -- You do get the drops! Yay~ cRoot:Get():FindAndDoWithPlayer(Split[2], ExplodePlayer)
end
end if not(HasExploded) then
-- We have not broken out so far, therefore, the player must not exist, send failure
SendMessageFailure(Player, Split[2] .. " was not found")
end
return true
end
function OnCollectingPickup(Player, Pickup) -- Again, see the API docs for parameters of all hooks. In this case, it is a Player and Pickup object
if (Player:GetClientHandle():GetPing() > 100) then -- Get ping of player, in milliseconds
return true -- Discriminate against high latency - you don't get drops :D
else
return false -- You do get the drops! Yay~
end
end
</pre> </pre>
<p> <p>
Make sure to read the comments for a description of what everything does. Also be sure to return true for all <b>command</b> handlers, unless you want MCS to print out an "Unknown command" message Make sure to read the comments for a description of what everything does. Also be sure to return true for all <b>command</b> handlers, unless you want MCS to print out an "Unknown command" message
when the command gets executed :P. Make sure to follow standards - use CoreMessaging.lua functions for messaging, dashes for no parameter commands and tildes for vice versa, when the command gets executed :P. Make sure to follow standards - use CoreMessaging.lua functions for messaging, dashes for no parameter commands and tildes for vice versa,
and finally, <a href="http://mc-server.xoft.cz/LuaAPI/">the API documentation</a> is your friend! and finally, <a href="index.html">the API documentation</a> is your friend!
</p> </p>
<p> <p>
Happy coding ;) Happy coding ;)
@ -247,7 +259,5 @@
prettyPrint(); prettyPrint();
</script> </script>
</div> </div>
<hr />
<footer>This tutorial was brought you by Aperture Science, in conjunction with McDonalds Enterprises.<br /></footer>
</body> </body>
</html> </html>

View File

@ -229,14 +229,99 @@ end
local function WriteArticles(f)
f:write([[
<a name="articles"><h2>Articles</h2></a>
<p>The following articles provide various extra information on plugin development</p>
<ul>
]]);
for i, extra in ipairs(g_APIDesc.ExtraPages) do
local SrcFileName = g_PluginFolder .. "/" .. extra.FileName;
if (cFile:Exists(SrcFileName)) then
local DstFileName = "API/" .. extra.FileName;
if (cFile:Exists(DstFileName)) then
cFile:Delete(DstFileName);
end
cFile:Copy(SrcFileName, DstFileName);
f:write("<li><a href=\"" .. extra.FileName .. "\">" .. extra.Title .. "</a></li>\n");
else
f:write("<li>" .. extra.Title .. " <i>(file is missing)</i></li>\n");
end
end
f:write("</ul><hr />");
end
local function WriteClasses(f, a_API, a_ClassMenu)
f:write([[
<a name="classes"><h2>Class index</h2></a>
<p>The following classes are available in the MCServer Lua scripting language:
<ul>
]]);
for i, cls in ipairs(a_API) do
f:write("<li><a href=\"", cls.Name, ".html\">", cls.Name, "</a></li>\n");
WriteHtmlClass(cls, a_API, a_ClassMenu);
end
f:write([[
</ul></p>
<hr />
]]);
end
local function WriteHooks(f, a_Hooks, a_UndocumentedHooks, a_HookNav)
f:write([[
<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(a_Hooks) do
if (hook.DefaultFnName == nil) then
-- The hook is not documented yet
f:write(" <tr>\n <td>" .. hook.Name .. "</td>\n <td><i>(No documentation yet)</i></td>\n </tr>\n");
table.insert(a_UndocumentedHooks, hook.Name);
else
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, a_HookNav);
end
end
f:write([[
</table>
<hr />
]]);
end
function DumpAPIHtml() function DumpAPIHtml()
LOG("Dumping all available functions and constants to API subfolder..."); LOG("Dumping all available functions and constants to API subfolder...");
LOG("Moving static files.."); LOG("Copying static files..");
cFile:CreateFolder("API/Static"); cFile:CreateFolder("API/Static");
local localFolder = g_Plugin:GetLocalFolder(); local localFolder = g_Plugin:GetLocalFolder();
for k, v in cFile:GetFolderContents(localFolder .. "/Static") do for idx, fnam in ipairs(cFile:GetFolderContents(localFolder .. "/Static")) do
cFile:Copy(localFolder .. "/Static/" .. v, "API/Static/" .. v); cFile:Copy(localFolder .. "/Static/" .. fnam, "API/Static/" .. fnam);
end end
LOG("Creating API tables..."); LOG("Creating API tables...");
@ -293,7 +378,30 @@ function DumpAPIHtml()
LOGINFO("Cannot output HTML API: " .. err); LOGINFO("Cannot output HTML API: " .. err);
return; return;
end 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, "<a href='");
table.insert(ClassMenuTab, cls.Name);
table.insert(ClassMenuTab, ".html'>");
table.insert(ClassMenuTab, cls.Name);
table.insert(ClassMenuTab, "</a><br />");
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, "<a href='");
table.insert(HookNavTab, hook.DefaultFnName);
table.insert(HookNavTab, ".html'>");
table.insert(HookNavTab, (hook.Name:gsub("^HOOK_", ""))); -- remove the "HOOK_" part of the name
table.insert(HookNavTab, "</a><br />");
end
local HookNav = table.concat(HookNavTab, "");
-- Write the HTML file:
f:write([[<!DOCTYPE html> f:write([[<!DOCTYPE html>
<html> <html>
<head> <head>
@ -308,74 +416,18 @@ function DumpAPIHtml()
</header> </header>
<p>The API reference is divided into the following sections:</p> <p>The API reference is divided into the following sections:</p>
<ul> <ul>
<li><a href="#articles">Articles</a></li>
<li><a href="#classes">Class index</a></li> <li><a href="#classes">Class index</a></li>
<li><a href="#hooks">Hooks</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> <li><a href="#docstats">Documentation statistics</a></li>
</ul> </ul>
<hr /> <hr />
<a name="classes"><h2>Class index</h2></a>
<p>The following classes are available in the MCServer Lua scripting language:
<ul>
]]); ]]);
for i, cls in ipairs(API) do
f:write("<li><a href=\"", cls.Name, ".html\">", cls.Name, "</a></li>\n");
WriteHtmlClass(cls, API);
end
f:write([[
</ul></p>
<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>\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>\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>
<hr /> WriteArticles(f);
<a name="extra"><h2>Extra pages</h2></a> WriteClasses(f, API, ClassMenu);
WriteHooks(f, Hooks, UndocumentedHooks, HookNav);
<p>The following pages provide various extra information</p>
<ul>
]]);
for i, extra in ipairs(g_APIDesc.ExtraPages) do
local SrcFileName = g_PluginFolder .. "/" .. extra.FileName;
if (cFile:Exists(SrcFileName)) then
local DstFileName = "API/" .. extra.FileName;
if (cFile:Exists(DstFileName)) then
cFile:Delete(DstFileName);
end
cFile:Copy(SrcFileName, DstFileName);
f:write(" <li><a href=\"" .. extra.FileName .. "\">" .. extra.Title .. "</a></li>\n");
else
f:write(" <li>" .. extra.Title .. " <i>(file is missing)</i></li>\n");
end
end
f:write("</ul>");
-- Copy the static files to the output folder (overwrite any existing): -- 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() .. "/main.css", "API/main.css");
cFile:Copy(g_Plugin:GetLocalFolder() .. "/prettify.js", "API/prettify.js"); cFile:Copy(g_Plugin:GetLocalFolder() .. "/prettify.js", "API/prettify.js");
@ -831,7 +883,7 @@ end
function WriteHtmlClass(a_ClassAPI, a_AllAPI) function WriteHtmlClass(a_ClassAPI, a_AllAPI, a_ClassMenu)
local cf, err = io.open("API/" .. a_ClassAPI.Name .. ".html", "w"); local cf, err = io.open("API/" .. a_ClassAPI.Name .. ".html", "w");
if (cf == nil) then if (cf == nil) then
return; return;
@ -946,7 +998,17 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI)
<h1>]], a_ClassAPI.Name, [[</h1> <h1>]], a_ClassAPI.Name, [[</h1>
<hr /> <hr />
</header> </header>
<h1>Contents</h1> <table><tr><td style="vertical-align: top;">
Index:<br />
<a href='index.html#articles'>Articles</a><br />
<a href='index.html#classes'>Classes</a><br />
<a href='index.html#hooks'>Hooks</a><br />
<br />
Quick navigation:<br />
]]);
cf:write(a_ClassMenu);
cf:write([[
</td><td style="vertical-align: top;"><h1>Contents</h1>
<p><ul> <p><ul>
]]); ]]);
@ -1044,7 +1106,7 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI)
end end
end end
cf:write([[</div><script>prettyPrint();</script></body></html>]]); cf:write([[</td></tr></table></div><script>prettyPrint();</script></body></html>]]);
cf:close(); cf:close();
end end
@ -1052,7 +1114,7 @@ end
function WriteHtmlHook(a_Hook) function WriteHtmlHook(a_Hook, a_HookNav)
local fnam = "API/" .. a_Hook.DefaultFnName .. ".html"; local fnam = "API/" .. a_Hook.DefaultFnName .. ".html";
local f, error = io.open(fnam, "w"); local f, error = io.open(fnam, "w");
if (f == nil) then if (f == nil) then
@ -1075,7 +1137,17 @@ function WriteHtmlHook(a_Hook)
<h1>]], a_Hook.Name, [[</h1> <h1>]], a_Hook.Name, [[</h1>
<hr /> <hr />
</header> </header>
<p> <table><tr><td style="vertical-align: top;">
Index:<br />
<a href='index.html#articles'>Articles</a><br />
<a href='index.html#classes'>Classes</a><br />
<a href='index.html#hooks'>Hooks</a><br />
<br />
Quick navigation:<br />
]]);
f:write(a_HookNav);
f:write([[
</td><td style="vertical-align: top;"><p>
]]); ]]);
f:write(LinkifyString(a_Hook.Desc, HookName)); 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("</p>\n<hr /><h1>Callback function</h1>\n<p>The default name for the callback function is ");
@ -1105,7 +1177,7 @@ function WriteHtmlHook(a_Hook)
f:write("<p>", (example.Desc or "<i>missing Desc</i>"), "</p>\n"); f:write("<p>", (example.Desc or "<i>missing Desc</i>"), "</p>\n");
f:write("<pre class=\"prettyprint lang-lua\">", (example.Code or "<i>missing Code</i>"), "\n</pre>\n\n"); f:write("<pre class=\"prettyprint lang-lua\">", (example.Code or "<i>missing Code</i>"), "\n</pre>\n\n");
end end
f:write([[</div><script>prettyPrint();</script></body></html>]]); f:write([[</td></tr></table></div><script>prettyPrint();</script></body></html>]]);
f:close(); f:close();
end end

View File

@ -38,7 +38,7 @@ cd ..
:: Upload the API to the web: :: Upload the API to the web:
ncftpput -p %ftppass% -u %ftpuser% -T temp_ %ftpsite% /LuaAPI MCServer/API/*.* ncftpput -p %ftppass% -u %ftpuser% -T temp_ -R %ftpsite% /LuaAPI MCServer/API/*.*
if errorlevel 1 goto haderror if errorlevel 1 goto haderror
echo Upload finished. echo Upload finished.