Difference between revisions of "Manual:Best Practices"

From Mudlet
Jump to navigation Jump to search
Line 96: Line 96:
 
=== Shield your complicated regular expression triggers with a substring trigger ===
 
=== 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 [https://forums.mudlet.org/viewtopic.php?f=12&t=1441 benchmarking done]
+
: 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 [https://forums.mudlet.org/viewtopic.php?f=12&t=1441 benchmarking done]
  
 
=== Avoid expandAlias where possible ===
 
=== Avoid expandAlias where possible ===
There's a whole wiki page on this at [[Manual:Functions_vs_expandAlias|Functions_vs_expandAlias]]
+
 
 +
: There's a whole wiki page on this at [[Manual:Functions_vs_expandAlias|Functions_vs_expandAlias]]
 +
 
 +
=== Package and Module best practices ===
 +
 
 +
: This is a list of things we have discovered over the years leads to the best user experience with Mudlet packages. They aren't all necessary, but you will find that the more of them you do, the easier the lives of your users and ultimately yourself.
 +
 
 +
* built-in auto-updates
 +
** so players stay up to date, which will not be the case with manual updates)
 +
* modular and extendable - use Mudlet modules for this
 +
** so people can disable ones not needed)
 +
 
 +
* event based - raise events of your own for others to hook into
 +
** so the order of scripts installed in the players profile doesn't matter
 +
* make sure aliases only call functions
 +
** so people can make their own aliases or keybindings to customize as needed without using expandAlias
 +
* don't pollute the global namespace, keep everything in a table
 +
** so you don't bugs from people overwriting it or vice versa)
 +
** touched on above but bears repeating
 +
* undo any UI changes on uninstall: set borders back, hide the UI, etc
 +
** so people have a good experience even if they didn't like the package
 +
 
 +
* if you're specifying any fonts, package them
 +
** while it might be available on your computer, not guaranteed to be available on every computer
 +
* if you're a game admin, install it automatically via GMCP
 +
** less overhead for players to get a good experience
 +
* if you're a game admin, provide GMCP data for your game
 +
** so people don't have to waste time trying to capture data, and can work with it instead
 +
* don't use \ in paths, use / - even for Windows
 +
** it'll work on Windows - and work on macOS and Linux too

Revision as of 15:20, 9 August 2021

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. If you will need to access it repeatedly in your script, make it local first. Always be asking yourself, "Could I be using a local instead?"

-- 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 variables 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 done

Avoid expandAlias where possible

There's a whole wiki page on this at Functions_vs_expandAlias

Package and Module best practices

This is a list of things we have discovered over the years leads to the best user experience with Mudlet packages. They aren't all necessary, but you will find that the more of them you do, the easier the lives of your users and ultimately yourself.
  • built-in auto-updates
    • so players stay up to date, which will not be the case with manual updates)
  • modular and extendable - use Mudlet modules for this
    • so people can disable ones not needed)
  • event based - raise events of your own for others to hook into
    • so the order of scripts installed in the players profile doesn't matter
  • make sure aliases only call functions
    • so people can make their own aliases or keybindings to customize as needed without using expandAlias
  • don't pollute the global namespace, keep everything in a table
    • so you don't bugs from people overwriting it or vice versa)
    • touched on above but bears repeating
  • undo any UI changes on uninstall: set borders back, hide the UI, etc
    • so people have a good experience even if they didn't like the package
  • if you're specifying any fonts, package them
    • while it might be available on your computer, not guaranteed to be available on every computer
  • if you're a game admin, install it automatically via GMCP
    • less overhead for players to get a good experience
  • if you're a game admin, provide GMCP data for your game
    • so people don't have to waste time trying to capture data, and can work with it instead
  • don't use \ in paths, use / - even for Windows
    • it'll work on Windows - and work on macOS and Linux too