summaryrefslogtreecommitdiffstats
path: root/MCServer
diff options
context:
space:
mode:
Diffstat (limited to 'MCServer')
-rw-r--r--MCServer/.gitignore1
-rw-r--r--MCServer/Plugins/APIDump/APIDesc.lua7
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnPlayerTossingItem.lua7
m---------MCServer/Plugins/Core0
-rw-r--r--MCServer/Plugins/Debuggers/Debuggers.lua75
-rw-r--r--MCServer/Plugins/InfoDump.lua218
-rw-r--r--MCServer/monsters.ini2
7 files changed, 256 insertions, 54 deletions
diff --git a/MCServer/.gitignore b/MCServer/.gitignore
index c18dd7a67..e3aebbf92 100644
--- a/MCServer/.gitignore
+++ b/MCServer/.gitignore
@@ -4,6 +4,7 @@
*.lib
*.ini
MCServer
+CommLogs/
logs
players
world*
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua
index bf32ea8c2..07345d51e 100644
--- a/MCServer/Plugins/APIDump/APIDesc.lua
+++ b/MCServer/Plugins/APIDump/APIDesc.lua
@@ -1667,7 +1667,7 @@ a_Player:OpenWindow(Window);
]],
Functions =
{
- Call = { Params = "Function name, [All the parameters divided with commas]", Notes = "This function allows you to call a function from another plugin. It can only use pass: integers, booleans, strings and usertypes (cPlayer, cEntity, cCuboid, etc.)." },
+ Call = { Params = "Function name, [All the parameters divided with commas]", Notes = "(<b>OBSOLETE</b>) This function allows you to call a function from another plugin. It can only use pass: integers, booleans, strings and usertypes (cPlayer, cEntity, cCuboid, etc.).<br /><br /><b>This function is obsolete and unsafe, use {{cPluginManager}}:CallPlugin() instead!</b>" },
GetDirectory = { Return = "string", Notes = "Returns the name of the folder where the plugin's files are. (APIDump)" },
GetLocalDirectory = { Notes = "OBSOLETE use GetLocalFolder instead." },
GetLocalFolder = { Return = "string", Notes = "Returns the path where the plugin's files are. (Plugins/APIDump)" },
@@ -1719,6 +1719,7 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
{ Params = "Command, Callback, HelpString", Return = "", Notes = "(STATIC) Binds a console command with the specified callback function and help string. By common convention, providing an empty string for HelpString will hide the command from the \"help\" console command." },
{ Params = "Command, Callback, HelpString", Return = "", Notes = "Binds a console command with the specified callback function and help string. By common convention, providing an empty string for HelpString will hide the command from the \"help\" console command." },
},
+ CallPlugin = { Params = "PluginName, FunctionName, [FunctionArgs...]", Return = "[FunctionRets]", Notes = "(STATIC) Calls the specified function in the specified plugin, passing all the given arguments to it. If it succeeds, it returns all the values returned by that function. If it fails, returns no value at all. Note that only strings, numbers, bools, nils and classes can be used for parameters and return values; tables and functions cannot be copied across plugins." },
DisablePlugin = { Params = "PluginName", Return = "bool", Notes = "Disables a plugin specified by its name. Returns true if the plugin was disabled, false if it wasn't found or wasn't active." },
ExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "bool", Notes = "Executes the command as if given by the specified Player. Checks permissions. Returns true if executed." },
FindPlugins = { Params = "", Return = "", Notes = "Refreshes the list of plugins to include all folders inside the Plugins folder (potentially new disabled plugins)" },
@@ -2085,9 +2086,9 @@ end
QueueBlockForTick = { Params = "BlockX, BlockY, BlockZ, TicksToWait", Return = "", Notes = "Queues the specified block to be ticked after the specified number of gameticks." },
QueueSaveAllChunks = { Params = "", Return = "", Notes = "Queues all chunks to be saved in the world storage thread" },
QueueSetBlock = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta, TickDelay", Return = "", Notes = "Queues the block to be set to the specified blocktype and meta after the specified amount of game ticks. Uses SetBlock() for the actual setting, so simulators are woken up and block entities are handled correctly." },
- QueueTask = { Params = "TaskFunction", Return = "", Notes = "Queues the specified function to be executed in the tick thread. This is the primary means of interaction with a cWorld from the WebAdmin page handlers (see {{WebWorldThreads}}). The function signature is <pre class=\"pretty-print lang-lua\">function()</pre>All return values from the function are ignored. Note that this function is actually called *after* the QueueTask() function returns." },
+ QueueTask = { Params = "TaskFunction", Return = "", Notes = "Queues the specified function to be executed in the tick thread. This is the primary means of interaction with a cWorld from the WebAdmin page handlers (see {{WebWorldThreads}}). The function signature is <pre class=\"pretty-print lang-lua\">function()</pre>All return values from the function are ignored. Note that this function is actually called *after* the QueueTask() function returns. Note that it is unsafe to store references to MCServer objects, such as entities, across from the caller to the task handler function; store the EntityID instead." },
RegenerateChunk = { Params = "ChunkX, ChunkZ", Return = "", Notes = "Queues the specified chunk to be re-generated, overwriting the current data. To queue a chunk for generating only if it doesn't exist, use the GenerateChunk() instead." },
- ScheduleTask = { Params = "TaskFunction, Ticks", Return = "", Notes = "Queues the specified function to be executed in the world's tick thread after a number of ticks. This enables operations to be queued for execution in the future. The function signature is <pre class=\"pretty-print lang-lua\">function({{cWorld|World}})</pre>All return values from the function are ignored." },
+ ScheduleTask = { Params = "DelayTicks, TaskFunction", Return = "", Notes = "Queues the specified function to be executed in the world's tick thread after a the specified number of ticks. This enables operations to be queued for execution in the future. The function signature is <pre class=\"pretty-print lang-lua\">function({{cWorld|World}})</pre>All return values from the function are ignored. Note that it is unsafe to store references to MCServer objects, such as entities, across from the caller to the task handler function; store the EntityID instead." },
SendBlockTo = { Params = "BlockX, BlockY, BlockZ, {{cPlayer|Player}}", Return = "", Notes = "Sends the block at the specified coords to the specified player's client, as an UpdateBlock packet." },
SetBlock = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta", Return = "", Notes = "Sets the block at the specified coords, replaces the block entities for the previous block type, creates a new block entity for the new block, if appropriate, and wakes up the simulators. This is the preferred way to set blocks, as opposed to FastSetBlock(), which is only to be used under special circumstances." },
SetBlockMeta =
diff --git a/MCServer/Plugins/APIDump/Hooks/OnPlayerTossingItem.lua b/MCServer/Plugins/APIDump/Hooks/OnPlayerTossingItem.lua
index 85c943721..82d5bb390 100644
--- a/MCServer/Plugins/APIDump/Hooks/OnPlayerTossingItem.lua
+++ b/MCServer/Plugins/APIDump/Hooks/OnPlayerTossingItem.lua
@@ -5,7 +5,7 @@ return
CalledWhen = "A player is tossing an item. Plugin may override / refuse.",
DefaultFnName = "OnPlayerTossingItem", -- also used as pagename
Desc = [[
- This hook is called when a {{cPlayer|player}} has tossed an item (Q keypress). The
+ This hook is called when a {{cPlayer|player}} has tossed an item. The
{{cPickup|pickup}} has not been spawned yet. Plugins may disallow the tossing, but in that case they
need to clean up - the player's client already thinks the item has been tossed so the
{{cInventory|inventory}} needs to be re-sent to the player.</p>
@@ -18,8 +18,9 @@ return
},
Returns = [[
If the function returns false or no value, other plugins' callbacks are called and finally MCServer
- creates the pickup for the item and tosses it, using {{cPlayer}}:TossItem. If the function returns
- true, no other callbacks are called for this event and MCServer doesn't toss the item.
+ creates the pickup for the item and tosses it, using {{cPlayer}}:TossHeldItem, {{cPlayer}}:TossEquippedItem,
+ or {{cPlayer}}:TossPickup. If the function returns true, no other callbacks are called for this event
+ and MCServer doesn't toss the item.
]],
}, -- HOOK_PLAYER_TOSSING_ITEM
}
diff --git a/MCServer/Plugins/Core b/MCServer/Plugins/Core
-Subproject e9695520248ef32f229162ba2b0ee6792749777
+Subproject 5fe3662a8719f79cb2ca0a16150c716a3c5eb19
diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua
index 8512fbd5f..624261cbf 100644
--- a/MCServer/Plugins/Debuggers/Debuggers.lua
+++ b/MCServer/Plugins/Debuggers/Debuggers.lua
@@ -52,6 +52,7 @@ function Initialize(Plugin)
PM:BindCommand("/fill", "debuggers", HandleFill, "- Fills all block entities in current chunk with junk");
PM:BindCommand("/fr", "debuggers", HandleFurnaceRecipe, "- Shows the furnace recipe for the currently held item");
PM:BindCommand("/ff", "debuggers", HandleFurnaceFuel, "- Shows how long the currently held item would burn in a furnace");
+ PM:BindCommand("/sched", "debuggers", HandleSched, "- Schedules a simple countdown using cWorld:ScheduleTask()");
Plugin:AddWebTab("Debuggers", HandleRequest_Debuggers);
@@ -63,7 +64,8 @@ function Initialize(Plugin)
-- TestBlockAreas();
-- TestSQLiteBindings();
-- TestExpatBindings();
-
+ TestPluginCalls();
+
return true
end;
@@ -71,6 +73,38 @@ end;
+function TestPluginCalls()
+ -- In order to test the inter-plugin communication, we're going to call Core's ReturnColorFromChar() function
+ -- It is a rather simple function that doesn't need any tables as its params and returns a value, too
+ -- Note the signature: function ReturnColorFromChar( Split, char ) ... return cChatColog.Gray ... end
+ -- The Split parameter should be a table, but it is not used in that function anyway,
+ -- so we can get away with passing nil to it.
+
+ -- Use the old, deprecated and unsafe method:
+ local Core = cPluginManager:Get():GetPlugin("Core")
+ if (Core ~= nil) then
+ LOGINFO("Calling Core::ReturnColorFromChar() the old-fashioned way...")
+ local Gray = Core:Call("ReturnColorFromChar", nil, "8")
+ if (Gray ~= cChatColor.Gray) then
+ LOGWARNING("Call failed, exp " .. cChatColor.Gray .. ", got " .. (Gray or "<nil>"))
+ else
+ LOGINFO("Call succeeded")
+ end
+ end
+
+ -- Use the new method:
+ LOGINFO("Calling Core::ReturnColorFromChar() the recommended way...")
+ local Gray = cPluginManager:CallPlugin("Core", "ReturnColorFromChar", nil, "8")
+ if (Gray ~= cChatColor.Gray) then
+ LOGWARNING("Call failed, exp " .. cChatColor.Gray .. ", got " .. (Gray or "<nil>"))
+ else
+ LOGINFO("Call succeeded")
+ end
+end
+
+
+
+
function TestBlockAreas()
LOG("Testing block areas...");
@@ -955,6 +989,45 @@ end
+function HandleSched(a_Split, a_Player)
+ local World = a_Player:GetWorld()
+
+ -- Schedule a broadcast of a countdown message:
+ for i = 1, 10 do
+ World:ScheduleTask(i * 20,
+ function(a_World)
+ a_World:BroadcastChat("Countdown: " .. 11 - i)
+ end
+ )
+ end
+
+ -- Schedule a broadcast of the final message and a note to the originating player
+ -- Note that we CANNOT us the a_Player in the callback - what if the player disconnected?
+ -- Therefore we store the player's EntityID
+ local PlayerID = a_Player:GetUniqueID()
+ World:ScheduleTask(220,
+ function(a_World)
+ a_World:BroadcastChat("Countdown: BOOM")
+ a_World:DoWithEntityByID(PlayerID,
+ function(a_Entity)
+ if (a_Entity:IsPlayer()) then
+ -- Although unlikely, it is possible that this player is not the originating player
+ -- However, I leave this as an excercise to you to fix this "bug"
+ local Player = tolua.cast(a_Entity, "cPlayer")
+ Player:SendMessage("Countdown finished")
+ end
+ end
+ )
+ end
+ )
+
+ return true
+end
+
+
+
+
+
function HandleRequest_Debuggers(a_Request)
local FolderContents = cFile:GetFolderContents("./");
return "<p>The following objects have been returned by cFile:GetFolderContents():<ul><li>" .. table.concat(FolderContents, "</li><li>") .. "</li></ul></p>";
diff --git a/MCServer/Plugins/InfoDump.lua b/MCServer/Plugins/InfoDump.lua
index df47d566b..6b1da7a77 100644
--- a/MCServer/Plugins/InfoDump.lua
+++ b/MCServer/Plugins/InfoDump.lua
@@ -2,10 +2,17 @@
-- 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
+--[[
+Loads plugins' 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.
+This script can be used in two ways:
+Executing "lua InfoDump.lua" will go through all subfolders and dump each Info.lua file it can find
+ Note that this mode of operation requires LuaRocks with LFS installed; instructions are printed
+ when the prerequisites are not met.
+Executing "lua InfoDump.lua PluginName" will load the Info.lua file from PluginName's folder and dump
+only that one plugin's documentation. This mode of operation doesn't require LuaRocks
+--]]
@@ -17,33 +24,6 @@ if (_VERSION ~= "Lua 5.1") then
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");
-
-
@@ -161,6 +141,21 @@ end
+--- Returns a string specifying the command.
+-- If a_Command is a simple string, returns a_Command colorized to blue
+-- If a_Command is a table, expects members Name (full command name) and Params (command parameters),
+-- colorizes command name blue and params green
+local function GetCommandRefForum(a_Command)
+ if (type(a_Command) == "string") then
+ return "[color=blue]" .. a_Command .. "[/color]";
+ end
+ return "[color=blue]" .. a_Command.Name .. "[/color] [color=green]" .. a_Command.Params .. "[/color]";
+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");
@@ -270,6 +265,97 @@ end
+--- Collects all permissions mentioned in the info, returns them as a sorted array
+-- Each array item is {Name = "PermissionName", Info = { PermissionInfo }}
+local function BuildPermissions(a_PluginInfo)
+ -- Collect all used permissions from Commands, reference the commands that use the permission:
+ local Permissions = a_PluginInfo.Permissions or {};
+ local function CollectPermissions(a_CmdPrefix, a_Commands)
+ for cmd, info in pairs(a_Commands) do
+ CommandString = a_CmdPrefix .. cmd;
+ if ((info.Permission ~= nil) and (info.Permission ~= "")) then
+ -- Add the permission to the list of permissions:
+ local Permission = Permissions[info.Permission] or {};
+ Permissions[info.Permission] = Permission;
+ -- Add the command to the list of commands using this permission:
+ Permission.CommandsAffected = Permission.CommandsAffected or {};
+ table.insert(Permission.CommandsAffected, CommandString);
+ end
+
+ -- Process the command param combinations for permissions:
+ local ParamCombinations = info.ParameterCombinations or {};
+ for idx, comb in ipairs(ParamCombinations) do
+ if ((comb.Permission ~= nil) and (comb.Permission ~= "")) then
+ -- Add the permission to the list of permissions:
+ local Permission = Permissions[comb.Permission] or {};
+ Permissions[info.Permission] = Permission;
+ -- Add the command to the list of commands using this permission:
+ Permission.CommandsAffected = Permission.CommandsAffected or {};
+ table.insert(Permission.CommandsAffected, {Name = CommandString, Params = comb.Params});
+ end
+ end
+
+ -- Process subcommands:
+ if (info.Subcommands ~= nil) then
+ CollectPermissions(CommandString .. " ", info.Subcommands);
+ end
+ end
+ end
+ CollectPermissions("", a_PluginInfo.Commands);
+
+ -- Copy the list of permissions to an array:
+ local PermArray = {};
+ for name, perm in pairs(Permissions) do
+ table.insert(PermArray, {Name = name, Info = perm});
+ end
+
+ -- Sort the permissions array:
+ table.sort(PermArray,
+ function(p1, p2)
+ return (p1.Name < p2.Name);
+ end
+ );
+ return PermArray;
+end
+
+
+
+
+
+local function DumpPermissionsForum(a_PluginInfo, f)
+ -- Get the processed sorted array of permissions:
+ local Permissions = BuildPermissions(a_PluginInfo);
+ if ((Permissions == nil) or (#Permissions <= 0)) then
+ return;
+ end
+
+ -- Dump the permissions:
+ f:write("\n[size=X-Large]Permissions[/size]\n[list]\n");
+ for idx, perm in ipairs(Permissions) do
+ f:write(" - [color=red]", perm.Name, "[/color] - ");
+ f:write(perm.Info.Description or "");
+ local CommandsAffected = perm.Info.CommandsAffected or {};
+ if (#CommandsAffected > 0) then
+ f:write("\n[list] Commands affected:\n- ");
+ local Affects = {};
+ for idx2, cmd in ipairs(CommandsAffected) do
+ table.insert(Affects, GetCommandRefForum(cmd));
+ end
+ f:write(table.concat(Affects, "\n - "));
+ f:write("\n[/list]");
+ end
+ if (perm.Info.RecommendedGroups ~= nil) then
+ f:write("\n[list] Recommended groups: ", perm.Info.RecommendedGroups, "[/list]");
+ end
+ f:write("\n");
+ end
+ f:write("[/list]");
+end
+
+
+
+
+
local function DumpPluginInfoForum(a_PluginFolder, a_PluginInfo)
-- Open the output file:
local f, msg = io.open(a_PluginInfo.Name .. "_forum.txt", "w");
@@ -282,6 +368,7 @@ local function DumpPluginInfoForum(a_PluginFolder, a_PluginInfo)
f:write(ForumizeString(a_PluginInfo.Description), "\n");
DumpAdditionalInfoForum(a_PluginInfo, f);
DumpCommandsForum(a_PluginInfo, f);
+ DumpPermissionsForum(a_PluginInfo, f);
f:close();
end
@@ -301,12 +388,6 @@ 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
@@ -332,7 +413,7 @@ 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);
+ print("\t" .. Msg);
end
return;
end
@@ -343,19 +424,64 @@ 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
+--- Tries to load LFS through LuaRocks, returns the LFS instance, or nil on error
+local function LoadLFS()
+ -- 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:
+ luarocks install luafilesystem (Windows)
+ sudo luarocks install luafilesystem (*nix)
+
+ If you don't have luarocks installed, you need to install them using your OS's package manager, usually:
+ sudo apt-get install luarocks (Ubuntu / Debian)
+ On windows, a binary distribution can be downloaded from the LuaRocks homepage, http://luarocks.org/en/Download
+ ]]);
+
+ print("Original error text: ", err);
+ return nil;
end
+
+ -- We now know that LFS is present, get it normally:
+ return require("lfs");
end
+
+local Arg1 = ...;
+if ((Arg1 ~= nil) and (Arg1 ~= "")) then
+ -- Called with a plugin folder name, export only that one
+ ProcessPluginFolder(Arg1)
+else
+ -- Called without any arguments, process all subfolders:
+ local lfs = LoadLFS();
+ if (lfs == nil) then
+ -- LFS not loaded, error has already been printed, just bail out
+ return;
+ 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
+end
+print("Done.");
+
+
diff --git a/MCServer/monsters.ini b/MCServer/monsters.ini
index 94454c355..a1b63423e 100644
--- a/MCServer/monsters.ini
+++ b/MCServer/monsters.ini
@@ -62,7 +62,7 @@ SightDistance=25.0
MaxHealth=12
[Creeper]
-AttackRange=5.0
+AttackRange=3.0
AttackRate=1
AttackDamage=0.0
SightDistance=25.0