Mudlet is a cross-platform MUD client built with Qt6 and C++20, providing scripting capabilities in Lua 5.1. The project emphasizes "powerful simplicity" - clean interface with deep customization options.
- C++20 with Qt6 (minimum 6.8.2)
- CMake build system (minimum 3.25.1)
- Lua 5.1 scripting engine
- Cross-platform: Windows, macOS, Linux
src/- main application source codetest/- Unit tests for C++ core, written in Qt Testsrc/mudlet-lua/tests/- Unit tests covering the Lua API, written in Busted3rdparty/- External dependencies and librariestranslations/- Internationalization files.github/workflows/- Github Actions workflows
All files should end with a newline character at the end of the file.
In general: write modern C++20 code, but avoid C++ exceptions, templates, and concepts as those have performance/complexity considerations, avoiding which has made Mudlet the success it is today.
Use range-based for loops instead of iterator-based or index-based loops where appropriate.
See .github/CONTRIBUTING.md for the coding standards as well as the information below:
// Class names: PascalCase with 'T' prefix for main classes
class TConsole : public QWidget
// Member variables: camelCase with 'm' prefix
QString mProfileName;
// Qt signals/slots: camelCase
signals:
void profileChanged(const QString& name);When working with Qt Q_DECLARE_FLAGS types, always use the proper enum/flags type - never pass them as raw int:
// Good - uses the typed enum
void setServerOrigin(Host*, const Host::DiscordOptionFlag);
QMap<Host*, Host::DiscordOptionFlags> mServerOriginFlags;
// Bad - loses type safety
void setServerOrigin(Host*, int flag);
QMap<Host*, int> mServerOriginFlags;// Use qsl() macro for string literals (defined as QStringLiteral)
QString objectName = qsl("timer(Host:%1)(TTimerId:%2)").arg(hostName, timerName);
// Prefer QString for UI, tr() for user-visible strings
QString displayText = tr("Connection failed: %1").arg(errorMessage);
// Always add contextual comments for translators using //:
//: Toast notification shown when user dismisses an editor tip banner
QString toastMessage = tr("Banner hidden. <a href='undo'>Undo</a>");// In .ui files, set "notr" attribute to true for string literals which require no translation
<widget>
<property name="text">
<string notr="true">-</string>
</property>
</widget>- Use Qt's parent-child system for automatic cleanup for Qt classes
- Otherwise, use C++ smart pointers for non-Qt classes
Minimize #include directives to reduce build times:
In header files (.h):
- Only include what's needed for declarations in that header
- Use forward declarations (
class Foo;) when only pointers or references are used - Never include headers "just in case" - each include in a header propagates to all files that include it
In source files (.cpp):
- Only include headers actually used in that file
- When adding new code, verify you need each include you add
- Don't copy includes from similar files without checking if they're needed
Forward declaration examples:
// In header: use forward declaration when only pointer/reference is needed
class Host; // Forward declare instead of #include "Host.h"
class QCloseEvent;
class MyClass {
Host* mpHost; // Pointer - forward declaration sufficient
void closeEvent(QCloseEvent* event); // Pointer param - forward declaration sufficient
};
// In cpp: include the full header where the type is actually used
#include "Host.h"
#include <QCloseEvent>Mudlet is single-threaded - all profiles, triggers, and the Lua engine run on the main thread. The only exception is networking, which is automatically handled in the background by Qt.
mudlet.h/cpp- main applicationHost.h/cpp- game connection management using profilesctelnet.h/cpp- telnet protocol handlingTConsole.h/cpp- text display and inputTLuaInterpreter.h/cpp- Lua scripting engineTMap.h/cpp- mapping systemsrc/lua-function-list.json- autogenerated functions file, do not update
// Standard Lua function template
int TLuaInterpreter::functionName(lua_State* L)
{
const QString param = getVerifiedString(L, __func__, 1, "parameter name");
// ... implementation
lua_pushboolean(L, true);
return 1; // number of return values
}Don't add comments for obvious code as that increases cognitive load on the reader. Only add comments in unintuitive situations to explain why something was done.
// Qt-style error handling
if (!file.open(QIODevice::ReadOnly)) {
qWarning() << "Failed to open file:" << file.errorString();
return false;
}- Dialog classes use
dlg*.h/cppnaming - Follow Qt's Model-View pattern
- Use Qt's signal/slot mechanism for communication
- Build system: CMake (handles platform-specific configurations). See https://wiki.mudlet.org/w/Compiling_Mudlet for instructions.
- Check code quality with clang-tidy using
.clang-tidyconfiguration file - Allow up to 10mins for a build - it can take a while
After editing any C++ files (.cpp, .h), run clang-format before committing:
clang-format -i path/to/edited/file.cpp path/to/edited/file.hOn macOS, use the Homebrew-installed LLVM version to ensure compatibility:
$(brew --prefix llvm)/bin/clang-format -i path/to/edited/file.cpp path/to/edited/file.hThe project uses the .clang-format configuration in the repo root. This ensures consistent code style across the codebase.
For complete setup instructions on how to run static analysis during a build see, see: https://wiki.mudlet.org/w/Compiling_Mudlet#Static_Analysis
src/CMakeLists.txt contains commented debugging defines for development (search "Debugging code inclusions"):
DEBUG_TELNET- Telnet protocol debuggingDEBUG_UTF8_PROCESSING- UTF-8 decoding messagesDEBUG_SGR_PROCESSING- ANSI color sequence debuggingDEBUG_WINDOW_HANDLING- UI window operations- And others for encoding, MXP, map autosave, etc.
Usage: Uncomment relevant target_compile_definitions(mudlet PRIVATE DEBUG_XXX) lines when debugging specific areas. Important: Do not commit uncommented debug lines to git.
Do not force-push to remote branches.
For complete setup instructions, see: https://wiki.mudlet.org/w/Compiling_Mudlet#Compiling_on_macOS
Essential build commands:
# Build
cd /path/to/Mudlet/build
# wait up to 10mins for a build
cmake ../../Mudlet -DCMAKE_PREFIX_PATH=`brew --prefix qt6`
make -j `sysctl -n hw.ncpu`
# Run Mudlet - use absolute path to avoid directory confusion
/path/to/Mudlet/build/src/mudlet.app/Contents/MacOS/mudletFor complete setup instructions, see: https://wiki.mudlet.org/w/Compiling_Mudlet
# cd to the right build directory
cd /path/to/Mudlet/build
# configure (only needed the first time)
cmake ../ -G Ninja
# Compile using this command and wait up to 10mins for a build. Cmake runs the build in parallel by default, no need to specify number of jobs:
cmake --build .
# Run Mudlet - it's a visual, desktop application
cd /path/to/Mudlet/build
./src/mudletFor complete setup instructions, see: https://wiki.mudlet.org/w/Compiling_Mudlet#Compiling_on_Windows