Manual:Best Practices

From Mudlet
Jump to navigation Jump to search

Best Practices

The hope is that this page will provide a place to collect a list of Mudlet 'best practices' in an effort to help people keep their Mudlet scripts and packages clean, efficient, and running smoothly. Another way to think of it is a collection of tips and tricks for making Mudlet run its best. They largely fall under three categories.
  • Lua best practices
    • Since Lua is the scripting language Mudlet makes use of, the majority of the best practices for Lua will apply when writing Mudlet scripts.
    • We do not aim to replace the internet as a source of Lua knowledge, but will try to highlight the most beneficial ones
    • Potential exceptions will be noted with their entries
  • GUI best practices
    • keeping your UI looking it best in as many situations as possible
  • Mudlet best practices
    • Optimizations and items specific to Mudlet's triggers, aliases, API, etc

Lua

Use local variables

You should really be using local variables wherever you can. If a variable is only needed for the duration of a function, alias, or trigger, then make it a local every time.

-- bad
function myEcho(msg)
  transformed_msg = "<cyan>(<yellow>Highlighter<cyan>)<reset>" .. msg
  cecho(transformed_msg)
end

-- better
function myEcho(msg)
  local transformed_msg = "<cyan>(<yellow>Highlighter<cyan>)<reset>" .. msg
  cecho(transformed_msg)
end

There are three main reasons for doing so

  • Making things local keeps them from colliding with developers in user by other package developers. You are probably not the first person to use "tbl" for a temporary table name.
  • You can make the code easier to both read and write.
    • "self.mc[tabName]" is a bit much to type and remember, but if you first do "local console = self.mc[tabName]" then it's obvious you're referring to a console where you use it in future
  • It's faster
    • local variables are inherently faster in Lua, as they exist in the virtual machine registers and are a simple index lookup to access, whereas globals reside in a table and are therefore a hash lookup
      • this means you can increase the speed of intensive processes just by making functions and variables local before using them.
      • For instance, if you are going to cycle through a large table and cecho items from within it, adding "local cecho = cecho" above the loop can improve performance.

Group your globals together in a table

Sometimes you have to use globals, to pass values easily between items or make functions and values available to others. Lots of package makers may have a reason to track health and want to be able to pass it easily between triggers and functions without having to make it a parameter for every function they write. Obviously they can't all use the 'health' variable though, especially if one is using percentages and the other direct values. So it's best if you keep the variables for your package grouped together within a table. "Demonnic.health" is a lot less likely to collide than just 'health' There is a forum post with more information.

declaring functions in tables: myTable:function() vs myTable.function()

Lua does not have as much syntactic sugar as some other languages, but using a : to call a function within a table is one of them. The following declarations are functionally identical

function myTable:coolFunction(parameter)
  self.thing = parameter
end

function myTable.coolFunction(self, parameter)
  self.thing = parameter
end

myTable.coolFunction = function(self, parameter)
  self.thing = parameter
end

And these calls are equivalent

myTable:coolFunction("Test")
myTable.coolFunction(myTable, "Test")

You can use this to make it easier to keep your code self-contained and grouped together. You can combine it with setmetatable() to allow a sort of Object Oriented like programming pattern. Which leads us to

Create objects and 'classes' using metatables and :

Geyser makes extensive use of this pattern and it allows you to create classes and objects within Lua's procedural environment. The chapter in Programming in Lua on Object Oriented Programming does a good job of explaining it.

GUI

Use percentages

A lot of your users will be on different resolution monitors, or may not want to have Mudlet covering their entire screen. If you hardcode your sizes as direct pixel values for your Geyser objects, they will not scale as well when the window is resized due to resolution restrictions or user preference. If you're personally working on a 1080p resolution UI but you want it to scale up or down, rather than using "640px" as the width to cover a third of the screen use "33%" for the width, and it will resize to cover a third of the window no matter the size.

Adjustable Containers put the power back in your user's hands

If you use Adjustable.Container for groupings of your UI it allows your users to resize and move them around if they don't like the way you've arranged things. This means more people using your package in the end.

Mudlet

Shield your complicated regular expression triggers with a substring trigger

Regular expressions are very useful, but are one of the slower trigger types to process for Mudlet. Using a substring trigger gate or a multiline trigger with the substring trigger ahead of the regular expression and a line delta of 0 you can reduce the processing time of your triggers overall by quite a bit. There has been benchmarking done on the forums: There has been benchmarking https://forums.mudlet.org/viewtopic.php?f=12&t=1441

Avoid expandAlias where possible

There's a whole wiki page on this at https://wiki.mudlet.org/w/Manual:Functions_vs_expandAlias