From 24e8edb19cc376223d4300c2ee3a92bea9d86c04 Mon Sep 17 00:00:00 2001 From: Julian Laubstein Date: Thu, 13 Aug 2015 10:19:24 +0200 Subject: Fixed a few missing MCServers in APIDump --- MCServer/Plugins/APIDump/APIDesc.lua | 12 +- MCServer/Plugins/APIDump/Classes/Plugins.lua | 2 +- .../Plugins/APIDump/Hooks/OnBlockToPickups.lua | 2 +- .../Plugins/APIDump/Hooks/OnChunkUnloading.lua | 2 +- .../Plugins/APIDump/Hooks/OnPlayerTossingItem.lua | 2 +- .../Plugins/APIDump/Hooks/OnPlayerUsingBlock.lua | 2 +- .../Plugins/APIDump/Hooks/OnPlayerUsingItem.lua | 2 +- MCServer/Plugins/APIDump/Hooks/OnPostCrafting.lua | 2 +- MCServer/Plugins/APIDump/Hooks/OnPreCrafting.lua | 6 +- MCServer/Plugins/APIDump/SettingUpDecoda.html | 4 +- MCServer/Plugins/APIDump/SettingUpZeroBrane.html | 4 +- .../Plugins/APIDump/Writing-a-MCServer-plugin.html | 255 --------------------- 12 files changed, 20 insertions(+), 275 deletions(-) delete mode 100644 MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html (limited to 'MCServer') diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 792bac171..187c72b9e 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -1332,7 +1332,7 @@ These ItemGrids are available in the API and can be manipulated by the plugins, Note that the object contained in a cItem class is quite complex and quite often new Minecraft versions add more stuff. Therefore it is recommended to copy cItem objects using the copy-constructor ("local copy = cItem(original);"), this is the only way that guarantees that - the object will be copied at full, even with future versions of MCServer. + the object will be copied at full, even with future versions of Cuberite. ]], }, { @@ -2033,7 +2033,7 @@ a_Player:OpenWindow(Window); cRoot = { Desc = [[ - This class represents the root of MCServer's object hierarchy. There is always only one cRoot + This class represents the root of Cuberite's object hierarchy. There is always only one cRoot object. It manages and allows querying all the other objects, such as {{cServer}}, {{cPluginManager}}, individual {{cWorld|worlds}} etc.

@@ -2230,7 +2230,7 @@ local CompressedString = cStringCompression.CompressStringGZIP("DataToCompress") cWindow = { Desc = [[ - This class is the common ancestor for all window classes used by MCServer. It is inherited by the + This class is the common ancestor for all window classes used by Cuberite. It is inherited by the {{cLuaWindow|cLuaWindow}} class that plugins use for opening custom windows. It is planned to be used for window-related hooks in the future. It implements the basic functionality of any window.

@@ -2763,7 +2763,7 @@ end tolua = { Desc = [[ - This class represents the tolua bridge between the Lua API and MCServer. It supports some low + This class represents the tolua bridge between the Lua API and Cuberite. It supports some low level operations and queries on the objects. See also the tolua++'s documentation at {{http://www.codenix.com/~tolua/tolua++.html#utilities}}. Normally you shouldn't use any of these functions except for type() @@ -2905,7 +2905,7 @@ end { Include = "^dim.*", TextBefore = [[ - These constants represent dimension of a world. In MCServer, the dimension is only reflected in + These constants represent dimension of a world. In Cuberite, the dimension is only reflected in the world's overall tint - overworld gets sky-like colors and dark shades, the nether gets reddish haze and the end gets dark haze. World generator is not directly affected by the dimension, same as fluid simulators; those only default to the expected values if not set @@ -3035,7 +3035,7 @@ end ExtraPages = { -- No sorting is provided for these, they will be output in the same order as defined here - { FileName = "Writing-a-MCServer-plugin.html", Title = "Writing a Cuberite plugin" }, + { FileName = "Writing-a-Cuberite-plugin.html", Title = "Writing a Cuberite plugin" }, { FileName = "InfoFile.html", Title = "Using the Info.lua file" }, { FileName = "SettingUpDecoda.html", Title = "Setting up the Decoda Lua IDE" }, { FileName = "SettingUpZeroBrane.html", Title = "Setting up the ZeroBrane Studio Lua IDE" }, diff --git a/MCServer/Plugins/APIDump/Classes/Plugins.lua b/MCServer/Plugins/APIDump/Classes/Plugins.lua index d36658042..0d0aae04f 100644 --- a/MCServer/Plugins/APIDump/Classes/Plugins.lua +++ b/MCServer/Plugins/APIDump/Classes/Plugins.lua @@ -39,7 +39,7 @@ return Plugins can be identified by either the PluginFolder or PluginName. Note that these two can differ, refer to the forum for detailed discussion.

- There is one instance of cPluginManager in MCServer, to get it, call either + There is one instance of cPluginManager in Cuberite, to get it, call either {{cRoot|cRoot}}:Get():GetPluginManager() or cPluginManager:Get() function.

Note that some functions are "static", that means that they are called using a dot operator instead diff --git a/MCServer/Plugins/APIDump/Hooks/OnBlockToPickups.lua b/MCServer/Plugins/APIDump/Hooks/OnBlockToPickups.lua index e6f115f37..d404da073 100644 --- a/MCServer/Plugins/APIDump/Hooks/OnBlockToPickups.lua +++ b/MCServer/Plugins/APIDump/Hooks/OnBlockToPickups.lua @@ -44,7 +44,7 @@ function OnBlockToPickups(a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_Blo return false; end - -- Remove all pickups suggested by MCServer: + -- Remove all pickups suggested by Cuberite: a_Pickups:Clear(); -- Drop a diamond: diff --git a/MCServer/Plugins/APIDump/Hooks/OnChunkUnloading.lua b/MCServer/Plugins/APIDump/Hooks/OnChunkUnloading.lua index 4ba995075..98e0a71fd 100644 --- a/MCServer/Plugins/APIDump/Hooks/OnChunkUnloading.lua +++ b/MCServer/Plugins/APIDump/Hooks/OnChunkUnloading.lua @@ -18,7 +18,7 @@ return { Name = "ChunkZ", Type = "number", Notes = "Z-coord of the chunk" }, }, Returns = [[ - If the function returns false or no value, the next plugin's callback is called and finally MCServer + If the function returns false or no value, the next plugin's callback is called and finally Cuberite unloads the chunk. If the function returns true, no other callback is called for this event and the chunk is left in the memory. ]], diff --git a/MCServer/Plugins/APIDump/Hooks/OnPlayerTossingItem.lua b/MCServer/Plugins/APIDump/Hooks/OnPlayerTossingItem.lua index d3daf850b..880404bf8 100644 --- a/MCServer/Plugins/APIDump/Hooks/OnPlayerTossingItem.lua +++ b/MCServer/Plugins/APIDump/Hooks/OnPlayerTossingItem.lua @@ -17,7 +17,7 @@ return { Name = "Player", Type = "{{cPlayer}}", Notes = "The player tossing an item" }, }, Returns = [[ - If the function returns false or no value, other plugins' callbacks are called and finally MCServer + If the function returns false or no value, other plugins' callbacks are called and finally Cuberite 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 Cuberite doesn't toss the item. diff --git a/MCServer/Plugins/APIDump/Hooks/OnPlayerUsingBlock.lua b/MCServer/Plugins/APIDump/Hooks/OnPlayerUsingBlock.lua index 48cb78af3..b4ecb5ca1 100644 --- a/MCServer/Plugins/APIDump/Hooks/OnPlayerUsingBlock.lua +++ b/MCServer/Plugins/APIDump/Hooks/OnPlayerUsingBlock.lua @@ -33,7 +33,7 @@ return { Name = "BlockMeta", Type = "number", Notes = "Block meta of the clicked block" }, }, Returns = [[ - If the function returns false or no value, other plugins' callbacks are called and then MCServer + If the function returns false or no value, other plugins' callbacks are called and then Cuberite processes the interaction. If the function returns true, no other callbacks are called for this event and the interaction is silently dropped. ]], diff --git a/MCServer/Plugins/APIDump/Hooks/OnPlayerUsingItem.lua b/MCServer/Plugins/APIDump/Hooks/OnPlayerUsingItem.lua index 3434acd54..9b2949f93 100644 --- a/MCServer/Plugins/APIDump/Hooks/OnPlayerUsingItem.lua +++ b/MCServer/Plugins/APIDump/Hooks/OnPlayerUsingItem.lua @@ -34,7 +34,7 @@ return { Name = "BlockMeta", Type = "number", Notes = "Block meta of the clicked block" }, }, Returns = [[ - If the function returns false or no value, other plugins' callbacks are called and then MCServer + If the function returns false or no value, other plugins' callbacks are called and then Cuberite processes the interaction. If the function returns true, no other callbacks are called for this event and the interaction is silently dropped. ]], diff --git a/MCServer/Plugins/APIDump/Hooks/OnPostCrafting.lua b/MCServer/Plugins/APIDump/Hooks/OnPostCrafting.lua index 26a450750..0dc9d4c73 100644 --- a/MCServer/Plugins/APIDump/Hooks/OnPostCrafting.lua +++ b/MCServer/Plugins/APIDump/Hooks/OnPostCrafting.lua @@ -6,7 +6,7 @@ return DefaultFnName = "OnPostCrafting", -- also used as pagename Desc = [[ This hook is called when a {{cPlayer|player}} changes contents of their - {{cCraftingGrid|crafting grid}}, after the recipe has been established by MCServer. Plugins may use + {{cCraftingGrid|crafting grid}}, after the recipe has been established by Cuberite. Plugins may use this to modify the resulting recipe or provide an alternate recipe.

If a plugin implements custom recipes, it should do so using the {{OnPreCrafting|HOOK_PRE_CRAFTING}} diff --git a/MCServer/Plugins/APIDump/Hooks/OnPreCrafting.lua b/MCServer/Plugins/APIDump/Hooks/OnPreCrafting.lua index 5a5347310..8f24fc881 100644 --- a/MCServer/Plugins/APIDump/Hooks/OnPreCrafting.lua +++ b/MCServer/Plugins/APIDump/Hooks/OnPreCrafting.lua @@ -6,13 +6,13 @@ return DefaultFnName = "OnPreCrafting", -- also used as pagename Desc = [[ This hook is called when a {{cPlayer|player}} changes contents of their - {{cCraftingGrid|crafting grid}}, before the built-in recipes are searched for a match by MCServer. + {{cCraftingGrid|crafting grid}}, before the built-in recipes are searched for a match by Cuberite. Plugins may use this hook to provide a custom recipe.

If you intend to tweak built-in recipes, use the {{OnPostCrafting|HOOK_POST_CRAFTING}} hook, because that will be called once the built-in recipe is matched.

- Also note a third hook, {{OnCraftingNoRecipe|HOOK_CRAFTING_NO_RECIPE}}, that is called when MCServer + Also note a third hook, {{OnCraftingNoRecipe|HOOK_CRAFTING_NO_RECIPE}}, that is called when Cuberite cannot find any built-in recipe for the given ingredients. ]], Params = @@ -22,7 +22,7 @@ return { Name = "Recipe", Type = "{{cCraftingRecipe}}", Notes = "The recipe that Cuberite will use. Modify this object to change the recipe" }, }, Returns = [[ - If the function returns false or no value, other plugins' callbacks are called and then MCServer + If the function returns false or no value, other plugins' callbacks are called and then Cuberite searches the built-in recipes. The Recipe output parameter is ignored in this case.

If the function returns true, no other callbacks are called for this event and Cuberite uses the diff --git a/MCServer/Plugins/APIDump/SettingUpDecoda.html b/MCServer/Plugins/APIDump/SettingUpDecoda.html index 7f51ad19d..8a74f0240 100644 --- a/MCServer/Plugins/APIDump/SettingUpDecoda.html +++ b/MCServer/Plugins/APIDump/SettingUpDecoda.html @@ -27,7 +27,7 @@

To begin using Decoda, you need to create a project, or load an existing one. Decoda projects have a .deproj extension, and are simply a list of Lua files that are to be opened. You can create a project through menu Project -> New Project. Save your project first, so that Decoda knows what relative paths to use for the files. Then either add existing Lua files or create new one, through menu Project -> Add Add New File / Add Existing File.

Next you need to set up the executable that Decoda will run when debugging your files. Select menu Project -> Settings. A new dialog will open:

-

In the debugging section, fill in the full path to MCServer.exe, or click the triple-dot button to browse for the file. Note that the Working directory will be automatically filled in for you with the folder where the executable is (until the last backslash). This is how it's supposed to work, don't change it; if it for some reason doesn't update, copy and paste the folder name from the Command edit box. All done, you can close this dialog now.

+

In the debugging section, fill in the full path to Cuberite.exe, or click the triple-dot button to browse for the file. Note that the Working directory will be automatically filled in for you with the folder where the executable is (until the last backslash). This is how it's supposed to work, don't change it; if it for some reason doesn't update, copy and paste the folder name from the Command edit box. All done, you can close this dialog now.

Debugging

You are now ready to debug your code. Before doing that, though, don't forget to save your project file. If you haven't done so already, enable your plugin in the settings.ini file. If you want the program to break at a certain line, it is best to set the breakpoint before starting the program. Set the cursor on the line and hit F9 (or use menu Debug -> Toggle Breakpoint) to toggle a breakpoint on that line. Finally, hit F5, or select menu Debug -> Start to launch Cuberite under the debugger. The Cuberite window comes up and loads your plugin. If Decoda displays the Project Settings dialog instead, you haven't set up the executable to run, see the Project management section for instructions.

@@ -39,7 +39,7 @@

So far everything seems wonderful. Unfortunately, it doesn't always go as easy. There are several limits to what Decoda can do:

-

Once running, if the execution hits a breakpoint, the ZBS window will come up and a green arrow is displayed next to the breakpoint line. You can step through the code using F10 (Step Into) and Shift+F10 (Step Over). You can also use the Watch window to inspect variable values, or simply hover your mouse over a variable to display its value in the tooltip. Use the Remote console pane to execute commands directly *inside* the MCServer's plugin context.

+

Once running, if the execution hits a breakpoint, the ZBS window will come up and a green arrow is displayed next to the breakpoint line. You can step through the code using F10 (Step Into) and Shift+F10 (Step Over). You can also use the Watch window to inspect variable values, or simply hover your mouse over a variable to display its value in the tooltip. Use the Remote console pane to execute commands directly *inside* the Cuberite's plugin context.

You can also use the Project -> Break menu item to break into the debugger as soon as possible. You can also set breakpoints while the Cuberite plugin is running. Note that due to the way in which the debugger is implemented, Cuberite may execute some more Lua code before the break / breakpoint comes into effect. If Cuberite is not executing any Lua code in your plugin, it will not break until the plugin code kicks in again. This may result in missed breakpoints and delays before the Break command becomes effective. Therefore it's best to set breakpoints before running the program, or while the program is waiting in another breakpoint.

diff --git a/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html b/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html deleted file mode 100644 index eb7014a8a..000000000 --- a/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html +++ /dev/null @@ -1,255 +0,0 @@ - - - - - Cuberite Plugin Tutorial - - - - - - - -
-

Writing a Cuberite plugin

-

- This article will explain how to write a basic plugin. It details basic requirements - for a plugin, explains how to register a hook and bind a command, and gives plugin - standards details. -

-

- Let us begin. In order to begin development, we must firstly obtain a compiled copy - of MCServer, and make sure that the Core plugin is within the Plugins folder, and activated. - Core handles much of the Cuberite end-user experience and gameplay will be very bland without it. -

-

Creating the basic template

-

- Plugins are written in Lua. Therefore, create a new Lua file. You can create as many files as you wish, with - any filename - Cuberite bungs them all together at runtime, however, let us create a file called main.lua for now. - Format it like so: -

-
-PLUGIN = nil
-
-function Initialize(Plugin)
-	Plugin:SetName("NewPlugin")
-	Plugin:SetVersion(1)
-	
-	-- Hooks
-	
-	PLUGIN = Plugin -- NOTE: only needed if you want OnDisable() to use GetName() or something like that
-	
-	-- Command Bindings
-
-	LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
-	return true
-end
-
-function OnDisable()
-	LOG(PLUGIN:GetName() .. " is shutting down...")
-end
-			
-

- Now for an explanation of the basics. -

- Be sure to return true for this function, else Cuberite thinks you plugin had failed to initialise and prints a stacktrace with an error message. -

- -

Registering hooks

-

- Hooks are things that Cuberite calls when an internal event occurs. For example, a hook is fired when a player places a block, moves, - logs on, eats, and many other things. For a full list, see the API documentation. -

-

- A hook can be either informative or overridable. In any case, returning false will not trigger a response, but returning true will cancel - the hook and prevent it from being propagated further to other plugins. An overridable hook simply means that there is visible behaviour - to a hook's cancellation, such as a chest being prevented from being opened. There are some exceptions to this where only changing the value the - hook passes has an effect, and not the actual return value, an example being the HOOK_KILLING hook. See the API docs for details. -

-

- To register a hook, insert the following code template into the "-- Hooks" area in the previous code example. -

-
-cPluginManager.AddHook(cPluginManager.HOOK_NAME_HERE, FunctionNameToBeCalled)
-			
-

- What does this code do? -

- What about the third parameter, you ask? Well, it is the name of the function that Cuberite calls when the hook fires. It is in this - function that you should handle or cancel the hook. -

-

- So in total, this is a working representation of what we have so far covered. -

-
-function Initialize(Plugin)
-	Plugin:SetName("DerpyPlugin")
-	Plugin:SetVersion(1)
-
-	cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_MOVING, OnPlayerMoving)
-
-	LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
-	return true
-end
-
-function OnPlayerMoving(Player) -- See API docs for parameters of all hooks
-	return true -- Prohibit player movement, see docs for whether a hook is cancellable
-end
-			
-

- So, that code stops the player from moving. Not particularly helpful, but yes :P. Note that ALL documentation is available - on the main API docs page, so if ever in doubt, go there. -

-

Binding a command

-

Format

-

- So now we know how to hook into MCServer, how do we bind a command, such as /explode, for a player to type? That is more complicated. - We firstly add this template to the "-- Command bindings" section of the initial example: -

-
--- ADD THIS IF COMMAND DOES NOT REQUIRE A PARAMETER (/explode)
-cPluginManager.BindCommand("/commandname", "permissionnode", FunctionToCall, " - Description of command")
-
--- ADD THIS IF COMMAND DOES REQUIRE A PARAMETER (/explode Notch)
-cPluginManager.BindCommand("/commandname", "permissionnode", FunctionToCall, " ~ Description of command and parameter(s)")
-			
-

- What does it do, and why are there two? -

- The command name is pretty self explanatory. The permission node is basically just a string that the player's group needs to have, so you can have anything in there, - though we recommend a style such as "derpyplugin.explode". The function to call is like the ones with Hooks, but with some fixed parameters which we will come on to later, - and the description is a description of the command which is shown when "/help" is typed. -

-

- So why are there two? Standards. A plugin that accepts a parameter MUST use a format for the description of " ~ Description of command and parms" - whereas a command that doesn't accept parameters MUST use " - Description of command" instead. Be sure to put a space before the tildes or dashes. - Additionally, try to keep the description brief and on one line on the client. -

-

Parameters

-

- What parameters are in the function Cuberite calls when the command is executed? A 'Split' array and a 'Player' object. -

-

The Split Array

-

- The Split array is an array of all text submitted to the server, including the actual command. Cuberite automatically splits the text into the array, - so plugin authors do not need to worry about that. An example of a Split array passed for the command, "/derp zubby explode" would be:

-    /derp (Split[1])
-    zubby (Split[2])
-    explode (Split[3])
-
-    The total amount of parameters passed were: 3 (#Split) -

-

The Player Object and sending them messages

-

- The Player object is basically a pointer to the player that has executed the command. You can do things with them, but most common is sending - a message. Again, see the API documentation for fuller details. But, you ask, how do we send a message to the client? -

-

- There are dedicated functions used for sending a player formatted messages. By format, I refer to coloured prefixes/coloured text (depending on configuration) - that clearly categorise what type of message a player is being sent. For example, an informational message has a yellow coloured [INFO] prefix, and a warning message - has a rose coloured [WARNING] prefix. A few of the most used functions are listed here, but see the API docs for more details. Look in the cRoot, cWorld, and cPlayer sections - for functions that broadcast to the entire server, the whole world, and a single player, respectively. -

-
--- Format: §yellow[INFO] §white%text% (yellow [INFO], white text following it)
--- Use: Informational message, such as instructions for usage of a command
-Player:SendMessageInfo("Usage: /explode [player]")
-
--- Format: §green[INFO] §white%text% (green [INFO] etc.)
--- Use: Success message, like when a command executes successfully
-Player:SendMessageSuccess("Notch was blown up!")
-
--- Format: §rose[INFO] §white%text% (rose coloured [INFO] etc.)
--- Use: Failure message, like when a command was entered correctly but failed to run, such as when the destination player wasn't found in a /tp command
-Player:SendMessageFailure("Player Salted was not found")
-			
-

- Those are the basics. If you want to output text to the player for a reason other than the three listed above, and you want to colour the text, simply concatenate - "cChatColor.*colorhere*" with your desired text, concatenate being "..". See the API docs for more details of all colours, as well as details on logging to console with - LOG("Text"). -

-

Final example and conclusion

-

- So, a working example that checks the validity of a command, and blows up a player, and also refuses pickup collection to players with >100ms ping. -

-
-function Initialize(Plugin)
-	Plugin:SetName("DerpyPluginThatBlowsPeopleUp")
-	Plugin:SetVersion(9001)
-
-	cPluginManager.BindCommand("/explode", "derpyplugin.explode", Explode, " ~ Explode a player");
-
-	cPluginManager:AddHook(cPluginManager.HOOK_COLLECTING_PICKUP, OnCollectingPickup)
-
-	LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
-	return true
-end
-
-function Explode(Split, Player)
-	if (#Split ~= 2) then
-		-- There was more or less than one argument (excluding the "/explode" bit)
-		-- Send the proper usage to the player and exit
-		Player:SendMessage("Usage: /explode [playername]")
-		return true
-	end
-
-	-- Create a callback ExplodePlayer with parameter Explodee, which Cuberite calls for every player on the server
-	local HasExploded = false
-	local ExplodePlayer = function(Explodee)
-		-- If the player we are currently at is the one we specified as the parameter
-		if (Explodee:GetName() == Split[2]) then
-			-- Create an explosion at the same position as they are; see API docs for further details of this function
-			Player:GetWorld():DoExplosionAt(Explodee:GetPosX(), Explodee:GetPosY(), Explodee:GetPosZ(), false, esPlugin)
-			Player:SendMessageSuccess(Split[2] .. " was successfully exploded")
-			HasExploded = true;
-			return true -- Signalize to Cuberite that we do not need to call this callback for any more players
-		end
-	end
-	
-	-- Tell Cuberite to loop through all players and call the callback above with the Player object it has found
-	cRoot:Get():FindAndDoWithPlayer(Split[2], ExplodePlayer)
-	
-	if not(HasExploded) then
-		-- We have not broken out so far, therefore, the player must not exist, send failure
-		Player:SendMessageFailure(Split[2] .. " was not found")
-	end
-	
-	return true
-end
-
-function OnCollectingPickup(Player, Pickup) -- Again, see the API docs for parameters of all hooks. In this case, it is a Player and Pickup object
-	if (Player:GetClientHandle():GetPing() > 100) then -- Get ping of player, in milliseconds
-		return true -- Discriminate against high latency - you don't get drops :D
-	else
-		return false -- You do get the drops! Yay~
-	end
-end
-			
-

- Make sure to read the comments for a description of what everything does. Also be sure to return true for all command handlers, unless you want Cuberite to print out an "Unknown command" message - when the command gets executed :P. Make sure to follow standards - use CoreMessaging.lua functions for messaging, dashes for no parameter commands and tildes for vice versa, - and finally, the API documentation is your friend! -

-

- Happy coding ;) -

- - -
- - -- cgit v1.2.3