summaryrefslogtreecommitdiffstats
path: root/MCServer/Plugins
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--MCServer/Plugins/APIDump/APIDesc.lua6
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnPlayerFished.lua20
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnPlayerFishing.lua21
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnPluginsLoaded.lua79
-rw-r--r--MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html6
-rw-r--r--MCServer/Plugins/APIDump/main.css5
-rw-r--r--MCServer/Plugins/APIDump/main_APIDump.lua20
m---------MCServer/Plugins/Core0
-rw-r--r--MCServer/Plugins/Debuggers/Debuggers.lua52
-rw-r--r--MCServer/Plugins/Handy/handy.lua2
-rw-r--r--MCServer/Plugins/Handy/handy_functions.lua427
-rw-r--r--MCServer/Plugins/InfoDump.deproj6
-rw-r--r--MCServer/Plugins/InfoDump.lua361
m---------MCServer/Plugins/ProtectionAreas0
-rw-r--r--MCServer/Plugins/TestLuaRocks/TestLuaRocks.lua2
15 files changed, 703 insertions, 304 deletions
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua
index d388d15dd..9b117b0fa 100644
--- a/MCServer/Plugins/APIDump/APIDesc.lua
+++ b/MCServer/Plugins/APIDump/APIDesc.lua
@@ -867,6 +867,10 @@ ValueName0=SomeOtherValue
{ Params = "KeyName, Comment", Return = "", Notes = "Adds a comment to be stored in the file under the specified key" },
},
AddKeyName = { Params = "KeyName", Returns = "number", Notes = "Adds a new key of the specified name. Returns the KeyID of the new key." },
+ AddValue = { Params = "KeyName, ValueName, Value", Return = "", Notes = "Adds a new value of the specified name to the specified key. If another value of the same name exists in the key, both are kept (nonstandard INI file)" },
+ AddValueB = { Params = "KeyName, ValueName, Value", Return = "", Notes = "Adds a new bool value of the specified name to the specified key. If another value of the same name exists in the key, both are kept (nonstandard INI file)" },
+ AddValueF = { Params = "KeyName, ValueName, Value", Return = "", Notes = "Adds a new float value of the specified name to the specified key. If another value of the same name exists in the key, both are kept (nonstandard INI file)" },
+ AddValueI = { Params = "KeyName, ValueName, Value", Return = "", Notes = "Adds a new integer value of the specified name to the specified key. If another value of the same name exists in the key, both are kept (nonstandard INI file)" },
CaseInsensitive = { Params = "", Return = "", Notes = "Sets key names' and value names' comparisons to case insensitive (default)." },
CaseSensitive = { Params = "", Return = "", Notes = "Sets key names and value names comparisons to case sensitive." },
Clear = { Params = "", Return = "", Notes = "Removes all the in-memory data. Note that , like all the other operations, this doesn't affect any file data." },
@@ -1713,7 +1717,7 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
ForceExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "bool", Notes = "Same as ExecuteCommand, but doesn't check permissions" },
ForEachCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindCommand(). The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function(Command, Permission, HelpString)</pre>. If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." },
ForEachConsoleCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindConsoleCommand(). The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function (Command, HelpString)</pre>. If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." },
- Get = { Params = "", Return = "cPluginManager", Notes = "Returns the single instance of the plugin manager" },
+ Get = { Params = "", Return = "cPluginManager", Notes = "(STATIC) Returns the single instance of the plugin manager" },
GetAllPlugins = { Params = "", Return = "table", Notes = "Returns a table (dictionary) of all plugins, [name => {{cPlugin}}] pairing." },
GetCommandPermission = { Params = "Command", Return = "Permission", Notes = "Returns the permission needed for executing the specified command" },
GetCurrentPlugin = { Params = "", Return = "{{cPlugin}}", Notes = "Returns the {{cPlugin}} object for the calling plugin. This is the same object that the Initialize function receives as the argument." },
diff --git a/MCServer/Plugins/APIDump/Hooks/OnPlayerFished.lua b/MCServer/Plugins/APIDump/Hooks/OnPlayerFished.lua
new file mode 100644
index 000000000..4e093f4ae
--- /dev/null
+++ b/MCServer/Plugins/APIDump/Hooks/OnPlayerFished.lua
@@ -0,0 +1,20 @@
+return
+{
+ HOOK_PLAYER_FISHED =
+ {
+ CalledWhen = "A player gets a reward from fishing.",
+ DefaultFnName = "OnPlayerFished", -- also used as pagename
+ Desc = [[
+ This hook gets called after a player reels in the fishing rod. This is a notification-only hook, the reward has already been decided. If a plugin needs to modify the reward, use the {{OnPlayerFishing|HOOK_PLAYER_FISHING}} hook.
+ ]],
+ Params =
+ {
+ { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who pulled the fish in." },
+ { Name = "Reward", Type = "{{cItems}}", Notes = "The reward the player gets. It can be a fish, treasure and junk." },
+ },
+ Returns = [[
+ If the function returns false or no value, the next plugin's callback is called. If the function returns true, no other
+ callback is called for this event.
+ ]],
+ }, -- HOOK_PLAYER_FISHED
+};
diff --git a/MCServer/Plugins/APIDump/Hooks/OnPlayerFishing.lua b/MCServer/Plugins/APIDump/Hooks/OnPlayerFishing.lua
new file mode 100644
index 000000000..c5aaecd92
--- /dev/null
+++ b/MCServer/Plugins/APIDump/Hooks/OnPlayerFishing.lua
@@ -0,0 +1,21 @@
+return
+{
+ HOOK_PLAYER_FISHING =
+ {
+ CalledWhen = "A player is about to get a reward from fishing.",
+ DefaultFnName = "OnPlayerFishing", -- also used as pagename
+ Desc = [[
+ This hook gets called when a player right clicks with a fishing rod while the floater is under water. The reward is already descided, but the plugin may change it.
+ ]],
+ Params =
+ {
+ { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who pulled the fish in." },
+ { Name = "Reward", Type = "{{cItems}}", Notes = "The reward the player gets. It can be a fish, treasure and junk." },
+ },
+ Returns = [[
+ If the function returns false or no value, the next plugin's callback is called. Afterwards, the
+ server gives the player his reward. If the function returns true, no other
+ callback is called for this event and the player doesn't get his reward.
+ ]],
+ }, -- HOOK_PLAYER_FISHING
+};
diff --git a/MCServer/Plugins/APIDump/Hooks/OnPluginsLoaded.lua b/MCServer/Plugins/APIDump/Hooks/OnPluginsLoaded.lua
new file mode 100644
index 000000000..0d5b7271e
--- /dev/null
+++ b/MCServer/Plugins/APIDump/Hooks/OnPluginsLoaded.lua
@@ -0,0 +1,79 @@
+return
+{
+ HOOK_PLUGINS_LOADED =
+ {
+ CalledWhen = "All the enabled plugins have been loaded",
+ DefaultFnName = "OnPluginsLoaded", -- also used as pagename
+ Desc = [[
+ This callback gets called when the server finishes loading and initializing plugins. This is the
+ perfect occasion for a plugin to query other plugins through {{cPluginManager}}:GetPlugin() and
+ possibly start communicating with them using the {{cPlugin}}:Call() function.
+ ]],
+ Params = {},
+ Returns = [[
+ The return value is ignored, all registered callbacks are called.
+ ]],
+ CodeExamples =
+ {
+ {
+ Title = "CoreMessaging",
+ Desc = [[
+ This example shows how to implement the CoreMessaging functionality - messages to players will be
+ sent through the Core plugin, formatted by that plugin. As a fallback for when the Core plugin is
+ not present, the messages are sent directly by this code, unformatted.
+ ]],
+ Code = [[
+-- These are the fallback functions used when the Core is not present:
+local function SendMessageFallback(a_Player, a_Message)
+ a_Player:SendMessage(a_Message);
+end
+
+local function SendMessageSuccessFallback(a_Player, a_Message)
+ a_Player:SendMessage(a_Message);
+end
+
+local function SendMessageFailureFallback(a_Player, a_Message)
+ a_Player:SendMessage(a_Message);
+end
+
+-- These three "variables" will hold the actual functions to call.
+-- By default they are initialized to the Fallback variants, but will be redirected to Core when all plugins load
+SendMessage = SendMessageFallback;
+SendMessageSuccess = SendMessageSuccessFallback;
+SendMessageFailure = SendMessageFailureFallback;
+
+-- The callback tries to connect to the Core, if successful, overwrites the three functions with Core ones
+local function OnPluginsLoaded()
+ local CorePlugin = cPluginManager:Get():GetPlugin("Core");
+ if (CorePlugin == nil) then
+ -- The Core is not loaded, keep the Fallback functions
+ return;
+ end
+
+ -- Overwrite the three functions with Core functionality:
+ SendMessage = function(a_Player, a_Message)
+ CorePlugin:Call("SendMessage", a_Player, a_Message);
+ end
+ SendMessageSuccess = function(a_Player, a_Message)
+ CorePlugin:Call("SendMessageSuccess", a_Player, a_Message);
+ end
+ SendMessageFailure = function(a_Player, a_Message)
+ CorePlugin:Call("SendMessageFailure", a_Player, a_Message);
+ end
+end
+
+-- Global scope, register the callback:
+cPluginManager.AddHook(cPluginManager.HOOK_PLUGINS_LOADED, CoreMessagingPluginsLoaded);
+
+
+-- Usage, anywhere else in the plugin:
+SendMessageFailure(a_Player, "Cannot teleport to player, the destination player " .. PlayerName .. " was not found");
+ ]],
+ },
+ } , -- CodeExamples
+ }, -- HOOK_PLUGINS_LOADED
+}
+
+
+
+
diff --git a/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html b/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html
index 50e39d533..0e07cebdf 100644
--- a/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html
+++ b/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html
@@ -84,7 +84,7 @@ end
To register a hook, insert the following code template into the "-- Hooks" area in the previous code example.
</p>
<pre class="prettyprint lang-lua">
-cPluginManager.AddHook(cPluginManager.HOOK_NAME_HERE, FunctionNameToBeCalled)
+cPluginManager:AddHook(cPluginManager.HOOK_NAME_HERE, FunctionNameToBeCalled)
</pre>
<p>
What does this code do?
@@ -102,7 +102,7 @@ function Initialize(Plugin)
Plugin:SetName("DerpyPlugin")
Plugin:SetVersion(1)
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_MOVING, OnPlayerMoving)
+ cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_MOVING, OnPlayerMoving)
local PluginManager = cPluginManager:Get()
-- Command bindings
@@ -200,7 +200,7 @@ function Initialize(Plugin)
local PluginManager = cPluginManager:Get()
PluginManager:BindCommand("/explode", "derpyplugin.explode", Explode, " ~ Explode a player");
- cPluginManager.AddHook(cPluginManager.HOOK_COLLECTING_PICKUP, OnCollectingPickup)
+ cPluginManager:AddHook(cPluginManager.HOOK_COLLECTING_PICKUP, OnCollectingPickup)
LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
return true
diff --git a/MCServer/Plugins/APIDump/main.css b/MCServer/Plugins/APIDump/main.css
index 797079873..aa26bd186 100644
--- a/MCServer/Plugins/APIDump/main.css
+++ b/MCServer/Plugins/APIDump/main.css
@@ -30,6 +30,11 @@ pre
{
border: 1px solid #ccc;
background-color: #eee;
+ -moz-tab-size: 2;
+ -o-tab-size: 2;
+ -webkit-tab-size: 2;
+ -ms-tab-size: 2;
+ tab-size: 2;
}
body
diff --git a/MCServer/Plugins/APIDump/main_APIDump.lua b/MCServer/Plugins/APIDump/main_APIDump.lua
index ff837ec4e..bd509dcb6 100644
--- a/MCServer/Plugins/APIDump/main_APIDump.lua
+++ b/MCServer/Plugins/APIDump/main_APIDump.lua
@@ -321,6 +321,7 @@ function DumpAPIHtml()
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
@@ -428,11 +429,18 @@ function DumpAPIHtml()
WriteClasses(f, API, ClassMenu);
WriteHooks(f, Hooks, UndocumentedHooks, HookNav);
- -- Copy the static files to the output folder (overwrite any existing):
- cFile:Copy(g_Plugin:GetLocalFolder() .. "/main.css", "API/main.css");
- cFile:Copy(g_Plugin:GetLocalFolder() .. "/prettify.js", "API/prettify.js");
- cFile:Copy(g_Plugin:GetLocalFolder() .. "/prettify.css", "API/prettify.css");
- cFile:Copy(g_Plugin:GetLocalFolder() .. "/lang-lua.js", "API/lang-lua.js");
+ -- 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...");
@@ -1169,7 +1177,7 @@ function WriteHtmlHook(a_Hook, a_HookNav)
f:write("</table>\n<p>" .. (a_Hook.Returns or "") .. "</p>\n\n");
f:write([[<hr /><h1>Code examples</h1><h2>Registering the callback</h2>]]);
f:write("<pre class=\"prettyprint lang-lua\">\n");
- f:write([[cPluginManager.AddHook(cPluginManager.]] .. a_Hook.Name .. ", My" .. a_Hook.DefaultFnName .. [[);]]);
+ f:write([[cPluginManager:AddHook(cPluginManager.]] .. a_Hook.Name .. ", My" .. a_Hook.DefaultFnName .. [[);]]);
f:write("</pre>\n\n");
local Examples = a_Hook.CodeExamples or {};
for i, example in ipairs(Examples) do
diff --git a/MCServer/Plugins/Core b/MCServer/Plugins/Core
-Subproject 351aff7efa94ce3c7ba733d5f83013e8a3fdfbf
+Subproject c65b56767a5e59ca387a05be72ef18791baa9ae
diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua
index c9a610f71..8512fbd5f 100644
--- a/MCServer/Plugins/Debuggers/Debuggers.lua
+++ b/MCServer/Plugins/Debuggers/Debuggers.lua
@@ -19,14 +19,16 @@ function Initialize(Plugin)
cPluginManager.AddHook(cPluginManager.HOOK_TICK, OnTick2);
--]]
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_USING_BLOCK, OnPlayerUsingBlock);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_USING_ITEM, OnPlayerUsingItem);
- cPluginManager.AddHook(cPluginManager.HOOK_TAKE_DAMAGE, OnTakeDamage);
- cPluginManager.AddHook(cPluginManager.HOOK_TICK, OnTick);
- cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChat);
- cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY, OnPlayerRightClickingEntity);
- cPluginManager.AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick);
- cPluginManager.AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated);
+ cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_USING_BLOCK, OnPlayerUsingBlock);
+ cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_USING_ITEM, OnPlayerUsingItem);
+ cPluginManager:AddHook(cPluginManager.HOOK_TAKE_DAMAGE, OnTakeDamage);
+ cPluginManager:AddHook(cPluginManager.HOOK_TICK, OnTick);
+ cPluginManager:AddHook(cPluginManager.HOOK_CHAT, OnChat);
+ cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY, OnPlayerRightClickingEntity);
+ cPluginManager:AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick);
+ cPluginManager:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated);
+ cPluginManager:AddHook(cPluginManager.HOOK_PLUGINS_LOADED, OnPluginsLoaded);
+ cPluginManager:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage);
PM = cRoot:Get():GetPluginManager();
PM:BindCommand("/le", "debuggers", HandleListEntitiesCmd, "- Shows a list of all the loaded entities");
@@ -524,6 +526,14 @@ end
+function OnPluginsLoaded()
+ LOG("All plugins loaded");
+end
+
+
+
+
+
function OnChunkGenerated(a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc)
-- Get the topmost block coord:
local Height = a_ChunkDesc:GetHeight(0, 0);
@@ -532,7 +542,6 @@ function OnChunkGenerated(a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc)
a_ChunkDesc:SetBlockTypeMeta(0, Height + 1, 0, E_BLOCK_SIGN_POST, 0);
local BlockEntity = a_ChunkDesc:GetBlockEntity(0, Height + 1, 0);
if (BlockEntity ~= nil) then
- LOG("Setting sign lines...");
local SignEntity = tolua.cast(BlockEntity, "cSignEntity");
SignEntity:SetLines("Chunk:", tonumber(a_ChunkX) .. ", " .. tonumber(a_ChunkZ), "", "(Debuggers)");
end
@@ -954,3 +963,28 @@ end
+
+function OnPluginMessage(a_Client, a_Channel, a_Message)
+ LOGINFO("Received a plugin message from client " .. a_Client:GetUsername() .. ": channel '" .. a_Channel .. "', message '" .. a_Message .. "'");
+
+ if (a_Channel == "REGISTER") then
+ if (a_Message:find("WECUI")) then
+ -- The client has WorldEditCUI mod installed, test the comm by sending a few WECUI messages:
+ --[[
+ WECUI messages have the following generic format:
+ <shape>|<params>
+ If shape is p (cuboid selection), the params are sent individually for each corner click and have the following format:
+ <point-index>|<x>|<y>|<z>|<volume>
+ point-index is 0 or 1 (lclk / rclk)
+ volume is the 3D volume of the current cuboid selected (all three coords' deltas multiplied), including the edge blocks; -1 if N/A
+ --]]
+ -- Select a 51 * 51 * 51 block cuboid:
+ a_Client:SendPluginMessage("WECUI", "p|0|50|50|50|-1");
+ a_Client:SendPluginMessage("WECUI", "p|1|100|100|100|132651"); -- 132651 = 51 * 51 * 51
+ end
+ end
+end
+
+
+
+
diff --git a/MCServer/Plugins/Handy/handy.lua b/MCServer/Plugins/Handy/handy.lua
index 6d226ccaf..e4e9d3f5f 100644
--- a/MCServer/Plugins/Handy/handy.lua
+++ b/MCServer/Plugins/Handy/handy.lua
@@ -1,7 +1,7 @@
-- Global variables
PLUGIN = {} -- Reference to own plugin object
CHEST_WIDTH = 9
-HANDY_VERSION = 1
+HANDY_VERSION = 2
--[[
Handy is a plugin for other plugins. It contain no commands, no hooks, but functions to ease plugins developers' life.
diff --git a/MCServer/Plugins/Handy/handy_functions.lua b/MCServer/Plugins/Handy/handy_functions.lua
index a76980c6e..c142ffd08 100644
--- a/MCServer/Plugins/Handy/handy_functions.lua
+++ b/MCServer/Plugins/Handy/handy_functions.lua
@@ -6,350 +6,211 @@ function GetHandyVersion()
return HANDY_VERSION
end
-- Checks if handy is in proper version
-function CheckForRequiedVersion(IN_version)
- if (IN_version > HANDY_VERSION) then return false end
+function CheckForRequiedVersion( inVersion )
+ if( inVersion > HANDY_VERSION ) then return false end
return true
end
--[[
MCS-specific _functions and nasty hacks :D
]]
--- There's a "GetChestHeight" function inside source code, but it's not lua-exported
-function GetChestHeightCheat(IN_chest)
- if (IN_chest:GetSlot(28) == nil) then -- this means we're trying to get double chest slot and FAIL
- LOGWARN("HANDY: single chest checked")
- return 3
+function GetChestHeightCheat( inChest )
+ local chestGrid = inChest:GetContents()
+ LOGINFO( "This function serves no purpose now! You should consider chest:GetContents():GetHeight() now!" )
+ LOGINFO( "Also, you might find Handy's new 'IsChestDouble()' useful for your case" )
+ return chestGrid:GetHeight()
+end
+function IsChestDouble( inChest )
+ local chestHeight = inChest:GetContents():GetHeight()
+ if( chestHeight == 3 ) then
+ return false
end
- LOGWARN("HANDY: double chest checked")
- return 6
+ return true
end
--- Those two checks how many items of given IN_itemID chest and player have, and how much they could fit inside them
-function ReadChestForItem(IN_chest, IN_itemID)
- local _items_found = 0
- local _free_space = 0
- -- stalk through chest slots...
- local _slot_counter = 0
- local _slot_item
- local _item_max_stack = GetItemMaxStack(IN_itemID)
- while true do
- _slot_item = IN_chest:GetSlot(_slot_counter)
- if (_slot_item ~= nil) then
- if (_slot_item.m_ItemID == IN_itemID) then
- _items_found = _items_found + _slot_item.m_ItemCount
- _free_space = _free_space + (_item_max_stack - _slot_item.m_ItemCount)
- end
- if (_slot_item:IsEmpty() == true) then
- _free_space = _free_space + _item_max_stack
- end
- end
- _slot_counter = _slot_counter + 1
- if (_slot_counter == CHEST_WIDTH*GetChestHeightCheat(IN_chest)) then
- break
- end
- end
- return _items_found, _free_space
+-- Those two checks how many items of given inItemID chest and player have, and how much they could fit inside them
+function ReadChestForItem( inChest, inItemID )
+ return ReadGridForItems( inChest:GetContents(), inItemID )
end
-function ReadPlayerForItem(IN_player, IN_itemID)
- local _items_found = 0
- local _free_space = 0
- -- stalk through IN_player inventory slots...
- local _slot_counter = 9
- if (ItemIsArmor(IN_itemID) == true) then _slot_counter = 5 end
- local _slot_item
- local _item_max_stack = GetItemMaxStack(IN_itemID)
- while true do
- _slot_item = IN_player:GetInventory():GetSlot(_slot_counter)
- if (_slot_item ~= nil) then
- if (_slot_item.m_ItemID == IN_itemID) then
- _items_found = _items_found + _slot_item.m_ItemCount
- _free_space = _free_space + (_item_max_stack - _slot_item.m_ItemCount)
- end
- if (_slot_item:IsEmpty() == true) then
- _free_space = _free_space + _item_max_stack
- end
- end
- _slot_counter = _slot_counter + 1
- if (_slot_counter == 45) then
- break
- end
- end
- return _items_found, _free_space
+function ReadPlayerForItem( inPlayer, inItemID )
+ local inventoryFound, inventoryFree = ReadGridForItems( inPlayer:GetInventory():GetInventoryGrid(), inItemID )
+ local hotbarFound, hotbarFree = ReadGridForItems( inPlayer:GetInventory():GetHotbarGrid(), inItemID )
+ local itemsFound = inventoryFound + hotbarFound
+ local freeSpace = inventoryFree + hotbarFree
+ return itemsFound, freeSpace
end
--- Following functions are for chest-related operations (since noone was bothered writing them in MCS code)
+-- Following functions are for chest-related operations
-- BEWARE! Those assume you did checked if chest has items/space in it!
-function TakeItemsFromChest(IN_chest, IN_itemID, IN_ammount) -- UNSAFE! CHECK FOR ITEMS FIRST!!
- -- stalk through chest slots...
- local _slot_counter = 0
- local _slot_item
- local _take_count = IN_ammount
- while true do
- _slot_item = IN_chest:GetSlot(_slot_counter)
- if (_slot_item ~= nil) then
- if (_slot_item.m_ItemID == IN_itemID) then
- -- assuming player have enought money
- if (_take_count > 0) then
- if (_take_count > _slot_item.m_ItemCount) then
- _take_count = _take_count - _slot_item.m_ItemCount
- IN_chest:SetSlot(_slot_counter, cItem()) -- a bit hacky, can't make cItem:Clear() work(
- else
- local _left_count = _slot_item.m_ItemCount - _take_count
- IN_chest:SetSlot(_slot_counter, cItem(_slot_item.m_ItemID, _left_count)) -- a bit hacky
- _take_count = 0
- end
- end
- if (_take_count == 0) then
- break
- end
+function ReadGridForItems( inGrid, inItemID )
+ local itemsFound = 0
+ local freeSpace = 0
+ local slotsCount = inGrid:GetNumSlots()
+ local testerItem = cItem( inItemID )
+ local maxStackSize = testerItem:GetMaxStackSize()
+ for index = 0, (slotsCount - 1) do
+ slotItem = inGrid:GetSlot( index )
+ if( slotItem:IsEmpty() ) then
+ freeSpace = freeSpace + maxStackSize
+ else
+ if( slotItem:IsStackableWith( testerItem ) ) then
+ itemsFound = itemsFound + slotItem.m_ItemCount
+ freeSpace = maxStackSize - slotItem.m_ItemCount
end
end
- _slot_counter = _slot_counter + 1
- if (_slot_counter == CHEST_WIDTH*GetChestHeightCheat(IN_chest)) then
- break
- end
end
-end
-function PutItemsToChest(IN_chest, IN_itemID, IN_ammount) -- UNSAFE! CHECK FOR SPACE FIRST!!
- -- stalk through chest slots...
- local _slot_counter = 0
- local _slot_item
- local _put_count = IN_ammount
- local _max_stack = GetItemMaxStack(IN_itemID)
- while true do
- _slot_item = IN_chest:GetSlot(_slot_counter)
- local _portion = 0
- local _ammount_to_set = 0
- if (_slot_item ~= nil) then
- if (_slot_item:IsEmpty() == true) then
- _portion = math.min(_max_stack, _put_count)
- _ammount_to_set = _portion
+ return itemsFound, freeSpace
+end
+
+function TakeItemsFromGrid( inGrid, inItem )
+ local slotsCount = inGrid:GetNumSlots()
+ local removedItem = cItem( inItem )
+ for index = 0, (slotsCount - 1) do
+ slotItem = inGrid:GetSlot( index )
+ if( slotItem:IsSameType( removedItem ) ) then
+ if( slotItem.m_ItemCount <= removedItem.m_ItemCount ) then
+ removedItem.m_ItemCount = removedItem.m_ItemCount - slotItem.m_ItemCount
+ inGrid:EmptySlot( index )
else
- if (_slot_item.m_ItemID == IN_itemID) then
- -- choose between how much we need to put and how much free space left
- _portion = math.min(_put_count, _max_stack - _slot_item.m_ItemCount)
- _ammount_to_set = _slot_item.m_ItemCount + _portion
- end
+ removedItem.m_ItemCount = slotItem.m_ItemCount - removedItem.m_ItemCount
+ inGrid:SetSlot( index, removedItem )
+ removedItem.m_ItemCount = 0
end
- end
- IN_chest:SetSlot(_slot_counter, cItem(IN_itemID, _ammount_to_set)) -- we add max stack to chest
- _put_count = _put_count - _portion
- if (_put_count == 0) then break end
- _slot_counter = _slot_counter + 1
- if (_slot_counter == CHEST_WIDTH*GetChestHeightCheat(IN_chest)) then
- break
+ if( removedItem.m_ItemCount <= 0 ) then break end
end
end
+ return removedItem.m_ItemCount
+end
+--------------
+function TakeItemsFromChest( inChest, inItemID, inAmount ) -- MIGHT BE UNSAFE! CHECK FOR ITEMS FIRST!!
+ local chestGrid = inChest:GetContents()
+ local removedItem = cItem( inItemID, inAmount )
+ TakeItemsFromGrid( chestGrid, removedItem )
+end
+function PutItemsToChest( inChest, inItemID, inAmount )
+ local chestGrid = inChest:GetContents()
+ local addedItem = cItem( inItemID, inAmount )
+ chestGrid:AddItem( addedItem )
end
-- Similar to chest-related.
-function TakeItemsFromPlayer(IN_player, IN_itemID, IN_ammount) -- UNSAFE! CHECK FIRST!
- local _put_count = IN_ammount
- local _max_stack = GetItemMaxStack(IN_itemID)
- while true do
- local _portion = math.min(_max_stack, _put_count)
- IN_player:GetInventory():RemoveItem(cItem(IN_itemID, _portion))
- _put_count = _put_count - _portion
- if (_put_count == 0) then break end
+function TakeItemsFromPlayer( inPlayer, inItemID, inAmount ) -- MIGHT BE UNSAFE! CHECK FIRST!
+ local removedItem = cItem( inItemID, inAmount )
+ local inventoryGrid = inPlayer:GetInventory():GetInventoryGrid()
+ local hotbarGrid = inPlayer:GetInventory():GetHotbarGrid()
+ local itemsLeft = TakeItemsFromGrid( inventoryGrid, removedItem )
+ if( itemsLeft > 0 ) then
+ removedItem = cItem( inItemID, itemsLeft )
+ TakeItemsFromGrid( hotbarGrid, removedItem )
end
end
-function GiveItemsToPlayer(IN_player, IN_itemID, IN_ammount) -- UNSAFE! CHECK FIRST!
- local _put_count = IN_ammount
- local _max_stack = GetItemMaxStack(IN_itemID)
- while true do
- local _portion = math.min(_max_stack, _put_count)
- IN_player:GetInventory():AddItem(cItem(IN_itemID, _portion))
- _put_count = _put_count - _portion
- if (_put_count == 0) then break end
+function GiveItemsToPlayer( inPlayer, inItemID, inAmount )
+ local addedItem = cItem( inItemID, inAmount )
+ local inventoryGrid = inPlayer:GetInventory():GetInventoryGrid()
+ local hotbarGrid = inPlayer:GetInventory():GetHotbarGrid()
+ local itemsAdded = inventoryGrid:AddItem( addedItem )
+ if( itemsAdded < inAmount ) then
+ addedItem.m_ItemCount = addedItem.m_ItemCount - itemsAdded
+ hotbarGrid:AddItem( addedItem )
end
end
-- This function returns item max stack for a given itemID. It uses vanilla max stack size, and uses several non-common items notations;
-- Those are:
--- oneonerecord (because aparently 11record wasn't the best idea in lua scripting application)
--- carrotonastick (because it wasn't added to items.txt yet)
--- waitrecord (for same reason)
+-- oneonerecord( because aparently 11record wasn't the best idea in lua scripting application )
+-- carrotonastick( because it wasn't added to items.txt yet )
+-- waitrecord( for same reason )
-- Feel free to ignore the difference, or to add those to items.txt
-function GetItemMaxStack(IN_itemID)
- local _result = 64
- -- Tools and swords
- if (IN_itemID == woodensword) then _result = 1 end
- if (IN_itemID == woodenshovel) then _result = 1 end
- if (IN_itemID == woodenpickaxe) then _result = 1 end
- if (IN_itemID == woodenaxe) then _result = 1 end
- if (IN_itemID == woodenhoe) then _result = 1 end
- if (IN_itemID == stonesword) then _result = 1 end
- if (IN_itemID == stoneshovel) then _result = 1 end
- if (IN_itemID == stonepickaxe) then _result = 1 end
- if (IN_itemID == stoneaxe) then _result = 1 end
- if (IN_itemID == stonehoe) then _result = 1 end
- if (IN_itemID == ironsword) then _result = 1 end
- if (IN_itemID == ironshovel) then _result = 1 end
- if (IN_itemID == ironpickaxe) then _result = 1 end
- if (IN_itemID == ironaxe) then _result = 1 end
- if (IN_itemID == ironhoe) then _result = 1 end
- if (IN_itemID == diamondsword) then _result = 1 end
- if (IN_itemID == diamondshovel) then _result = 1 end
- if (IN_itemID == diamondpickaxe) then _result = 1 end
- if (IN_itemID == diamondaxe) then _result = 1 end
- if (IN_itemID == diamondhoe) then _result = 1 end
- if (IN_itemID == goldensword) then _result = 1 end
- if (IN_itemID == goldenshovel) then _result = 1 end
- if (IN_itemID == goldenpickaxe) then _result = 1 end
- if (IN_itemID == goldenaxe) then _result = 1 end
- if (IN_itemID == goldenhoe) then _result = 1 end
-
- if (IN_itemID == flintandsteel) then _result = 1 end
- if (IN_itemID == bow) then _result = 1 end
- if (IN_itemID == sign) then _result = 16 end
- if (IN_itemID == woodendoor) then _result = 1 end
- if (IN_itemID == irondoor) then _result = 1 end
- if (IN_itemID == cake) then _result = 1 end
- if (IN_itemID == cauldron) then _result = 1 end
- if (IN_itemID == mushroomstew) then _result = 1 end
- if (IN_itemID == painting) then _result = 1 end
- if (IN_itemID == bucket) then _result = 16 end
- if (IN_itemID == waterbucket) then _result = 1 end
- if (IN_itemID == lavabucket) then _result = 1 end
- if (IN_itemID == minecart) then _result = 1 end
- if (IN_itemID == saddle) then _result = 1 end
- if (IN_itemID == snowball) then _result = 16 end
- if (IN_itemID == boat) then _result = 1 end
- if (IN_itemID == milkbucket) then _result = 1 end
- if (IN_itemID == storageminecart) then _result = 1 end
- if (IN_itemID == poweredminecart) then _result = 1 end
- if (IN_itemID == egg) then _result = 16 end
- if (IN_itemID == fishingrod) then _result = 1 end
- if (IN_itemID == bed) then _result = 1 end
- if (IN_itemID == map) then _result = 1 end
- if (IN_itemID == shears) then _result = 1 end
- if (IN_itemID == enderpearl) then _result = 16 end
- if (IN_itemID == potion) then _result = 1 end
- if (IN_itemID == spawnegg) then _result = 1 end
- if (IN_itemID == bookandquill) then _result = 1 end
- if (IN_itemID == writtenbook) then _result = 1 end
- if (IN_itemID == carrotonastick) then _result = 1 end
-
- if (IN_itemID == goldrecord) then _result = 1 end
- if (IN_itemID == greenrecord) then _result = 1 end
- if (IN_itemID == blocksrecord) then _result = 1 end
- if (IN_itemID == chirprecord) then _result = 1 end
- if (IN_itemID == farrecord) then _result = 1 end
- if (IN_itemID == mallrecord) then _result = 1 end
- if (IN_itemID == mellohirecord) then _result = 1 end
- if (IN_itemID == stalrecord) then _result = 1 end
- if (IN_itemID == stradrecord) then _result = 1 end
- if (IN_itemID == wardrecord) then _result = 1 end
- if (IN_itemID == oneonerecord) then _result = 1 end
- if (IN_itemID == waitrecord) then _result = 1 end
-
- --if (IN_itemID == xxxxxxxxx) then _result = 1 end
-
- if (IN_itemID == leatherhelmet) then _result = 1 end
- if (IN_itemID == leatherchestplate) then _result = 1 end
- if (IN_itemID == leatherpants) then _result = 1 end
- if (IN_itemID == leatherboots) then _result = 1 end
-
- if (IN_itemID == chainmailhelmet) then _result = 1 end
- if (IN_itemID == chainmailchestplate) then _result = 1 end
- if (IN_itemID == chainmailpants) then _result = 1 end
- if (IN_itemID == chainmailboots) then _result = 1 end
+function GetItemMaxStack( inItemID )
+ local testerItem = cItem( inItemID )
+ LOGINFO( "This function serves no real purpose now, maybe consider using cItem:GetMaxStackSize()?" )
+ return testerItem:GetMaxStackSize()
+end
+function ItemIsArmor( inItemID, inCheckForHorseArmor )
+ inCheckForHorseArmor = inCheckForHorseArmor or false
+ if( inItemID == E_ITEM_LEATHER_CAP ) then return true end
+ if( inItemID == E_ITEM_LEATHER_TUNIC ) then return true end
+ if( inItemID == E_ITEM_LEATHER_PANTS ) then return true end
+ if( inItemID == E_ITEM_LEATHER_BOOTS ) then return true end
- if (IN_itemID == ironhelmet) then _result = 1 end
- if (IN_itemID == ironchestplate) then _result = 1 end
- if (IN_itemID == ironpants) then _result = 1 end
- if (IN_itemID == ironboots) then _result = 1 end
+ if( inItemID == E_ITEM_CHAIN_HELMET ) then return true end
+ if( inItemID == E_ITEM_CHAIN_CHESTPLATE ) then return true end
+ if( inItemID == E_ITEM_CHAIN_LEGGINGS ) then return true end
+ if( inItemID == E_ITEM_CHAIN_BOOTS ) then return true end
- if (IN_itemID == diamondhelmet) then _result = 1 end
- if (IN_itemID == diamondchestplate) then _result = 1 end
- if (IN_itemID == diamondpants) then _result = 1 end
- if (IN_itemID == diamondboots) then _result = 1 end
+ if( inItemID == E_ITEM_IRON_HELMET ) then return true end
+ if( inItemID == E_ITEM_IRON_CHESTPLATE ) then return true end
+ if( inItemID == E_ITEM_IRON_LEGGINGS ) then return true end
+ if( inItemID == E_ITEM_IRON_BOOTS ) then return true end
- if (IN_itemID == goldenhelmet) then _result = 1 end
- if (IN_itemID == goldenchestplate) then _result = 1 end
- if (IN_itemID == goldenpants) then _result = 1 end
- if (IN_itemID == goldenboots) then _result = 1 end
- return _result
-end
-function ItemIsArmor(IN_itemID)
- local _result = false
- if (IN_itemID == leatherhelmet) then _result = true end
- if (IN_itemID == leatherchestplate) then _result = true end
- if (IN_itemID == leatherpants) then _result = true end
- if (IN_itemID == leatherboots) then _result = true end
-
- if (IN_itemID == chainmailhelmet) then _result = true end
- if (IN_itemID == chainmailchestplate) then _result = true end
- if (IN_itemID == chainmailpants) then _result = true end
- if (IN_itemID == chainmailboots) then _result = true end
-
- if (IN_itemID == ironhelmet) then _result = true end
- if (IN_itemID == ironchestplate) then _result = true end
- if (IN_itemID == ironpants) then _result = true end
- if (IN_itemID == ironboots) then _result = true end
+ if( inItemID == E_ITEM_DIAMOND_HELMET ) then return true end
+ if( inItemID == E_ITEM_DIAMOND_CHESTPLATE ) then return true end
+ if( inItemID == E_ITEM_DIAMOND_LEGGINGS ) then return true end
+ if( inItemID == E_ITEM_DIAMOND_BOOTS ) then return true end
- if (IN_itemID == diamondhelmet) then _result = true end
- if (IN_itemID == diamondchestplate) then _result = true end
- if (IN_itemID == diamondpants) then _result = true end
- if (IN_itemID == diamondboots) then _result = true end
+ if( inItemID == E_ITEM_GOLD_HELMET ) then return true end
+ if( inItemID == E_ITEM_GOLD_CHESTPLATE ) then return true end
+ if( inItemID == E_ITEM_GOLD_LEGGINGS ) then return true end
+ if( inItemID == E_ITEM_GOLD_BOOTS ) then return true end
- if (IN_itemID == goldenhelmet) then _result = true end
- if (IN_itemID == goldenchestplate) then _result = true end
- if (IN_itemID == goldenpants) then _result = true end
- if (IN_itemID == goldenboots) then _result = true end
- return _result
+ if( inCheckForHorseArmor ) then
+ if( inItemID == E_ITEM_IRON_HORSE_ARMOR ) then return true end
+ if( inItemID == E_ITEM_GOLD_HORSE_ARMOR ) then return true end
+ if( inItemID == E_ITEM_DIAMOND_HORSE_ARMOR ) then return true end
+ end
+ return false
end
--- Returns full-length playername for a short name (usefull for parsing commands)
-function GetExactPlayername(IN_playername)
- local _result = IN_playername
- local function SetProcessingPlayername(IN_player)
- _result = IN_player:GetName()
+-- Returns full-length playername for a short name( usefull for parsing commands )
+function GetExactPlayername( inPlayerName )
+ local _result = inPlayerName
+ local function SetProcessingPlayername( inPlayer )
+ _result = inPlayer:GetName()
end
- cRoot:Get():FindAndDoWithPlayer(IN_playername, SetProcessingPlayername)
+ cRoot:Get():FindAndDoWithPlayer( inPlayerName, SetProcessingPlayername )
return _result
end
-function GetPlayerByName(IN_playername)
+function GetPlayerByName( inPlayerName )
local _player
- local PlayerSetter = function (Player)
+ local PlayerSetter = function( Player )
_player = Player
end
- cRoot:Get():FindAndDoWithPlayer(IN_playername, PlayerSetter)
+ cRoot:Get():FindAndDoWithPlayer( inPlayerName, PlayerSetter )
return _player
end
--[[
Not-so-usual math _functions
]]
-- Rounds floating point number. Because lua guys think this function doesn't deserve to be presented in lua's math
-function round(IN_x)
- if (IN_x%2 ~= 0.5) then
- return math.floor(IN_x+0.5)
+function round( inX )
+ if( inX%2 ~= 0.5 ) then
+ return math.floor( inX + 0.5 )
end
- return IN_x-0.5
+ return inX - 0.5
end
--[[
Functions I use for filework and stringswork
]]
-function PluralString(IN_value, IN_singular_string, IN_plural_string)
- local _value_string = tostring(IN_value)
- if (_value_string[#_value_string] == "1") then
- return IN_singular_string
+function PluralString( inValue, inSingularString, inPluralString )
+ local _value_string = tostring( inValue )
+ if( _value_string[#_value_string] == "1" ) then
+ return inSingularString
end
- return IN_plural_string
+ return inPluralString
end
-function PluralItemName(IN_itemID, IN_ammount) -- BEWARE! TEMPORAL SOLUTION THERE! :D
- local _value_string = tostring(IN_value)
+function PluralItemName( inItemID, inAmount ) -- BEWARE! TEMPORAL SOLUTION THERE! :D
+ local _value_string = tostring( inValue )
local _name = ""
- if (_value_string[#_value_string] == "1") then
+ if( _value_string[#_value_string] == "1" ) then
-- singular names
- _name = ItemTypeToString(IN_itemID)
+ _name = ItemTypeToString( inItemID )
else
-- plural names
- _name = ItemTypeToString(IN_itemID).."s"
+ _name = ItemTypeToString( inItemID ).."s"
end
return _name
end
-- for filewriting purposes. 0 = false, 1 = true
-function StringToBool(value)
- if value=="1" then return true end
+function StringToBool( inValue )
+ if( inValue == "1" ) then return true end
return false
end
-- same, but reversal
-function BoolToString(value)
- if value==true then return 1 end
+function BoolToString( inValue )
+ if( inValue == true ) then return 1 end
return 0
end \ No newline at end of file
diff --git a/MCServer/Plugins/InfoDump.deproj b/MCServer/Plugins/InfoDump.deproj
new file mode 100644
index 000000000..30003c36d
--- /dev/null
+++ b/MCServer/Plugins/InfoDump.deproj
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<project>
+ <file>
+ <filename>InfoDump.lua</filename>
+ </file>
+</project>
diff --git a/MCServer/Plugins/InfoDump.lua b/MCServer/Plugins/InfoDump.lua
new file mode 100644
index 000000000..df47d566b
--- /dev/null
+++ b/MCServer/Plugins/InfoDump.lua
@@ -0,0 +1,361 @@
+#!/usr/bin/lua
+
+-- InfoDump.lua
+
+-- Goes through all subfolders, loads Info.lua and dumps its g_PluginInfo into various text formats
+-- This is used for generating plugin documentation for the forum and for GitHub's INFO.md files
+
+-- This script requires LuaRocks with LFS installed, instructions are printed when this is not present.
+
+
+
+
+
+-- Check Lua version. We use 5.1-specific construct when loading the plugin info, 5.2 is not compatible!
+if (_VERSION ~= "Lua 5.1") then
+ print("Unsupported Lua version. This script requires Lua version 5.1, this Lua is version " .. (_VERSION or "<nil>"));
+ return;
+end
+
+-- Try to load lfs, do not abort if not found
+local lfs, err = pcall(
+ function()
+ return require("lfs")
+ end
+);
+
+-- Rather, print a nice message with instructions:
+if not(lfs) then
+ print([[
+Cannot load LuaFileSystem
+Install it through luarocks by executing the following command:
+ sudo luarocks install luafilesystem
+
+If you don't have luarocks installed, you need to install them using your OS's package manager, usually:
+ sudo apt-get install luarocks
+On windows, a binary distribution can be downloaded from the LuaRocks homepage, http://luarocks.org/en/Download
+]]);
+
+ print("Original error text: ", err);
+ return;
+end
+
+-- We now know that LFS is present, get it normally:
+lfs = require("lfs");
+
+
+
+
+
+
+--- Replaces generic formatting with forum-specific formatting
+-- Also removes the single line-ends
+local function ForumizeString(a_Str)
+ assert(type(a_Str) == "string");
+
+ -- Remove the indentation, unless in the code tag:
+ -- Only one code or /code tag per line is supported!
+ local IsInCode = false;
+ local function RemoveIndentIfNotInCode(s)
+ if (IsInCode) then
+ -- we're in code section, check if this line terminates it
+ IsInCode = (s:find("{%%/code}") ~= nil);
+ return s .. "\n";
+ else
+ -- we're not in code section, check if this line starts it
+ IsInCode = (s:find("{%%code}") ~= nil);
+ return s:gsub("^%s*", "") .. "\n";
+ end
+ end
+ a_Str = a_Str:gsub("(.-)\n", RemoveIndentIfNotInCode);
+
+ -- Replace multiple line ends with {%p} and single line ends with a space,
+ -- so that manual word-wrap in the Info.lua file doesn't wrap in the forum.
+ a_Str = a_Str:gsub("\n\n", "{%%p}");
+ a_Str = a_Str:gsub("\n", " ");
+
+ -- Replace the generic formatting:
+ a_Str = a_Str:gsub("{%%p}", "\n\n");
+ a_Str = a_Str:gsub("{%%b}", "[b]"):gsub("{%%/b}", "[/b]");
+ a_Str = a_Str:gsub("{%%i}", "[i]"):gsub("{%%/i}", "[/i]");
+ a_Str = a_Str:gsub("{%%list}", "[list]"):gsub("{%%/list}", "[/list]");
+ a_Str = a_Str:gsub("{%%li}", "[*]"):gsub("{%%/li}", "");
+ -- TODO: Other formatting
+
+ return a_Str;
+end
+
+
+
+
+
+--- Builds an array of categories, each containing all the commands belonging to the category,
+-- and the category description, if available.
+-- Returns the array table, each item has the following format:
+-- { Name = "CategoryName", Description = "CategoryDescription", Commands = {{CommandString = "/cmd verb", Info = {...}}, ...}}
+local function BuildCategories(a_PluginInfo)
+ -- The returned result
+ -- This will contain both an array and a dict of the categories, to allow fast search
+ local res = {};
+
+ -- For each command add a reference to it into all of its categories:
+ local function AddCommands(a_CmdPrefix, a_Commands)
+ for cmd, info in pairs(a_Commands) do
+ local NewCmd =
+ {
+ CommandString = a_CmdPrefix .. cmd,
+ Info = info,
+ }
+
+ if ((info.HelpString ~= nil) and (info.HelpString ~= "")) then
+ -- Add to each specified category:
+ local Category = info.Category;
+ if (type(Category) == "string") then
+ Category = {Category};
+ end
+ for idx, cat in ipairs(Category or {""}) do
+ local CatEntry = res[cat];
+ if (CatEntry == nil) then
+ -- First time we came across this category, create it:
+ local NewCat = {Name = cat, Description = "", Commands = {NewCmd}};
+ table.insert(res, NewCat);
+ res[cat] = NewCat;
+ else
+ -- We already have this category, just add the command to its list of commands:
+ table.insert(CatEntry.Commands, NewCmd);
+ end
+ end -- for idx, cat - Category[]
+ end -- if (HelpString valid)
+
+ -- Recurse all subcommands:
+ if (info.Subcommands ~= nil) then
+ AddCommands(a_CmdPrefix .. cmd .. " ", info.Subcommands);
+ end
+ end -- for cmd, info - a_Commands[]
+ end -- AddCommands()
+
+ AddCommands("", a_PluginInfo.Commands);
+
+ -- Assign descriptions to categories:
+ for name, desc in pairs(a_PluginInfo.Categories or {}) do
+ local CatEntry = res[name];
+ if (CatEntry ~= nil) then
+ -- The result has this category, add the description:
+ CatEntry.Description = desc.Description;
+ end
+ end
+
+ -- Alpha-sort each category's command list:
+ for idx, cat in ipairs(res) do
+ table.sort(cat.Commands,
+ function (cmd1, cmd2)
+ return (string.lower(cmd1.CommandString) < string.lower(cmd2.CommandString));
+ end
+ );
+ end
+
+ return res;
+end
+
+
+
+
+
+--- Writes the specified command detailed help array to the output file, in the forum dump format
+local function WriteCommandParameterCombinationsForum(a_CmdString, a_ParameterCombinations, f)
+ assert(type(a_CmdString) == "string");
+ assert(type(a_ParameterCombinations) == "table");
+ assert(f ~= nil);
+
+ if (#a_ParameterCombinations == 0) then
+ -- No explicit parameter combinations to write
+ return;
+ end
+
+ f:write("The following parameter combinations are recognized:\n");
+ for idx, combination in ipairs(a_ParameterCombinations) do
+ f:write("[color=blue]", a_CmdString, "[/color] [color=green]", combination.Params, "[/color]");
+ if (combination.Help ~= nil) then
+ f:write(" - ", ForumizeString(combination.Help));
+ end
+ if (combination.Permission ~= nil) then
+ f:write(" (Requires permission '[color=red]", combination.Permission, "[/color]')");
+ end
+ f:write("\n");
+ end
+end
+
+
+
+
+
+--- Writes all commands in the specified category to the output file, in the forum dump format
+local function WriteCommandsCategoryForum(a_Category, f)
+ -- Write category name:
+ local CategoryName = a_Category.Name;
+ if (CategoryName == "") then
+ CategoryName = "General";
+ end
+ f:write("\n[size=Large]", ForumizeString(a_Category.DisplayName or CategoryName), "[/size]\n");
+
+ -- Write description:
+ if (a_Category.Description ~= "") then
+ f:write(ForumizeString(a_Category.Description), "\n");
+ end
+
+ -- Write commands:
+ f:write("\n[list]");
+ for idx2, cmd in ipairs(a_Category.Commands) do
+ f:write("\n[b]", cmd.CommandString, "[/b] - ", ForumizeString(cmd.Info.HelpString or "UNDOCUMENTED"), "\n");
+ if (cmd.Info.Permission ~= nil) then
+ f:write("Permission required: [color=red]", cmd.Info.Permission, "[/color]\n");
+ end
+ if (cmd.Info.DetailedDescription ~= nil) then
+ f:write(cmd.Info.DetailedDescription);
+ end
+ if (cmd.Info.ParameterCombinations ~= nil) then
+ WriteCommandParameterCombinationsForum(cmd.CommandString, cmd.Info.ParameterCombinations, f);
+ end
+ end
+ f:write("[/list]\n\n")
+end
+
+
+
+
+
+local function DumpCommandsForum(a_PluginInfo, f)
+ -- Copy all Categories from a dictionary into an array:
+ local Categories = BuildCategories(a_PluginInfo);
+
+ -- Sort the categories by name:
+ table.sort(Categories,
+ function(cat1, cat2)
+ return (string.lower(cat1.Name) < string.lower(cat2.Name));
+ end
+ );
+
+ if (#Categories == 0) then
+ return;
+ end
+
+ f:write("\n[size=X-Large]Commands[/size]\n");
+
+ -- Dump per-category commands:
+ for idx, cat in ipairs(Categories) do
+ WriteCommandsCategoryForum(cat, f);
+ end
+end
+
+
+
+
+
+local function DumpAdditionalInfoForum(a_PluginInfo, f)
+ local AInfo = a_PluginInfo.AdditionalInfo;
+ if ((AInfo == nil) or (type(AInfo) ~= "table")) then
+ -- There is no AdditionalInfo in a_PluginInfo
+ return;
+ end
+
+ for idx, info in ipairs(a_PluginInfo.AdditionalInfo) do
+ if ((info.Title ~= nil) and (info.Contents ~= nil)) then
+ f:write("\n[size=X-Large]", ForumizeString(info.Title), "[/size]\n");
+ f:write(ForumizeString(info.Contents), "\n");
+ end
+ end
+end
+
+
+
+
+
+local function DumpPluginInfoForum(a_PluginFolder, a_PluginInfo)
+ -- Open the output file:
+ local f, msg = io.open(a_PluginInfo.Name .. "_forum.txt", "w");
+ if (f == nil) then
+ print("\tCannot dump forum info for plugin " .. a_PluginFolder .. ": " .. msg);
+ return;
+ end
+
+ -- Write the description:
+ f:write(ForumizeString(a_PluginInfo.Description), "\n");
+ DumpAdditionalInfoForum(a_PluginInfo, f);
+ DumpCommandsForum(a_PluginInfo, f);
+
+ f:close();
+end
+
+
+
+
+
+local function DumpPluginInfoGitHub()
+ -- TODO
+end
+
+
+
+
+
+--- Tries to load the g_PluginInfo from the plugin's Info.lua file
+-- Returns the g_PluginInfo table on success, or nil and error message on failure
+local function LoadPluginInfo(a_FolderName)
+ -- Check if the Info file is present at all:
+ local Attribs = lfs.attributes(a_FolderName .. "/Info.lua");
+ if ((Attribs == nil) or (Attribs.mode ~= "file")) then
+ return nil;
+ end
+
+ -- Load and compile the Info file:
+ local cfg, err = loadfile(a_FolderName .. "/Info.lua");
+ if (cfg == nil) then
+ return nil, "Cannot open 'Info.lua': " .. err;
+ end
+
+ -- Execute the loaded file in a sandbox:
+ -- This is Lua-5.1-specific and won't work in Lua 5.2!
+ local Sandbox = {};
+ setfenv(cfg, Sandbox);
+ cfg();
+ if (Sandbox.g_PluginInfo == nil) then
+ return nil, "Info.lua doesn't contain the g_PluginInfo declaration";
+ end
+ return Sandbox.g_PluginInfo;
+end
+
+
+
+
+
+local function ProcessPluginFolder(a_FolderName)
+ local PluginInfo, Msg = LoadPluginInfo(a_FolderName);
+ if (PluginInfo == nil) then
+ if (Msg ~= nil) then
+ print("\tCannot load Info.lua: " .. Msg);
+ end
+ return;
+ end
+ DumpPluginInfoForum(a_FolderName, PluginInfo);
+end
+
+
+
+
+
+print("Processing plugin subfolders:");
+for fnam in lfs.dir(".") do
+ if ((fnam ~= ".") and (fnam ~= "..")) then
+ local Attributes = lfs.attributes(fnam);
+ if (Attributes ~= nil) then
+ if (Attributes.mode == "directory") then
+ print(fnam);
+ ProcessPluginFolder(fnam);
+ end
+ end
+ end
+end
+
+
+
+
diff --git a/MCServer/Plugins/ProtectionAreas b/MCServer/Plugins/ProtectionAreas
-Subproject 70d95481e323874b147e3296a2bd0c35563b0be
+Subproject 9edfee93048f214175cbed7eb2a3f77f7ac4abb
diff --git a/MCServer/Plugins/TestLuaRocks/TestLuaRocks.lua b/MCServer/Plugins/TestLuaRocks/TestLuaRocks.lua
index 6e90b1ae9..4a7cd4e1e 100644
--- a/MCServer/Plugins/TestLuaRocks/TestLuaRocks.lua
+++ b/MCServer/Plugins/TestLuaRocks/TestLuaRocks.lua
@@ -27,7 +27,7 @@ LOG("headers: ");
for k, v in pairs(headers or {}) do
LOG(" " .. k .. ": " .. v);
end
-LOG("body length: " .. string.length(body));
+LOG("body length: " .. string.len(body));