diff options
Diffstat (limited to 'MCServer/Plugins')
-rw-r--r-- | MCServer/Plugins/APIDump/APIDesc.lua | 6 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/Hooks/OnPlayerFished.lua | 20 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/Hooks/OnPlayerFishing.lua | 21 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/Hooks/OnPluginsLoaded.lua | 79 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html | 6 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/main.css | 5 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/main_APIDump.lua | 20 | ||||
m--------- | MCServer/Plugins/Core | 0 | ||||
-rw-r--r-- | MCServer/Plugins/Debuggers/Debuggers.lua | 52 | ||||
-rw-r--r-- | MCServer/Plugins/Handy/handy.lua | 2 | ||||
-rw-r--r-- | MCServer/Plugins/Handy/handy_functions.lua | 427 | ||||
-rw-r--r-- | MCServer/Plugins/InfoDump.deproj | 6 | ||||
-rw-r--r-- | MCServer/Plugins/InfoDump.lua | 361 | ||||
m--------- | MCServer/Plugins/ProtectionAreas | 0 | ||||
-rw-r--r-- | MCServer/Plugins/TestLuaRocks/TestLuaRocks.lua | 2 |
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)); |