1
0

Added analytics to APIDump

This commit is contained in:
Alexander Harkness 2015-12-21 18:07:13 +00:00
parent c0732cc666
commit 23e2bc5042
8 changed files with 6780 additions and 57 deletions

View File

@ -22,25 +22,25 @@
<li><a href="#Using">Using the file in code</a></li> <li><a href="#Using">Using the file in code</a></li>
<li><a href="#Examples">Examples</a></li> <li><a href="#Examples">Examples</a></li>
</ul> </ul>
<hr /> <hr />
<a name="Introduction"><h2>Introduction</h2></a> <a name="Introduction"><h2>Introduction</h2></a>
<p>For a long time Cuberite plugins were plagued by poor documentation. The plugins worked, people who wrote them knew how to use them, but for anyone new to the plugin it was a terrible ordeal learning how to use it. Most of the times, the plugin authors only wrote what commands the plugin supported, sometimes not even that. Then, there was a call to action to put an end to this, to make documenting the plugins easy and at the same time centralized. Thus, the Info.lua file was born.</p> <p>For a long time Cuberite plugins were plagued by poor documentation. The plugins worked, people who wrote them knew how to use them, but for anyone new to the plugin it was a terrible ordeal learning how to use it. Most of the times, the plugin authors only wrote what commands the plugin supported, sometimes not even that. Then, there was a call to action to put an end to this, to make documenting the plugins easy and at the same time centralized. Thus, the Info.lua file was born.</p>
<p>Most plugins have some parts that are the same across all the plugins. These are commands, console commands and their permissions. If a plugin implemented a command, it would practically copy &amp; paste the same code over and over again. So it makes sense to extract only unique information, centralize it and automate all the parts around it. This was another reason for the Info.lua file - it is a central hub of commands, console commands and their permissions.</p> <p>Most plugins have some parts that are the same across all the plugins. These are commands, console commands and their permissions. If a plugin implemented a command, it would practically copy &amp; paste the same code over and over again. So it makes sense to extract only unique information, centralize it and automate all the parts around it. This was another reason for the Info.lua file - it is a central hub of commands, console commands and their permissions.</p>
<p>Last, but not least, we want to make a plugin repository on the web in the future, a repository that would store plugins, their descriptions, comments. It makes sense that the centralized information can be parsed by the repository automatically, so that advanced things, such as searching for a plugin based on a command, or determining whether two plugins collide command-wise, are possible.</p> <p>Last, but not least, we want to make a plugin repository on the web in the future, a repository that would store plugins, their descriptions, comments. It makes sense that the centralized information can be parsed by the repository automatically, so that advanced things, such as searching for a plugin based on a command, or determining whether two plugins collide command-wise, are possible.</p>
<p>After this file format has been devised, a tool has been written that allows for an easy generation of the documentation for the plugin in various formats. It outputs the documentation in a format that is perfect for pasting into the forum. It generates documentation in a Markup format to use in README.md on GitHub and similar sites. The clever thing is that you don't need to keep all those formats in sync manually - you edit the Info.lua file and this tool will re-generate the documentation for you.</p> <p>After this file format has been devised, a tool has been written that allows for an easy generation of the documentation for the plugin in various formats. It outputs the documentation in a format that is perfect for pasting into the forum. It generates documentation in a Markup format to use in README.md on GitHub and similar sites. The clever thing is that you don't need to keep all those formats in sync manually - you edit the Info.lua file and this tool will re-generate the documentation for you.</p>
<p>So to sum up, the Info.lua file contains the plugins' commands, console commands, their permissions and possibly the overall plugin documentation, in a structured manner that can be parsed by a program, yet is human readable and editable.</p> <p>So to sum up, the Info.lua file contains the plugins' commands, console commands, their permissions and possibly the overall plugin documentation, in a structured manner that can be parsed by a program, yet is human readable and editable.</p>
<hr /> <hr />
<a name="Overall"><h2>The overall structure</h2></a> <a name="Overall"><h2>The overall structure</h2></a>
<p>The file consist of a declaration of a single Lua table, g_PluginInfo. This table contains all the information, structured, as its members. Each member can be a structure by itself. The entire file is a valid Lua source file, so any tool that syntax-checks Lua source can syntax-check this file. The file is somewhat forward- and backward- compatible, in the sense that it can be extended in any way without breaking.</p> <p>The file consist of a declaration of a single Lua table, g_PluginInfo. This table contains all the information, structured, as its members. Each member can be a structure by itself. The entire file is a valid Lua source file, so any tool that syntax-checks Lua source can syntax-check this file. The file is somewhat forward- and backward- compatible, in the sense that it can be extended in any way without breaking.</p>
<p>Here's a skeleton of the file:</p> <p>Here's a skeleton of the file:</p>
<pre class="prettyprint lang-lua"> <pre class="prettyprint lang-lua">
@ -49,7 +49,7 @@ g_PluginInfo =
Name = "Example Plugin", Name = "Example Plugin",
Date = "2014-06-12", Date = "2014-06-12",
Description = "This is an example plugin that shows how to use the Info.lua file", Description = "This is an example plugin that shows how to use the Info.lua file",
-- The following members will be documented in greater detail later: -- The following members will be documented in greater detail later:
AdditionalInfo = {}, AdditionalInfo = {},
Commands = {}, Commands = {},
@ -58,15 +58,15 @@ g_PluginInfo =
} }
</pre> </pre>
<p>As you can see, the structure is pretty straightforward. Note that the order of the elements inside the table is not important (Lua property).</p> <p>As you can see, the structure is pretty straightforward. Note that the order of the elements inside the table is not important (Lua property).</p>
<p>The first few elements are for book-keeping. They declare the plugin's name, the date in ISO-format, representing the version of the plugin, and the description. The idea is that the description sums up what the plugin is all about, within some two or three sentences.</p> <p>The first few elements are for book-keeping. They declare the plugin's name, the date in ISO-format, representing the version of the plugin, and the description. The idea is that the description sums up what the plugin is all about, within some two or three sentences.</p>
<hr /> <hr />
<a name="AdditionalInfo"><h2>AdditionalInfo table</h2></a> <a name="AdditionalInfo"><h2>AdditionalInfo table</h2></a>
<p>This table is used for more detailed description of the plugin. If there is any non-trivial setup process, dependencies, describe them here. This is where the description should get detailed. Don't worry about using several paragraphs of text here, if it makes the plugin easier to understand.</p> <p>This table is used for more detailed description of the plugin. If there is any non-trivial setup process, dependencies, describe them here. This is where the description should get detailed. Don't worry about using several paragraphs of text here, if it makes the plugin easier to understand.</p>
<p>The table should have the following layout:</p> <p>The table should have the following layout:</p>
<pre class="prettyprint lang-lua"> <pre class="prettyprint lang-lua">
AdditionalInfo = AdditionalInfo =
@ -82,13 +82,13 @@ AdditionalInfo =
} }
</pre> </pre>
<p>The idea here is that the tool that is used to generate the documentation from the Info.lua file will create a linkified table of contents and then each of the information elements' contents. This information should be all that is needed to successfully configure, run and manage the plugin.</p> <p>The idea here is that the tool that is used to generate the documentation from the Info.lua file will create a linkified table of contents and then each of the information elements' contents. This information should be all that is needed to successfully configure, run and manage the plugin.</p>
<hr /> <hr />
<a name="Commands"><h2>Commands table</h2></a> <a name="Commands"><h2>Commands table</h2></a>
<p>The commands table lists all the commands that the plugin implements, together with their handler functions, required permissions, help strings and further information. The table supports recursion, which allows plugins to create multi-word commands easily (such as "//schematic load" and "//schematic save"), each having its own separate handler.</p> <p>The commands table lists all the commands that the plugin implements, together with their handler functions, required permissions, help strings and further information. The table supports recursion, which allows plugins to create multi-word commands easily (such as "//schematic load" and "//schematic save"), each having its own separate handler.</p>
<p>The table uses structure similar to the following:</p> <p>The table uses structure similar to the following:</p>
<pre class="prettyprint lang-lua"> <pre class="prettyprint lang-lua">
Commands = Commands =
@ -144,21 +144,21 @@ Commands =
}, },
} }
</pre> </pre>
<p>Although it may seem overwhelming at first, there is a "method to this madness". Each element of the Commands table defines one command. Most commands start with a slash, so the special Lua syntax for table elements with non-standard names needs to be applied (<code>["/cmd1"] =</code>). The command can either specify subcommands, or a handler function (specifying both is UndefinedBehavior). Subcommands uses the same structure as the entire Commands table, recursively.</p> <p>Although it may seem overwhelming at first, there is a "method to this madness". Each element of the Commands table defines one command. Most commands start with a slash, so the special Lua syntax for table elements with non-standard names needs to be applied (<code>["/cmd1"] =</code>). The command can either specify subcommands, or a handler function (specifying both is UndefinedBehavior). Subcommands uses the same structure as the entire Commands table, recursively.</p>
<p>The permission element specifies that the command is only available with the specified permission. Note that the permission for subcommand's parent isn't checked when the subcommand is called. This means that specifying the permission for a command that has subcommands has no effect whatsoever, but is discouraged because we may add processing for that in the future.</p> <p>The permission element specifies that the command is only available with the specified permission. Note that the permission for subcommand's parent isn't checked when the subcommand is called. This means that specifying the permission for a command that has subcommands has no effect whatsoever, but is discouraged because we may add processing for that in the future.</p>
<p>The ParameterCombinations table is used only for generating the documentation, it lists the various combinations of parameters that the command supports. It's worth specifying even if the command supports only one combination, because that combination will get documented this way.</p> <p>The ParameterCombinations table is used only for generating the documentation, it lists the various combinations of parameters that the command supports. It's worth specifying even if the command supports only one combination, because that combination will get documented this way.</p>
<p>The Alias member specifies any possible aliases for the command. Each alias is registered separately and if there is a subcommand table, it is applied to all aliases, just as one would expect. You can specify either a single string as the value (if there's only one alias), or a table of strings for multiple aliases. Commands with no aliases do not need to specify this member at all.</p> <p>The Alias member specifies any possible aliases for the command. Each alias is registered separately and if there is a subcommand table, it is applied to all aliases, just as one would expect. You can specify either a single string as the value (if there's only one alias), or a table of strings for multiple aliases. Commands with no aliases do not need to specify this member at all.</p>
<hr /> <hr />
<a name="ConsoleCommands"><h2>ConsoleCommands table</h2> <a name="ConsoleCommands"><h2>ConsoleCommands table</h2>
<p>This table serves a purpose similar to that of the Commands table, only these commands are provided for the server console. Therefore, there are no permissions specified for these commands. Since most console commands don't use a leading slash, the command names don't need the special syntax. Also, the handler function doesn't receive the Player parameter.</p> <p>This table serves a purpose similar to that of the Commands table, only these commands are provided for the server console. Therefore, there are no permissions specified for these commands. Since most console commands don't use a leading slash, the command names don't need the special syntax. Also, the handler function doesn't receive the Player parameter.</p>
<p>Here's an example of a ConsoleCommands table:</p> <p>Here's an example of a ConsoleCommands table:</p>
<pre class="prettyprint lang-lua"> <pre class="prettyprint lang-lua">
ConsoleCommands = ConsoleCommands =
@ -193,9 +193,9 @@ ConsoleCommands =
<hr /> <hr />
<a name="Permissions"><h2>Permissions table</h2></a> <a name="Permissions"><h2>Permissions table</h2></a>
<p>The purpose of this table is to document permissions that the plugin uses. The documentation generator automatically collects the permissions specified in the Command table; the Permissions table adds a description for these permissions and may declare other permissions that aren't specifically included in the Command table.</p> <p>The purpose of this table is to document permissions that the plugin uses. The documentation generator automatically collects the permissions specified in the Command table; the Permissions table adds a description for these permissions and may declare other permissions that aren't specifically included in the Command table.</p>
<pre class="prettyprint lang-lua"> <pre class="prettyprint lang-lua">
Permissions = Permissions =
{ {
@ -217,7 +217,7 @@ Permissions =
<hr /> <hr />
<a name="Using"><h2>Using the file in code</h2></a> <a name="Using"><h2>Using the file in code</h2></a>
<p>Just writing the Info.lua file and saving it to the plugin folder is not enough for it to actually be used. Your plugin needs to include the following boilerplate code, preferably in its Initialize() function:</p> <p>Just writing the Info.lua file and saving it to the plugin folder is not enough for it to actually be used. Your plugin needs to include the following boilerplate code, preferably in its Initialize() function:</p>
<pre class="prettyprint lang-lua"> <pre class="prettyprint lang-lua">
-- Use the InfoReg shared library to process the Info.lua file: -- Use the InfoReg shared library to process the Info.lua file:
@ -227,11 +227,11 @@ RegisterPluginInfoConsoleCommands()
</pre> </pre>
<p>Of course, if your plugin doesn't have any console commands, it doesn't need to call the RegisterPluginInfoConsoleCommands() function, and similarly if it doesn't have any in-game commands, it doesn't need to call the RegisterPluginInfoCommands() function.</p> <p>Of course, if your plugin doesn't have any console commands, it doesn't need to call the RegisterPluginInfoConsoleCommands() function, and similarly if it doesn't have any in-game commands, it doesn't need to call the RegisterPluginInfoCommands() function.</p>
<hr /> <hr />
<a name="Examples"><h2>Examples</h2></a> <a name="Examples"><h2>Examples</h2></a>
<p>There are several plugins that already implement this approach. You can visit them for inspiration and to see what the generated documentation looks like:</p> <p>There are several plugins that already implement this approach. You can visit them for inspiration and to see what the generated documentation looks like:</p>
<ul> <ul>
<li>Gallery plugin: <a href="https://github.com/mc-server/Gallery/blob/master/Info.lua">Info.lua</a>, <a href="http://forum.mc-server.org/showthread.php?tid=1306">Forum</a> documentation</li> <li>Gallery plugin: <a href="https://github.com/mc-server/Gallery/blob/master/Info.lua">Info.lua</a>, <a href="http://forum.mc-server.org/showthread.php?tid=1306">Forum</a> documentation</li>
@ -242,5 +242,22 @@ RegisterPluginInfoConsoleCommands()
prettyPrint(); prettyPrint();
</script> </script>
</div> </div>
<!-- Piwik -->
<script type="text/javascript">
var _paq = _paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://analytics.cuberite.org/";
_paq.push(['setTrackerUrl', u+'piwik.php']);
_paq.push(['setSiteId', 5]);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src='piwik.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<noscript><p><img src="https://analytics.cuberite.org/piwik.php?idsite=5" style="border:0;" alt="" /></p></noscript>
<!-- End Piwik Code -->
</body> </body>
</html> </html>

View File

@ -16,7 +16,7 @@
This article will explain how to set up Decoda, an IDE for writing Lua code, so that you can develop Cuberite plugins with the comfort of an IDE.</p> This article will explain how to set up Decoda, an IDE for writing Lua code, so that you can develop Cuberite plugins with the comfort of an IDE.</p>
<h2><img src="Static/decoda_logo.png" /> About Decoda</h2> <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>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 /> <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> <img src="Static/decoda_workspace.png" /></p>
@ -45,5 +45,22 @@
<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> <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> </ul>
</div> </div>
<!-- Piwik -->
<script type="text/javascript">
var _paq = _paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://analytics.cuberite.org/";
_paq.push(['setTrackerUrl', u+'piwik.php']);
_paq.push(['setSiteId', 5]);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src='piwik.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<noscript><p><img src="https://analytics.cuberite.org/piwik.php?idsite=5" style="border:0;" alt="" /></p></noscript>
<!-- End Piwik Code -->
</body> </body>
</html> </html>

View File

@ -16,20 +16,20 @@
This article will explain how to set up ZeroBrane Studio, an IDE for writing Lua code, so that you can develop Cuberite plugins with the comfort of an IDE.</p> This article will explain how to set up ZeroBrane Studio, an IDE for writing Lua code, so that you can develop Cuberite plugins with the comfort of an IDE.</p>
<h2><img src="Static/zbs_logo.png" /> About ZeroBrane Studio</h2> <h2><img src="Static/zbs_logo.png" /> About ZeroBrane Studio</h2>
<p>To quickly introduce ZeroBrane Studio, it is an IDE for writing Lua code. It has the basic features expected of an IDE - it allows you to manage groups of files as a 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 uses Lua and can load Lua packages. It is written using the multiplatform WxWidgets toolkit, and runs on multiple platforms, including Windows, Linux and MacOS.</p> <p>To quickly introduce ZeroBrane Studio, it is an IDE for writing Lua code. It has the basic features expected of an IDE - it allows you to manage groups of files as a 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 uses Lua and can load Lua packages. It is written using the multiplatform WxWidgets toolkit, and runs on multiple platforms, including Windows, Linux and MacOS.</p>
<p>Here's a screenshot of a default ZBS window with the debugger stepping through the code (scaled down):<br /> <p>Here's a screenshot of a default ZBS window with the debugger stepping through the code (scaled down):<br />
<img src="Static/zbs_workspace.png" /></p> <img src="Static/zbs_workspace.png" /></p>
<p>As you can see, you can set breakpoints in the code, inspect variables' values, view the Lua call-stacks.</p> <p>As you can see, you can set breakpoints in the code, inspect variables' values, view the Lua call-stacks.</p>
<p>ZBS is open-source, the sources are on GitHub: <a href="https://github.com/pkulchenko/ZeroBraneStudio">https://github.com/pkulchenko/ZeroBraneStudio</a>. The project's homepage is at <a href="http://studio.zerobrane.com/">http://studio.zerobrane.com/</a>. <p>ZBS is open-source, the sources are on GitHub: <a href="https://github.com/pkulchenko/ZeroBraneStudio">https://github.com/pkulchenko/ZeroBraneStudio</a>. The project's homepage is at <a href="http://studio.zerobrane.com/">http://studio.zerobrane.com/</a>.
<h2><img src="Static/zbs_logo.png" /> First-time setup</h2> <h2><img src="Static/zbs_logo.png" /> First-time setup</h2>
<p>Since ZBS is a universal Lua IDE, you need to first set it up so that it is ready for Cuberite plugin development. For that, you need to download one file, <a href="https://raw.githubusercontent.com/pkulchenko/ZeroBranePackage/master/cuberite.lua">cuberite.lua</a> from the <a href="https://github.com/pkulchenko/ZeroBranePackage">ZBS's plugin repository</a>. Place that file in the "packages" folder inside your ZBS's folder. Note that there are other useful plugins in the repository and you may want to have a look there later on to further customize your ZBS. To install them, simply save them into the same folder.</p> <p>Since ZBS is a universal Lua IDE, you need to first set it up so that it is ready for Cuberite plugin development. For that, you need to download one file, <a href="https://raw.githubusercontent.com/pkulchenko/ZeroBranePackage/master/cuberite.lua">cuberite.lua</a> from the <a href="https://github.com/pkulchenko/ZeroBranePackage">ZBS's plugin repository</a>. Place that file in the "packages" folder inside your ZBS's folder. Note that there are other useful plugins in the repository and you may want to have a look there later on to further customize your ZBS. To install them, simply save them into the same folder.</p>
<p>Next you should install the code-completion support specific for Cuberite. You should repeat this step from time to time, because the API evolves in time so new functions and classes are added to it quite often. You should have an APIDump plugin in your Cuberite installation. Enable the APIDump plugin in the server settings, it's very cheap to keep it enabled and it doesn't cost any performance during normal gameplay. To generate the code-completion support file, enter the <code style="background: #ddd; border: 1px solid #aaa">api</code> command into the server console. This will create a new file, "cuberite_api.lua", next to the Cuberite executable. Move that file into the "api/lua" subfolder inside your ZBS's folder. (Note that if you had the "mcserver_api.lua" file from previous versions, you should remove it)</p> <p>Next you should install the code-completion support specific for Cuberite. You should repeat this step from time to time, because the API evolves in time so new functions and classes are added to it quite often. You should have an APIDump plugin in your Cuberite installation. Enable the APIDump plugin in the server settings, it's very cheap to keep it enabled and it doesn't cost any performance during normal gameplay. To generate the code-completion support file, enter the <code style="background: #ddd; border: 1px solid #aaa">api</code> command into the server console. This will create a new file, "cuberite_api.lua", next to the Cuberite executable. Move that file into the "api/lua" subfolder inside your ZBS's folder. (Note that if you had the "mcserver_api.lua" file from previous versions, you should remove it)</p>
<p>After you download the cuberite.lua file and install the completion support, you need to restart ZBS in order for the plugin to load. If there are no errors, you should see two new items in the Project -> Lua Interpreter submenu: "Cuberite - debug mode" and "Cuberite - release mode". The only difference between the two is which filename they use to launch Cuberite - cuberite_debug(.exe) for the debug option and "cuberite(.exe)" for the release option. If you built your own Cuberite executable and you built it in debug mode, you should select the debug mode option. In all other cases, including if you downloaded the already-compiled Cuberite executable from the internet, you should select the release mode option.</p> <p>After you download the cuberite.lua file and install the completion support, you need to restart ZBS in order for the plugin to load. If there are no errors, you should see two new items in the Project -> Lua Interpreter submenu: "Cuberite - debug mode" and "Cuberite - release mode". The only difference between the two is which filename they use to launch Cuberite - cuberite_debug(.exe) for the debug option and "cuberite(.exe)" for the release option. If you built your own Cuberite executable and you built it in debug mode, you should select the debug mode option. In all other cases, including if you downloaded the already-compiled Cuberite executable from the internet, you should select the release mode option.</p>
<p>For a first time user, it might be a bit overwhelming that there are no GUI settings in the ZBS, yet the IDE is very configurable. There are two files that you edit in order to change settings, either system-wide (all users of the computer share those settings) or user-wide (the settings are only for a specific user of the computer). Those files are regular Lua sources and you can quickly locate them and edit them from within the IDE itself, select Edit -> Preferences -> Settings: XYZ from the menu, with XYZ being either System or User.</p> <p>For a first time user, it might be a bit overwhelming that there are no GUI settings in the ZBS, yet the IDE is very configurable. There are two files that you edit in order to change settings, either system-wide (all users of the computer share those settings) or user-wide (the settings are only for a specific user of the computer). Those files are regular Lua sources and you can quickly locate them and edit them from within the IDE itself, select Edit -> Preferences -> Settings: XYZ from the menu, with XYZ being either System or User.</p>
<p>There is a documentation on most of the settings on ZBS's webpage, have a look at <a href="http://studio.zerobrane.com/documentation.html">http://studio.zerobrane.com/documentation.html</a>, especially the Preferences section. Personally I recommend setting editor.usetabs to true and possibly adjusting the editor.tabwidth, turn off the editor.smartindent feature and for debugging the option debugger.alloweditting should be set to true unless you feel like punishing yourself.</p> <p>There is a documentation on most of the settings on ZBS's webpage, have a look at <a href="http://studio.zerobrane.com/documentation.html">http://studio.zerobrane.com/documentation.html</a>, especially the Preferences section. Personally I recommend setting editor.usetabs to true and possibly adjusting the editor.tabwidth, turn off the editor.smartindent feature and for debugging the option debugger.alloweditting should be set to true unless you feel like punishing yourself.</p>
<h2><img src="Static/zbs_logo.png" /> Project management</h2> <h2><img src="Static/zbs_logo.png" /> Project management</h2>
<p>ZBS works with projects, it considers all files and subfolder in a specific folder to be a project. There's no need for a special project file nor for adding individual files to the workspace, all files are added automatically. To open a Cuberite plugin as the project, click the triple-dot button in the Project pane, or select Project -> Project directory -> Choose... from the menu. Browse and select the Cuberite plugin's folder. ZBS will load all the files in the plugin's folder and you can start editting code.</p> <p>ZBS works with projects, it considers all files and subfolder in a specific folder to be a project. There's no need for a special project file nor for adding individual files to the workspace, all files are added automatically. To open a Cuberite plugin as the project, click the triple-dot button in the Project pane, or select Project -> Project directory -> Choose... from the menu. Browse and select the Cuberite plugin's folder. ZBS will load all the files in the plugin's folder and you can start editting code.</p>
<p>Note that although ZBS allows you to work with subfolders in your plugins (and you should, especially with larger plugins), the current Cuberite ZBS plugin will not be able to start debugging unless you have a file open in the editor that is at the root level of the Cuberite plugin's folder.</p> <p>Note that although ZBS allows you to work with subfolders in your plugins (and you should, especially with larger plugins), the current Cuberite ZBS plugin will not be able to start debugging unless you have a file open in the editor that is at the root level of the Cuberite plugin's folder.</p>
@ -42,5 +42,22 @@
<p>Once running, if the execution hits a breakpoint, the ZBS window will come up and a green arrow is displayed next to the breakpoint line. You can step through the code using F10 (Step Into) and Shift+F10 (Step Over). 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. Use the Remote console pane to execute commands directly *inside* the Cuberite's plugin context.</p> <p>Once running, if the execution hits a breakpoint, the ZBS window will come up and a green arrow is displayed next to the breakpoint line. You can step through the code using F10 (Step Into) and Shift+F10 (Step Over). 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. Use the Remote console pane to execute commands directly *inside* the Cuberite's plugin context.</p>
<p>You can also use the Project -> Break menu item to break into the debugger as soon as possible. You can also set breakpoints while the Cuberite plugin is running. Note that due to the way in which the debugger is implemented, Cuberite may execute some more Lua code before the break / breakpoint comes into effect. If Cuberite is not executing any Lua code in your plugin, it will not break until the plugin code kicks in again. This may result in missed breakpoints and delays before the Break command becomes effective. Therefore it's best to set breakpoints before running the program, or while the program is waiting in another breakpoint.</p> <p>You can also use the Project -> Break menu item to break into the debugger as soon as possible. You can also set breakpoints while the Cuberite plugin is running. Note that due to the way in which the debugger is implemented, Cuberite may execute some more Lua code before the break / breakpoint comes into effect. If Cuberite is not executing any Lua code in your plugin, it will not break until the plugin code kicks in again. This may result in missed breakpoints and delays before the Break command becomes effective. Therefore it's best to set breakpoints before running the program, or while the program is waiting in another breakpoint.</p>
</div> </div>
<!-- Piwik -->
<script type="text/javascript">
var _paq = _paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://analytics.cuberite.org/";
_paq.push(['setTrackerUrl', u+'piwik.php']);
_paq.push(['setSiteId', 5]);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src='piwik.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<noscript><p><img src="https://analytics.cuberite.org/piwik.php?idsite=5" style="border:0;" alt="" /></p></noscript>
<!-- End Piwik Code -->
</body> </body>
</html> </html>

View File

@ -14,7 +14,7 @@
<p> <p>
A plugin may need to manipulate data in arbitrary chunks, and it needs a way to make the server A plugin may need to manipulate data in arbitrary chunks, and it needs a way to make the server
guarantee that the chunks are available in memory.</p> guarantee that the chunks are available in memory.</p>
<h2>The problem</h2> <h2>The problem</h2>
<p> <p>
Usually when plugins want to manipulate larger areas of world data, they need to make sure that the Usually when plugins want to manipulate larger areas of world data, they need to make sure that the
@ -27,7 +27,7 @@
either the block area has incomplete data (Read() failed) or incomplete data has been written to the either the block area has incomplete data (Read() failed) or incomplete data has been written to the
world (Write() failed). Recovery from this is near impossible - you can't simply read or write again world (Write() failed). Recovery from this is near impossible - you can't simply read or write again
later, because the world may have changed in the meantime.</p> later, because the world may have changed in the meantime.</p>
<h2>The solution</h2> <h2>The solution</h2>
<p> <p>
The naive solution would be to monitor chunk loads and unloads, and postpone the operations until all The naive solution would be to monitor chunk loads and unloads, and postpone the operations until all
@ -47,7 +47,7 @@
despawned. So the callback must use the longer way to access such objects, such as calling despawned. So the callback must use the longer way to access such objects, such as calling
<a href="cWorld.html">cWorld:DoWithEntityByID()</a> or <a href="cWorld.html">cWorld:DoWithEntityByID()</a> or
<a href="cWorld.html">cWorld:DoWithPlayer()</a>.</p> <a href="cWorld.html">cWorld:DoWithPlayer()</a>.</p>
<h2>The example</h2> <h2>The example</h2>
<p> <p>
As a simple example, consider a theoretical plugin that allows a player to save the immediate As a simple example, consider a theoretical plugin that allows a player to save the immediate
@ -66,13 +66,13 @@ function HandleCommandSaveSpawn(a_Split, a_Player)
local SpawnZ = a_Player:GetWorld():GetSpawnZ() local SpawnZ = a_Player:GetWorld():GetSpawnZ()
local Bounds = cCuboid(SpawnX - 25, SpawnY - 25, SpawnZ - 25, SpawnX + 25, SpawnY + 25, SpawnZ + 25) local Bounds = cCuboid(SpawnX - 25, SpawnY - 25, SpawnZ - 25, SpawnX + 25, SpawnY + 25, SpawnZ + 25)
Bounds:ClampY(0, 255) Bounds:ClampY(0, 255)
-- Read the area around spawn into a cBlockArea, save to file: -- Read the area around spawn into a cBlockArea, save to file:
local Area = cBlockArea() local Area = cBlockArea()
local FileName = a_Player:GetName() .. "_spawn.schematic" local FileName = a_Player:GetName() .. "_spawn.schematic"
Area:Read(a_Player:GetWorld(), Bounds, cBlockArea.baTypes + cBlockArea.baMetas) Area:Read(a_Player:GetWorld(), Bounds, cBlockArea.baTypes + cBlockArea.baMetas)
Area:SaveToSchematicFile(FileName) Area:SaveToSchematicFile(FileName)
-- Notify the player: -- Notify the player:
a_Player:SendMessage(cCompositeChat("The spawn has been saved", mtInfo)) a_Player:SendMessage(cCompositeChat("The spawn has been saved", mtInfo))
return true return true
@ -96,7 +96,7 @@ function HandleCommandSaveSpawn(a_Split, a_Player)
local SpawnZ = a_Player:GetWorld():GetSpawnZ() local SpawnZ = a_Player:GetWorld():GetSpawnZ()
local Bounds = cCuboid(SpawnX - 25, SpawnY - 25, SpawnZ - 25, SpawnX + 25, SpawnY + 25, SpawnZ + 25) local Bounds = cCuboid(SpawnX - 25, SpawnY - 25, SpawnZ - 25, SpawnX + 25, SpawnY + 25, SpawnZ + 25)
Bounds:ClampY(0, 255) Bounds:ClampY(0, 255)
-- Get a list of chunks that we need loaded: -- Get a list of chunks that we need loaded:
local MinChunkX = math.floor((SpawnX - 25) / 16) local MinChunkX = math.floor((SpawnX - 25) / 16)
local MaxChunkX = math.ceil ((SpawnX + 25) / 16) local MaxChunkX = math.ceil ((SpawnX + 25) / 16)
@ -108,11 +108,11 @@ function HandleCommandSaveSpawn(a_Split, a_Player)
table.insert(Chunks, {x, z}) table.insert(Chunks, {x, z})
end end
end -- for x end -- for x
-- Store the player's name and world to use in the callback, because the a_Player object may no longer be valid: -- Store the player's name and world to use in the callback, because the a_Player object may no longer be valid:
local PlayerName = a_Player:GetName() local PlayerName = a_Player:GetName()
local World = a_Player:GetWorld() local World = a_Player:GetWorld()
-- This is the callback that is executed once all the chunks are loaded: -- This is the callback that is executed once all the chunks are loaded:
local OnAllChunksAvailable = function() local OnAllChunksAvailable = function()
-- Read the area around spawn into a cBlockArea, save to file: -- Read the area around spawn into a cBlockArea, save to file:
@ -124,7 +124,7 @@ function HandleCommandSaveSpawn(a_Split, a_Player)
else else
Msg = cCompositeChat("Cannot save the spawn", mtFailure) Msg = cCompositeChat("Cannot save the spawn", mtFailure)
end end
-- Notify the player: -- Notify the player:
-- Note that we cannot use a_Player here, because it may no longer be valid (if the player disconnected before the command completes) -- Note that we cannot use a_Player here, because it may no longer be valid (if the player disconnected before the command completes)
World:DoWithPlayer(PlayerName, World:DoWithPlayer(PlayerName,
@ -133,14 +133,14 @@ function HandleCommandSaveSpawn(a_Split, a_Player)
end end
) )
end end
-- Ask the server to load our chunks and notify us once it's done: -- Ask the server to load our chunks and notify us once it's done:
World:ChunkStay(Chunks, nil, OnAllChunksAvailable) World:ChunkStay(Chunks, nil, OnAllChunksAvailable)
-- Note that code here may get executed before the callback is called! -- Note that code here may get executed before the callback is called!
-- The ChunkStay says "once you have the chunks", not "wait until you have the chunks" -- The ChunkStay says "once you have the chunks", not "wait until you have the chunks"
-- So you can't notify the player here, because the saving needn't have occurred yet. -- So you can't notify the player here, because the saving needn't have occurred yet.
return true return true
end end
</pre> </pre>
@ -150,7 +150,7 @@ end
when we've got the chunks loaded, there's still the issue of free RAM - if the memory for the area when we've got the chunks loaded, there's still the issue of free RAM - if the memory for the area
cannot be allocated, it cannot be read even with all the chunks present. So we still do need that cannot be allocated, it cannot be read even with all the chunks present. So we still do need that
check.</p> check.</p>
<h2>The conclusion</h2> <h2>The conclusion</h2>
<p> <p>
Although it makes the code a little bit longer and is a bit more difficult to grasp at first, the Although it makes the code a little bit longer and is a bit more difficult to grasp at first, the
@ -163,5 +163,22 @@ end
prettyPrint(); prettyPrint();
</script> </script>
</div> </div>
<!-- Piwik -->
<script type="text/javascript">
var _paq = _paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://analytics.cuberite.org/";
_paq.push(['setTrackerUrl', u+'piwik.php']);
_paq.push(['setSiteId', 5]);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src='piwik.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<noscript><p><img src="https://analytics.cuberite.org/piwik.php?idsite=5" style="border:0;" alt="" /></p></noscript>
<!-- End Piwik Code -->
</body> </body>
</html> </html>

View File

@ -68,5 +68,22 @@ cRoot:Get():ForEachWorld( -- For each world...
prettyPrint(); prettyPrint();
</script> </script>
</div> </div>
<!-- Piwik -->
<script type="text/javascript">
var _paq = _paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://analytics.cuberite.org/";
_paq.push(['setTrackerUrl', u+'piwik.php']);
_paq.push(['setSiteId', 5]);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src='piwik.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<noscript><p><img src="https://analytics.cuberite.org/piwik.php?idsite=5" style="border:0;" alt="" /></p></noscript>
<!-- End Piwik Code -->
</body> </body>
</html> </html>

View File

@ -252,5 +252,22 @@ end
prettyPrint(); prettyPrint();
</script> </script>
</div> </div>
<!-- Piwik -->
<script type="text/javascript">
var _paq = _paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://analytics.cuberite.org/";
_paq.push(['setTrackerUrl', u+'piwik.php']);
_paq.push(['setSiteId', 5]);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src='piwik.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<noscript><p><img src="https://analytics.cuberite.org/piwik.php?idsite=5" style="border:0;" alt="" /></p></noscript>
<!-- End Piwik Code -->
</body> </body>
</html> </html>

View File

@ -309,6 +309,22 @@ local function WriteHtmlHook(a_Hook, a_HookNav)
end end
f:write([[</td></tr></table></div><script>prettyPrint();</script>]]) f:write([[</td></tr></table></div><script>prettyPrint();</script>]])
f:write(GetHtmlTimestamp()) f:write(GetHtmlTimestamp())
f:write([[<!-- Piwik -->
<script type="text/javascript">
var _paq = _paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://analytics.cuberite.org/";
_paq.push(['setTrackerUrl', u+'piwik.php']);
_paq.push(['setSiteId', 5]);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src='piwik.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<noscript><p><img src="https://analytics.cuberite.org/piwik.php?idsite=5" style="border:0;" alt="" /></p></noscript>
<!-- End Piwik Code -->
]])
f:write([[</body></html>]]) f:write([[</body></html>]])
f:close(); f:close();
end end
@ -956,6 +972,22 @@ local function WriteHtmlClass(a_ClassAPI, a_ClassMenu)
cf:write([[</td></tr></table></div><script>prettyPrint();</script>]]) cf:write([[</td></tr></table></div><script>prettyPrint();</script>]])
cf:write(GetHtmlTimestamp()) cf:write(GetHtmlTimestamp())
cf:write([[<!-- Piwik -->
<script type="text/javascript">
var _paq = _paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://analytics.cuberite.org/";
_paq.push(['setTrackerUrl', u+'piwik.php']);
_paq.push(['setSiteId', 5]);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src='piwik.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<noscript><p><img src="https://analytics.cuberite.org/piwik.php?idsite=5" style="border:0;" alt="" /></p></noscript>
<!-- End Piwik Code -->
]])
cf:write([[</body></html>]]) cf:write([[</body></html>]])
cf:close() cf:close()
end end
@ -1318,6 +1350,7 @@ local function DumpAPIHtml(a_API)
local StaticFiles = local StaticFiles =
{ {
"main.css", "main.css",
"piwik.js",
"prettify.js", "prettify.js",
"prettify.css", "prettify.css",
"lang-lua.js", "lang-lua.js",
@ -1337,6 +1370,22 @@ local function DumpAPIHtml(a_API)
f:write([[</ul></div>]]) f:write([[</ul></div>]])
f:write(GetHtmlTimestamp()) f:write(GetHtmlTimestamp())
f:write([[<!-- Piwik -->
<script type="text/javascript">
var _paq = _paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://analytics.cuberite.org/";
_paq.push(['setTrackerUrl', u+'piwik.php']);
_paq.push(['setSiteId', 5]);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src='piwik.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<noscript><p><img src="https://analytics.cuberite.org/piwik.php?idsite=5" style="border:0;" alt="" /></p></noscript>
<!-- End Piwik Code -->
]])
f:write([[</body></html>]]) f:write([[</body></html>]])
f:close() f:close()
@ -1686,7 +1735,7 @@ local function HandleCmdApiCheck(a_Split, a_EntireCmd)
if (OfficialStats == "") then if (OfficialStats == "") then
return true, "Cannot load official stats" return true, "Cannot load official stats"
end end
-- Load the API stats as a Lua file, process into usable dictionary: -- Load the API stats as a Lua file, process into usable dictionary:
local Loaded, Msg = loadstring(OfficialStats) local Loaded, Msg = loadstring(OfficialStats)
if not(Loaded) then if not(Loaded) then
@ -1714,10 +1763,10 @@ local function HandleCmdApiCheck(a_Split, a_EntireCmd)
end end
Parsed[clsK] = cls Parsed[clsK] = cls
end end
-- Get the current API's undocumented stats: -- Get the current API's undocumented stats:
local API = PrepareApi() local API = PrepareApi()
-- Compare the two sets of undocumented stats, list anything extra in current: -- Compare the two sets of undocumented stats, list anything extra in current:
local res = {} local res = {}
local ins = table.insert local ins = table.insert
@ -1740,7 +1789,7 @@ local function HandleCmdApiCheck(a_Split, a_EntireCmd)
end end
end end
table.sort(res) table.sort(res)
-- Bail out if no items found: -- Bail out if no items found:
if not(res[1]) then if not(res[1]) then
return true, "No new undocumented functions" return true, "No new undocumented functions"
@ -1751,7 +1800,7 @@ local function HandleCmdApiCheck(a_Split, a_EntireCmd)
f:write(table.concat(res, "\n")) f:write(table.concat(res, "\n"))
f:write("\n") f:write("\n")
f:close() f:close()
return true, "Newly undocumented items: " .. #res .. "\n" .. table.concat(res, "\n") return true, "Newly undocumented items: " .. #res .. "\n" .. table.concat(res, "\n")
end end

File diff suppressed because it is too large Load Diff