Skip to content
Permalink
add-windows-pa…
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
/***************************************************************************
* Copyright (C) 2008-2013 by Heiko Koehn - KoehnHeiko@googlemail.com *
* Copyright (C) 2013-2021 by Stephen Lyons - slysven@virginmedia.com *
* Copyright (C) 2014-2017 by Ahmed Charles - acharles@outlook.com *
* Copyright (C) 2016 by Eric Wallace - eewallace@gmail.com *
* Copyright (C) 2016 by Chris Leacy - cleacy1972@gmail.com *
* Copyright (C) 2016-2018 by Ian Adkins - ieadkins@gmail.com *
* Copyright (C) 2017 by Chris Reid - WackyWormer@hotmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "TLuaInterpreter.h"
#include "Host.h"
#include "TAlias.h"
#include "TArea.h"
#include "TCommandLine.h"
#include "TConsole.h"
#include "TDebug.h"
#include "TEvent.h"
#include "TForkedProcess.h"
#include "TMapLabel.h"
#include "TRoomDB.h"
#include "TTabBar.h"
#include "TTextEdit.h"
#include "TTimer.h"
#include "TTrigger.h"
#include "dlgComposer.h"
#include "dlgIRC.h"
#include "dlgMapper.h"
#include "dlgTriggerEditor.h"
#include "mudlet.h"
#if defined(INCLUDE_3DMAPPER)
#include "glwidget.h"
#endif
#include "mapInfoContributorManager.h"
#include "math.h"
#include "pre_guard.h"
#include <QtConcurrent>
#include <QCollator>
#include <QCoreApplication>
#include <QDesktopServices>
#include <QFileDialog>
#include <QTableWidget>
#include <QFileInfo>
#include <QVector>
#ifdef QT_TEXTTOSPEECH_LIB
#include <QTextToSpeech>
#endif // QT_TEXTTOSPEECH_LIB
#include "post_guard.h"
#include <limits>
const QMap<Qt::MouseButton, QString> TLuaInterpreter::mMouseButtons = {
{Qt::NoButton, QStringLiteral("NoButton")}, {Qt::LeftButton, QStringLiteral("LeftButton")}, {Qt::RightButton, QStringLiteral("RightButton")},
{Qt::MiddleButton, QStringLiteral("MidButton")}, {Qt::BackButton, QStringLiteral("BackButton")}, {Qt::ForwardButton, QStringLiteral("ForwardButton")},
{Qt::TaskButton, QStringLiteral("TaskButton")}, {Qt::ExtraButton4, QStringLiteral("ExtraButton4")}, {Qt::ExtraButton5, QStringLiteral("ExtraButton5")},
{Qt::ExtraButton6, QStringLiteral("ExtraButton6")}, {Qt::ExtraButton7, QStringLiteral("ExtraButton7")}, {Qt::ExtraButton8, QStringLiteral("ExtraButton8")},
{Qt::ExtraButton9, QStringLiteral("ExtraButton9")}, {Qt::ExtraButton10, QStringLiteral("ExtraButton10")}, {Qt::ExtraButton11, QStringLiteral("ExtraButton11")},
{Qt::ExtraButton12, QStringLiteral("ExtraButton12")}, {Qt::ExtraButton13, QStringLiteral("ExtraButton13")}, {Qt::ExtraButton14, QStringLiteral("ExtraButton14")},
{Qt::ExtraButton15, QStringLiteral("ExtraButton15")}, {Qt::ExtraButton16, QStringLiteral("ExtraButton16")}, {Qt::ExtraButton17, QStringLiteral("ExtraButton17")},
{Qt::ExtraButton18, QStringLiteral("ExtraButton18")}, {Qt::ExtraButton19, QStringLiteral("ExtraButton19")}, {Qt::ExtraButton20, QStringLiteral("ExtraButton20")},
{Qt::ExtraButton21, QStringLiteral("ExtraButton21")}, {Qt::ExtraButton22, QStringLiteral("ExtraButton22")}, {Qt::ExtraButton23, QStringLiteral("ExtraButton23")},
{Qt::ExtraButton24, QStringLiteral("ExtraButton24")},
};
extern "C" {
int luaopen_yajl(lua_State*);
}
#ifdef QT_TEXTTOSPEECH_LIB
QPointer<QTextToSpeech> speechUnit;
QVector<QString> speechQueue;
bool bSpeechBuilt;
bool bSpeechQueueing;
int speechState = QTextToSpeech::Ready;
QString speechCurrent;
#endif // QT_TEXTTOSPEECH_LIB
// No documentation available in wiki - internal function
static bool isMain(const QString& name)
{
if (name.isEmpty()) {
return true;
}
if (!name.compare(QStringLiteral("main"))) {
return true;
}
return false;
}
static const char *bad_window_type = "%s: bad argument #%d type (window name as string expected, got %s)!";
static const char *bad_cmdline_type = "%s: bad argument #%d type (command line name as string expected, got %s)!";
static const char *bad_window_value = "window \"%s\" not found";
static const char *bad_cmdline_value = "command line \"%s\" not found";
#define WINDOW_NAME(_L, _pos) \
({ \
int pos_ = (_pos); \
const char *res_; \
if ((lua_gettop(_L) < pos_) || lua_isnil(_L, pos_)) { \
res_ = ""; \
} else if (!lua_isstring(_L, pos_)) { \
lua_pushfstring(_L, bad_window_type, __FUNCTION__, pos_, luaL_typename(_L, pos_)); \
return lua_error(_L); \
} else { \
res_ = lua_tostring(_L, pos_); \
} \
res_; \
})
#define CMDLINE_NAME(_L, _pos) \
({ \
int pos_ = (_pos); \
if (!lua_isstring(_L, pos_)) { \
lua_pushfstring(_L, bad_cmdline_type, __FUNCTION__, pos_, luaL_typename(_L, pos_));\
return lua_error(_L); \
} \
lua_tostring(_L, pos_); \
})
#define CONSOLE_NIL(_L, _name) \
({ \
auto name_ = (_name); \
auto console_ = getHostFromLua(_L).findConsole(name_); \
console_; \
})
#define CONSOLE(_L, _name) \
({ \
auto name_ = (_name); \
auto console_ = getHostFromLua(_L).findConsole(name_); \
if (!console_) { \
lua_pushnil(L); \
lua_pushfstring(L, bad_window_value, name_.toUtf8().constData()); \
return 2; \
} \
console_; \
})
#define COMMANDLINE(_L, _name) \
({ \
const QString name_ = (_name); \
auto console_ = getHostFromLua(_L).mpConsole; \
auto cmdLine_ = isMain(name_) ? &*console_->mpCommandLine \
: console_->mSubCommandLineMap.value(name_); \
if (!cmdLine_) { \
lua_pushnil(L); \
lua_pushfstring(L, bad_cmdline_value, name_.toUtf8().constData()); \
return 2; \
} \
cmdLine_; \
})
// variable names within these macros have trailing underscores because in
// at least one case, masking an existing variable with the new one confused
// GCC, leading to a crash.
TLuaInterpreter::TLuaInterpreter(Host* pH, const QString& hostName, int id) : mpHost(pH), hostName(hostName), mHostID(id), purgeTimer(this)
{
pGlobalLua = nullptr;
connect(&purgeTimer, &QTimer::timeout, this, &TLuaInterpreter::slotPurge);
mpFileDownloader = new QNetworkAccessManager(this);
connect(mpFileDownloader, &QNetworkAccessManager::finished, this, &TLuaInterpreter::slot_httpRequestFinished);
initLuaGlobals();
purgeTimer.start(2000);
}
TLuaInterpreter::~TLuaInterpreter()
{
lua_close(pGlobalLua);
}
// No documentation available in wiki - internal function
// Replaces a check like this:
// if (!lua_isboolean(L, 14)) {
// lua_pushfstring(L,
// "createMapLabel: bad argument #14 type (showOnTop as boolean is optional, got %s!)",
// luaL_typename(L, 14));
// return lua_error(L);
// }
// bool showOnTop = lua_toboolean(L, 14);
//
// With reduced repetition like that:
// bool showOnTop = getVerifiedBool(L, "createMapLabel", 14, "showOnTop", true);
//
// The "isOptional" parameter is optional, and will default to not-optional parameters! :)
//
// See also: getVerifiedString, getVerifiedInt, getVerifiedFloat, errorArgumentType
bool TLuaInterpreter::getVerifiedBool(lua_State* L, const char* functionName, const int pos, const char* publicName, const bool isOptional)
{
if (!lua_isboolean(L, pos)) {
errorArgumentType(L, functionName, pos, publicName, "boolean", isOptional);
lua_error(L);
Q_UNREACHABLE();
return false;
}
return lua_toboolean(L, pos);
}
// No documentation available in wiki - internal function
// See also: getVerifiedBool
QString TLuaInterpreter::getVerifiedString(lua_State* L, const char* functionName, const int pos, const char* publicName, const bool isOptional)
{
if (!lua_isstring(L, pos)) {
errorArgumentType(L, functionName, pos, publicName, "string", isOptional);
lua_error(L);
Q_UNREACHABLE();
return QString();
}
return lua_tostring(L, pos);
}
// No documentation available in wiki - internal function
// See also: getVerifiedBool
int TLuaInterpreter::getVerifiedInt(lua_State* L, const char* functionName, const int pos, const char* publicName, const bool isOptional)
{
if (!lua_isnumber(L, pos)) {
errorArgumentType(L, functionName, pos, publicName, "number", isOptional);
lua_error(L);
Q_UNREACHABLE();
return -1;
}
return lua_tointeger(L, pos);
}
// No documentation available in wiki - internal function
// See also: getVerifiedBool
float TLuaInterpreter::getVerifiedFloat(lua_State* L, const char* functionName, const int pos, const char* publicName, const bool isOptional)
{
if (!lua_isnumber(L, pos)) {
errorArgumentType(L, functionName, pos, publicName, "number", isOptional);
lua_error(L);
Q_UNREACHABLE();
return 0;
}
return static_cast <float> (lua_tonumber(L, pos));
}
// No documentation available in wiki - internal function
// See also: getVerifiedBool
double TLuaInterpreter::getVerifiedDouble(lua_State* L, const char* functionName, const int pos, const char* publicName, const bool isOptional)
{
if (!lua_isnumber(L, pos)) {
errorArgumentType(L, functionName, pos, publicName, "number", isOptional);
lua_error(L);
Q_UNREACHABLE();
return 0;
}
return lua_tonumber(L, pos);
}
// No documentation available in wiki - internal function
// Raises a Lua error in case of an API usage mistake
// See also: getVerifiedBool, warnArgumentValue
void TLuaInterpreter::errorArgumentType(lua_State* L, const char* functionName, const int pos, const char* publicName, const char* publicType, const bool isOptional)
{
if (isOptional) {
lua_pushfstring(L, "%s: bad argument #%d type (%s as %s is optional, got %s!)",
functionName, pos, publicName, publicType, luaL_typename(L, pos));
} else {
lua_pushfstring(L, "%s: bad argument #%d type (%s as %s expected, got %s!)",
functionName, pos, publicName, publicType, luaL_typename(L, pos));
}
}
// No documentation available in wiki - internal function
// returns nil+msg in case of a data mistake, for example a missing room. Should not raise a Lua error
// See also: announceWrongArgumentType
int TLuaInterpreter::warnArgumentValue(lua_State* L, const char* functionName, const QString& message, const bool useFalseInsteadofNil)
{
if (Q_LIKELY(!useFalseInsteadofNil)) {
lua_pushnil(L);
} else {
lua_pushboolean(L, false);
}
lua_pushstring(L, message.toUtf8().constData());
if (mudlet::debugMode) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
TDebug(QColor(Qt::white), QColorConstants::Svg::orange) << "Lua: " << functionName << ": " << message << "\n" >> 0;
#else
TDebug(QColor(Qt::white), QColor("orange")) << "Lua: " << functionName << ": " << message << "\n" >> 0;
#endif
}
return 2;
}
int TLuaInterpreter::warnArgumentValue(lua_State* L, const char* functionName, const char* message, const bool useFalseInsteadofNil)
{
if (Q_LIKELY(!useFalseInsteadofNil)) {
lua_pushnil(L);
} else {
lua_pushboolean(L, false);
}
lua_pushstring(L, message);
if (mudlet::debugMode) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
TDebug(QColor(Qt::white), QColorConstants::Svg::orange) << "Lua: " << functionName << ": " << message << "\n" >> 0;
#else
TDebug(QColor(Qt::white), QColor("orange")) << "Lua: " << functionName << ": " << message << "\n" >> 0;
#endif
}
return 2;
}
// No documentation available in wiki - internal function
// Raises additional sysDownloadError Events on failure to process
// the local file, the second argument is "failureToWriteLocalFile" and besides
// the file to be written being the third argument (as multiple downloads are
// supported) a fourth argument gives the local file problem, one of:
// * "unableToOpenLocalFileForWriting"
// * "unableToWriteLocalFile"
// or a QFile::errorString() for the issue at hand
// Upon success we now give an additional (third value) which gives the number
// of bytes written into the downloaded file.
void TLuaInterpreter::slot_httpRequestFinished(QNetworkReply* reply)
{
Host* pHost = mpHost;
if (!pHost) {
qWarning() << QStringLiteral("TLuaInterpreter::slot_httpRequestFinished(...) ERROR: NULL Host pointer!");
return;
}
if (reply->error() != QNetworkReply::NoError) {
TEvent event {};
QString localFileName;
switch (reply->operation()) {
case QNetworkAccessManager::PostOperation:
event.mArgumentList << QStringLiteral("sysPostHttpError");
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << reply->errorString();
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << reply->url().toString();
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
break;
case QNetworkAccessManager::PutOperation:
event.mArgumentList << QStringLiteral("sysPutHttpError");
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << reply->errorString();
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << reply->url().toString();
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
break;
case QNetworkAccessManager::GetOperation:
localFileName = downloadMap.value(reply);
event.mArgumentList << (localFileName.isEmpty()
? QStringLiteral("sysGetHttpError")
: QStringLiteral("sysDownloadError"));
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << reply->errorString();
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
if (!localFileName.isEmpty()) {
event.mArgumentList << localFileName;
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
}
event.mArgumentList << reply->url().toString();
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
downloadMap.remove(reply);
break;
case QNetworkAccessManager::DeleteOperation:
event.mArgumentList << QStringLiteral("sysDeleteHttpError");
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << reply->errorString();
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << reply->url().toString();
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
break;
case QNetworkAccessManager::HeadOperation:
break;
case QNetworkAccessManager::CustomOperation:
break;
case QNetworkAccessManager::UnknownOperation:
break;
}
event.mArgumentList << QString::number(createHttpResponseTable(reply));
event.mArgumentTypeList << ARGUMENT_TYPE_TABLE;
reply->deleteLater();
downloadMap.remove(reply);
pHost->raiseEvent(event);
return;
}
handleHttpOK(reply);
}
void TLuaInterpreter::handleHttpOK(QNetworkReply* reply)
{
TEvent event {};
Host* pHost = mpHost;
if (!pHost) {
return;
}
switch (reply->operation()) {
case QNetworkAccessManager::HeadOperation:
break;
case QNetworkAccessManager::DeleteOperation:
event.mArgumentList << QStringLiteral("sysDeleteHttpDone");
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << reply->url().toString();
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << QString(reply->readAll());
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
break;
case QNetworkAccessManager::CustomOperation:
break;
case QNetworkAccessManager::UnknownOperation:
break;
case QNetworkAccessManager::PostOperation:
event.mArgumentList << QStringLiteral("sysPostHttpDone");
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << reply->url().toString();
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << QString(reply->readAll());
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
break;
case QNetworkAccessManager::PutOperation:
event.mArgumentList << QStringLiteral("sysPutHttpDone");
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << reply->url().toString();
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << QString(reply->readAll());
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
break;
case QNetworkAccessManager::GetOperation:
QString localFileName = downloadMap.value(reply);
downloadMap.remove(reply);
// If the user did not give us a file path, we're not going to
// consider this an error, we're just going to attach the reply
// directly. Another way this could happen is the user made a POST
// request, and it redirected to a GET. In the case of POST requests,
// we don't ask the user for a file path.
if (localFileName.isEmpty()) {
event.mArgumentList << QLatin1String("sysGetHttpDone");
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << reply->url().toString();
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << QString(reply->readAll());
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
break;
}
QFile localFile(localFileName);
if (!localFile.open(QFile::WriteOnly)) {
event.mArgumentList << QLatin1String("sysDownloadError");
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << QLatin1String("Couldn't save to the destination file");
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << localFileName;
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << QLatin1String("Couldn't open the destination file for writing (permission errors?)");
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
break;
}
qint64 bytesWritten = localFile.write(reply->readAll());
if (bytesWritten == -1) {
event.mArgumentList << QLatin1String("sysDownloadError");
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << QLatin1String("Couldn't save to the destination file");
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << localFileName;
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << QLatin1String("Couldn't write downloaded content into the destination file");
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
break;
}
localFile.flush();
if (localFile.error() == QFile::NoError) {
event.mArgumentList << QLatin1String("sysDownloadDone");
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << localFileName;
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << QString::number(bytesWritten);
event.mArgumentTypeList << ARGUMENT_TYPE_NUMBER;
} else {
event.mArgumentList << QLatin1String("sysDownloadError");
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << QLatin1String("Couldn't save to the destination file");
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << localFileName;
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << localFile.errorString();
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
}
localFile.close();
break;
}
event.mArgumentList << QString::number(createHttpResponseTable(reply));
event.mArgumentTypeList << ARGUMENT_TYPE_TABLE;
reply->deleteLater();
pHost->raiseEvent(event);
}
void TLuaInterpreter::raiseDownloadProgressEvent(lua_State* L, QString fileUrl, qint64 bytesDownloaded, qint64 totalBytes)
{
Host& host = getHostFromLua(L);
TEvent event {};
event.mArgumentList << QStringLiteral("sysDownloadFileProgress");
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << fileUrl;
event.mArgumentTypeList << ARGUMENT_TYPE_STRING;
event.mArgumentList << QString::number(bytesDownloaded);
event.mArgumentTypeList << ARGUMENT_TYPE_NUMBER;
if(totalBytes >= 0) {
event.mArgumentList << QString::number(totalBytes);
event.mArgumentTypeList << ARGUMENT_TYPE_NUMBER;
} else {
event.mArgumentList << QString();
event.mArgumentTypeList << ARGUMENT_TYPE_NIL;
}
host.raiseEvent(event);
}
// No documentation available in wiki - internal function
void TLuaInterpreter::slotDeleteSender(int exitCode, QProcess::ExitStatus exitStatus)
{
Q_UNUSED(exitCode);
Q_UNUSED(exitStatus);
objectsToDelete.append(sender());
}
// No documentation available in wiki - internal function
void TLuaInterpreter::slotPurge()
{
while (!objectsToDelete.isEmpty()) {
delete objectsToDelete.takeFirst();
}
}
// No documentation available in wiki - internal function
int TLuaInterpreter::Wait(lua_State* L)
{
int n = lua_gettop(L);
if (n != 1) {
lua_pushstring(L, "Wait: wrong number of arguments");
return lua_error(L);
}
int luaSleepMsec = getVerifiedInt(L, __func__, 1, "sleep time in msec");
msleep(luaSleepMsec); // FIXME thread::sleep()
return 0;
}
// No documentation available in wiki - internal function
// dirToString will now catch and validate pretty much any string that could
// be a normal direction in a case insensitive manner and convert it to a
// standard value (one of: "n", "ne", ..., "nw", "up", "down", "in" or
// "out") but leave anything else as entered; OR convert a direction code as
// a number from 1 to 12 to those same standard direction strings.
// This is intended as a temporary step until a uniform means of specifying
// both "normal" and "special" exits for all lua commands that take exit
// directions as arguments in an unambiguous manner can be formulated - though
// to maintain backwards compatibility the current functions will remain even
// if they get marked out as being deprecated.
QString TLuaInterpreter::dirToString(lua_State* L, int position)
{
if (lua_isnumber(L, position)) {
qint64 dirNum = lua_tonumber(L, position);
switch (dirNum) {
// breaks not needed - all handled cases end in a return!
case 1:
return QStringLiteral("n");
case 2:
return QStringLiteral("ne");
case 3:
return QStringLiteral("nw");
case 4:
return QStringLiteral("e");
case 5:
return QStringLiteral("w");
case 6:
return QStringLiteral("s");
case 7:
return QStringLiteral("se");
case 8:
return QStringLiteral("sw");
case 9:
return QStringLiteral("up");
case 10:
return QStringLiteral("down");
case 11:
return QStringLiteral("in");
case 12:
return QStringLiteral("out");
default:
return QString();
}
} else if (lua_isstring(L, position)) {
QString direction{lua_tostring(L, position)};
if (!direction.compare(QLatin1String("n"), Qt::CaseInsensitive) || !direction.compare(QLatin1String("north"), Qt::CaseInsensitive)) {
return QLatin1String("n");
} else if (!direction.compare(QLatin1String("e"), Qt::CaseInsensitive) || !direction.compare(QLatin1String("east"), Qt::CaseInsensitive)) {
return QLatin1String("e");
} else if (!direction.compare(QLatin1String("s"), Qt::CaseInsensitive) || !direction.compare(QLatin1String("south"), Qt::CaseInsensitive)) {
return QLatin1String("s");
} else if (!direction.compare(QLatin1String("w"), Qt::CaseInsensitive) || !direction.compare(QLatin1String("west"), Qt::CaseInsensitive)) {
return QLatin1String("w");
} else if (!direction.compare(QLatin1String("u"), Qt::CaseInsensitive) || !direction.compare(QLatin1String("up"), Qt::CaseInsensitive)) {
return QLatin1String("up");
} else if (!direction.compare(QLatin1String("d"), Qt::CaseInsensitive) || !direction.compare(QLatin1String("down"), Qt::CaseInsensitive)) {
return QLatin1String("down");
} else if (!direction.compare(QLatin1String("ne"), Qt::CaseInsensitive) || !direction.compare(QLatin1String("northeast"), Qt::CaseInsensitive)
|| !direction.compare(QLatin1String("north-east"), Qt::CaseInsensitive)) {
return QLatin1String("ne");
} else if (!direction.compare(QLatin1String("se"), Qt::CaseInsensitive) || !direction.compare(QLatin1String("southeast"), Qt::CaseInsensitive)
|| !direction.compare(QLatin1String("south-east"), Qt::CaseInsensitive)) {
return QLatin1String("se");
} else if (!direction.compare(QLatin1String("sw"), Qt::CaseInsensitive) || !direction.compare(QLatin1String("southwest"), Qt::CaseInsensitive)
|| !direction.compare(QLatin1String("south-west"), Qt::CaseInsensitive)) {
return QLatin1String("sw");
} else if (!direction.compare(QLatin1String("nw"), Qt::CaseInsensitive) || !direction.compare(QLatin1String("northwest"), Qt::CaseInsensitive)
|| !direction.compare(QLatin1String("north-west"), Qt::CaseInsensitive)) {
return QLatin1String("nw");
} else if (!direction.compare(QLatin1String("i"), Qt::CaseInsensitive) || !direction.compare(QLatin1String("in"), Qt::CaseInsensitive)) {
return QLatin1String("in");
} else if (!direction.compare(QLatin1String("o"), Qt::CaseInsensitive) || !direction.compare(QLatin1String("out"), Qt::CaseInsensitive)) {
return QLatin1String("out");
} else {
return direction;
}
} else {
return QString();
}
}
// No documentation available in wiki - internal function
int TLuaInterpreter::dirToNumber(lua_State* L, int position)
{
QString dir;
int dirNum;
if (lua_isstring(L, position)) {
dir = lua_tostring(L, position);
dir = dir.toLower();
if (dir == QStringLiteral("n") || dir == QStringLiteral("north")) {
return 1;
}
if (dir == QStringLiteral("ne") || dir == QStringLiteral("northeast")) {
return 2;
}
if (dir == QStringLiteral("nw") || dir == QStringLiteral("northwest")) {
return 3;
}
if (dir == QStringLiteral("e") || dir == QStringLiteral("east")) {
return 4;
}
if (dir == QStringLiteral("w") || dir == QStringLiteral("west")) {
return 5;
}
if (dir == QStringLiteral("s") || dir == QStringLiteral("south")) {
return 6;
}
if (dir == QStringLiteral("se") || dir == QStringLiteral("southeast")) {
return 7;
}
if (dir == QStringLiteral("sw") || dir == QStringLiteral("southwest")) {
return 8;
}
if (dir == QStringLiteral("u") || dir == QStringLiteral("up")) {
return 9;
}
if (dir == QStringLiteral("d") || dir == QStringLiteral("down")) {
return 10;
}
if (dir == QStringLiteral("in")) {
return 11;
}
if (dir == QStringLiteral("out")) {
return 12;
}
}
if (lua_isnumber(L, position)) {
dirNum = lua_tonumber(L, position);
return (dirNum >= 1 && dirNum <= 12 ? dirNum : 0);
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#denyCurrentSend
int TLuaInterpreter::denyCurrentSend(lua_State* L)
{
Host& host = getHostFromLua(L);
host.mAllowToSendCommand = false;
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#raiseEvent
int TLuaInterpreter::raiseEvent(lua_State* L)
{
Host& host = getHostFromLua(L);
TEvent event {};
int n = lua_gettop(L);
// We go from the top of the stack down, because luaL_ref will
// only reference the object at the top of the stack
for (int i = n; i >= 1; i--) {
switch (lua_type(L, -1)) {
case LUA_TNUMBER:
// https://en.wikipedia.org/wiki/Double-precision_floating-point_format#IEEE_754_double-precision_binary_floating-point_format:_binary64
// suggests that 17 decimal digits is the most we can rely on:
event.mArgumentList.prepend(QString::number(lua_tonumber(L, -1), 'g', 17));
event.mArgumentTypeList.prepend(ARGUMENT_TYPE_NUMBER);
lua_pop(L, 1);
break;
case LUA_TSTRING:
event.mArgumentList.prepend(lua_tostring(L, -1));
event.mArgumentTypeList.prepend(ARGUMENT_TYPE_STRING);
lua_pop(L, 1);
break;
case LUA_TBOOLEAN:
event.mArgumentList.prepend(QString::number(lua_toboolean(L, -1)));
event.mArgumentTypeList.prepend(ARGUMENT_TYPE_BOOLEAN);
lua_pop(L, 1);
break;
case LUA_TNIL:
event.mArgumentList.prepend(QString());
event.mArgumentTypeList.prepend(ARGUMENT_TYPE_NIL);
lua_pop(L, 1);
break;
case LUA_TTABLE:
event.mArgumentList.prepend(QString::number(luaL_ref(L, LUA_REGISTRYINDEX)));
event.mArgumentTypeList.prepend(ARGUMENT_TYPE_TABLE);
// luaL_ref pops the object, so we don't have to
break;
case LUA_TFUNCTION:
event.mArgumentList.prepend(QString::number(luaL_ref(L, LUA_REGISTRYINDEX)));
event.mArgumentTypeList.prepend(ARGUMENT_TYPE_FUNCTION);
// luaL_ref pops the object, so we don't have to
break;
default:
lua_pushfstring(L,
"raiseEvent: bad argument #%d type (string, number, boolean, table,\n"
"function, or nil expected, got a %s!)",
i,
luaL_typename(L, -1));
return lua_error(L);
}
}
host.raiseEvent(event);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getProfileName
int TLuaInterpreter::getProfileName(lua_State* L)
{
Host& host = getHostFromLua(L);
lua_pushstring(L, host.getName().toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getProfileTabNumber
int TLuaInterpreter::getProfileTabNumber(lua_State* L)
{
Host& host = getHostFromLua(L);
auto profileIndex = mudlet::self()->mpTabBar->tabIndex(host.getName());
if (profileIndex != -1) {
lua_pushnumber(L, profileIndex + 1);
return 1;
}
return warnArgumentValue(L, __func__, "could not retrieve the tab number");
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getCommandSeparator
int TLuaInterpreter::getCommandSeparator(lua_State* L)
{
Host& host = getHostFromLua(L);
lua_pushstring(L, host.getCommandSeparator().toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#raiseGlobalEvent
int TLuaInterpreter::raiseGlobalEvent(lua_State* L)
{
Host& host = getHostFromLua(L);
int n = lua_gettop(L);
if (!n) {
lua_pushstring(L, "raiseGlobalEvent: missing argument #1 (eventName as, probably, a string expected!)");
return lua_error(L);
}
TEvent event {};
for (int i = 1; i <= n; ++i) {
// The sending profile of the event does not receive the event if
// sent via this command but if the same eventName is to be used for
// an event within a profile and to other profiles it is safest to
// insert a string like "local" or "self" or the profile name from
// getProfileName() as an (last) additional argument after all the
// other so the handler can tell it is handling a local event from
// raiseEvent(...) and not one from another profile! - Slysven
switch (lua_type(L, i)) {
case LUA_TNUMBER:
event.mArgumentList.append(QString::number(lua_tonumber(L, i), 'g', 17));
event.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER);
break;
case LUA_TSTRING:
event.mArgumentList.append(lua_tostring(L, i));
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
break;
case LUA_TBOOLEAN:
event.mArgumentList.append(QString::number(lua_toboolean(L, i)));
event.mArgumentTypeList.append(ARGUMENT_TYPE_BOOLEAN);
break;
case LUA_TNIL:
event.mArgumentList.append(QString());
event.mArgumentTypeList.append(ARGUMENT_TYPE_NIL);
break;
default:
lua_pushfstring(L,
"raiseGlobalEvent: bad argument type #%d (boolean, number, string or nil\n"
"expected, got a %s!)",
i,
luaL_typename(L, i));
return lua_error(L);
}
}
event.mArgumentList.append(host.getName());
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
mudlet::self()->getHostManager().postInterHostEvent(&host, event);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#resetProfile
int TLuaInterpreter::resetProfile(lua_State* L)
{
Host& host = getHostFromLua(L);
host.resetProfile_phase1();
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#selectString
int TLuaInterpreter::selectString(lua_State* L)
{
int s = 1;
QString windowName;
if (lua_gettop(L) > 2) {
windowName = WINDOW_NAME(L, s++);
}
QString searchText = getVerifiedString(L, __func__, s++, "text to select");
// CHECK: Do we need to qualify this for a non-blank string?
qint64 numOfMatch = static_cast <qint64> (getVerifiedInt(L, __func__, s, "match count {1 for first}"));
auto console = CONSOLE(L, windowName);
lua_pushnumber(L, console->select(searchText, numOfMatch));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#selectCurrentLine
int TLuaInterpreter::selectCurrentLine(lua_State* L)
{
QString windowName;
if (lua_gettop(L) > 0) {
windowName = WINDOW_NAME(L, 1);
}
auto console = CONSOLE(L, windowName);
console->selectCurrentLine();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#isAnsiFgColor
int TLuaInterpreter::isAnsiFgColor(lua_State* L)
{
std::string windowName = "main";
int ansiFg = getVerifiedInt(L, __func__, 1, "ANSI color");
std::list<int> result;
Host& host = getHostFromLua(L);
result = host.mpConsole->getFgColor(windowName);
auto it = result.begin();
if (result.size() < 3) {
return 0;
}
if (ansiFg < 0) {
return 0;
}
if (ansiFg > 16) {
return 0;
}
QColor c;
switch (ansiFg) {
case 0:
c = host.mFgColor;
break;
case 1:
c = host.mLightBlack;
break;
case 2:
c = host.mBlack;
break;
case 3:
c = host.mLightRed;
break;
case 4:
c = host.mRed;
break;
case 5:
c = host.mLightGreen;
break;
case 6:
c = host.mGreen;
break;
case 7:
c = host.mLightYellow;
break;
case 8:
c = host.mYellow;
break;
case 9:
c = host.mLightBlue;
break;
case 10:
c = host.mBlue;
break;
case 11:
c = host.mLightMagenta;
break;
case 12:
c = host.mMagenta;
break;
case 13:
c = host.mLightCyan;
break;
case 14:
c = host.mCyan;
break;
case 15:
c = host.mLightWhite;
break;
case 16:
c = host.mWhite;
break;
}
int val = *it;
if (val == c.red()) {
it++;
val = *it;
if (val == c.green()) {
it++;
val = *it;
if (val == c.blue()) {
lua_pushboolean(L, true);
return 1;
}
}
}
lua_pushboolean(L, false);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#isAnsiBgColor
int TLuaInterpreter::isAnsiBgColor(lua_State* L)
{
std::string windowName = "main";
int ansiBg = getVerifiedInt(L, __func__, 1, "ANSI color");
std::list<int> result;
Host& host = getHostFromLua(L);
result = host.mpConsole->getBgColor(windowName);
auto it = result.begin();
if (result.size() < 3) {
return 0;
}
if (ansiBg < 0) {
return 0;
}
if (ansiBg > 16) {
return 0;
}
QColor c;
switch (ansiBg) {
case 0:
c = host.mBgColor;
break;
case 1:
c = host.mLightBlack;
break;
case 2:
c = host.mBlack;
break;
case 3:
c = host.mLightRed;
break;
case 4:
c = host.mRed;
break;
case 5:
c = host.mLightGreen;
break;
case 6:
c = host.mGreen;
break;
case 7:
c = host.mLightYellow;
break;
case 8:
c = host.mYellow;
break;
case 9:
c = host.mLightBlue;
break;
case 10:
c = host.mBlue;
break;
case 11:
c = host.mLightMagenta;
break;
case 12:
c = host.mMagenta;
break;
case 13:
c = host.mLightCyan;
break;
case 14:
c = host.mCyan;
break;
case 15:
c = host.mLightWhite;
break;
case 16:
c = host.mWhite;
break;
}
int val = *it;
if (val == c.red()) {
it++;
val = *it;
if (val == c.green()) {
it++;
val = *it;
if (val == c.blue()) {
lua_pushboolean(L, true);
return 1;
}
}
}
lua_pushboolean(L, false);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getFgColor
int TLuaInterpreter::getFgColor(lua_State* L)
{
std::string windowName = "main";
if (lua_gettop(L) > 0) {
windowName = getVerifiedString(L, __func__, 1, "window name", true).toStdString();
}
Host& host = getHostFromLua(L);
std::list<int> result = host.mpConsole->getFgColor(windowName);
for (int pos : result) {
lua_pushnumber(L, pos);
}
return result.size();
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getBgColor
int TLuaInterpreter::getBgColor(lua_State* L)
{
std::string windowName = "main";
if (lua_gettop(L) > 0) {
windowName = getVerifiedString(L, __func__, 1, "window name", true).toStdString();
}
Host& host = getHostFromLua(L);
std::list<int> result = host.mpConsole->getBgColor(windowName);
for (int pos : result) {
lua_pushnumber(L, pos);
}
return result.size();
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getTextFormat
int TLuaInterpreter::getTextFormat(lua_State* L)
{
QString windowName;
if (lua_gettop(L)) {
windowName = getVerifiedString(L, __func__, 1, "window name", true);
}
Host& host = getHostFromLua(L);
QPair<quint8, TChar> result = host.mpConsole->getTextAttributes(windowName);
if (result.first == 1) {
return warnArgumentValue(L, __func__, QStringLiteral("window '%1' not found").arg(windowName));
}
if (result.first == 2) {
return warnArgumentValue(L, __func__, QStringLiteral("current selection invalid in window '%1'").arg(windowName));
}
lua_newtable(L);
TChar::AttributeFlags format = result.second.allDisplayAttributes();
lua_pushstring(L, "bold");
lua_pushboolean(L, format & TChar::Bold);
lua_settable(L, -3);
lua_pushstring(L, "italic");
lua_pushboolean(L, format & TChar::Italic);
lua_settable(L, -3);
lua_pushstring(L, "overline");
lua_pushboolean(L, format & TChar::Overline);
lua_settable(L, -3);
lua_pushstring(L, "reverse");
lua_pushboolean(L, format & TChar::Reverse);
lua_settable(L, -3);
lua_pushstring(L, "strikeout");
lua_pushboolean(L, format & TChar::StrikeOut);
lua_settable(L, -3);
lua_pushstring(L, "underline");
lua_pushboolean(L, format & TChar::Underline);
lua_settable(L, -3);
QColor foreground(result.second.foreground());
lua_pushstring(L, "foreground");
lua_newtable(L);
lua_pushnumber(L, 1);
lua_pushnumber(L, foreground.red());
lua_settable(L, -3);
lua_pushnumber(L, 2);
lua_pushnumber(L, foreground.green());
lua_settable(L, -3);
lua_pushnumber(L, 3);
lua_pushnumber(L, foreground.blue());
lua_settable(L, -3);
lua_settable(L, -3);
QColor background(result.second.background());
lua_pushstring(L, "background");
lua_newtable(L);
lua_pushnumber(L, 1);
lua_pushnumber(L, background.red());
lua_settable(L, -3);
lua_pushnumber(L, 2);
lua_pushnumber(L, background.green());
lua_settable(L, -3);
lua_pushnumber(L, 3);
lua_pushnumber(L, background.blue());
lua_settable(L, -3);
lua_settable(L, -3);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getWindowsCodepage
int TLuaInterpreter::getWindowsCodepage(lua_State* L)
{
#if defined (Q_OS_WIN32)
QSettings registry(QStringLiteral(R"(HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage)"),
QSettings::NativeFormat);
auto value = registry.value(QStringLiteral("ACP"));
lua_pushstring(L, value.toString().toUtf8().constData());
return 1;
#else
return warnArgumentValue(L, __func__, "this function is only needed on Windows, and does not work here");
#endif
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#wrapLine
int TLuaInterpreter::wrapLine(lua_State* L)
{
int s = 1;
std::string windowName;
if (lua_gettop(L)) {
windowName = getVerifiedString(L, __func__, s++, "window name").toStdString();
}
int lineNumber = getVerifiedInt(L, __func__, s, "line");
Host& host = getHostFromLua(L);
host.mpConsole->luaWrapLine(windowName, lineNumber);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#spawn
int TLuaInterpreter::spawn(lua_State* L)
{
Host& host = getHostFromLua(L);
return TForkedProcess::startProcess(host.getLuaInterpreter(), L);
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#selectCaptureGroup
int TLuaInterpreter::selectCaptureGroup(lua_State* L)
{
int captureGroup = getVerifiedInt(L, __func__, 1, "capture group");
Host& host = getHostFromLua(L);
if (captureGroup < 1) {
lua_pushnumber(L, -1);
return 1;
}
// We want capture groups to start with 1 instead of 0 so predecrement
// luaNumOfMatch :
if (--captureGroup < static_cast<int>(host.getLuaInterpreter()->mCaptureGroupList.size())) {
TLuaInterpreter* pL = host.getLuaInterpreter();
auto iti = pL->mCaptureGroupPosList.begin();
auto its = pL->mCaptureGroupList.begin();
int begin = *iti;
std::string& s = *its;
for (int i = 0; iti != pL->mCaptureGroupPosList.end(); ++iti, ++i) {
begin = *iti;
if (i >= captureGroup) {
break;
}
}
for (int i = 0; its != pL->mCaptureGroupList.end(); ++its, ++i) {
s = *its;
if (i >= captureGroup) {
break;
}
}
int length = QString::fromStdString(s).size();
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::red)) << "selectCaptureGroup(" << begin << ", " << length << ")\n" >> 0;
}
int pos = host.mpConsole->selectSection(begin, length);
lua_pushnumber(L, pos);
} else {
lua_pushnumber(L, -1);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getLines
int TLuaInterpreter::getLines(lua_State* L)
{
int n = lua_gettop(L);
int s = 1;
QString windowName;
if (n > 2) {
windowName = getVerifiedString(L, __func__, s++, "mini console, user window or buffer name {may be omitted for the \"main\" console}", true);
}
int lineFrom = getVerifiedInt(L, __func__, s++, "start line");
int lineTo = getVerifiedInt(L, __func__, s, "end line");
Host& host = getHostFromLua(L);
QPair<bool, QStringList> result = host.getLines(windowName, lineFrom, lineTo);
if (!result.first) {
// Only one QString in .second - the error message
return warnArgumentValue(L, __func__, result.second.at(0));
}
lua_newtable(L);
for (int i = 0, total = result.second.size(); i < total; ++i) {
lua_pushnumber(L, i + 1);
lua_pushstring(L, result.second.at(i).toUtf8().constData());
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#loadReplay
int TLuaInterpreter::loadReplay(lua_State* L)
{
QString replayFileName = getVerifiedString(L, __func__, 1, "replay file name");
if (replayFileName.isEmpty()) {
return warnArgumentValue(L, __func__, "a blank string is not a valid replay file name");
}
Host& host = getHostFromLua(L);
QString errMsg;
if (mudlet::self()->loadReplay(&host, replayFileName, &errMsg)) {
lua_pushboolean(L, true);
return 1;
} else {
// Although we only use English text for Lua messages the errMsg could
// contain a Windows pathFileName which may use non-ASCII characters:
return warnArgumentValue(L, __func__, QStringLiteral("unable to start replay, reason: '%1'").arg(errMsg));
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setProfileIcon
int TLuaInterpreter::setProfileIcon(lua_State* L)
{
QString iconPath = getVerifiedString(L, __func__, 1, "icon file path");
if (iconPath.isEmpty()) {
return warnArgumentValue(L, __func__, "a blank string is not a valid icon file path");
}
if (!QFileInfo::exists(iconPath)) {
return warnArgumentValue(L, __func__, QStringLiteral("path '%1' doesn't exist").arg(iconPath));
}
Host& host = getHostFromLua(L);
auto[success, message] = mudlet::self()->setProfileIcon(host.getName(), iconPath);
if (!success) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#resetProfileIcon
int TLuaInterpreter::resetProfileIcon(lua_State* L)
{
Host& host = getHostFromLua(L);
auto [success, message] = mudlet::self()->resetProfileIcon(host.getName());
if (!success) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getCurrentLine
int TLuaInterpreter::getCurrentLine(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
auto console = getHostFromLua(L).findConsole(windowName);
if (!console) {
// the next line should be "pushnil"; compatibility with old bugs and all that
lua_pushstring(L, "ERROR: mini console does not exist");
lua_pushfstring(L, bad_window_value, windowName.toUtf8().constData());
return 2;
}
QString line = console->getCurrentLine();
lua_pushstring(L, line.toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setMiniConsoleFontSize
int TLuaInterpreter::setMiniConsoleFontSize(lua_State* L)
{
QString windowName = getVerifiedString(L, __func__, 1, "miniconsole name");
int size = getVerifiedInt(L, __func__, 2, "font size");
auto console = CONSOLE(L, windowName);
if (console->setFontSize(size)) {
lua_pushboolean(L, true);
} else {
return warnArgumentValue(L, __func__, QStringLiteral("setting font size of '%1' failed").arg(windowName));
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getLineNumber
int TLuaInterpreter::getLineNumber(lua_State* L)
{
QString windowName;
int s = 0;
if (lua_gettop(L) > 0) { // Have more than one argument so first must be a console name
windowName = WINDOW_NAME(L, ++s);
}
auto console = CONSOLE(L, windowName);
lua_pushnumber(L, console->getLineNumber());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#updateMap
int TLuaInterpreter::updateMap(lua_State* L)
{
Host& host = getHostFromLua(L);
if (host.mpMap) {
host.mpMap->update();
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#addMapMenu
int TLuaInterpreter::addMapMenu(lua_State* L)
{
// first arg = unique name, second arg= parent name, third arg = display name (=unique name if not provided)
QStringList menuList;
QString uniqueName = getVerifiedString(L, __func__, 1, "uniquename");
if (!lua_isstring(L, 2)) {
menuList << "";
} else {
menuList << lua_tostring(L, 2);
}
if (!lua_isstring(L, 3)) {
menuList << uniqueName;
} else {
menuList << lua_tostring(L, 3);
}
Host& host = getHostFromLua(L);
if (host.mpMap) {
if (host.mpMap->mpMapper) {
if (host.mpMap->mpMapper->mp2dMap) {
host.mpMap->mpMapper->mp2dMap->mUserMenus.insert(uniqueName, menuList);
}
}
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#removeMapMenu
int TLuaInterpreter::removeMapMenu(lua_State* L)
{
QString uniqueName = getVerifiedString(L, __func__, 1, "Menu name");
if (uniqueName.isEmpty()) {
return 0;
}
Host& host = getHostFromLua(L);
if (host.mpMap) {
if (host.mpMap->mpMapper) {
if (host.mpMap->mpMapper->mp2dMap) {
host.mpMap->mpMapper->mp2dMap->mUserMenus.remove(uniqueName);
//remove all entries with this as parent
QStringList removeList;
removeList.append(uniqueName);
bool newElement = true;
while (newElement) {
newElement = false;
QMapIterator<QString, QStringList> it(host.mpMap->mpMapper->mp2dMap->mUserMenus);
while (it.hasNext()) {
it.next();
QStringList menuInfo = it.value();
QString parent = menuInfo[0];
if (removeList.contains(parent)) {
host.mpMap->mpMapper->mp2dMap->mUserMenus.remove(it.key());
if (it.key() != "" && !removeList.contains(it.key())) {
host.mpMap->mpMapper->mp2dMap->mUserMenus.remove(it.key());
removeList.append(it.key());
newElement = true;
}
}
}
}
QMapIterator<QString, QStringList> it2(host.mpMap->mpMapper->mp2dMap->mUserActions);
while (it2.hasNext()) {
it2.next();
QString actParent = it2.value()[1];
if (removeList.contains(actParent)) {
host.mpMap->mpMapper->mp2dMap->mUserActions.remove(it2.key());
}
}
}
}
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getMapMenus
int TLuaInterpreter::getMapMenus(lua_State* L)
{
Host& host = getHostFromLua(L);
if (host.mpMap) {
if (host.mpMap->mpMapper) {
if (host.mpMap->mpMapper->mp2dMap) {
lua_newtable(L);
QMapIterator<QString, QStringList> it(host.mpMap->mpMapper->mp2dMap->mUserMenus);
while (it.hasNext()) {
it.next();
QString parent, display;
QStringList menuInfo = it.value();
parent = menuInfo[0];
display = menuInfo[1];
lua_pushstring(L, it.key().toUtf8().constData());
lua_pushstring(L, parent.toUtf8().constData());
lua_pushstring(L, display.toUtf8().constData());
lua_settable(L, -3);
}
}
return 1;
}
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#addMapEvent
int TLuaInterpreter::addMapEvent(lua_State* L)
{
QString eventName, parent, displayName;
QStringList actionInfo;
QString uniqueName = getVerifiedString(L, __func__, 1, "uniquename");
actionInfo << getVerifiedString(L, __func__, 2, "event name");
if (!lua_isstring(L, 3)) {
actionInfo << QString();
} else {
actionInfo << lua_tostring(L, 3);
}
if (!lua_isstring(L, 4)) {
actionInfo << uniqueName;
} else {
actionInfo << lua_tostring(L, 4);
}
//variable number of arguments
for (int i = 5; i <= lua_gettop(L); i++) {
actionInfo << lua_tostring(L, i);
}
Host& host = getHostFromLua(L);
if (host.mpMap) {
if (host.mpMap->mpMapper) {
if (host.mpMap->mpMapper->mp2dMap) {
host.mpMap->mpMapper->mp2dMap->mUserActions.insert(uniqueName, actionInfo);
}
}
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#removeMapEvent
int TLuaInterpreter::removeMapEvent(lua_State* L)
{
QString displayName = getVerifiedString(L, __func__, 1, "event name");
Host& host = getHostFromLua(L);
if (host.mpMap) {
if (host.mpMap->mpMapper) {
if (host.mpMap->mpMapper->mp2dMap) {
host.mpMap->mpMapper->mp2dMap->mUserActions.remove(displayName);
}
}
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getMapEvents
int TLuaInterpreter::getMapEvents(lua_State* L)
{
Host& host = getHostFromLua(L);
if (host.mpMap) {
if (host.mpMap->mpMapper) {
if (host.mpMap->mpMapper->mp2dMap) {
// create the result table
lua_newtable(L);
QMapIterator<QString, QStringList> it(host.mpMap->mpMapper->mp2dMap->mUserActions);
while (it.hasNext()) {
it.next();
QStringList eventInfo = it.value();
lua_createtable(L, 0, 4);
lua_pushstring(L, eventInfo.at(0).toUtf8().constData());
lua_setfield(L, -2, "event name");
lua_pushstring(L, eventInfo.at(1).toUtf8().constData());
lua_setfield(L, -2, "parent");
lua_pushstring(L, eventInfo.at(2).toUtf8().constData());
lua_setfield(L, -2, "display name");
lua_createtable(L, eventInfo.length() - 3, 0);
for (int i = 3; i < eventInfo.length(); i++) {
lua_pushinteger(L, i - 2); //lua indexes are 1 based!
lua_pushstring(L, eventInfo.at(i).toUtf8().constData());
lua_settable(L, -3);
}
lua_setfield(L, -2, "arguments");
// Add the mapEvent object to the result table
lua_setfield(L, -2, it.key().toUtf8().constData());
}
}
return 1;
}
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#centerview
int TLuaInterpreter::centerview(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB || !host.mpMap->mpMapper) {
return warnArgumentValue(L, __func__, "you haven't opened a map yet");
}
int roomId = getVerifiedInt(L, __func__, 1, "room id");
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomId);
if (pR) {
host.mpMap->mRoomIdHash[host.getName()] = roomId;
host.mpMap->mNewMove = true;
#if defined(INCLUDE_3DMAPPER)
if (host.mpMap->mpM) {
host.mpMap->mpM->update();
}
#endif
if (host.mpMap->mpMapper->mp2dMap) {
host.mpMap->mpMapper->mp2dMap->isCenterViewCall = true;
host.mpMap->mpMapper->mp2dMap->update();
host.mpMap->mpMapper->mp2dMap->isCenterViewCall = false;
host.mpMap->mpMapper->resetAreaComboBoxToPlayerRoomArea();
}
lua_pushboolean(L, true);
return 1;
} else {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid room id.").arg(roomId));
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getPlayerRoom
int TLuaInterpreter::getPlayerRoom(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB || !host.mpMap->mpMapper) {
return warnArgumentValue(L, __func__, "you haven't opened a map yet");
}
auto roomID = host.mpMap->mRoomIdHash.value(host.getName(), -1);
if (roomID == -1) {
return warnArgumentValue(L, __func__, "the player does not have a valid room id set");
}
lua_pushnumber(L, roomID);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#copy
int TLuaInterpreter::copy(lua_State* L)
{
QString windowName;
if (lua_gettop(L) > 0) {
windowName = WINDOW_NAME(L, 1);
}
auto console = CONSOLE(L, windowName);
console->copy();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#cut
int TLuaInterpreter::cut(lua_State* L)
{
Host& host = getHostFromLua(L);
host.mpConsole->cut();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#paste
int TLuaInterpreter::paste(lua_State* L)
{
QString windowName;
if (lua_gettop(L) > 0) {
windowName = WINDOW_NAME(L, 1);
}
auto console = CONSOLE(L, windowName);
console->paste();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#feedTriggers
int TLuaInterpreter::feedTriggers(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!lua_isstring(L, 1)) {
lua_pushfstring(L,
"feedTriggers: bad argument #1 type (imitation game server text as string\n"
"expected, got %s!)",
luaL_typename(L, 1));
return lua_error(L);
}
QByteArray data{lua_tostring(L, 1)};
bool dataIsUtf8Encoded = true;
if (lua_gettop(L) > 1) {
dataIsUtf8Encoded = getVerifiedBool(L, __func__, 2, "Utf8Encoded", true);
}
QByteArray currentEncoding = host.mTelnet.getEncoding();
if (dataIsUtf8Encoded) {
// We can convert the data from a QByteArray to a QString:
if (currentEncoding == "UTF-8") {
// Simple case: the encoding is already what we are using:
std::string dataStdString{data.toStdString()};
host.mpConsole->printOnDisplay(dataStdString);
lua_pushboolean(L, true);
return 1;
}
QString dataQString{data};
// else
// We need to transcode it from UTF-8 into the current Game Server
// encoding - this can fail if it includes any characters (as UTF-8)
// that the game encoding cannot convey:
auto* pDataCodec = QTextCodec::codecForName(currentEncoding);
auto* pDataEncoder = pDataCodec->makeEncoder(QTextCodec::IgnoreHeader);
if (!(currentEncoding.isEmpty() || currentEncoding == "ASCII")) {
if (!pDataCodec->canEncode(dataQString)) {
return warnArgumentValue(L, __func__, QStringLiteral(
"cannot send '%1' as it contains one or more characters that cannot be conveyed in the current game server encoding of '%2'")
.arg(data.constData(), currentEncoding.constData()));
}
std::string encodedText{pDataEncoder->fromUnicode(dataQString).toStdString()};
host.mpConsole->printOnDisplay(encodedText);
lua_pushboolean(L, true);
return 1;
}
// else plain, raw ASCII, we hope!
for (int i = 0, total = dataQString.size(); i < total; ++i) {
if (dataQString.at(i).row() || dataQString.at(i).cell() > 127) {
return warnArgumentValue(L, __func__, QStringLiteral(
"cannot send '%1' as it contains one or more characters that cannot be conveyed in the current game server encoding of 'ASCII'")
.arg(data.constData()));
}
}
// It is safe to use the data directly now as we have already proved it
// to be plain ASCII
std::string dataStdString{dataQString.toStdString()};
host.mpConsole->printOnDisplay(dataStdString);
lua_pushboolean(L, true);
return 1;
}
// else the user is assumed to have coded it themselves into the Game
// Server's current encoding - the backwards "compatible" form:
std::string dataStdString{data.toStdString()};
host.mpConsole->printOnDisplay(dataStdString);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#isPrompt
int TLuaInterpreter::isPrompt(lua_State* L)
{
Host& host = getHostFromLua(L);
int userCursorY = host.mpConsole->getLineNumber();
if (userCursorY < host.mpConsole->buffer.promptBuffer.size() && userCursorY >= 0) {
lua_pushboolean(L, host.mpConsole->buffer.promptBuffer.at(userCursorY));
return 1;
} else {
if (host.mpConsole->mTriggerEngineMode && host.mpConsole->mIsPromptLine) {
lua_pushboolean(L, true);
} else {
lua_pushboolean(L, false);
}
return 1;
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setWindowWrap
int TLuaInterpreter::setWindowWrap(lua_State* L)
{
int s = 1;
QString windowName;
if (lua_gettop(L) > 1) {
windowName = WINDOW_NAME(L, s++);
}
int luaFrom = getVerifiedInt(L, __func__, s, "wrapAt");
auto console = CONSOLE(L, windowName);
console->setWrapAt(luaFrom);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getWindowWrap
int TLuaInterpreter::getWindowWrap(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
auto console = CONSOLE(L, windowName);
lua_pushnumber(L, console->getWrapAt());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setWindowWrapIndent
int TLuaInterpreter::setWindowWrapIndent(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
int luaFrom = getVerifiedInt(L, __func__, 2, "wrapTo");
auto console = CONSOLE(L, windowName);
console->setIndentCount(luaFrom);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getLineCount
int TLuaInterpreter::getLineCount(lua_State* L)
{
QString windowName;
if (lua_gettop(L) > 0) {
windowName = WINDOW_NAME(L, 1);
}
auto console = CONSOLE(L, windowName);
lua_pushnumber(L, console->getLineCount());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getColumnNumber
int TLuaInterpreter::getColumnNumber(lua_State* L)
{
QString windowName;
if (lua_gettop(L) > 0) {
windowName = WINDOW_NAME(L, 1);
}
auto console = CONSOLE(L, windowName);
lua_pushnumber(L, console->getColumnNumber());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getStopWatchTime
int TLuaInterpreter::getStopWatchTime(lua_State* L)
{
if (!(lua_isnumber(L, 1) || lua_isstring(L, 1))) {
lua_pushfstring(L, "getStopWatchTime: bad argument #1 type (stopwatch id as number or name as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
int watchId = 0;
QPair<bool, double> result;
Host& host = getHostFromLua(L);
if (lua_type(L, 1) == LUA_TNUMBER) {
watchId = static_cast<int>(lua_tointeger(L, 1));
result = host.getStopWatchTime(watchId);
if (!result.first) {
return warnArgumentValue(L, __func__, QStringLiteral("stopwatch with id %1 not found").arg(watchId));
}
} else {
QString name{lua_tostring(L, 1)};
// Using an empty string will return the first unnamed stopwatch:
watchId = host.findStopWatchId(name);
if (!watchId) {
if (name.isEmpty()) {
return warnArgumentValue(L, __func__, "no unnamed stopwatches found");
}
return warnArgumentValue(L, __func__, QStringLiteral("stopwatch with name '%1' not found").arg(name));
}
result = host.getStopWatchTime(watchId);
// We have already validated the name to get the watchId - so for things
// to fail now is, unlikely?
if (Q_UNLIKELY(!result.first)) {
return warnArgumentValue(L, __func__, QStringLiteral(
"stopwatch with name '%1' (id: %2) has disappeared - this should not happen, please report it to Mudlet developers")
.arg(name, QString::number(watchId)));
}
}
lua_pushnumber(L, result.second);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#createStopWatch
int TLuaInterpreter::createStopWatch(lua_State* L)
{
QString name;
bool autoStart = true;
int n = lua_gettop(L);
int s = 1;
if (n) {
if (lua_type(L, s) == LUA_TBOOLEAN) {
autoStart = lua_toboolean(L, s);
} else if (lua_type(L, s) == LUA_TSTRING) {
autoStart = false;
name = lua_tostring(L, 1);
} else if (lua_type(L, s) == LUA_TNIL) {
; // fallthrough for compatibility with old-style stopwatches in case createStopWatch(nil) is passed
// note that 'nil' will still count towards the stack's gettop amount
} else {
lua_pushfstring(L, "createStopWatch: bad argument #%d type (name as string or autostart as boolean are optional, got %s!)", s, luaL_typename(L, s));
return lua_error(L);
}
if (n > 1) {
autoStart = getVerifiedBool(L, __func__, ++s, "autostart", true);
}
}
Host& host = getHostFromLua(L);
QPair<int, QString> result = host.createStopWatch(name);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
}
if (autoStart) {
host.startStopWatch(result.first);
}
lua_pushnumber(L, result.first);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#stopStopWatch
int TLuaInterpreter::stopStopWatch(lua_State* L)
{
if (!(lua_isnumber(L, 1) || lua_isstring(L, 1))) {
lua_pushfstring(L, "stopStopWatch: bad argument #1 type (stopwatch id as number or name as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
Host& host = getHostFromLua(L);
int watchId = 0;
if (lua_type(L, 1) == LUA_TNUMBER) {
watchId = static_cast<int>(lua_tointeger(L, 1));
QPair<bool, QString> result = host.stopStopWatch(watchId);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
}
} else {
QString name{lua_tostring(L, 1)};
QPair<bool, QString> result = host.stopStopWatch(name);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
}
watchId = host.findStopWatchId(name);
// We have already validated the name to get the watchId - so for things
// to fail now is, unlikely?
if (Q_UNLIKELY(!watchId)) {
return warnArgumentValue(L, __func__, QStringLiteral(
"stopwatch with name '%1' (id: %2) has disappeared - this should not happen, please report it to Mudlet developers")
.arg(name, QString::number(watchId)));
}
}
// We know that this watchId is valid so can use the return value directly
// as we want to emulate the past behaviour where stopping the stopWatch
// returned the elapsed time ONCE:
lua_pushnumber(L, host.getStopWatchTime(watchId).second);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#startStopWatch
int TLuaInterpreter::startStopWatch(lua_State* L)
{
if (!(lua_isnumber(L, 1) || lua_isstring(L, 1))) {
lua_pushfstring(L, "startStopWatch: bad argument #1 type (stopwatch id as number or name as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
Host& host = getHostFromLua(L);
if (lua_type(L, 1) == LUA_TNUMBER) {
// Flag (if true) to replicate previous (reset and start again from zero
// if call is repeated without any other actions being carried out on
// stopwatch) behaviour if only a single NUMERIC argument (ID) supplied:
bool autoResetAndRestart = true;
if (lua_gettop(L) > 1) {
autoResetAndRestart = getVerifiedBool(L, __func__, 2, "automatic reset and restart with a numeric stopwatch id", true);
}
QPair<bool, QString> result;
if (autoResetAndRestart) {
result = host.resetAndRestartStopWatch(static_cast<int>(lua_tointeger(L, 1)));
} else {
result = host.startStopWatch(static_cast<int>(lua_tointeger(L, 1)));
}
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
}
lua_pushboolean(L, true);
return 1;
}
QPair<bool, QString> result = host.startStopWatch(lua_tostring(L, 1));
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#resetStopWatch
int TLuaInterpreter::resetStopWatch(lua_State* L)
{
if (!(lua_isnumber(L, 1) || lua_isstring(L, 1))) {
lua_pushfstring(L, "resetStopWatch: bad argument #1 type (stopwatch id as number or name as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
Host& host = getHostFromLua(L);
if (lua_type(L, 1) == LUA_TNUMBER) {
QPair<bool, QString> result = host.resetStopWatch(static_cast<int>(lua_tointeger(L, 1)));
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
}
lua_pushboolean(L, true);
return 1;
}
QPair<bool, QString> result = host.resetStopWatch(lua_tostring(L, 1));
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
}
lua_pushboolean(L, true);
return 1;
}
// No documentation available in wiki - internal helper
// to get ID of stopwatch from either a (numeric) ID argument or a (string) name
// - used to refactor the same code out of four separate stop-watch functions:
std::tuple<bool, int> TLuaInterpreter::getWatchId(lua_State* L, Host& h)
{
if (lua_type(L, 1) == LUA_TNUMBER) {
return std::make_tuple(true, static_cast<int>(lua_tointeger(L, 1)));
}
QString name{lua_tostring(L, 1)};
// Using an empty string will return the first unnamed stopwatch:
int watchId = h.findStopWatchId(name);
if (!watchId) {
lua_pushnil(L);
if (name.isEmpty()) {
lua_pushstring(L, "no unnamed stopwatches found");
} else {
lua_pushfstring(L, "stopwatch with name '%s' not found", name.toUtf8().constData());
}
return std::make_tuple(false, 0);
}
return std::make_tuple(true, watchId);
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#adjustStopWatch
int TLuaInterpreter::adjustStopWatch(lua_State* L)
{
if (!(lua_isnumber(L, 1) || lua_isstring(L, 1))) {
lua_pushfstring(L, "adjustStopWatch: bad argument #1 type (stopwatch id as number or name as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
Host& host = getHostFromLua(L);
auto [success, watchId] = getWatchId(L, host);
if (!success) {
return 2;
}
double adjustment = getVerifiedDouble(L, __func__, 2, "modification in seconds");
bool result = host.adjustStopWatch(watchId, qRound(adjustment * 1000.0));
// This is only likely to fail when a numeric first argument was given:
if (!result) {
return warnArgumentValue(L, __func__, QStringLiteral("stopwatch with id %1 not found").arg(watchId));
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#deleteStopWatch
int TLuaInterpreter::deleteStopWatch(lua_State* L)
{
if (!(lua_isnumber(L, 1) || lua_isstring(L, 1))) {
lua_pushfstring(L, "deleteStopWatch: bad argument #1 type (stopwatch id as number or name as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
Host& host = getHostFromLua(L);
auto [success, watchId] = getWatchId(L, host);
if (!success) {
return 2;
}
bool result = host.destroyStopWatch(watchId);
// This is only likely to fail when a numeric first argument was given:
if (!result) {
return warnArgumentValue(L, __func__, QStringLiteral("stopwatch with id %1 not found").arg(watchId));
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setStopWatchPersistence
int TLuaInterpreter::setStopWatchPersistence(lua_State* L)
{
if (!(lua_isnumber(L, 1) || lua_isstring(L, 1))) {
lua_pushfstring(L, "setStopWatchPersistence: bad argument #1 type (stopwatch id as number or name as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
Host& host = getHostFromLua(L);
auto [success, watchId] = getWatchId(L, host);
if (!success) {
return 2;
}
bool isPersistent = getVerifiedBool(L, __func__, 2, "persistence");
// This is only likely to fail when a numeric first argument was given:
if (!host.makeStopWatchPersistent(watchId, isPersistent)) {
return warnArgumentValue(L, __func__, QStringLiteral("stopwatch with id %1 not found").arg(watchId));
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setStopWatchName
int TLuaInterpreter::setStopWatchName(lua_State* L)
{
if (!(lua_isnumber(L, 1) || lua_isstring(L, 1))) {
lua_pushfstring(L, "setStopWatchName: bad argument #1 type (stopwatch id as number or current name as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
int watchId = 0;
Host& host = getHostFromLua(L);
QString currentName;
if (lua_type(L, 1) == LUA_TNUMBER) {
watchId = static_cast<int>(lua_tointeger(L, 1));
} else {
// Using an empty string will return the first unnamed stopwatch:
currentName = lua_tostring(L, 1);
}
QString newName = getVerifiedString(L, __func__, 2, "stopwatch new name");
QPair<bool, QString> result;
if (currentName.isNull()) {
// Will be null if no value was assigned to it - so use the id form:
result = host.setStopWatchName(watchId, newName);
} else {
result = host.setStopWatchName(currentName, newName);
}
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: none - internal helper for getStopWatchBrokenDownTime()/getStopWatches()
void TLuaInterpreter::generateElapsedTimeTable(lua_State* L, const QStringList& elapsedTimeSplitString, const bool includeDecimalSeconds, const qint64 elapsedTimeMilliSeconds)
{
lua_newtable(L);
lua_pushstring(L, "negative");
// Qt 5.7 seemed to not like comparing a QString with a QLatin1Char so
// use a QLatin1String instead even though it is only a single character:
lua_pushboolean(L, elapsedTimeSplitString.at(0) == QLatin1String("-"));
lua_settable(L, -3);
lua_pushstring(L, "days");
lua_pushinteger(L, elapsedTimeSplitString.at(1).toInt());
lua_settable(L, -3);
lua_pushstring(L, "hours");
lua_pushinteger(L, elapsedTimeSplitString.at(2).toInt());
lua_settable(L, -3);
lua_pushstring(L, "minutes");
lua_pushinteger(L, elapsedTimeSplitString.at(3).toInt());
lua_settable(L, -3);
lua_pushstring(L, "seconds");
lua_pushinteger(L, elapsedTimeSplitString.at(4).toInt());
lua_settable(L, -3);
lua_pushstring(L, "milliSeconds");
lua_pushinteger(L, elapsedTimeSplitString.at(5).toInt());
lua_settable(L, -3);
if (includeDecimalSeconds) {
lua_pushstring(L, "decimalSeconds");
lua_pushnumber(L, elapsedTimeMilliSeconds / 1000.0);
lua_settable(L, -3);
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getStopWatchBrokenDownTime
int TLuaInterpreter::getStopWatchBrokenDownTime(lua_State* L)
{
if (!(lua_isnumber(L, 1) || lua_isstring(L, 1))) {
lua_pushfstring(L, "getStopWatchBrokenDownTime: bad argument #1 type (stopwatch id as number or name as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
Host& host = getHostFromLua(L);
auto [success, watchId] = getWatchId(L, host);
if (!success) {
return 2;
}
QPair<bool, QString> result = host.getBrokenDownStopWatchTime(watchId);
// This is only likely to fail when a numeric first argument was given:
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
}
const QStringList splitTimeString(result.second.split(QLatin1Char(':')));
generateElapsedTimeTable(L, splitTimeString, false);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getStopWatches
int TLuaInterpreter::getStopWatches(lua_State* L)
{
Host& host = getHostFromLua(L);
const QList<int> stopWatchIds = host.getStopWatchIds();
lua_newtable(L);
for (int index = 0, total = stopWatchIds.count(); index < total; ++index) {
int watchId = stopWatchIds.at(index);
lua_pushnumber(L, watchId);
auto pStopWatch = host.getStopWatch(watchId);
lua_newtable(L);
{
lua_pushstring(L, "name");
lua_pushstring(L, pStopWatch->name().toUtf8().constData());
lua_settable(L, -3);
lua_pushstring(L, "isRunning");
lua_pushboolean(L, pStopWatch->running());
lua_settable(L, -3);
lua_pushstring(L, "isPersistent");
lua_pushboolean(L, pStopWatch->persistent());
lua_settable(L, -3);
lua_pushstring(L, "elapsedTime");
const QStringList splitTimeString(pStopWatch->getElapsedDayTimeString().split(QLatin1Char(':')));
generateElapsedTimeTable(L, splitTimeString, true, pStopWatch->getElapsedMilliSeconds());
lua_settable(L, -3);
}
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#selectSection
int TLuaInterpreter::selectSection(lua_State* L)
{
int s = 1;
QString windowName;
if (lua_gettop(L) > 2) {
windowName = WINDOW_NAME(L, s++);
}
int from = getVerifiedInt(L, __func__, s++, "from position");
int to = getVerifiedInt(L, __func__, s, "length");
auto console = CONSOLE(L, windowName);
int ret = console->selectSection(from, to);
lua_pushboolean(L, ret != -1);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getSelection
int TLuaInterpreter::getSelection(lua_State* L)
{
bool valid;
QString text;
int start, length;
QString windowName;
if (lua_gettop(L) > 0) {
windowName = WINDOW_NAME(L, 1);
}
auto console = CONSOLE(L, windowName);
std::tie(valid, text, start, length) = console->getSelection();
if (!valid) {
return warnArgumentValue(L, __func__, text);
}
lua_pushstring(L, text.toUtf8().constData());
lua_pushnumber(L, start);
lua_pushnumber(L, length);
return 3;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#moveCursor
int TLuaInterpreter::moveCursor(lua_State* L)
{
int s = 1;
int n = lua_gettop(L);
QString windowName;
if (n > 2) {
windowName = WINDOW_NAME(L, s++);
}
int luaFrom = getVerifiedInt(L, __func__, s++, "x");
int luaTo = getVerifiedInt(L, __func__, s, "y");
auto console = CONSOLE(L, windowName);
lua_pushboolean(L, console->moveCursor(luaFrom, luaTo));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setConsoleBufferSize
int TLuaInterpreter::setConsoleBufferSize(lua_State* L)
{
int s = 1;
int n = lua_gettop(L);
QString windowName;
if (n > 2) {
windowName = WINDOW_NAME(L, s++);
}
int luaFrom = getVerifiedInt(L, __func__, s++, "linesLimit");
int luaTo = getVerifiedInt(L, __func__, s, "sizeOfBatchDeletion");
auto console = CONSOLE(L, windowName);
console->buffer.setBufferSize(luaFrom, luaTo);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#enableScrollBar
int TLuaInterpreter::enableScrollBar(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
auto console = CONSOLE(L, windowName);
console->setScrollBarVisible(true);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#disableScrollBar
int TLuaInterpreter::disableScrollBar(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
auto console = CONSOLE(L, windowName);
console->setScrollBarVisible(false);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#enableHorizontalScrollBar
int TLuaInterpreter::enableHorizontalScrollBar(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
auto console = CONSOLE(L, windowName);
console->setHorizontalScrollBar(true);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#disableHorizontalScrollBar
int TLuaInterpreter::disableHorizontalScrollBar(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
auto console = CONSOLE(L, windowName);
console->setHorizontalScrollBar(false);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#enableCommandLine
int TLuaInterpreter::enableCommandLine(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
auto console = CONSOLE(L, windowName);
console->setCmdVisible(true);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#disableCommandLine
int TLuaInterpreter::disableCommandLine(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
auto console = CONSOLE(L, windowName);
console->setCmdVisible(false);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#replace
int TLuaInterpreter::replace(lua_State* L)
{
int n = lua_gettop(L);
int s = 1;
QString windowName;
if (n > 1) {
windowName = WINDOW_NAME(L, s++);
}
QString text = getVerifiedString(L, __func__, s, "with");
auto console = CONSOLE(L, windowName);
console->replace(text);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#deleteLine
int TLuaInterpreter::deleteLine(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
auto console = CONSOLE(L, windowName);
console->skipLine();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#saveMap
int TLuaInterpreter::saveMap(lua_State* L)
{
QString location;
int saveVersion = 0;
if (lua_gettop(L) > 0) {
location = getVerifiedString(L, __func__, 1, "save location path and file name", true);
if (lua_gettop(L) > 1) {
saveVersion = getVerifiedInt(L, __func__, 2, "map format version", true);
}
}
Host& host = getHostFromLua(L);
bool error = host.mpConsole->saveMap(location, saveVersion);
lua_pushboolean(L, error);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setExitStub
int TLuaInterpreter::setExitStub(lua_State* L)
{
int roomId = getVerifiedInt(L, __func__, 1, "roomID");
int dir = dirToNumber(L, 2);
if (!dir) {
lua_pushfstring(L, "setExitStub: bad argument #2 type (direction as number or string expected, got %s!)", luaL_typename(L, 2));
return lua_error(L);
}
bool status = getVerifiedBool(L, __func__, 3, "set/unset");
Host& host = getHostFromLua(L);
if (!host.mpMap) {
return 0;
}
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomId);
if (!pR) {
lua_pushstring(L, "setExitStub: RoomId doesn't exist");
return lua_error(L);
}
if (dir > 12 || dir < 1) {
lua_pushstring(L, "setExitStub: direction must be between 1 and 12");
return lua_error(L);
}
pR->setExitStub(dir, status);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#connectExitStub
int TLuaInterpreter::connectExitStub(lua_State* L)
{
int toRoom;
int roomsGiven = 0;
int roomId = getVerifiedInt(L, __func__, 1, "fromID");
int dirType = dirToNumber(L, 2);
if (!dirType) {
lua_pushfstring(L, "connectExitStub: bad argument #2 type (toID as number or direction as number or string expected, got %s!)");
return lua_error(L);
}
if (!lua_isnumber(L, 3) && !lua_isstring(L, 3)) {
roomsGiven = 0;
} else {
roomsGiven = 1;
toRoom = lua_tonumber(L, 2);
dirType = dirToNumber(L, 3);
if (!dirType) {
lua_pushstring(L, "connectExitStub: Invalid direction entered.");
return lua_error(L);
}
}
Host& host = getHostFromLua(L);
if (!host.mpMap) {
return 0;
}
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomId);
if (!pR) {
lua_pushstring(L, "connectExitStub: RoomId doesn't exist");
return lua_error(L);
}
if (!pR->exitStubs.contains(dirType)) {
lua_pushstring(L, "connectExitStub: ExitStub doesn't exist");
return lua_error(L);
}
if (roomsGiven) {
TRoom* pR_to = host.mpMap->mpRoomDB->getRoom(toRoom);
if (!pR_to) {
lua_pushstring(L, "connectExitStub: toRoom doesn't exist");
return lua_error(L);
}
lua_pushboolean(L, host.mpMap->setExit(roomId, toRoom, dirType));
} else {
if (!pR->exitStubs.contains(dirType)) {
lua_pushstring(L, "connectExitStub: ExitStub doesn't exist");
return lua_error(L);
}
host.mpMap->connectExitStub(roomId, dirType);
// Nothing has yet been put onto stack for a LUA return value in this case,
// and it should always be possible to add a stub exit, so provide a true value :
lua_pushboolean(L, true);
}
host.mpMap->mMapGraphNeedsUpdate = true;
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getExitStubs
int TLuaInterpreter::getExitStubs(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int roomId = getVerifiedInt(L, __func__, 1, "room id");
// Previously threw a Lua error on non-existent room!
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomId);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid room id").arg(roomId));
}
QList<int> stubs = pR->exitStubs;
if (stubs.empty()) {
return warnArgumentValue(L, __func__, QStringLiteral("no stubs in this room with id %1").arg(roomId));
}
lua_newtable(L);
for (int i = 0, total = stubs.size(); i < total; ++i) {
lua_pushnumber(L, i);
lua_pushnumber(L, stubs.at(i));
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getExitStubs1
int TLuaInterpreter::getExitStubs1(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int roomId = getVerifiedInt(L, __func__, 1, "room id");
// Previously threw a Lua error on non-existent room!
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomId);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid room id").arg(roomId));
}
QList<int> stubs = pR->exitStubs;
if (stubs.empty()) {
return warnArgumentValue(L, __func__, QStringLiteral("no stubs in this room with id %1").arg(roomId));
}
lua_newtable(L);
for (int i = 0, total = stubs.size(); i < total; ++i) {
lua_pushnumber(L, i + 1);
lua_pushnumber(L, stubs.at(i));
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getModulePath
int TLuaInterpreter::getModulePath(lua_State* L)
{
QString moduleName = getVerifiedString(L, __func__, 1, "module name");
Host& host = getHostFromLua(L);
QMap<QString, QStringList> modules = host.mInstalledModules;
if (modules.contains(moduleName)) {
QString modPath = modules[moduleName][0];
lua_pushstring(L, modPath.toUtf8().constData());
return 1;
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getModulePriority
int TLuaInterpreter::getModulePriority(lua_State* L)
{
QString moduleName = getVerifiedString(L, __func__, 1, "module name");
Host& host = getHostFromLua(L);
if (host.mModulePriorities.contains(moduleName)) {
int priority = host.mModulePriorities[moduleName];
lua_pushnumber(L, priority);
return 1;
} else {
lua_pushstring(L, "getModulePriority: Module doesn't exist");
return lua_error(L);
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setModulePriority
int TLuaInterpreter::setModulePriority(lua_State* L)
{
QString moduleName = getVerifiedString(L, __func__, 1, "module name");
int modulePriority = getVerifiedInt(L, __func__, 2, "module priority");
Host& host = getHostFromLua(L);
if (!host.mInstalledModules.contains(moduleName)) {
return warnArgumentValue(L, __func__, "module doesn't exist");
}
host.mModulePriorities[moduleName] = modulePriority;
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#loadMap
int TLuaInterpreter::loadMap(lua_State* L)
{
Host& host = getHostFromLua(L);
QString location;
if (lua_gettop(L)) {
location = getVerifiedString(L, __func__, 1, "Map pathFile {loads last stored map if omitted}", true);
}
bool isOk = false;
if (!location.isEmpty() && location.endsWith(QStringLiteral(".xml"), Qt::CaseInsensitive)) {
QString errMsg;
isOk = host.mpConsole->importMap(location, &errMsg);
if (!isOk) {
// A false was returned which indicates an error, convert it to a nil
lua_pushnil(L);
// And add the expected error message, is to be structured in a
// compatible manner
if (!errMsg.isEmpty()) {
lua_pushstring(L, errMsg.toUtf8().constData());
return 2;
} else {
return 1;
}
}
} else {
isOk = host.mpConsole->loadMap(location);
}
lua_pushboolean(L, isOk);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#enableTimer
int TLuaInterpreter::enableTimer(lua_State* L)
{
QString text = getVerifiedString(L, __func__, 1, "name");
Host& host = getHostFromLua(L);
bool error = host.getTimerUnit()->enableTimer(text);
lua_pushboolean(L, error);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#disableTimer
int TLuaInterpreter::disableTimer(lua_State* L)
{
QString text = getVerifiedString(L, __func__, 1, "name");
Host& host = getHostFromLua(L);
bool error = host.getTimerUnit()->disableTimer(text);
lua_pushboolean(L, error);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#enableKey
int TLuaInterpreter::enableKey(lua_State* L)
{
QString keyName = getVerifiedString(L, __func__, 1, "key name");
Host& host = getHostFromLua(L);
bool error = host.getKeyUnit()->enableKey(keyName);
lua_pushboolean(L, error);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#disableKey
int TLuaInterpreter::disableKey(lua_State* L)
{
QString keyName = getVerifiedString(L, __func__, 1, "key name");
Host& host = getHostFromLua(L);
bool error = host.getKeyUnit()->disableKey(keyName);
lua_pushboolean(L, error);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#killKey
int TLuaInterpreter::killKey(lua_State* L)
{
QString keyName = getVerifiedString(L, __func__, 1, "key name");
Host& host = getHostFromLua(L);
bool error = host.getKeyUnit()->killKey(keyName);
lua_pushboolean(L, error);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#enableAlias
int TLuaInterpreter::enableAlias(lua_State* L)
{
QString text = getVerifiedString(L, __func__, 1, "name");
Host& host = getHostFromLua(L);
bool error = host.getAliasUnit()->enableAlias(text);
lua_pushboolean(L, error);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#disableAlias
int TLuaInterpreter::disableAlias(lua_State* L)
{
QString text = getVerifiedString(L, __func__, 1, "name");
Host& host = getHostFromLua(L);
bool error = host.getAliasUnit()->disableAlias(text);
lua_pushboolean(L, error);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#killAlias
int TLuaInterpreter::killAlias(lua_State* L)
{
QString text = getVerifiedString(L, __func__, 1, "name");
Host& host = getHostFromLua(L);
lua_pushboolean(L, host.getAliasUnit()->killAlias(text));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#enableTrigger
int TLuaInterpreter::enableTrigger(lua_State* L)
{
QString text = getVerifiedString(L, __func__, 1, "name");
Host& host = getHostFromLua(L);
bool error = host.getTriggerUnit()->enableTrigger(text);
lua_pushboolean(L, error);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#disableTrigger
int TLuaInterpreter::disableTrigger(lua_State* L)
{
QString text = getVerifiedString(L, __func__, 1, "name");
Host& host = getHostFromLua(L);
bool error = host.getTriggerUnit()->disableTrigger(text);
lua_pushboolean(L, error);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#enableScript
int TLuaInterpreter::enableScript(lua_State* L)
{
QString name = getVerifiedString(L, __func__, 1, "script name");
Host& host = getHostFromLua(L);
int cnt = 0;
QMap<int, TScript*> scripts = host.getScriptUnit()->getScriptList();
for (auto script : scripts) {
if (script->getName() == name) {
cnt++;
script->setIsActive(true);
}
}
if (cnt == 0) {
return warnArgumentValue(L, __func__, QStringLiteral("script '%1' not found").arg(name));
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#disableScript
int TLuaInterpreter::disableScript(lua_State* L)
{
QString name = getVerifiedString(L, __func__, 1, "script name");
Host& host = getHostFromLua(L);
int cnt = 0;
QMap<int, TScript*> scripts = host.getScriptUnit()->getScriptList();
for (auto script : scripts) {
if (script->getName() == name) {
cnt++;
script->setIsActive(false);
}
}
if (cnt == 0) {
return warnArgumentValue(L, __func__, QStringLiteral("script '%1' not found").arg(name));
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#killTimer
int TLuaInterpreter::killTimer(lua_State* L)
{
QString text = getVerifiedString(L, __func__, 1, "ID");
Host& host = getHostFromLua(L);
lua_pushboolean(L, host.killTimer(text));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#killTrigger
int TLuaInterpreter::killTrigger(lua_State* L)
{
QString text = getVerifiedString(L, __func__, 1, "name");
Host& host = getHostFromLua(L);
lua_pushboolean(L, host.killTrigger(text));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#remainingTime
int TLuaInterpreter::remainingTime(lua_State* L)
{
if (!lua_isstring(L, 1)) {
lua_pushfstring(L, "remainingTime: bad argument #1 (timer name as string or timer id as number expected, got %s!", luaL_typename(L, 1));
return lua_error(L);
}
Host& host = getHostFromLua(L);
int result = -2;
QString timerName;
qint64 timerId = 0;
if (lua_type(L, 1) == LUA_TNUMBER) {
// Is definitely a number and not a string that can be coerced into a number
timerId = lua_tointeger(L, 1);
result = host.getTimerUnit()->remainingTime(static_cast<int>(timerId));
} else {
timerName = lua_tostring(L, 1);
result = host.getTimerUnit()->remainingTime(timerName);
}
if (result == -1) {
return warnArgumentValue(L, __func__, "timer is inactive or expired");
}
if (result == -2) {
if (timerName.isNull()) {
// timerName was never set so we must have used the number
return warnArgumentValue(L, __func__, QStringLiteral("timer id %1 not found").arg(timerId));
}
return warnArgumentValue(L, __func__, QStringLiteral("timer named '%1' not found").arg(timerName));
}
lua_pushnumber(L, result / 1000.0);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#closeMudlet
int TLuaInterpreter::closeMudlet(lua_State* L)
{
Q_UNUSED(L)
mudlet::self()->forceClose();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#loadWindowLayout
int TLuaInterpreter::loadWindowLayout(lua_State* L)
{
lua_pushboolean(L, mudlet::self()->loadWindowLayout());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#saveWindowLayout
int TLuaInterpreter::saveWindowLayout(lua_State* L)
{
mudlet::self()->mHasSavedLayout = false;
lua_pushboolean(L, mudlet::self()->saveWindowLayout());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#saveProfile
int TLuaInterpreter::saveProfile(lua_State* L)
{
Host& host = getHostFromLua(L);
QString saveToDir;
if (lua_isstring(L, 1)) {
saveToDir = lua_tostring(L, 1);
}
std::tuple<bool, QString, QString> result = host.saveProfile(saveToDir);
if (std::get<0>(result)) {
lua_pushboolean(L, true);
lua_pushstring(L, (std::get<1>(result).toUtf8().constData()));
return 2;
} else {
auto message = QString("Couldn't save '%1' to '%2' because: %3").arg(host.getName(), std::get<1>(result), std::get<2>(result));
return warnArgumentValue(L, __func__, message);
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setFont
int TLuaInterpreter::setFont(lua_State* L)
{
Host& host = getHostFromLua(L);
QString windowName;
int s = 1;
if (lua_gettop(L) > 1) { // Have more than one argument so first must be a console name
windowName = WINDOW_NAME(L, s++);
}
QString font = getVerifiedString(L, __func__, s, "name");
if (!mudlet::self()->getAvailableFonts().contains(font, Qt::CaseInsensitive)) {
return warnArgumentValue(L, __func__, QStringLiteral("font '%1' is not available").arg(font));
}
#if defined(Q_OS_LINUX)
// On Linux ensure that emojis are displayed in colour even if this font
// doesn't support it:
QFont::insertSubstitution(font, QStringLiteral("Noto Color Emoji"));
// TODO issue #4159: a nonexisting font breaks the console
#endif
auto console = CONSOLE(L, windowName);
if (console == host.mpConsole) {
// apply changes to main console and its while-scrolling component too.
auto result = host.setDisplayFont(QFont(font, host.getDisplayFont().pointSize()));
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
}
console->refreshView();
} else {
console->setFont(font);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getFont
int TLuaInterpreter::getFont(lua_State* L)
{
QString windowName = QStringLiteral("main");
QString font;
windowName = WINDOW_NAME(L, 1);
auto console = CONSOLE(L, windowName);
font = console->mUpperPane->fontInfo().family();
lua_pushstring(L, font.toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setFontSize
int TLuaInterpreter::setFontSize(lua_State* L)
{
Host& host = getHostFromLua(L);
QString windowName;
int s = 1;
if (lua_gettop(L) > 1) { // Have more than one argument so first must be a console name
windowName = WINDOW_NAME(L, s++);
}
int size = getVerifiedInt(L, __func__, s, "size");
if (size <= 0) {
// just throw an error, no default needed.
return warnArgumentValue(L, __func__, "size cannot be 0 or negative");
}
auto console = CONSOLE(L, windowName);
if (console == host.mpConsole) {
// get host profile display font and alter it, since that is how it's done in Settings.
host.setDisplayFontSize(size);
} else {
console->setFontSize(size);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getFontSize
int TLuaInterpreter::getFontSize(lua_State* L)
{
Host& host = getHostFromLua(L);
int rval = -1;
QString windowName {WINDOW_NAME(L, 1)};
auto console = CONSOLE(L, windowName);
if (console == host.mpConsole) {
rval = host.getDisplayFont().pointSize();
} else {
rval = console->mUpperPane->mDisplayFont.pointSize();
}
if (rval <= -1) {
lua_pushnil(L);
} else {
lua_pushnumber(L, rval);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#openUserWindow
int TLuaInterpreter::openUserWindow(lua_State* L)
{
int n = lua_gettop(L);
if (lua_type(L, 1) != LUA_TSTRING) {
lua_pushfstring(L, "openUserWindow: bad argument #1 type (name as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
QString name{lua_tostring(L, 1)};
bool loadLayout = true;
if (n > 1) {
loadLayout = getVerifiedBool(L, __func__, 2, "loadLayout", true);
}
bool autoDock = true;
if (n > 2) {
autoDock = getVerifiedBool(L, __func__, 3, "autoDock", true);
}
QString area = QString();
if (n > 3) {
if (lua_type(L, 4) != LUA_TSTRING) {
lua_pushfstring(L, "openUserWindow: bad argument #4 type (area as string expected, got %s!)", luaL_typename(L, 4));
return lua_error(L);
}
area = lua_tostring(L, 4);
}
Host& host = getHostFromLua(L);
//Don't create Userwindow if there is a Label with the same name already. It breaks the UserWindow
if (auto [success, message] = host.openWindow(name, loadLayout, autoDock, area.toLower()); !success) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setUserWindowTitle
int TLuaInterpreter::setUserWindowTitle(lua_State* L)
{
QString name = getVerifiedString(L, __func__, 1, "name");
QString title;
if (lua_gettop(L) > 1) {
title = getVerifiedString(L, __func__, 2, "title", true);
}
Host& host = getHostFromLua(L);
if (auto [success, message] = host.mpConsole->setUserWindowTitle(name, title); !success) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setMapWindowTitle
int TLuaInterpreter::setMapWindowTitle(lua_State* L)
{
QString title;
if (lua_gettop(L)) {
title = getVerifiedString(L, __func__, 1, "title", true);
}
Host& host = getHostFromLua(L);
if (auto [success, message] = host.setMapperTitle(title); !success) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
int TLuaInterpreter::getMudletInfo(lua_State* L)
{
Host& host = getHostFromLua(L);
QStringList knownEncodings{"ASCII"};
// cTelnet::getEncoding() returns a QByteArray NOT a QString:
QString currentEncoding{host.mTelnet.getEncoding()};
{
auto adjustEncoding = [](auto encodingName) {
auto originalEncoding = encodingName;
if (encodingName.startsWith(QStringLiteral("M_"))) {
encodingName.remove(0, 2);
}
return (originalEncoding == encodingName) ? originalEncoding : QStringLiteral("%1 (%2)").arg(encodingName).arg(originalEncoding);
};
// cTelnet::getEncodingsList() returns a QByteArrayList NOT a QStringList/QList<QString>:
for (const auto& encoding : host.mTelnet.getEncodingsList()) {
knownEncodings.append(adjustEncoding(QString(encoding)));
}
QCollator sorter;
sorter.setNumericMode(true);
sorter.setCaseSensitivity(Qt::CaseInsensitive);
std::sort(knownEncodings.begin(), knownEncodings.end(), sorter);
if (currentEncoding.isEmpty()) {
currentEncoding = QStringLiteral("\"ASCII\"");
} else {
currentEncoding = adjustEncoding(currentEncoding);
}
}
host.postMessage(QStringLiteral("[ INFO ] - Current encoding: %1").arg(currentEncoding));
host.postMessage(QStringLiteral("[ INFO ] - Available encodings:"));
host.postMessage(QStringLiteral(" %1").arg(knownEncodings.join(QStringLiteral(", "))));
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#createMiniConsole
int TLuaInterpreter::createMiniConsole(lua_State* L)
{
QString name = "";
int counter = 3;
//make the windowname optional by using counter. If windowname "main" add to main console
QString windowName = getVerifiedString(L, __func__, 1, "miniconsole name");
if (isMain(windowName)) {
// createMiniConsole only accepts the empty name as the main window
windowName.clear();
}
if (!lua_isnumber(L, 2)) {
name = getVerifiedString(L, __func__, 2, "miniconsole name");
} else {
name = windowName;
windowName.clear();
counter = 2;
}
int x = getVerifiedInt(L, __func__, counter, "miniconsole x-coordinate");
counter++;
int y = getVerifiedInt(L, __func__, counter, "miniconsole y-coordinate");
counter++;
int width = getVerifiedInt(L, __func__, counter, "miniconsole width");
counter++;
int height = getVerifiedInt(L, __func__, counter, "miniconsole height");
Host& host = getHostFromLua(L);
if (auto [success, message] = host.createMiniConsole(windowName, name, x, y, width, height); !success) {
return warnArgumentValue(L, __func__, message, true);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#createLabel
int TLuaInterpreter::createLabel(lua_State* L)
{
QString labelName;
QString windowName = QLatin1String("main");
if (lua_type(L, 1) != LUA_TSTRING) {
lua_pushfstring(L, "createLabel: bad argument #1 type (label or parent window name as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
if ((lua_type(L, 1) == LUA_TSTRING) && (lua_type(L, 2) == LUA_TSTRING)) {
windowName = lua_tostring(L, 1);
labelName = lua_tostring(L, 2);
createLabelUserWindow(L, windowName, labelName);
} else if ((lua_type(L, 1) == LUA_TSTRING) && (lua_type(L, 2) == LUA_TNUMBER)) {
labelName = lua_tostring(L, 1);
createLabelMainWindow(L, labelName);
} else {
lua_pushfstring(L, "createLabel: bad argument #2 type (label name as string or label x-coordinate as number expected, got %s!)", luaL_typename(L, 2));
return lua_error(L);
}
return 1;
}
// Internal Function createLabel in an UserWindow
int TLuaInterpreter::createLabelUserWindow(lua_State* L, const QString& windowName, const QString& labelName)
{
int n = lua_gettop(L);
int x = getVerifiedInt(L, "createLabel", 3, "label x-coordinate");
int y = getVerifiedInt(L, "createLabel", 4, "label y-coordinate");
int width = getVerifiedInt(L, "createLabel", 5, "label width");
int height = getVerifiedInt(L, "createLabel", 6, "label height");
bool fillBackground = false;
if ((!lua_isnumber(L, 7)) && (!lua_isboolean(L, 7))) {
lua_pushfstring(L, "createLabel: bad argument #7 type (label fillBackground as boolean/number (0/1) expected, got %s!)", luaL_typename(L, 7));
return lua_error(L);
}
if (lua_isboolean(L, 7)) {
fillBackground = lua_toboolean(L, 7);
} else {
fillBackground = (lua_tointeger(L, 7) != 0);
}
bool clickthrough = false;
if (n >= 8) {
if ((!lua_isnumber(L, 8)) && (!lua_isboolean(L, 8))) {
lua_pushfstring(L, "createLabel: bad argument #8 type (label clickthrough as boolean/number (0/1) expected, got %s!)", luaL_typename(L, 8));
return lua_error(L);
}
if (lua_isboolean(L, 8)) {
clickthrough = lua_toboolean(L, 8);
} else {
clickthrough = (lua_tointeger(L, 8) != 0);
}
}
Host& host = getHostFromLua(L);
if (auto [success, message] = host.createLabel(windowName, labelName, x, y, width, height, fillBackground, clickthrough); !success) {
// We should, perhaps be returning a nil here but the published API
// says the function returns true or false and we cannot change that now
return warnArgumentValue(L, "createLabel", message, true);
}
lua_pushboolean(L, true);
return 1;
}
// Internal Function create Label in MainWindow
int TLuaInterpreter::createLabelMainWindow(lua_State* L, const QString& labelName)
{
QString windowName = QLatin1String("main");
int n = lua_gettop(L);
int x = getVerifiedInt(L, "createLabel", 2, "label x-coordinate");
int y = getVerifiedInt(L, "createLabel", 3, "label y-coordinate");
int width = getVerifiedInt(L, "createLabel", 4, "label width");
int height = getVerifiedInt(L, "createLabel", 5, "label height");
bool fillBackground = false;
if ((!lua_isnumber(L, 6)) && (!lua_isboolean(L, 6))) {
lua_pushfstring(L, "createLabel: bad argument #6 type (label fillBackground as boolean/number (0/1) expected, got %s!)", luaL_typename(L, 6));
return lua_error(L);
}
if (lua_isboolean(L, 6)) {
fillBackground = lua_toboolean(L, 6);
} else {
fillBackground = (lua_tointeger(L, 6) != 0);
}
bool clickthrough = false;
if (n >= 7) {
if ((!lua_isnumber(L, 7)) && (!lua_isboolean(L, 7))) {
lua_pushfstring(L, "createLabel: bad argument #7 type (label clickthrough as boolean/number (0/1) expected, got %s!)", luaL_typename(L, 7));
return lua_error(L);
}
if (lua_isboolean(L, 7)) {
clickthrough = lua_toboolean(L, 7);
} else {
clickthrough = (lua_tointeger(L, 7) != 0);
}
}
Host& host = getHostFromLua(L);
if (auto [success, message] = host.createLabel(windowName, labelName, x, y, width, height, fillBackground, clickthrough); !success) {
// We should, perhaps be returning a nil here but the published API
// says the function returns true or false and we cannot change that now
return warnArgumentValue(L, "createLabel", message, true);
}
lua_pushboolean(L, true);
return 1;
}
int TLuaInterpreter::deleteLabel(lua_State* L)
{
QString labelName = getVerifiedString(L, __func__, 1, "label name");
Host& host = getHostFromLua(L);
if (auto [success, message] = host.mpConsole->deleteLabel(labelName); !success) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
int TLuaInterpreter::setLabelToolTip(lua_State* L)
{
QString labelName = getVerifiedString(L, __func__, 1, "label name");
QString labelToolTip = getVerifiedString(L, __func__, 2, "text");
double duration = 0;
if (lua_gettop(L) > 2) {
duration = getVerifiedDouble(L, __func__, 3, "duration");
}
Host& host = getHostFromLua(L);
if (auto [success, message] = host.mpConsole->setLabelToolTip(labelName, labelToolTip, duration); !success) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
int TLuaInterpreter::setLabelCursor(lua_State* L)
{
QString labelName = getVerifiedString(L, __func__, 1, "label name");
int labelCursor = getVerifiedInt(L, __func__, 2, "cursortype");
Host& host = getHostFromLua(L);
if (auto [success, message] = host.mpConsole->setLabelCursor(labelName, labelCursor); !success) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
int TLuaInterpreter::setLabelCustomCursor(lua_State* L)
{
int n = lua_gettop(L);
int hotX = -1, hotY = -1;
QString labelName = getVerifiedString(L, __func__, 1, "label name");
QString pixmapLocation = getVerifiedString(L, __func__, 2, "custom cursor location");
if (n > 2) {
hotX = getVerifiedInt(L, __func__, 3, "hot spot x-coordinate");
hotY = getVerifiedInt(L, __func__, 4, "hot spot y-coordinate");
}
Host& host = getHostFromLua(L);
if (auto [success, message] = host.mpConsole->setLabelCustomCursor(labelName, pixmapLocation, hotX, hotY); !success) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#createMapper
int TLuaInterpreter::createMapper(lua_State* L)
{
int n = lua_gettop(L);
QString windowName = "";
int counter = 1;
if (n > 4) {
if (lua_type(L, 1) != LUA_TSTRING) {
lua_pushfstring(L, "createMapper: bad argument #1 type (parent window name as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
windowName = lua_tostring(L, 1);
counter++;
if (isMain(windowName)) {
// createMapper only accepts the empty name as the main window
windowName.clear();
}
}
int x = getVerifiedInt(L, __func__, counter, "mapper x-coordinate");
counter++;
int y = getVerifiedInt(L, __func__, counter, "mapper y-coordinate");
counter++;
int width = getVerifiedInt(L, __func__, counter, "mapper width");
counter++;
int height = getVerifiedInt(L, __func__, counter, "mapper height");
Host& host = getHostFromLua(L);
if (auto [success, message] = host.mpConsole->createMapper(windowName, x, y, width, height); !success) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#createCommandLine
int TLuaInterpreter::createCommandLine(lua_State* L)
{
QString windowName = QLatin1String("main");
int n = lua_gettop(L);
int counter = 1;
if (n > 5) {
if (lua_type(L, 1) != LUA_TSTRING) {
lua_pushfstring(L, "createCommandLine: bad argument #1 type (parent window name as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
windowName = lua_tostring(L, 1);
counter++;
if (isMain(windowName)) {
// createCommandLine only accepts the empty name as the main window
windowName.clear();
}
}
if (lua_type(L, counter) != LUA_TSTRING) {
lua_pushfstring(L, "createCommandLine: bad argument #%d type (commandLine name as string expected, got %s!)", counter, luaL_typename(L, counter));
return lua_error(L);
}
QString commandLineName{lua_tostring(L, counter)};
counter++;
int x = getVerifiedInt(L, __func__, counter, "commandline x-coordinate");
counter++;
int y = getVerifiedInt(L, __func__, counter, "commandline y-coordinate");
counter++;
int width = getVerifiedInt(L, __func__, counter, "commandline width");
counter++;
int height = getVerifiedInt(L, __func__, counter, "commandline height");
counter++;
Host& host = getHostFromLua(L);
if (auto [success, message] = host.mpConsole->createCommandLine(windowName, commandLineName, x, y, width, height); !success) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#createBuffer
int TLuaInterpreter::createBuffer(lua_State* L)
{
QString text = getVerifiedString(L, __func__, 1, "name");
Host& host = getHostFromLua(L);
host.createBuffer(text);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#clearUserWindow
int TLuaInterpreter::clearUserWindow(lua_State* L)
{
if (!lua_isstring(L, 1)) {
Host& host = getHostFromLua(L);
host.mpConsole->mUpperPane->resetHScrollbar();
host.mpConsole->buffer.clear();
host.mpConsole->mUpperPane->forceUpdate();
return 0;
}
QString text = lua_tostring(L, 1);
Host& host = getHostFromLua(L);
host.clearWindow(text);
return 0;
}
// Documentation: ? - public function but should stay undocumented -- compare https://github.com/Mudlet/Mudlet/issues/1149
int TLuaInterpreter::closeUserWindow(lua_State* L)
{
QString text = getVerifiedString(L, __func__, 1, "name");
Host& host = getHostFromLua(L);
host.closeWindow(text);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#hideWindow
int TLuaInterpreter::hideWindow(lua_State* L)
{
QString text = getVerifiedString(L, __func__, 1, "name");
Host& host = getHostFromLua(L);
host.hideWindow(text);
return 0;
}
// No documentation available in wiki - internal function
void TLuaInterpreter::setBorderSize(lua_State* L, int size, int position, bool resizeMudlet)
{
Host& host = getHostFromLua(L);
switch (position) {
case Qt::TopSection: host.mBorderTopHeight = size; break;
case Qt::RightSection: host.mBorderRightWidth = size; break;
case Qt::BottomSection: host.mBorderBottomHeight = size; break;
case Qt::LeftSection: host.mBorderLeftWidth = size; break;
}
if (resizeMudlet) {
int x, y;
x = host.mpConsole->width();
y = host.mpConsole->height();
QSize s = QSize(x, y);
QResizeEvent event(s, s);
QApplication::sendEvent(host.mpConsole, &event);
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setBorderSizes
int TLuaInterpreter::setBorderSizes(lua_State* L)
{
int numberOfArguments = lua_gettop(L);
int sizeTop = 0;
int sizeRight = 0;
int sizeBottom = 0;
int sizeLeft = 0;
switch (numberOfArguments) {
case 0: break;
case 1: {
sizeTop = getVerifiedInt(L, __func__, 1, "new size");
sizeRight = sizeTop;
sizeBottom = sizeTop;
sizeLeft = sizeTop;
break;
}
case 2: {
sizeTop = getVerifiedInt(L, __func__, 1, "new height");
sizeRight = getVerifiedInt(L, __func__, 2, "new width");
sizeBottom = sizeTop;
sizeLeft = sizeRight;
break;
}
case 3: {
sizeTop = getVerifiedInt(L, __func__, 1, "new top size");
sizeRight = getVerifiedInt(L, __func__, 2, "new width");
sizeBottom = getVerifiedInt(L, __func__, 3, "new bottom size");
sizeLeft = sizeRight;
break;
}
default: {
sizeTop = getVerifiedInt(L, __func__, 1, "new top size");
sizeRight = getVerifiedInt(L, __func__, 2, "new right size");
sizeBottom = getVerifiedInt(L, __func__, 3, "new bottom size");
sizeBottom = getVerifiedInt(L, __func__, 4, "new left size");
break;
}
}
setBorderSize(L, sizeTop, Qt::TopSection, false);
setBorderSize(L, sizeRight, Qt::RightSection, false);
setBorderSize(L, sizeBottom, Qt::BottomSection, false);
setBorderSize(L, sizeLeft, Qt::LeftSection, true); // only now send update event to resize Mudlet window
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setBorderTop
int TLuaInterpreter::setBorderTop(lua_State* L)
{
int size = getVerifiedInt(L, __func__, 1, "new size");
setBorderSize(L, size, Qt::TopSection);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setBorderRight
int TLuaInterpreter::setBorderRight(lua_State* L)
{
int size = getVerifiedInt(L, __func__, 1, "new size");
setBorderSize(L, size, Qt::RightSection);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setBorderBottom
int TLuaInterpreter::setBorderBottom(lua_State* L)
{
int size = getVerifiedInt(L, __func__, 1, "new size");
setBorderSize(L, size, Qt::BottomSection);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setBorderLeft
int TLuaInterpreter::setBorderLeft(lua_State* L)
{
int size = getVerifiedInt(L, __func__, 1, "new size");
setBorderSize(L, size, Qt::LeftSection);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getBorderTop
int TLuaInterpreter::getBorderTop(lua_State* L)
{
Host& host = getHostFromLua(L);
lua_pushnumber(L, host.mBorderTopHeight);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getBorderLeft
int TLuaInterpreter::getBorderLeft(lua_State* L)
{
Host& host = getHostFromLua(L);
lua_pushnumber(L, host.mBorderLeftWidth);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getBorderBottom
int TLuaInterpreter::getBorderBottom(lua_State* L)
{
Host& host = getHostFromLua(L);
lua_pushnumber(L, host.mBorderBottomHeight);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getBorderRight
int TLuaInterpreter::getBorderRight(lua_State* L)
{
Host& host = getHostFromLua(L);
lua_pushnumber(L, host.mBorderRightWidth);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getBorderSizes
int TLuaInterpreter::getBorderSizes(lua_State* L)
{
Host& host = getHostFromLua(L);
lua_createtable(L, 0, 4);
lua_pushinteger(L, host.mBorderTopHeight);
lua_setfield(L, -2, "top");
lua_pushinteger(L, host.mBorderRightWidth);
lua_setfield(L, -2, "right");
lua_pushinteger(L, host.mBorderBottomHeight);
lua_setfield(L, -2, "bottom");
lua_pushinteger(L, host.mBorderLeftWidth);
lua_setfield(L, -2, "left");
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#resizeWindow
int TLuaInterpreter::resizeWindow(lua_State* L)
{
QString text = getVerifiedString(L, __func__, 1, "windowName");
double x1 = getVerifiedDouble(L, __func__, 2, "width");
double y1 = getVerifiedDouble(L, __func__, 3, "height");
Host& host = getHostFromLua(L);
host.resizeWindow(text, static_cast<int>(x1), static_cast<int>(y1));
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#moveWindow
int TLuaInterpreter::moveWindow(lua_State* L)
{
QString text = getVerifiedString(L, __func__, 1, "name");
double x1 = getVerifiedDouble(L, __func__, 2, "x");
double y1 = getVerifiedDouble(L, __func__, 3, "y");
Host& host = getHostFromLua(L);
host.moveWindow(text, static_cast<int>(x1), static_cast<int>(y1));
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setWindow
int TLuaInterpreter::setWindow(lua_State* L)
{
int n = lua_gettop(L);
int x = 0, y = 0;
bool show = true;
QString windowname {WINDOW_NAME(L, 1)};
if (lua_type(L, 2) != LUA_TSTRING) {
lua_pushfstring(L, "setWindow: bad argument #2 type (element name as string expected, got %s!)", luaL_typename(L, 2));
return lua_error(L);
}
QString name{lua_tostring(L, 2)};
if (n > 2) {
x = getVerifiedInt(L, __func__, 3, "x-coordinate");
y = getVerifiedInt(L, __func__, 4, "y-coordinate");
show = getVerifiedBool(L, __func__, 5, "show element");
}
Host& host = getHostFromLua(L);
if (auto [success, message] = host.setWindow(windowname, name, x, y, show); !success) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
int TLuaInterpreter::openMapWidget(lua_State* L)
{
int n = lua_gettop(L);
QString area = QString();
int x = -1, y = -1, width = -1, height = -1;
if (n == 1) {
if (lua_type(L, 1) != LUA_TSTRING) {
lua_pushfstring(L, "openMapWidget: bad argument #1 type (area as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
area = lua_tostring(L, 1);
}
if (n > 1) {
area = QStringLiteral("f");
x = getVerifiedInt(L, __func__, 1, "x-coordinate");
y = getVerifiedInt(L, __func__, 2, "y-coordinate");
}
if (n > 2) {
width = getVerifiedInt(L, __func__, 3, "width");
height = getVerifiedInt(L, __func__, 4, "height");
}
Host& host = getHostFromLua(L);
if (auto [success, message] = host.openMapWidget(area.toLower(), x, y, width, height); !success) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
int TLuaInterpreter::closeMapWidget(lua_State* L)
{
Host& host = getHostFromLua(L);
if (auto [success, message] = host.closeMapWidget(); !success) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setMainWindowSize
int TLuaInterpreter::setMainWindowSize(lua_State* L)
{
int x1 = getVerifiedInt(L, __func__, 1, "mainWidth");
int y1 = getVerifiedInt(L, __func__, 2, "mainHeight");
mudlet::self()->resize(x1, y1);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setBackgroundColor
int TLuaInterpreter::setBackgroundColor(lua_State* L)
{
Host& host = getHostFromLua(L);
QString windowName;
int r, alpha;
int s = 1;
auto validRange = [](int number) {
return number >= 0 && number <= 255;
};
if (lua_isstring(L, s)) {
windowName = WINDOW_NAME(L, s++);
r = getVerifiedInt(L, __func__, s, "red value 0-255");
if (!validRange(r)) {
return warnArgumentValue(L, __func__, QStringLiteral("red value %1 needs to be between 0-255").arg(r));
}
} else if (lua_isnumber(L, s)) {
r = static_cast<int>(lua_tonumber(L, s));
if (!validRange(r)) {
return warnArgumentValue(L, __func__, QStringLiteral("red value %1 needs to be between 0-255").arg(r));
}
} else {
lua_pushfstring(L, "setBackgroundColor: bad argument #%d type (window name as string, or red value 0-255 as number expected, got %s!)", s, luaL_typename(L, s));
return lua_error(L);
}
int g = getVerifiedInt(L, __func__, ++s, "green value 0-255");
if (!validRange(g)) {
return warnArgumentValue(L, __func__, QStringLiteral("green value %1 needs to be between 0-255").arg(g));
}
int b = getVerifiedInt(L, __func__, ++s, "blue value 0-255");
if (!validRange(b)) {
return warnArgumentValue(L, __func__, QStringLiteral("blue value %1 needs to be between 0-255").arg(b));
}
// if we get nothing for the alpha value, assume it is 255. If we get a non-number value, complain.
alpha = 255;
if (lua_gettop(L) > s) {
alpha = getVerifiedInt(L, __func__, ++s, "alpha value 0-255", true);
if (!validRange(alpha)) {
return warnArgumentValue(L, __func__, QStringLiteral("alpha value %1 needs to be between 0-255").arg(alpha));
}
}
if (isMain(windowName)) {
host.mBgColor.setRgb(r, g, b, alpha);
host.mpConsole->setConsoleBgColor(r, g, b, alpha);
} else if (!host.setBackgroundColor(windowName, r, g, b, alpha)) {
return warnArgumentValue(L, __func__, QStringLiteral("window/label '%1' not found").arg(windowName));
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#calcFontSize
int TLuaInterpreter::calcFontSize(lua_State* L)
{
Host& host = getHostFromLua(L);
QString windowName = QStringLiteral("main");
QSize size;
// font name and size are passed in as arguments
if (lua_gettop(L) == 2) {
auto font = QFont(getVerifiedString(L, __func__, 2, "font name"),
getVerifiedInt(L, __func__, 1, "font size"), QFont::Normal);
auto fontMetrics = QFontMetrics(font);
size = QSize(fontMetrics.averageCharWidth(), fontMetrics.height());
lua_pushnumber(L, size.width());
lua_pushnumber(L, size.height());
return 2;
}
// otherwise either window name or font size is passed in
if (lua_gettop(L) == 1 && lua_isnumber(L, 1)) {
auto fontSize = lua_tonumber(L, 1);
auto font = QFont(QStringLiteral("Bitstream Vera Sans Mono"), fontSize, QFont::Normal);
auto fontMetrics = QFontMetrics(font);
size = QSize(fontMetrics.averageCharWidth(), fontMetrics.height());
} else {
windowName = WINDOW_NAME(L, 1);
size = host.calcFontSize(windowName);
}
if (size.width() <= -1) {
lua_pushnil(L);
return 1;
}
lua_pushnumber(L, size.width());
lua_pushnumber(L, size.height());
return 2;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#startLogging
int TLuaInterpreter::startLogging(lua_State* L)
{
Host& host = getHostFromLua(L);
bool logOn = getVerifiedBool(L, __func__, 1, "turn logging on/off");
QString savedLogFileName;
if (host.mpConsole->mLogToLogFile) {
savedLogFileName = host.mpConsole->mLogFileName;
// Don't assume we will be able to find the file name once recording has
// stopped.
}
if (host.mpConsole->mLogToLogFile != logOn) {
host.mpConsole->toggleLogging(false);
// Changes state of host.mpConsole->mLogToLogFile, but that can't be
// really be called a side-effect!
lua_pushboolean(L, true);
if (host.mpConsole->mLogToLogFile) {
host.mpConsole->logButton->setChecked(true);
// Sets the button as checked but clicked() & pressed() signals are NOT generated
lua_pushfstring(L, "Main console output has started to be logged to file: %s.", host.mpConsole->mLogFileName.toUtf8().constData());
lua_pushstring(L, host.mpConsole->mLogFileName.toUtf8().constData());
lua_pushnumber(L, 1);
} else {
host.mpConsole->logButton->setChecked(false);
lua_pushfstring(L, "Main console output has stopped being logged to file: %s.", savedLogFileName.toUtf8().constData());
lua_pushstring(L, host.mpConsole->mLogFileName.toUtf8().constData());
lua_pushnumber(L, 0);
}
} else {
lua_pushnil(L);
if (host.mpConsole->mLogToLogFile) {
lua_pushfstring(L, "Main console output is already being logged to file: %s.", host.mpConsole->mLogFileName.toUtf8().constData());
lua_pushstring(L, host.mpConsole->mLogFileName.toUtf8().constData());
lua_pushnumber(L, -1);
} else {
lua_pushstring(L, "Main console output was already not being logged to a file.");
lua_pushnil(L);
lua_pushnumber(L, -2);
}
}
return 4;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setBackgroundImage
int TLuaInterpreter::setBackgroundImage(lua_State* L)
{
QString windowName = QStringLiteral("main");
QString imgPath;
int mode = 1;
int counter = 1;
int n = lua_gettop(L);
if (n > 1 && lua_type(L, 2) == LUA_TSTRING) {
windowName = getVerifiedString(L, __func__, 1, "console or label name");
counter++;
}
imgPath = getVerifiedString(L, __func__, counter, "image path");
counter++;
if (n > 2 || (counter == 2 && n > 1)) {
mode = getVerifiedInt(L, __func__, counter, "mode");
}
if (mode < 1 || mode > 4) {
return warnArgumentValue(L, __func__, QStringLiteral(
"%1 is not a valid mode! Valid modes are 1 'border', 2 'center', 3 'tile', 4 'style'").arg(mode));
}
Host* host = &getHostFromLua(L);
if (!host->setBackgroundImage(windowName, imgPath, mode)) {
return warnArgumentValue(L, __func__, QStringLiteral("console or label '%1' not found").arg(windowName));
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#resetBackgroundImage
int TLuaInterpreter::resetBackgroundImage(lua_State* L)
{
QString windowName = QStringLiteral("main");
int n = lua_gettop(L);
if (n > 0) {
windowName = getVerifiedString(L, __func__, 1, "console name");
}
Host* host = &getHostFromLua(L);
if (!host->resetBackgroundImage(windowName)) {
return warnArgumentValue(L, __func__, QStringLiteral("console '%1' not found").arg(windowName));
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getImageSize
int TLuaInterpreter::getImageSize(lua_State* L)
{
QString imageLocation = getVerifiedString(L, __func__, 1, "image location");
if (imageLocation.isEmpty()) {
return warnArgumentValue(L, __func__, "image location cannot be an empty string");
}
auto size = mudlet::self()->getImageSize(imageLocation);
if (!size) {
return warnArgumentValue(L, __func__, QStringLiteral("couldn't retrieve image size, is the location '%1' correct?").arg(imageLocation));
}
lua_pushnumber(L, size->width());
lua_pushnumber(L, size->height());
return 2;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setCmdLineAction
int TLuaInterpreter::setCmdLineAction(lua_State* L)
{
Host& host = getHostFromLua(L);
QString name = getVerifiedString(L, __func__, 1, "command line name");
if (name.isEmpty()) {
return warnArgumentValue(L, __func__, "command line name cannot be an empty string");
}
lua_remove(L, 1);
if (!lua_isfunction(L, 1)) {
lua_pushfstring(L, "setCmdLineAction: bad argument #2 type (function expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
int func = luaL_ref(L, LUA_REGISTRYINDEX);
if (!host.setCmdLineAction(name, func)) {
return warnArgumentValue(L, __func__, QStringLiteral("command line name '%1' not found").arg(name));
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#resetCmdLineAction
int TLuaInterpreter::resetCmdLineAction(lua_State* L){
Host& host = getHostFromLua(L);
QString name = getVerifiedString(L, __func__, 1, "command line name");
if (name.isEmpty()) {
return warnArgumentValue(L, __func__, "command line name cannot be an empty string");
}
bool lua_result = false;
lua_result = host.resetCmdLineAction(name);
if (lua_result) {
lua_pushboolean(L, true);
return 1;
} else {
return warnArgumentValue(L, __func__, QStringLiteral("command line name '%1' not found").arg(name));
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setCmdLineStyleSheet
int TLuaInterpreter::setCmdLineStyleSheet(lua_State* L)
{
int n = lua_gettop(L);
QString name = "main";
if (n > 1) {
name = getVerifiedString(L, __func__, 1, "command line name", true);
}
QString styleSheet = getVerifiedString(L, __func__, n, "StyleSheet");
Host& host = getHostFromLua(L);
if (auto [success, message] = host.mpConsole->setCmdLineStyleSheet(name, styleSheet); !success) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
// No documentation available in wiki - internal function
int TLuaInterpreter::setLabelCallback(lua_State* L, const QString& funcName)
{
Host& host = getHostFromLua(L);
QString labelName = getVerifiedString(L, funcName.toUtf8().constData(), 1, "label name");
if (labelName.isEmpty()) {
return warnArgumentValue(L, __func__, "label name cannot be an empty string");
}
lua_remove(L, 1);
if (!lua_isfunction(L, 1)) {
lua_pushfstring(L, "%s: bad argument #2 type (function expected, got %s!)", funcName.toUtf8().constData(), luaL_typename(L, 1));
return lua_error(L);
}
int func = luaL_ref(L, LUA_REGISTRYINDEX);
bool lua_result = false;
if (funcName == QStringLiteral("setLabelClickCallback"))
lua_result = host.setLabelClickCallback(labelName, func);
else if (funcName == QStringLiteral("setLabelDoubleClickCallback"))
lua_result = host.setLabelDoubleClickCallback(labelName, func);
else if (funcName == QStringLiteral("setLabelReleaseCallback"))
lua_result = host.setLabelReleaseCallback(labelName, func);
else if (funcName == QStringLiteral("setLabelMoveCallback"))
lua_result = host.setLabelMoveCallback(labelName, func);
else if (funcName == QStringLiteral("setLabelWheelCallback"))
lua_result = host.setLabelWheelCallback(labelName, func);
else if (funcName == QStringLiteral("setLabelOnEnter"))
lua_result = host.setLabelOnEnter(labelName, func);
else if (funcName == QStringLiteral("setLabelOnLeave"))
lua_result = host.setLabelOnLeave(labelName, func);
else {
return warnArgumentValue(L, __func__, QStringLiteral("'%1' is not a known function name - bug in Mudlet, please report it").arg(funcName));
}
if (lua_result) {
return warnArgumentValue(L, __func__, QStringLiteral("label name '%1' not found").arg(labelName));
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setLabelClickCallback
int TLuaInterpreter::setLabelClickCallback(lua_State* L)
{
return setLabelCallback(L, QStringLiteral("setLabelClickCallback"));
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setLabelDoubleClickCallback
int TLuaInterpreter::setLabelDoubleClickCallback(lua_State* L)
{
return setLabelCallback(L, QStringLiteral("setLabelDoubleClickCallback"));
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setLabelReleaseCallback
int TLuaInterpreter::setLabelReleaseCallback(lua_State* L)
{
return setLabelCallback(L, QStringLiteral("setLabelReleaseCallback"));
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setLabelMoveCallback
int TLuaInterpreter::setLabelMoveCallback(lua_State* L)
{
return setLabelCallback(L, QStringLiteral("setLabelMoveCallback"));
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setLabelWheelCallback
int TLuaInterpreter::setLabelWheelCallback(lua_State* L)
{
return setLabelCallback(L, QStringLiteral("setLabelWheelCallback"));
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setLabelOnEnter
int TLuaInterpreter::setLabelOnEnter(lua_State* L)
{
return setLabelCallback(L, QStringLiteral("setLabelOnEnter"));
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setLabelOnLeave
int TLuaInterpreter::setLabelOnLeave(lua_State* L)
{
return setLabelCallback(L, QStringLiteral("setLabelOnLeave"));
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setTextFormat
int TLuaInterpreter::setTextFormat(lua_State* L)
{
Host& host = getHostFromLua(L);
int n = lua_gettop(L);
QString windowName {WINDOW_NAME(L, 1)};
QVector<int> colorComponents(6); // 0-2 RGB background, 3-5 RGB foreground
colorComponents[0] = qRound(qBound(0.0, getVerifiedDouble(L, __func__, 2, "red background color component"), 255.0));
colorComponents[1] = qRound(qBound(0.0, getVerifiedDouble(L, __func__, 3, "green background color component"), 255.0));
colorComponents[2] = qRound(qBound(0.0, getVerifiedDouble(L, __func__, 4, "blue background color component"), 255.0));
colorComponents[3] = qRound(qBound(0.0, getVerifiedDouble(L, __func__, 5, "red foreground color component"), 255.0));
colorComponents[4] = qRound(qBound(0.0, getVerifiedDouble(L, __func__, 6, "green foreground color component"), 255.0));
colorComponents[5] = qRound(qBound(0.0, getVerifiedDouble(L, __func__, 7, "blue foreground color component"), 255.0));
int s = 7;
bool bold;
if (lua_isboolean(L, ++s)) {
bold = lua_toboolean(L, s);
} else if (lua_isnumber(L, s)) {
bold = !qFuzzyCompare(1.0, 1.0 + lua_tonumber(L, s));
} else {
lua_pushfstring(L, "setTextFormat: bad argument #%d type (bold format as boolean or number {true/non-zero to enable} expected, got %s!)",
s, luaL_typename(L, s));
return lua_error(L);
}
bool underline;
if (lua_isboolean(L, ++s)) {
underline = lua_toboolean(L, s);
} else if (lua_isnumber(L, s)) {
underline = !qFuzzyCompare(1.0, 1.0 + lua_tonumber(L, s));
} else {
lua_pushfstring(L, "setTextFormat: bad argument #%d type (underline format as boolean or number {true/non-zero to enable} expected, got %s!)",
s, luaL_typename(L, s));
return lua_error(L);
}
bool italics;
if (lua_isboolean(L, ++s)) {
italics = lua_toboolean(L, s);
} else if (lua_isnumber(L, s)) {
italics = !qFuzzyCompare(1.0, 1.0 + lua_tonumber(L, s));
} else {
lua_pushfstring(L, "setTextFormat: bad argument #%d type (italic format as boolean or number {true/non-zero to enable} expected, got %s!)",
s, luaL_typename(L, s));
return lua_error(L);
}
bool strikeout = false;
if (s < n) {
// s has not been incremented yet so this means we still have another argument!
if (lua_isboolean(L, ++s)) {
strikeout = lua_toboolean(L, s);
} else if (lua_isnumber(L, s)) {
strikeout = !qFuzzyCompare(1.0, 1.0 + lua_tonumber(L, s));
} else {
lua_pushfstring(L, "setTextFormat: bad argument #%d type (strikeout format as boolean or number {true/non-zero to enable} is optional, got %s!)",
s, luaL_typename(L, s));
return lua_error(L);
}
}
bool overline = false;
if (s < n) {
// s has not been incremented yet so this means we still have another argument!
if (lua_isboolean(L, ++s)) {
overline = lua_toboolean(L, s);
} else if (lua_isnumber(L, s)) {
overline = !qFuzzyCompare(1.0, 1.0 + lua_tonumber(L, s));
} else {
lua_pushfstring(L, "setTextFormat: bad argument #%d type (overline format as boolean or number {true/non-zero to enable} is optional, got %s!)",
s, luaL_typename(L, s));
return lua_error(L);
}
}
bool reverse = false;
if (s < n) {
// s has not been incremented yet so this means we still have another argument!
if (lua_isboolean(L, ++s)) {
reverse = lua_toboolean(L, s);
} else if (lua_isnumber(L, s)) {
reverse = !qFuzzyCompare(1.0, 1.0 + lua_tonumber(L, s));
} else {
lua_pushfstring(L, "setTextFormat: bad argument #%d type (reverse format as boolean or number {true/non-zero to enable} is optional, got %s!)",
s, luaL_typename(L, s));
return lua_error(L);
}
}
TChar::AttributeFlags flags = (bold ? TChar::Bold : TChar::None)
| (italics ? TChar::Italic : TChar::None)
| (overline ? TChar::Overline : TChar::None)
| (reverse ? TChar::Reverse : TChar::None)
| (strikeout ? TChar::StrikeOut : TChar::None)
| (underline ? TChar::Underline : TChar::None);
if (!host.mpConsole->setTextFormat(windowName,
QColor(colorComponents.at(3), colorComponents.at(4), colorComponents.at(5)),
QColor(colorComponents.at(0), colorComponents.at(1), colorComponents.at(2)),
flags)) {
return warnArgumentValue(L, __func__, QStringLiteral("window '%1' does not exist").arg(windowName), true);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#raiseWindow
int TLuaInterpreter::raiseWindow(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
Host& host = getHostFromLua(L);
lua_pushboolean(L, host.mpConsole->raiseWindow(windowName));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#lowerWindow
int TLuaInterpreter::lowerWindow(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
Host& host = getHostFromLua(L);
lua_pushboolean(L, host.mpConsole->lowerWindow(windowName));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#showWindow
int TLuaInterpreter::showWindow(lua_State* L)
{
QString text = getVerifiedString(L, __func__, 1, "name");
Host& host = getHostFromLua(L);
lua_pushboolean(L, host.showWindow(text));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setRoomEnv
int TLuaInterpreter::setRoomEnv(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "room id");
int env = getVerifiedInt(L, __func__, 2, "environment id");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("room %1 doesn't exist").arg(id));
}
pR->environment = env;
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setRoomName
int TLuaInterpreter::setRoomName(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int id = getVerifiedInt(L, __func__, 1, "room id");
QString name = getVerifiedString(L, __func__, 2, "room name", true);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid room id").arg(id));
}
pR->name = name;
updateMap(L);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getRoomName
int TLuaInterpreter::getRoomName(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int id = getVerifiedInt(L, __func__, 1, "room id");
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("room %1 doesn't exist").arg(id));
}
lua_pushstring(L, pR->name.toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setRoomWeight
int TLuaInterpreter::setRoomWeight(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "roomID");
int w = getVerifiedInt(L, __func__, 2, "weight");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (pR) {
pR->setWeight(w);
host.mpMap->mMapGraphNeedsUpdate = true;
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#connectToServer
int TLuaInterpreter::connectToServer(lua_State* L)
{
// The lua_tointeger(...) call can return a 64-bit integer number, on
// Windows Platform that is bigger than the int32_t type (a.k.a. "int" AND
// "long" types on that platform)! 8-O
lua_Integer port = 23;
bool isToSaveToProfile = false;
Host& host = getHostFromLua(L);
QString url = getVerifiedString(L, __func__, 1, "url");
if (!lua_isnoneornil(L, 2)) {
port = getVerifiedInt(L, __func__, 2, "port number {default = 23}", true);
if (port > 65535 || port < 1) {
return warnArgumentValue(L, __func__, QStringLiteral(
"invalid port number %1 given, if supplied it must be in range 1 to 65535, {defaults to 23 if not provided}").arg(port));
}
}
// Optional argument to save this new connection to disk for this profile.
if (!lua_isnoneornil(L, 3)) {
isToSaveToProfile = getVerifiedBool(L, __func__, 3, "save host name and port number", true);
}
if (isToSaveToProfile) {
QPair<bool, QString> result = host.writeProfileData(QLatin1String("url"), url);
if (!result.first) {
return warnArgumentValue(L, __func__, QStringLiteral("unable to save host name, reason: %1").arg(result.second));
}
result = host.writeProfileData(QLatin1String("port"), QString::number(port));
if (!result.first) {
return warnArgumentValue(L, __func__, QStringLiteral("unable to save port number, reason: %1").arg(result.second));
}
}
host.mTelnet.connectIt(url, port);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setRoomIDbyHash
int TLuaInterpreter::setRoomIDbyHash(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "room id");
QString hash = getVerifiedString(L, __func__, 2, "hash");
Host& host = getHostFromLua(L);
if (host.mpMap->mpRoomDB->roomIDToHash.contains(id)) {
host.mpMap->mpRoomDB->hashToRoomID.remove(host.mpMap->mpRoomDB->roomIDToHash[id]);
}
if (host.mpMap->mpRoomDB->hashToRoomID.contains(hash)) {
host.mpMap->mpRoomDB->roomIDToHash.remove(host.mpMap->mpRoomDB->hashToRoomID[hash]);
}
host.mpMap->mpRoomDB->hashToRoomID[hash] = id;
host.mpMap->mpRoomDB->roomIDToHash[id] = hash;
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getRoomIDbyHash
int TLuaInterpreter::getRoomIDbyHash(lua_State* L)
{
QString hash = getVerifiedString(L, __func__, 1, "hash");
Host& host = getHostFromLua(L);
int retID = host.mpMap->mpRoomDB->hashToRoomID.value(hash, -1);
lua_pushnumber(L, retID);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getRoomHashByID
int TLuaInterpreter::getRoomHashByID(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "room id");
Host& host = getHostFromLua(L);
if (!host.mpMap->mpRoomDB->roomIDToHash.contains(id)) {
return warnArgumentValue(L, __func__, QStringLiteral("no hash for room %1").arg(id));
}
QString retHash = host.mpMap->mpRoomDB->roomIDToHash[id];
lua_pushstring(L, retHash.toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#roomLocked
int TLuaInterpreter::roomLocked(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "roomID");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (pR) {
bool r = pR->isLocked;
lua_pushboolean(L, r);
} else {
lua_pushboolean(L, false);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#lockRoom
int TLuaInterpreter::lockRoom(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "roomID");
bool b = getVerifiedBool(L, __func__, 2, "lockIfTrue");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (pR) {
pR->isLocked = b;
host.mpMap->mMapGraphNeedsUpdate = true;
lua_pushboolean(L, true);
} else {
lua_pushboolean(L, false);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#lockExit
int TLuaInterpreter::lockExit(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "roomID");
int dir = dirToNumber(L, 2);
if (!dir) {
lua_pushfstring(L, "lockExit: bad argument #2 type (direction as number or string expected, got %s!)", luaL_typename(L, 2));
return lua_error(L);
}
bool b = getVerifiedBool(L, __func__, 3, "lockIfTrue");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (pR) {
pR->setExitLock(dir, b);
host.mpMap->mMapGraphNeedsUpdate = true;
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#lockSpecialExit
int TLuaInterpreter::lockSpecialExit(lua_State* L)
{
int fromRoomID = getVerifiedInt(L, __func__, 1, "exit room id");
// The second argument (was the toRoomID) is now ignored as it is not required/considered in any way
QString dir = getVerifiedString(L, __func__, 3, "special exit name/command");
if (dir.isEmpty()) {
return warnArgumentValue(L, __func__, "the special exit name/command cannot be empty");
}
bool b = getVerifiedBool(L, __func__, 4, "special exit lock state");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(fromRoomID);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("exit room id %1 does not exist").arg(fromRoomID));
}
if (!pR->setSpecialExitLock(dir, b)) {
return warnArgumentValue(L, __func__, QStringLiteral("the special exit name/command %1 does not exist in room id %2")
.arg(dir, QString::number(fromRoomID)));
}
lua_pushboolean(L, true);
host.mpMap->mMapGraphNeedsUpdate = true;
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#hasSpecialExitLock
int TLuaInterpreter::hasSpecialExitLock(lua_State* L)
{
int fromRoomID = getVerifiedInt(L, __func__, 1, "exit room id");
// Second argument was the entrance room id but it is not needed any more and is ignored
QString dir = getVerifiedString(L, __func__, 3, "special exit name/command");
if (dir.isEmpty()) {
return warnArgumentValue(L, __func__, "the special exit name/command cannot be empty");
}
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(fromRoomID);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("exit room id %1 does not exist").arg(fromRoomID));
}
if (!pR->getSpecialExits().contains(dir)) {
return warnArgumentValue(L, __func__, QStringLiteral("the special exit name/command '%1' does not exist in room id %2")
.arg(dir, QString::number(fromRoomID)));
}
lua_pushboolean(L, pR->hasSpecialExitLock(dir));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#hasExitLock
int TLuaInterpreter::hasExitLock(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "roomID");
int dir = dirToNumber(L, 2);
if (!dir) {
lua_pushfstring(L, "hasExitLock: bad argument #2 type (direction as number or string expected, got %s!)");
return lua_error(L);
}
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (pR) {
lua_pushboolean(L, pR->hasExitLock(dir));
return 1;
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getRoomExits
int TLuaInterpreter::getRoomExits(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "roomID");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (pR) {
lua_newtable(L);
if (pR->getNorth() != -1) {
lua_pushstring(L, "north");
lua_pushnumber(L, pR->getNorth());
lua_settable(L, -3);
}
if (pR->getNorthwest() != -1) {
lua_pushstring(L, "northwest");
lua_pushnumber(L, pR->getNorthwest());
lua_settable(L, -3);
}
if (pR->getNortheast() != -1) {
lua_pushstring(L, "northeast");
lua_pushnumber(L, pR->getNortheast());
lua_settable(L, -3);
}
if (pR->getSouth() != -1) {
lua_pushstring(L, "south");
lua_pushnumber(L, pR->getSouth());
lua_settable(L, -3);
}
if (pR->getSouthwest() != -1) {
lua_pushstring(L, "southwest");
lua_pushnumber(L, pR->getSouthwest());
lua_settable(L, -3);
}
if (pR->getSoutheast() != -1) {
lua_pushstring(L, "southeast");
lua_pushnumber(L, pR->getSoutheast());
lua_settable(L, -3);
}
if (pR->getWest() != -1) {
lua_pushstring(L, "west");
lua_pushnumber(L, pR->getWest());
lua_settable(L, -3);
}
if (pR->getEast() != -1) {
lua_pushstring(L, "east");
lua_pushnumber(L, pR->getEast());
lua_settable(L, -3);
}
if (pR->getUp() != -1) {
lua_pushstring(L, "up");
lua_pushnumber(L, pR->getUp());
lua_settable(L, -3);
}
if (pR->getDown() != -1) {
lua_pushstring(L, "down");
lua_pushnumber(L, pR->getDown());
lua_settable(L, -3);
}
if (pR->getIn() != -1) {
lua_pushstring(L, "in");
lua_pushnumber(L, pR->getIn());
lua_settable(L, -3);
}
if (pR->getOut() != -1) {
lua_pushstring(L, "out");
lua_pushnumber(L, pR->getOut());
lua_settable(L, -3);
}
return 1;
} else {
return 0;
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getAllRoomEntrances
int TLuaInterpreter::getAllRoomEntrances(lua_State* L)
{
int roomId = getVerifiedInt(L, __func__, 1, "room id");
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomId);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid room id").arg(roomId));
}
lua_newtable(L);
QList<int> entrances = host.mpMap->mpRoomDB->getEntranceHash().values(roomId);
// Could use a .toSet().toList() to remove duplicates values
if (entrances.count() > 1) {
std::sort(entrances.begin(), entrances.end());
}
for (int i = 0; i < entrances.size(); i++) {
lua_pushnumber(L, i + 1);
lua_pushnumber(L, entrances.at(i));
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#searchRoom
int TLuaInterpreter::searchRoom(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int room_id = 0;
int n = lua_gettop(L);
bool gotRoomID = false;
bool caseSensitive = false;
bool exactMatch = false;
QString room;
if (lua_isnumber(L, 1)) {
room_id = lua_tointeger(L, 1);
gotRoomID = true;
} else if (lua_isstring(L, 1)) {
if (n > 1) {
if (lua_isboolean(L, 2)) {
caseSensitive = lua_toboolean(L, 2);
if (n > 2) {
if (lua_isboolean(L, 3)) {
exactMatch = lua_toboolean(L, 3);
} else {
lua_pushfstring(L, R"(searchRoom: bad argument #3 type ("exact match" as boolean is optional, got %s!))", luaL_typename(L, 3));
return lua_error(L);
}
}
} else {
lua_pushfstring(L, R"(searchRoom: bad argument #2 type ("case sensitive" as boolean is optional, got %s!))", luaL_typename(L, 2));
return lua_error(L);
}
}
room = lua_tostring(L, 1);
} else {
lua_pushfstring(L, R"(searchRoom: bad argument #1 ("room name" as string expected, got %s!))", luaL_typename(L, 1));
return lua_error(L);
}
if (gotRoomID) {
TRoom* pR = host.mpMap->mpRoomDB->getRoom(room_id);
if (pR) {
lua_pushstring(L, pR->name.toUtf8().constData());
return 1;
} else {
lua_pushfstring(L, "searchRoom: bad argument #1 value (room id %d does not exist!)", room_id);
// Should've been a nil with this as an second returned string!
return 1;
}
} else {
QList<TRoom*> roomList = host.mpMap->mpRoomDB->getRoomPtrList();
lua_newtable(L);
QList<int> roomIdsFound;
for (auto pR : roomList) {
if (!pR) {
continue;
}
if (exactMatch) {
if (pR->name.compare(room, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive) == 0) {
roomIdsFound.append(pR->getId());
}
} else {
if (pR->name.contains(room, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive)) {
roomIdsFound.append(pR->getId());
}
}
}
if (!roomIdsFound.isEmpty()) {
for (int i : roomIdsFound) {
TRoom* pR = host.mpMap->mpRoomDB->getRoom(i);
// This test is to keep Coverity happy as it thinks pR could be
// a nullptr in some odd situation {CID 1415023}:
if (pR) {
QString name = pR->name;
int roomID = pR->getId();
lua_pushnumber(L, roomID);
lua_pushstring(L, name.toUtf8().constData());
lua_settable(L, -3);
}
}
}
return 1;
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#searchRoomUserData
int TLuaInterpreter::searchRoomUserData(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
QString key = QString();
QString value = QString(); //both of these assigns a null value which is detectably different from the empty value
if (lua_gettop(L)) {
key = getVerifiedString(L, __func__, 1, "key", true);
if (lua_gettop(L) > 1) {
value = getVerifiedString(L, __func__, 2, "value", true);
}
}
lua_newtable(L);
QHashIterator<int, TRoom*> itRoom(host.mpMap->mpRoomDB->getRoomMap());
// For best performance do the three different types of action in three
// different branches each with a loop - rather than choosing a branch
// within a loop for each room
lua_newtable(L);
if (key.isNull()) { // Find all keys everywhere
QSet<QString> keysSet;
while (itRoom.hasNext()) {
itRoom.next();
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
// In the brave new world of range based initializers one must use
// a pair of iterators that point to the SAME thing that lasts
// long enough - using the output of a Qt method that returns a
// QList twice is not good enough and causes seg. faults...
QList<QString> roomDataKeysList{itRoom.value()->userData.keys()};
keysSet.unite(QSet<QString>{roomDataKeysList.begin(), roomDataKeysList.end()});
#else
keysSet.unite(itRoom.value()->userData.keys().toSet());
#endif
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QStringList keys{keysSet.begin(), keysSet.end()};
#else
QStringList keys{keysSet.toList()};
#endif
if (keys.size() > 1) {
std::sort(keys.begin(), keys.end());
}
for (unsigned int i = 0, total = keys.size(); i < total; ++i) {
lua_pushnumber(L, i + 1);
lua_pushstring(L, keys.at(i).toUtf8().constData());
lua_settable(L, -3);
}
} else if (value.isNull()) { // Find all values for a particular key in every room
QSet<QString> valuesSet; // Use a set as it automatically eliminates duplicates
while (itRoom.hasNext()) {
itRoom.next();
QString roomValueForKey = itRoom.value()->userData.value(key, QString());
// If the key is NOT present, will return second argument which is a
// null QString which is NOT the same as an empty QString.
if (!roomValueForKey.isNull()) {
valuesSet.insert(roomValueForKey);
}
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QStringList values{valuesSet.begin(), valuesSet.end()};
#else
QStringList values{valuesSet.toList()};
#endif
if (values.size() > 1) {
std::sort(values.begin(), values.end());
}
for (unsigned int i = 0, total = values.size(); i < total; ++i) {
lua_pushnumber(L, i + 1);
lua_pushstring(L, values.at(i).toUtf8().constData());
lua_settable(L, -3);
}
} else { // Find all rooms where key and value match
QSet<int> roomIdsSet;
while (itRoom.hasNext()) {
itRoom.next();
QString roomDataValue = itRoom.value()->userData.value(key, QString());
if ((!roomDataValue.isNull()) && (!value.compare(roomDataValue, Qt::CaseSensitive))) {
roomIdsSet.insert(itRoom.key());
}
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QList<int> roomIds{roomIdsSet.begin(), roomIdsSet.end()};
#else
QList<int> roomIds{roomIdsSet.toList()};
#endif
if (roomIds.size() > 1) {
std::sort(roomIds.begin(), roomIds.end());
}
for (unsigned int i = 0, total = roomIds.size(); i < total; ++i) {
lua_pushnumber(L, i + 1);
lua_pushnumber(L, roomIds.at(i));
lua_settable(L, -3);
}
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#searchAreaUserData
int TLuaInterpreter::searchAreaUserData(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
QString key = QString();
QString value = QString(); //both of these assigns a null value which is detectably different from the empty value
if (lua_gettop(L)) {
key = getVerifiedString(L, __func__, 1, "key", true);
if (lua_gettop(L) > 1) {
value = getVerifiedString(L, __func__, 2, "value", true);
}
}
lua_newtable(L);
QMapIterator<int, TArea*> itArea(host.mpMap->mpRoomDB->getAreaMap());
// For best performance do the three different types of action in three
// different branches each with a loop - rather than choosing a branch
// within a loop for each room
lua_newtable(L);
if (key.isNull()) { // Find all keys everywhere
QSet<QString> keysSet;
while (itArea.hasNext()) {
itArea.next();
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QList<QString> areaDataKeysList{itArea.value()->mUserData.keys()};
keysSet.unite(QSet<QString>{areaDataKeysList.begin(), areaDataKeysList.end()});
#else
keysSet.unite(itArea.value()->mUserData.keys().toSet());
#endif
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QStringList keys{keysSet.begin(), keysSet.end()};
#else
QStringList keys{keysSet.toList()};
#endif
if (keys.size() > 1) {
std::sort(keys.begin(), keys.end());
}
for (unsigned int i = 0, total = keys.size(); i < total; ++i) {
lua_pushnumber(L, i + 1);
lua_pushstring(L, keys.at(i).toUtf8().constData());
lua_settable(L, -3);
}
} else if (value.isNull()) { // Find all values for a particular key in every room
QSet<QString> valuesSet; // Use a set as it automatically eliminates duplicates
while (itArea.hasNext()) {
itArea.next();
QString areaValueForKey = itArea.value()->mUserData.value(key, QString());
// If the key is NOT present, will return second argument which is a
// null QString which is NOT the same as an empty QString.
if (!areaValueForKey.isNull()) {
valuesSet.insert(areaValueForKey);
}
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QStringList values{valuesSet.begin(), valuesSet.end()};
#else
QStringList values{valuesSet.toList()};
#endif
if (values.size() > 1) {
std::sort(values.begin(), values.end());
}
for (unsigned int i = 0, total = values.size(); i < total; ++i) {
lua_pushnumber(L, i + 1);
lua_pushstring(L, values.at(i).toUtf8().constData());
lua_settable(L, -3);
}
} else {
QSet<int> areaIdsSet;
while (itArea.hasNext()) { // Find all areas with a particular key AND value
itArea.next();
QString areaDataValue = itArea.value()->mUserData.value(key, QString());
if ((!areaDataValue.isNull()) && (!value.compare(areaDataValue, Qt::CaseSensitive))) {
areaIdsSet.insert(itArea.key());
}
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QList<int> areaIds{areaIdsSet.begin(), areaIdsSet.begin()};
#else
QList<int> areaIds{areaIdsSet.toList()};
#endif
if (areaIds.size() > 1) {
std::sort(areaIds.begin(), areaIds.end());
}
for (unsigned int i = 0, total = areaIds.size(); i < total; ++i) {
lua_pushnumber(L, i + 1);
lua_pushnumber(L, areaIds.at(i));
lua_settable(L, -3);
}
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getAreaTable
int TLuaInterpreter::getAreaTable(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
QMapIterator<int, QString> it(host.mpMap->mpRoomDB->getAreaNamesMap());
lua_newtable(L);
while (it.hasNext()) {
it.next();
int areaId = it.key();
QString name = it.value();
lua_pushstring(L, name.toUtf8().constData());
lua_pushnumber(L, areaId);
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getAreaTableSwap
int TLuaInterpreter::getAreaTableSwap(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
QMapIterator<int, QString> it(host.mpMap->mpRoomDB->getAreaNamesMap());
lua_newtable(L);
while (it.hasNext()) {
it.next();
int areaId = it.key();
QString name = it.value();
lua_pushnumber(L, areaId);
lua_pushstring(L, name.toUtf8().constData());
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getAreaRooms
int TLuaInterpreter::getAreaRooms(lua_State* L)
{
int area = getVerifiedInt(L, __func__, 1, "areaID");
Host& host = getHostFromLua(L);
TArea* pA = host.mpMap->mpRoomDB->getArea(area);
if (!pA) {
lua_pushnil(L);
return 1;
}
lua_newtable(L);
QSetIterator<int> itAreaRoom(pA->getAreaRooms());
int i = -1;
while (itAreaRoom.hasNext()) {
lua_pushnumber(L, ++i);
// We should have started at 1 but past code had incorrectly started
// with a zero index and we must maintain compatibilty with code written
// for that
lua_pushnumber(L, itAreaRoom.next());
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getRooms
int TLuaInterpreter::getRooms(lua_State* L)
{
Host& host = getHostFromLua(L);
lua_newtable(L);
QHashIterator<int, TRoom*> it(host.mpMap->mpRoomDB->getRoomMap());
while (it.hasNext()) {
it.next();
lua_pushnumber(L, it.key());
lua_pushstring(L, it.value()->name.toUtf8().constData());
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getAreaExits
int TLuaInterpreter::getAreaExits(lua_State* L)
{
int n = lua_gettop(L);
bool isFullDataRequired = false;
int area = getVerifiedInt(L, __func__, 1, "area id");
if (n > 1) {
isFullDataRequired = getVerifiedBool(L, __func__, 2, "full data wanted", true);
}
Host& host = getHostFromLua(L);
TArea* pA = host.mpMap->mpRoomDB->getArea(area);
if (!pA) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid area id").arg(area));
}
lua_newtable(L);
if (n < 2 || (n > 1 && !isFullDataRequired)) {
// Replicate original implimentation
QList<int> areaExits = pA->getAreaExitRoomIds();
if (areaExits.size() > 1) {
std::sort(areaExits.begin(), areaExits.end());
}
for (int i = 0; i < areaExits.size(); i++) {
lua_pushnumber(L, i + 1); // Lua lists/arrays begin at 1 not 0!
lua_pushnumber(L, areaExits.at(i));
lua_settable(L, -3);
}
} else {
QMultiMap<int, QPair<QString, int>> areaExits = pA->getAreaExitRoomData();
QList<int> fromRooms = areaExits.uniqueKeys();
for (int fromRoom : fromRooms) {
lua_pushnumber(L, fromRoom);
lua_newtable(L);
QList<QPair<QString, int>> toRoomsData = areaExits.values(fromRoom);
for (const auto& j : toRoomsData) {
lua_pushstring(L, j.first.toUtf8().constData());
lua_pushnumber(L, j.second);
lua_settable(L, -3);
}
lua_settable(L, -3);
}
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#auditAreas
int TLuaInterpreter::auditAreas(lua_State* L)
{
Host& host = getHostFromLua(L);
host.mpMap->audit();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getRoomWeight
int TLuaInterpreter::getRoomWeight(lua_State* L)
{
Host& host = getHostFromLua(L);
int roomId;
if (lua_gettop(L) > 0) {
roomId = getVerifiedInt(L, __func__, 1, "roomID");
} else {
roomId = host.mpMap->mRoomIdHash.value(host.getName());
}
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomId);
if (pR) {
lua_pushnumber(L, pR->getWeight());
return 1;
} else {
return 0;
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#gotoRoom
int TLuaInterpreter::gotoRoom(lua_State* L)
{
int targetRoomId = getVerifiedInt(L, __func__, 1, "target room id");
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
} else if (!host.mpMap->mpRoomDB->getRoom(targetRoomId)) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid target room id").arg(targetRoomId));
}
if (!host.mpMap->gotoRoom(targetRoomId)) {
int totalWeight = host.assemblePath(); // Needed if unsucessful to clear lua speedwalk tables
Q_UNUSED(totalWeight);
return warnArgumentValue(L, __func__, QStringLiteral("no path found from current room to room with id %1").arg(targetRoomId), true);
}
host.startSpeedWalk();
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getPath
int TLuaInterpreter::getPath(lua_State* L)
{
int originRoomId = getVerifiedInt(L, __func__, 1, "starting room id");
int targetRoomId = getVerifiedInt(L, __func__, 2, "target room id");
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
} else if (!host.mpMap->mpRoomDB->getRoom(originRoomId)) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid source room id").arg(originRoomId));
} else if (!host.mpMap->mpRoomDB->getRoom(targetRoomId)) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid target room id").arg(targetRoomId));
}
bool ret = host.mpMap->gotoRoom(originRoomId, targetRoomId);
int totalWeight = host.assemblePath(); // Needed even if unsucessful, to clear lua tables then
if (ret) {
lua_pushboolean(L, true);
lua_pushnumber(L, totalWeight);
return 2;
} else {
lua_pushboolean(L, false);
lua_pushnumber(L, -1);
lua_pushfstring(L, "getPath: no path found from room, with Id %d to room %d!", originRoomId, targetRoomId);
return 3;
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#deselect
int TLuaInterpreter::deselect(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
auto console = CONSOLE(L, windowName);
console->deselect();
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#resetFormat
int TLuaInterpreter::resetFormat(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
auto console = CONSOLE(L, windowName);
console->reset();
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#hasFocus
int TLuaInterpreter::hasFocus(lua_State* L)
{
Host& host = getHostFromLua(L);
lua_pushboolean(L, host.mpConsole->hasFocus()); //FIXME
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#echoUserWindow
int TLuaInterpreter::echoUserWindow(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
QString text = getVerifiedString(L, __func__, 2, "text");
Host& host = getHostFromLua(L);
host.echoWindow(windowName, text);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setAppStyleSheet
int TLuaInterpreter::setAppStyleSheet(lua_State* L)
{
QString styleSheet;
QString tag;
int n = lua_gettop(L);
styleSheet = getVerifiedString(L, __func__, 1, "style sheet");
if (n > 1) {
tag = getVerifiedString(L, __func__, 2, "tag");
}
Host& host = getHostFromLua(L);
TEvent event {};
event.mArgumentList.append(QLatin1String("sysAppStyleSheetChange"));
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
event.mArgumentList.append(tag);
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
event.mArgumentList.append(host.getName());
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
qApp->setStyleSheet(styleSheet);
mudlet::self()->getHostManager().postInterHostEvent(nullptr, event, true);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setAppStyleSheet
int TLuaInterpreter::setProfileStyleSheet(lua_State* L)
{
QString styleSheet = getVerifiedString(L, __func__, 1, "style sheet");
Host& host = getHostFromLua(L);
lua_pushboolean(L, host.setProfileStyleSheet(styleSheet));
return 1;
}
// No documentation available in wiki - internal function
// this was an internal only function used by the package system, but it was
// inactive and has been removed
int TLuaInterpreter::showUnzipProgress(lua_State* L)
{
return warnArgumentValue(L, __func__, "removed command, this function is now inactive and does nothing");
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#playSoundFile
int TLuaInterpreter::playSoundFile(lua_State* L)
{
QString sound = getVerifiedString(L, __func__, 1, "fileName");
if (QDir::homePath().contains('\\')) {
sound.replace('/', R"(\)");
} else {
sound.replace('\\', "/");
}
/* if no volume provided, substitute 100 (maximum) */
mudlet::self()->playSound(sound, lua_isnumber(L, 2) ? lua_tointeger(L, 2) : 100);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#stopSounds
int TLuaInterpreter::stopSounds(lua_State* L)
{
Q_UNUSED(L)
//doesn't take an argument
mudlet::self()->stopSounds();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#moveCursorEnd
int TLuaInterpreter::moveCursorEnd(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
auto console = CONSOLE(L, windowName);
console->moveCursorEnd();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getLastLineNumber
int TLuaInterpreter::getLastLineNumber(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
auto console = CONSOLE_NIL(L, windowName);
int number = console ? console->getLastLineNumber() : -1;
lua_pushnumber(L, number);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getMudletHomeDir
int TLuaInterpreter::getMudletHomeDir(lua_State* L)
{
Host& host = getHostFromLua(L);
QString nativeHomeDirectory = mudlet::getMudletPath(mudlet::profileHomePath, host.getName());
lua_pushstring(L, nativeHomeDirectory.toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#disconnect
int TLuaInterpreter::disconnect(lua_State* L)
{
Host& host = getHostFromLua(L);
host.mTelnet.disconnectIt();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#reconnect
int TLuaInterpreter::reconnect(lua_State* L)
{
Host& host = getHostFromLua(L);
host.mTelnet.reconnect();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setTriggerStayOpen
int TLuaInterpreter::setTriggerStayOpen(lua_State* L)
{
QString windowName;
int s = 1;
if (lua_gettop(L) > 1) {
windowName = WINDOW_NAME(L, s++);
}
double b = getVerifiedDouble(L, __func__, s, "number of lines");
Host& host = getHostFromLua(L);
host.getTriggerUnit()->setTriggerStayOpen(windowName, static_cast<int>(b));
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setLink
int TLuaInterpreter::setLink(lua_State* L)
{
QString windowName;
int s = 1;
if (lua_gettop(L) > 2) {
windowName = WINDOW_NAME(L, s++);
}
QString linkFunction = getVerifiedString(L, __func__, s++, "command");
QString linkHint = getVerifiedString(L, __func__, s++, "tooltip");
Host& host = getHostFromLua(L);
QStringList _linkFunction;
_linkFunction << linkFunction;
QStringList _linkHint;
_linkHint << linkHint;
auto console = CONSOLE(L, windowName);
console->setLink(_linkFunction, _linkHint);
if (console != host.mpConsole) {
console->mUpperPane->forceUpdate();
console->mLowerPane->forceUpdate();
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setPopup
int TLuaInterpreter::setPopup(lua_State* L)
{
QString windowName = "";
QStringList _hintList;
QStringList _commandList;
int s = 1;
int n = lua_gettop(L);
// console name is an optional first argument
if (n > 4) {
windowName = WINDOW_NAME(L, s++);
}
if (!lua_isstring(L, s)) {
lua_pushstring(L, "setPopup: wrong argument type");
return lua_error(L);
}
QString txt = lua_tostring(L, s++);
if (!lua_istable(L, s)) {
lua_pushstring(L, "setPopup: wrong argument type");
return lua_error(L);
}
lua_pushnil(L);
while (lua_next(L, s) != 0) {
// key at index -2 and value at index -1
if (lua_type(L, -1) == LUA_TSTRING) {
QString cmd = lua_tostring(L, -1);
_commandList << cmd;
}
// removes value, but keeps key for next iteration
lua_pop(L, 1);
}
if (!lua_istable(L, ++s)) {
lua_pushstring(L, "setPopup: wrong argument type");
return lua_error(L);
}
lua_pushnil(L);
while (lua_next(L, s) != 0) {
// key at index -2 and value at index -1
if (lua_type(L, -1) == LUA_TSTRING) {
QString hint = lua_tostring(L, -1);
_hintList << hint;
}
// removes value, but keeps key for next iteration
lua_pop(L, 1);
}
Host& host = getHostFromLua(L);
if (_commandList.size() != _hintList.size()) {
lua_pushstring(L, "Error: command list size and hint list size do not match cannot create popup");
return lua_error(L);
}
auto console = CONSOLE(L, windowName);
console->setLink(_commandList, _hintList);
if (console != host.mpConsole) {
console->mUpperPane->forceUpdate();
console->mLowerPane->forceUpdate();
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setBold
int TLuaInterpreter::setBold(lua_State* L)
{
QString windowName;
int s = 1;
if (lua_gettop(L) > 1) { // Have more than one argument so first must be a console name
windowName = WINDOW_NAME(L, s++);
}
bool isAttributeEnabled = getVerifiedBool(L, __func__, s, "enable bold attribute");
auto console = CONSOLE(L, windowName);
console->setDisplayAttributes(TChar::Bold, isAttributeEnabled);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setItalics
int TLuaInterpreter::setItalics(lua_State* L)
{
QString windowName;
int s = 1;
if (lua_gettop(L) > 1) { // Have more than one argument so first must be a console name
windowName = WINDOW_NAME(L, s++);
}
bool isAttributeEnabled = getVerifiedBool(L, __func__, s, "enable italic attribute");
auto console = CONSOLE(L, windowName);
console->setDisplayAttributes(TChar::Italic, isAttributeEnabled);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setOverline
int TLuaInterpreter::setOverline(lua_State* L)
{
QString windowName;
int s = 1;
if (lua_gettop(L) > 1) { // Have more than one argument so first must be a console name
windowName = WINDOW_NAME(L, s++);
}
bool isAttributeEnabled = getVerifiedBool(L, __func__, s, "enable overline attribute");
auto console = CONSOLE(L, windowName);
console->setDisplayAttributes(TChar::Overline, isAttributeEnabled);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setReverse
int TLuaInterpreter::setReverse(lua_State* L)
{
QString windowName;
int s = 1;
if (lua_gettop(L) > 1) { // Have more than one argument so first must be a console name
windowName = WINDOW_NAME(L, s++);
}
bool isAttributeEnabled = getVerifiedBool(L, __func__, s, "enable reverse attribute");
auto console = CONSOLE(L, windowName);
console->setDisplayAttributes(TChar::Reverse, isAttributeEnabled);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setStrikeOut
int TLuaInterpreter::setStrikeOut(lua_State* L)
{
QString windowName;
int s = 1;
if (lua_gettop(L) > 1) { // Have more than one argument so first must be a console name
windowName = WINDOW_NAME(L, s++);
}
bool isAttributeEnabled = getVerifiedBool(L, __func__, s, "enable strikeout attribute");
auto console = CONSOLE(L, windowName);
console->setDisplayAttributes(TChar::StrikeOut, isAttributeEnabled);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setUnderline
int TLuaInterpreter::setUnderline(lua_State* L)
{
QString windowName;
int s = 1;
if (lua_gettop(L) > 1) { // Have more than one argument so first must be a console name
windowName = WINDOW_NAME(L, s++);
}
bool isAttributeEnabled = getVerifiedBool(L, __func__, s, "enable underline attribute");
auto console = CONSOLE(L, windowName);
console->setDisplayAttributes(TChar::Underline, isAttributeEnabled);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#debugc -- not #debug - compare GlobalLua
int TLuaInterpreter::debug(lua_State* L)
{
Host& host = getHostFromLua(L);
int n = lua_gettop(L);
if (!n) {
// Nothing to show
return 0;
}
QString luaDebugText;
if (n > 1) {
for (int i = 0; i < n; ++i) {
luaDebugText += QStringLiteral(" (%1) %2").arg(QString::number(i + 1), lua_tostring(L, i + 1));
}
} else {
// n == 1
luaDebugText = QStringLiteral(" %1").arg(lua_tostring(L, 1));
}
luaDebugText.append(QChar::LineFeed);
if (host.mpEditorDialog) {
host.mpEditorDialog->mpErrorConsole->print(QLatin1String("[DEBUG:]"), QColor(Qt::blue), QColor(Qt::black));
host.mpEditorDialog->mpErrorConsole->print(luaDebugText, QColor(Qt::green), QColor(Qt::black));
}
return 0;
}
// No documentation available in wiki - internal function
int TLuaInterpreter::showHandlerError(lua_State* L)
{
Host& host = getHostFromLua(L);
QString event = getVerifiedString(L, __func__, 1, "event name");
QString error = getVerifiedString(L, __func__, 2, "error message");
host.mLuaInterpreter.logEventError(event, error);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#hideToolBar
int TLuaInterpreter::hideToolBar(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
Host& host = getHostFromLua(L);
host.getActionUnit()->hideToolBar(windowName);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#showToolBar
int TLuaInterpreter::showToolBar(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
Host& host = getHostFromLua(L);
host.getActionUnit()->showToolBar(windowName);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#sendATCP
int TLuaInterpreter::sendATCP(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!lua_isstring(L, 1)) {
lua_pushfstring(L, "sendATCP: bad argument #1 type (message as string expected, got %1!)", luaL_typename(L, 1));
return lua_error(L);
}
std::string msg = host.mTelnet.encodeAndCookBytes(lua_tostring(L, 1));
std::string what;
if (lua_gettop(L) > 1) {
if (!lua_isstring(L, 2)) {
lua_pushfstring(L, "sendATCP: bad argument #2 type (what as string is optional, got %1!)", luaL_typename(L, 2));
return lua_error(L);
}
what = host.mTelnet.encodeAndCookBytes(lua_tostring(L, 2));
}
std::string output;
output += TN_IAC;
output += TN_SB;
output += OPT_ATCP;
output += msg;
if (!what.empty()) {
output += " ";
output += what;
}
output += TN_IAC;
output += TN_SE;
if (!host.mTelnet.isATCPEnabled()) {
return warnArgumentValue(L, __func__, "ATCP is not currently enabled");
}
// output is in Mud Server Encoding form here:
if (!host.mTelnet.socketOutRaw(output)) {
return warnArgumentValue(L, __func__, "unable to send all of the ATCP message");
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#receiveMSP
int TLuaInterpreter::receiveMSP(lua_State* L)
{
Host& host = getHostFromLua(L);
std::string msg;
if (!host.mTelnet.isMSPEnabled()) {
return warnArgumentValue(L, __func__, "MSP is not currently enabled");
}
if (!lua_isstring(L, 1)) {
lua_pushfstring(L, "receiveMSP: bad argument #1 type (message as string expected, got %1!)", luaL_typename(L, 1));
return lua_error(L);
}
msg = host.mTelnet.encodeAndCookBytes(lua_tostring(L, 1));
host.mTelnet.setMSPVariables(QByteArray(msg.c_str(), msg.length()));
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#purgeMediaCache
int TLuaInterpreter::purgeMediaCache(lua_State* L)
{
Host& host = getHostFromLua(L);
host.mTelnet.purgeMediaCache();
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#sendGMCP
int TLuaInterpreter::sendGMCP(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!lua_isstring(L, 1)) {
lua_pushfstring(L, "sendGMCP: bad argument #1 type (message as string expected, got %1!)", luaL_typename(L, 1));
return lua_error(L);
}
std::string msg = host.mTelnet.encodeAndCookBytes(lua_tostring(L, 1));
std::string what;
if (lua_gettop(L) > 1) {
if (!lua_isstring(L, 2)) {
lua_pushfstring(L, "sendGMCP: bad argument #2 type (what as string is optional, got %1!)", luaL_typename(L, 2));
return lua_error(L);
}
what = host.mTelnet.encodeAndCookBytes(lua_tostring(L, 2));
}
std::string output;
output += TN_IAC;
output += TN_SB;
output += OPT_GMCP;
output += msg;
if (!what.empty()) {
output += " ";
output += what;
}
output += TN_IAC;
output += TN_SE;
if (!host.mTelnet.isGMCPEnabled()) {
return warnArgumentValue(L, __func__, "GMCP is not currently enabled");
}
// output is in Mud Server Encoding form here:
if (!host.mTelnet.socketOutRaw(output)) {
return warnArgumentValue(L, __func__, "unable to send all of the GMCP message");
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#sendMSDP
int TLuaInterpreter::sendMSDP(lua_State* L)
{
Host& host = getHostFromLua(L);
int n = lua_gettop(L);
if (n < 1) {
lua_pushstring(L, "sendMSDP: bad argument #1 type (variable name as string expected, got nil!)");
return lua_error(L);
}
for (int i = 1; i <= n; ++i) {
if (!lua_isstring(L, i)) {
lua_pushfstring(L, "sendMSDP: bad argument #%d type (%s as string expected, got %s!)", i, (i == 1 ? "variable name" : "value"), luaL_typename(L, i));
return lua_error(L);
}
}
std::string variable = host.mTelnet.encodeAndCookBytes(lua_tostring(L, 1));
std::string output;
output += TN_IAC;
output += TN_SB;
output += OPT_MSDP;
output += MSDP_VAR;
output += variable;
for (int i = 2; i <= n; ++i) {
output += MSDP_VAL;
output += host.mTelnet.encodeAndCookBytes(lua_tostring(L, i));
}
output += TN_IAC;
output += TN_SE;
// output is in Mud Server Encoding form here:
host.mTelnet.socketOutRaw(output);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#sendTelnetChannel102
int TLuaInterpreter::sendTelnetChannel102(lua_State* L)
{
if (!lua_isstring(L, 1)) {
lua_pushfstring(L, "sendTelnetChannel102: bad argument #1 type (message bytes {2 characters} as string expected, got %s!)",
luaL_typename(L, 1));
return lua_error(L);
}
std::string msg = lua_tostring(L, 1);
if (msg.length() != 2) {
return warnArgumentValue(L, __func__, QStringLiteral(
"invalid message of length %1 supplied, it should be two bytes (may use lua \\### for each byte where ### is a number between 1 and 254)")
.arg(msg.length()));
}
std::string output;
output += TN_IAC;
output += TN_SB;
output += OPT_102;
output += msg;
output += TN_IAC;
output += TN_SE;
Host& host = getHostFromLua(L);
if (!host.mTelnet.isChannel102Enabled()) {
return warnArgumentValue(L, __func__, "unable to send message as the 102 subchannel support has not been enabled by the game server");
}
// We have already validated output to contain a 2 byte payload so we
// should not need to worry about the "encoding" in this use of
// socketOutRaw(...) - with the exception of handling any occurance of
// 0xFF as either of the bytes to send - however Aardwolf does not use
// *THAT* value so, though it is probably okay to not worry about the
// need to "escape" it to get it through the telnet protocol unscathed
// it is trival to fix:
output = mudlet::replaceString(output, "\xff", "\xff\xff");
host.mTelnet.socketOutRaw(output);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getButtonState
int TLuaInterpreter::getButtonState(lua_State* L)
{
Host& host = getHostFromLua(L);
int state;
state = host.mpConsole->getButtonState();
lua_pushnumber(L, state);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getNetworkLatency
int TLuaInterpreter::getNetworkLatency(lua_State* L)
{
Host& host = getHostFromLua(L);
lua_pushnumber(L, host.mTelnet.networkLatencyTime);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getMainConsoleWidth
int TLuaInterpreter::getMainConsoleWidth(lua_State* L)
{
Host& host = getHostFromLua(L);
int fw = QFontMetrics(host.getDisplayFont()).averageCharWidth();
fw *= host.mWrapAt + 1;
lua_pushnumber(L, fw);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getMainWindowSize
int TLuaInterpreter::getMainWindowSize(lua_State* L)
{
Host& host = getHostFromLua(L);
QSize mainWindowSize = host.mpConsole->getMainWindowSize();
lua_pushnumber(L, mainWindowSize.width());
lua_pushnumber(L, mainWindowSize.height());
return 2;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getUserWindowSize
int TLuaInterpreter::getUserWindowSize(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
Host& host = getHostFromLua(L);
QSize userWindowSize = host.mpConsole->getUserWindowSize(windowName);
lua_pushnumber(L, userWindowSize.width());
lua_pushnumber(L, userWindowSize.height());
return 2;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getMousePosition
int TLuaInterpreter::getMousePosition(lua_State* L)
{
Host& host = getHostFromLua(L);
QPoint pos = host.mpConsole->mapFromGlobal(QCursor::pos());
lua_pushnumber(L, pos.x());
lua_pushnumber(L, pos.y());
return 2;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#tempTimer
int TLuaInterpreter::tempTimer(lua_State* L)
{
bool repeating{};
double time = getVerifiedDouble(L, __func__, 1, "time in seconds {maybe decimal}");
int n = lua_gettop(L);
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
if (lua_isfunction(L, 2)) {
if (n > 2) {
repeating = getVerifiedBool(L, __func__, 3, "repeating", true);
}
QPair<int, QString> result = pLuaInterpreter->startTempTimer(time, QString(), repeating);
if (result.first == -1) {
lua_pushnumber(L, -1);
lua_pushstring(L, result.second.toUtf8().constData());
return 2;
}
TTimer* timer = host.getTimerUnit()->getTimer(result.first);
Q_ASSERT_X(timer,
"TLuaInterpreter::tempTimer(...)",
"Got a positive result from LuaInterpreter::startTempTimer(...) but that failed to produce pointer to it from Host::mTimerUnit::getTimer(...)");
timer->mRegisteredAnonymousLuaFunction = true;
lua_pushlightuserdata(L, timer);
lua_pushvalue(L, 2);
lua_settable(L, LUA_REGISTRYINDEX);
lua_pushnumber(L, result.first);
return 1;
}
QString luaCode = getVerifiedString(L, __func__, 2, "script or function name");
if (n > 2) {
repeating = getVerifiedBool(L, __func__, 3, "repeating", true);
}
QPair<int, QString> result = pLuaInterpreter->startTempTimer(time, luaCode, repeating);
lua_pushnumber(L, result.first);
if (result.first == -1) {
lua_pushstring(L, result.second.toUtf8().constData());
return 2;
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#tempExactMatchTrigger
int TLuaInterpreter::tempExactMatchTrigger(lua_State* L)
{
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
int triggerID;
int expiryCount = -1;
QString exactMatchPattern = getVerifiedString(L, __func__, 1, "exact match pattern");
if (lua_isnumber(L, 3)) {
expiryCount = lua_tonumber(L, 3);
if (expiryCount < 1) {
return warnArgumentValue(L, __func__, QStringLiteral(
"trigger expiration count must be nil or greater than zero, got %1").arg(expiryCount));
}
} else if (!lua_isnoneornil(L, 3)) {
lua_pushfstring(L, "tempExactMatchTrigger: bad argument #3 value (trigger expiration count must be nil or a number, got %s!)", luaL_typename(L, 3));
return lua_error(L);
}
if (lua_isstring(L, 2)) {
triggerID = pLuaInterpreter->startTempExactMatchTrigger(exactMatchPattern, QString(lua_tostring(L, 2)), expiryCount);
} else if (lua_isfunction(L, 2)) {
triggerID = pLuaInterpreter->startTempExactMatchTrigger(exactMatchPattern, QString(), expiryCount);
auto trigger = host.getTriggerUnit()->getTrigger(triggerID);
trigger->mRegisteredAnonymousLuaFunction = true;
lua_pushlightuserdata(L, trigger);
lua_pushvalue(L, 2);
lua_settable(L, LUA_REGISTRYINDEX);
} else {
lua_pushfstring(L, "tempExactMatchTrigger: bad argument #2 type (code to run as a string or a function expected, got %s!)", luaL_typename(L, 2));
return lua_error(L);
}
lua_pushnumber(L, triggerID);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#tempBeginOfLineTrigger
int TLuaInterpreter::tempBeginOfLineTrigger(lua_State* L)
{
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
int triggerID;
int expiryCount = -1;
QString pattern = getVerifiedString(L, __func__, 1, "pattern");
if (lua_isnumber(L, 3)) {
expiryCount = lua_tonumber(L, 3);
if (expiryCount < 1) {
return warnArgumentValue(L, __func__, QStringLiteral(
"trigger expiration count must be nil or greater than zero, got %1").arg(expiryCount));
}
} else if (!lua_isnoneornil(L, 3)) {
lua_pushfstring(L, "tempBeginOfLineTrigger: bad argument #3 value (trigger expiration count must be nil or a number, got %s!)", luaL_typename(L, 3));
return lua_error(L);
}
if (lua_isstring(L, 2)) {
triggerID = pLuaInterpreter->startTempBeginOfLineTrigger(pattern, QString(lua_tostring(L, 2)), expiryCount);
} else if (lua_isfunction(L, 2)) {
triggerID = pLuaInterpreter->startTempBeginOfLineTrigger(pattern, QString(), expiryCount);
auto trigger = host.getTriggerUnit()->getTrigger(triggerID);
trigger->mRegisteredAnonymousLuaFunction = true;
lua_pushlightuserdata(L, trigger);
lua_pushvalue(L, 2);
lua_settable(L, LUA_REGISTRYINDEX);
} else {
lua_pushfstring(L, "tempBeginOfLineTrigger: bad argument #2 type (code to run as a string or a function expected, got %s!)", luaL_typename(L, 2));
return lua_error(L);
}
lua_pushnumber(L, triggerID);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#tempTrigger
int TLuaInterpreter::tempTrigger(lua_State* L)
{
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
int triggerID;
int expiryCount = -1;
QString substringPattern = getVerifiedString(L, __func__, 1, "substring pattern");
if (lua_isnumber(L, 3)) {
expiryCount = lua_tonumber(L, 3);
if (expiryCount < 1) {
return warnArgumentValue(L, __func__, QStringLiteral(
"trigger expiration count must be nil or greater than zero, got %1").arg(expiryCount));
}
} else if (!lua_isnoneornil(L, 3)) {
lua_pushfstring(L, "tempTrigger: bad argument #3 value (trigger expiration count must be nil or a number, got %s!)", luaL_typename(L, 3));
return lua_error(L);
}
if (lua_isstring(L, 2)) {
triggerID = pLuaInterpreter->startTempTrigger(substringPattern, QString(lua_tostring(L, 2)), expiryCount);
} else if (lua_isfunction(L, 2)) {
triggerID = pLuaInterpreter->startTempTrigger(substringPattern, QString(), expiryCount);
auto trigger = host.getTriggerUnit()->getTrigger(triggerID);
trigger->mRegisteredAnonymousLuaFunction = true;
lua_pushlightuserdata(L, trigger);
lua_pushvalue(L, 2);
lua_settable(L, LUA_REGISTRYINDEX);
} else {
lua_pushfstring(L, "tempTrigger: bad argument #2 type (code to run as a string or a function expected, got %s!)", luaL_typename(L, 2));
return lua_error(L);
}
lua_pushnumber(L, triggerID);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#tempPromptTrigger
int TLuaInterpreter::tempPromptTrigger(lua_State* L)
{
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
int triggerID;
int expiryCount = -1;
if (lua_isnumber(L, 2)) {
expiryCount = lua_tonumber(L, 2);
if (expiryCount < 1) {
return warnArgumentValue(L, __func__, QStringLiteral(
"trigger expiration count must be nil or greater than zero, got %1").arg(expiryCount));
}
} else if (!lua_isnoneornil(L, 2)) {
lua_pushfstring(L, "tempPromptTrigger: bad argument #2 value (trigger expiration count must be nil or a number, got %s!)", luaL_typename(L, 2));
return lua_error(L);
}
if (lua_isstring(L, 1)) {
triggerID = pLuaInterpreter->startTempPromptTrigger(QString(lua_tostring(L, 1)), expiryCount);
} else if (lua_isfunction(L, 1)) {
triggerID = pLuaInterpreter->startTempPromptTrigger(QString(), expiryCount);
auto trigger = host.getTriggerUnit()->getTrigger(triggerID);
trigger->mRegisteredAnonymousLuaFunction = true;
lua_pushlightuserdata(L, trigger);
lua_pushvalue(L, 1);
lua_settable(L, LUA_REGISTRYINDEX);
} else {
lua_pushfstring(L, "tempPromptTrigger: bad argument #1 type (code to run as a string or a function expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
lua_pushnumber(L, triggerID);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#tempColorTrigger
// This is documented as using a simple color table - but the numbers do not
// match ANSI numbering and fixing that would break existing scripts.
int TLuaInterpreter::tempColorTrigger(lua_State* L)
{
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
int value = getVerifiedInt(L, __func__, 1, "foreground color");
// match ANSI numbering and fixing that would break existing scripts so it has
// to be tweaked here (and in the Mudlet XML save file format!)
int foregroundColor = TTrigger::scmIgnored;
// clang-format off
switch (value) {
case 0: foregroundColor = TTrigger::scmDefault; break; // Default foreground colour
case 1: foregroundColor = 8; break; // light black (dark gray)
case 2: foregroundColor = 0; break; // black
case 3: foregroundColor = 9; break; // light red
case 4: foregroundColor = 1; break; // red
case 5: foregroundColor = 10; break; // light green
case 6: foregroundColor = 2; break; // green
case 7: foregroundColor = 11; break; // light yellow
case 8: foregroundColor = 3; break; // yellow
case 9: foregroundColor = 12; break; // light blue
case 10: foregroundColor = 4; break; // blue
case 11: foregroundColor = 13; break; // light magenta
case 12: foregroundColor = 5; break; // magenta
case 13: foregroundColor = 14; break; // light cyan
case 14: foregroundColor = 6; break; // cyan
case 15: foregroundColor = 15; break; // light white
case 16: foregroundColor = 7; break; // white (light gray)
// The default includes case -1: foregroundColor = TTrigger::scmIgnored
// which means only consider the background color now (and that cannot be
// set to this value) - NOTE: TTrigger::scmIgnored has been set to BE -1
// when it was added after Mudlet 3.7.1 but if that is subsequently changed
// it will break the API for this lua function
// other colours in ANSI 256 colours handled but not mentioned in Wiki
default: foregroundColor = value; break;
// clang-format on
}
value = getVerifiedInt(L, __func__, 2, "background color");
int backgroundColor = TTrigger::scmIgnored;
// clang-format off
switch (value) {
case 0: backgroundColor = TTrigger::scmDefault; break; // Default background colour
case 1: backgroundColor = 8; break; // light black (dark gray)
case 2: backgroundColor = 0; break; // black
case 3: backgroundColor = 9; break; // light red
case 4: backgroundColor = 1; break; // red
case 5: backgroundColor = 10; break; // light green
case 6: backgroundColor = 2; break; // green
case 7: backgroundColor = 11; break; // light yellow
case 8: backgroundColor = 3; break; // yellow
case 9: backgroundColor = 12; break; // light blue
case 10: backgroundColor = 4; break; // blue
case 11: backgroundColor = 13; break; // light magenta
case 12: backgroundColor = 5; break; // magenta
case 13: backgroundColor = 14; break; // light cyan
case 14: backgroundColor = 6; break; // cyan
case 15: backgroundColor = 15; break; // light white
case 16: backgroundColor = 7; break; // white (light gray)
// The default includes case -1: backgroundColor = TTrigger::scmIgnored
// but this cannot be used for the foreground case at the same time:
default: backgroundColor = value; break;
// clang-format on
}
if (foregroundColor == TTrigger::scmIgnored && backgroundColor == TTrigger::scmIgnored) {
return warnArgumentValue(L, __func__, "only one of foreground and background colors can be -1 (ignored)");
}
int triggerID;
int expiryCount = -1;
if (lua_isnumber(L, 4)) {
expiryCount = lua_tonumber(L, 4);
if (expiryCount < 1) {
return warnArgumentValue(L, __func__, QStringLiteral(
"trigger expiration count must be greater than zero, got %1").arg(expiryCount));
}
} else if (!lua_isnoneornil(L, 4)) {
lua_pushfstring(L, "tempColorTrigger: bad argument #4 value (trigger expiration count must be nil or a number, got %s!)", luaL_typename(L, 4));
return lua_error(L);
}
if (lua_isstring(L, 3)) {
triggerID = pLuaInterpreter->startTempColorTrigger(foregroundColor, backgroundColor, QString(lua_tostring(L, 3)), expiryCount);
} else if (lua_isfunction(L, 3)) {
triggerID = pLuaInterpreter->startTempColorTrigger(foregroundColor, backgroundColor, QString(), expiryCount);
auto trigger = host.getTriggerUnit()->getTrigger(triggerID);
trigger->mRegisteredAnonymousLuaFunction = true;
lua_pushlightuserdata(L, trigger);
lua_pushvalue(L, 3);
lua_settable(L, LUA_REGISTRYINDEX);
} else {
lua_pushfstring(L, "tempColorTrigger: bad argument #3 type (code to run as a string or a function expected, got %s!)", luaL_typename(L, 3));
return lua_error(L);
}
lua_pushnumber(L, triggerID);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Mudlet_Object_Functions#tempAnsiColorTrigger
// This is the replacement for tempColorTrigger() which uses the right numbers
// for ANSI colours in the range 0 to 255 or TTrigger::scmDefault for default
// colour or TTrigger::scmIgnored ignore; it is anticipated that additional
// special values less than zero may be added to detect other types of text (or
// for a 16M colour value where the components have to be given)
// Note that this function has four arguments, of which the *second* may be omitted. :-/
int TLuaInterpreter::tempAnsiColorTrigger(lua_State* L)
{
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
QString code;
int ansiFgColor = TTrigger::scmIgnored;
int ansiBgColor = TTrigger::scmIgnored;
int s = 0;
if (!lua_isnumber(L, ++s)) {
lua_pushfstring(L, "tempAnsiColorTrigger: bad argument #%d type (foreground color as ANSI Color number {%d = ignore foreground color, %d = default color, 0 to 255 ANSI color} expected, got %s!)",
s, TTrigger::scmIgnored, TTrigger::scmDefault, luaL_typename(L, s));
return lua_error(L);
}
{ // separate block so that "value" is not scoped to the whole function
int value = lua_tointeger(L, s);
if (value == TTrigger::scmIgnored && lua_gettop(L) < 2) {
return warnArgumentValue(L, __func__, QStringLiteral(
"invalid ANSI color number %1, it cannot be used (to ignore the foreground color) if the background color is ommitted")
.arg(value));
}
// At present we limit the range to (Trigger::scmIgnored),
// (Trigger::scmDefault) and 0-255 ANSI colors - in the future we could
// extend it to other "coded" values for locally generated textual
// content
if (!(value == TTrigger::scmIgnored || value == TTrigger::scmDefault || (value >= 0 && value <= 255))) {
return warnArgumentValue(L, __func__, QStringLiteral(
"invalid ANSI color number %1, only %2 (ignore foreground color), %3 (default foregroud color) or 0 to 255 recognised")
.arg(QString::number(value), QString::number(TTrigger::scmIgnored), QString::number(TTrigger::scmDefault)));
}
if (value == TTrigger::scmIgnored && lua_gettop(L) < 4) {
return warnArgumentValue(L, __func__, QStringLiteral(
"invalid ANSI color number %1, you cannot ignore both foreground and background color (omitted)").arg(value));
}
ansiFgColor = value;
}
// s=1 at this point. If top=4 the next argument must be the BG color number,
// otherwise it may have been omitted.
if (lua_gettop(L) < s+3 && !lua_isnumber(L, s+1)) {
// BG color omitted, skip this part
} else if (!lua_isnumber(L, ++s)) {
lua_pushfstring(L, "tempAnsiColorTrigger: bad argument #%d type (background color as ANSI Color number {%d = ignore foreground color, %d = default color, 0 to 255 ANSI color} expected, got %s!)",
s, TTrigger::scmIgnored, TTrigger::scmDefault, luaL_typename(L, s));
return lua_error(L);
} else {
int value = lua_tointeger(L, s);
if (!(value == TTrigger::scmIgnored || value == TTrigger::scmDefault || (value >= 0 && value <= 255))) {
return warnArgumentValue(L, __func__, QStringLiteral(
"invalid ANSI color number %1, only %2 (ignore background color), %3 (default background color) or 0 to 255 recognised")
.arg(QString::number(value), QString::number(TTrigger::scmIgnored), QString::number(TTrigger::scmDefault)));
} else if (value == TTrigger::scmIgnored && ansiFgColor == TTrigger::scmIgnored) {
return warnArgumentValue(L, __func__, QStringLiteral(
"invalid ANSI color number %1, you cannot ignore both foreground and background color")
.arg(value));
} else {
ansiBgColor = value;
}
}
if (lua_isstring(L, ++s)) {
code = QString::fromUtf8(lua_tostring(L, s));
} else if (lua_isfunction(L, s)) {
// leave code as a null QString(), see below
} else {
lua_pushfstring(L, "tempAnsiColorTrigger: bad argument #%d type (code to run as a string or a function expected, got %s!)", s, luaL_typename(L, s));
return lua_error(L);
}
int expiryCount = -1;
if (lua_isnumber(L, ++s)) {
expiryCount = lua_tonumber(L, s);
if (expiryCount < 1) {
return warnArgumentValue(L, __func__, QStringLiteral(
"trigger expiration count must be nil or greater than zero, got %1").arg(expiryCount));
}
} else if (!lua_isnoneornil(L, ++s)) {
lua_pushfstring(L, "tempAnsiColorTrigger: bad argument #%d value (trigger expiration count must be a number, got %s!)", s, luaL_typename(L, s));
return lua_error(L);
}
int triggerID = pLuaInterpreter->startTempColorTrigger(ansiFgColor, ansiBgColor, code, expiryCount);
if (code.isNull()) {
auto trigger = host.getTriggerUnit()->getTrigger(triggerID);
trigger->mRegisteredAnonymousLuaFunction = true;
lua_pushlightuserdata(L, trigger);
lua_pushvalue(L, s-1);
lua_settable(L, LUA_REGISTRYINDEX);
}
lua_pushnumber(L, triggerID);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#tempLineTrigger
int TLuaInterpreter::tempLineTrigger(lua_State* L)
{
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
int from = getVerifiedInt(L, __func__, 1, "line to start matching from");
int howMany = getVerifiedInt(L, __func__, 2, "how many lines to match for");
int triggerID;
int expiryCount = -1;
if (lua_isnumber(L, 4)) {
expiryCount = lua_tonumber(L, 4);
if (expiryCount < 1) {
return warnArgumentValue(L, __func__, QStringLiteral(
"trigger expiration count must be nil or greater than zero, got %1").arg(expiryCount));
}
} else if (!lua_isnoneornil(L, 4)) {
lua_pushfstring(L, "tempLineTrigger: bad argument #4 value (trigger expiration count must be nil or a number, got %s!)", luaL_typename(L, 4));
return lua_error(L);
}
if (lua_isstring(L, 3)) {
triggerID = pLuaInterpreter->startTempLineTrigger(from, howMany, QString(lua_tostring(L, 3)), expiryCount);
} else if (lua_isfunction(L, 3)) {
triggerID = pLuaInterpreter->startTempLineTrigger(from, howMany, QString(), expiryCount);
auto trigger = host.getTriggerUnit()->getTrigger(triggerID);
trigger->mRegisteredAnonymousLuaFunction = true;
lua_pushlightuserdata(L, trigger);
lua_pushvalue(L, 3);
lua_settable(L, LUA_REGISTRYINDEX);
} else {
lua_pushfstring(L, "tempLineTrigger: bad argument #3 type (code to run as a string or a function expected, got %s!)", luaL_typename(L, 3));
return lua_error(L);
}
lua_pushnumber(L, triggerID);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#tempComplexRegexTrigger
int TLuaInterpreter::tempComplexRegexTrigger(lua_State* L)
{
Host& host = getHostFromLua(L);
QString triggerName = getVerifiedString(L, __func__, 1, "trigger name create or add to");
QString pattern = getVerifiedString(L, __func__, 2, "regex pattern to match");
if (!lua_isstring(L, 3) && !lua_isfunction(L, 3)) {
lua_pushfstring(L, "tempComplexRegexTrigger: bad argument #3 type (code to run as a string or a function expected, got %s!)", luaL_typename(L, 3));
return lua_error(L);
}
if (!lua_isnumber(L, 4)) {
lua_pushfstring(L, "tempComplexRegexTrigger: bad argument #4 type (multiline flag as number expected, got %s!)", luaL_typename(L, 4));
return lua_error(L);
}
bool multiLine = lua_tonumber(L, 4);
if (!lua_isnumber(L, 7)) {
lua_pushfstring(L, "tempComplexRegexTrigger: bad argument #7 type (filter flag as number expected, got %s!)", luaL_typename(L, 7));
return lua_error(L);
}
bool filter = lua_tonumber(L, 7);
if (!lua_isnumber(L, 8)) {
lua_pushfstring(L, "tempComplexRegexTrigger: bad argument #8 type (match all flag as number expected, got %s!)", luaL_typename(L, 8));
return lua_error(L);
}
bool matchAll = lua_tonumber(L, 8);
int fireLength = getVerifiedInt(L, __func__, 12, "fire length");
int lineDelta = getVerifiedInt(L, __func__, 13, "line delta");
bool colorTrigger;
QString fgColor;
if (lua_isnumber(L, 5)) {
colorTrigger = false;
} else {
colorTrigger = true;
fgColor = lua_tostring(L, 5);
}
QString bgColor;
if (lua_isnumber(L, 6)) {
colorTrigger = false;
} else {
bgColor = lua_tostring(L, 6);
}
bool highlight;
QColor hlFgColor;
if (lua_isnumber(L, 9)) {
highlight = false;
} else {
highlight = true;
hlFgColor.setNamedColor(lua_tostring(L, 9));
}
QColor hlBgColor;
if (lua_isnumber(L, 10)) {
highlight = false;
} else {
highlight = true;
hlBgColor.setNamedColor(lua_tostring(L, 10));
}
QString soundFile;
bool playSound;
if (lua_isstring(L, 11)) {
playSound = true;
soundFile = lua_tostring(L, 11);
} else {
playSound = false;
}
int expiryCount = -1;
if (lua_isnumber(L, 14)) {
expiryCount = lua_tonumber(L, 14);
if (expiryCount < 1) {
return warnArgumentValue(L, __func__, QStringLiteral(
"trigger expiration count must be nil or greater than zero, got %1").arg(expiryCount));
}
} else if (!lua_isnoneornil(L, 14)) {
lua_pushfstring(L, "tempComplexRegexTrigger: bad argument #14 value (trigger expiration count must be nil or a number, got %s!)", luaL_typename(L, 14));
return lua_error(L);
}
QStringList regexList;
QList<int> propertyList;
TTrigger* pP = host.getTriggerUnit()->findTrigger(triggerName);
if (!pP) {
regexList << pattern;
if (colorTrigger) {
propertyList << REGEX_COLOR_PATTERN;
} else {
propertyList << REGEX_PERL;
}
} else {
regexList = pP->getRegexCodeList();
propertyList = pP->getRegexCodePropertyList();
}
auto pT = new TTrigger("a", regexList, propertyList, multiLine, &host);
pT->setIsFolder(false);
pT->setIsActive(true);
pT->setTemporary(true);
pT->registerTrigger();
pT->setName(pattern);
pT->mPerlSlashGOption = matchAll; //match all
pT->mFilterTrigger = filter;
pT->setConditionLineDelta(lineDelta); //line delta
pT->mStayOpen = fireLength; //fire length
pT->mSoundTrigger = playSound; //sound trigger, need to set sound file if true
if (playSound) {
pT->setSound(soundFile);
}
pT->setIsColorizerTrigger(highlight); //highlight
pT->setExpiryCount(expiryCount);
if (highlight) {
pT->setColorizerFgColor(hlFgColor);
pT->setColorizerBgColor(hlBgColor);
}
if (lua_isstring(L, 3)) {
pT->setScript(lua_tostring(L, 3));
} else if (lua_isfunction(L, 3)) {
pT->setScript(QString());
pT->mRegisteredAnonymousLuaFunction = true;
lua_pushlightuserdata(L, pT);
lua_pushvalue(L, 3);
lua_settable(L, LUA_REGISTRYINDEX);
}
lua_pushstring(L, pattern.toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#tempButton
int TLuaInterpreter::tempButton(lua_State* L)
{
//args: parent, name, orientation
QString cmdButtonUp = "";
QString cmdButtonDown = "";
QString script = "";
QString toolbar;
QStringList nameL;
nameL << toolbar;
toolbar = getVerifiedString(L, __func__, 1, "toolbar name");
QString name = getVerifiedString(L, __func__, 2, "button text");
int orientation = getVerifiedInt(L, __func__, 3, "orientation");
Host& host = getHostFromLua(L);
TAction* pP = host.getActionUnit()->findAction(toolbar);
if (!pP) {
return 0;
}
TAction* pT = host.getActionUnit()->findAction(name);
if (pT) {
return 0;
}
pT = new TAction(pP, &host);
pT->setName(name);
pT->setCommandButtonUp(cmdButtonUp);
pT->setCommandButtonDown(cmdButtonDown);
pT->setIsPushDownButton(false);
pT->mLocation = pP->mLocation;
pT->mOrientation = orientation;
pT->setScript(script);
pT->setIsFolder(false);
pT->setIsActive(true);
// pT->setIsPushDownButton( isChecked );
// pT->mLocation = location;
// pT->mOrientation = orientation;
// pT->setIsActive( pT->shouldBeActive() );
// pT->setButtonColor( color );
// pT->setButtonRotation( rotation );
// pT->setButtonColumns( columns );
//// pT->setButtonFlat( flatButton );
// pT->mUseCustomLayout = useCustomLayout;
// pT->mPosX = posX;
// pT->mPosY = posY;
// pT->mSizeX = sizeX;
// pT->mSizeY = sizeY;
// pT->css = mpActionsMainArea->css->toPlainText();
pT->registerAction();
// N/U: int childID = pT->getID();
host.getActionUnit()->updateToolbar();
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setButtonStyleSheet
int TLuaInterpreter::setButtonStyleSheet(lua_State* L)
{
//args: name, css text
QString name = getVerifiedString(L, __func__, 1, "name");
QString css = getVerifiedString(L, __func__, 2, "css");
Host& host = getHostFromLua(L);
auto actionsList = host.getActionUnit()->findActionsByName(name);
if (actionsList.empty()) {
return warnArgumentValue(L, __func__, QStringLiteral("no button named '%1' found").arg(name));
}
for (auto action : actionsList) {
action->css = css;
}
host.getActionUnit()->updateToolbar();
lua_pushboolean(L, 1);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#tempButtonToolbar
int TLuaInterpreter::tempButtonToolbar(lua_State* L)
{
QString name;
QString cmdButtonUp = "";
QString cmdButtonDown = "";
QString script = "";
QStringList nameL;
nameL << name;
name = getVerifiedString(L, __func__, 1, "name");
int location = getVerifiedInt(L, __func__, 2, "location");
int orientation = getVerifiedInt(L, __func__, 3, "orientation");
if (location > 0) {
location++;
}
Host& host = getHostFromLua(L);
TAction* pT = host.getActionUnit()->findAction(name);
if (pT) {
return 0;
}
//insert a new root item
//ROOT_ACTION:
pT = new TAction(name, &host);
pT->setCommandButtonUp(cmdButtonUp);
QStringList nl;
nl << name;
pT->setName(name);
pT->setCommandButtonUp(cmdButtonUp);
pT->setCommandButtonDown(cmdButtonDown);
pT->setIsPushDownButton(false);
pT->mLocation = location;
pT->mOrientation = orientation;
pT->setScript(script);
pT->setIsFolder(true);
pT->setIsActive(true);
pT->registerAction();
// N/U: int childID = pT->getID();
host.getActionUnit()->updateToolbar();
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#tempRegexTrigger
int TLuaInterpreter::tempRegexTrigger(lua_State* L)
{
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
int triggerID;
int expiryCount = -1;
QString regexPattern = getVerifiedString(L, __func__, 1, "regex pattern");
if (lua_isnumber(L, 3)) {
expiryCount = lua_tonumber(L, 3);
if (expiryCount < 1) {
return warnArgumentValue(L, __func__, QStringLiteral(
"trigger expiration count must be nil or greater than zero, got %1").arg(expiryCount));
}
} else if (!lua_isnoneornil(L, 3)) {
lua_pushfstring(L, "tempRegexTrigger: bad argument #3 value (trigger expiration count must be nil or a number, got %s!)", luaL_typename(L, 3));
return lua_error(L);
}
if (lua_isstring(L, 2)) {
triggerID = pLuaInterpreter->startTempRegexTrigger(regexPattern, lua_tostring(L, 2), expiryCount);
} else if (lua_isfunction(L, 2)) {
triggerID = pLuaInterpreter->startTempRegexTrigger(regexPattern, QString(), expiryCount);
auto trigger = host.getTriggerUnit()->getTrigger(triggerID);
trigger->mRegisteredAnonymousLuaFunction = true;
lua_pushlightuserdata(L, trigger);
lua_pushvalue(L, 2);
lua_settable(L, LUA_REGISTRYINDEX);
} else {
lua_pushfstring(L, "tempRegexTrigger: bad argument #2 type (code to run as a string or a function expected, got %s!)", luaL_typename(L, 2));
return lua_error(L);
}
lua_pushnumber(L, triggerID);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#tempAlias
int TLuaInterpreter::tempAlias(lua_State* L)
{
QString regex = getVerifiedString(L, __func__, 1, "regex-type pattern");
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
if (lua_isfunction(L, 2)) {
int result = pLuaInterpreter->startTempAlias(regex, QString());
if (result == -1) {
lua_pushnumber(L, -1);
return 2;
}
TAlias* alias = host.getAliasUnit()->getAlias(result);
Q_ASSERT_X(alias,
"TLuaInterpreter::tempAlias(...)",
"Got a positive result from LuaInterpreter::startTempAlias(...) but that failed to produce pointer to it from Host::mAliasUnit::getAlias(...)");
alias->mRegisteredAnonymousLuaFunction = true;
lua_pushlightuserdata(L, alias);
lua_pushvalue(L, 2);
lua_settable(L, LUA_REGISTRYINDEX);
lua_pushnumber(L, result);
return 1;
}
if (!lua_isstring(L, 2)) {
lua_pushfstring(L, "tempAlias: bad argument #2 type (lua script as string or function expected, got %s!)", luaL_typename(L, 2));
return lua_error(L);
}
QString script{lua_tostring(L, 2)};
lua_pushnumber(L, pLuaInterpreter->startTempAlias(regex, script));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#exists
int TLuaInterpreter::exists(lua_State* L)
{
QString name = getVerifiedString(L, __func__, 1, "name");
QString type = getVerifiedString(L, __func__, 2, "type");
Host& host = getHostFromLua(L);
int count = 0;
type = type.toLower();
if (type == QStringLiteral("timer")) {
count = host.getTimerUnit()->mLookupTable.count(name);
} else if (type == QStringLiteral("trigger")) {
count = host.getTriggerUnit()->mLookupTable.count(name);
} else if (type == QStringLiteral("alias")) {
count = host.getAliasUnit()->mLookupTable.count(name);
} else if (type == QStringLiteral("keybind")) {
count = host.getKeyUnit()->mLookupTable.count(name);
} else if (type == QStringLiteral("button")) {
count = host.getActionUnit()->findActionsByName(name).size();
} else if (type == QStringLiteral("script")) {
count = host.getScriptUnit()->findScriptId(name).size();
} else {
return warnArgumentValue(L, __func__, QStringLiteral(
"invalid item type '%1' given, it should be one of: 'alias', 'button', 'script', 'keybind', 'timer' or 'trigger'").arg(type));
}
lua_pushnumber(L, count);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#isActive
int TLuaInterpreter::isActive(lua_State* L)
{
QString name = getVerifiedString(L, __func__, 1, "item name");
// Although we only use 4 ASCII strings the user may not enter a purely
// ASCII value which we might have to report...
QString type = getVerifiedString(L, __func__, 2, "item type");
Host& host = getHostFromLua(L);
int cnt = 0;
if (type.compare(QLatin1String("timer"), Qt::CaseInsensitive) == 0) {
QMap<QString, TTimer*>::const_iterator it1 = host.getTimerUnit()->mLookupTable.constFind(name);
while (it1 != host.getTimerUnit()->mLookupTable.cend() && it1.key() == name) {
if (it1.value()->isActive()) {
cnt++;
}
it1++;
}
} else if (type.compare(QLatin1String("trigger"), Qt::CaseInsensitive) == 0) {
QMap<QString, TTrigger*>::const_iterator it1 = host.getTriggerUnit()->mLookupTable.constFind(name);
while (it1 != host.getTriggerUnit()->mLookupTable.cend() && it1.key() == name) {
if (it1.value()->isActive()) {
cnt++;
}
it1++;
}
} else if (type.compare(QLatin1String("alias"), Qt::CaseInsensitive) == 0) {
QMap<QString, TAlias*>::const_iterator it1 = host.getAliasUnit()->mLookupTable.constFind(name);
while (it1 != host.getAliasUnit()->mLookupTable.cend() && it1.key() == name) {
if (it1.value()->isActive()) {
cnt++;
}
it1++;
}
} else if (type.compare(QLatin1String("keybind"), Qt::CaseInsensitive) == 0) {
QMap<QString, TKey*>::const_iterator it1 = host.getKeyUnit()->mLookupTable.constFind(name);
while (it1 != host.getKeyUnit()->mLookupTable.cend() && it1.key() == name) {
if (it1.value()->isActive()) {
cnt++;
}
it1++;
}
} else if (type.compare(QLatin1String("button"), Qt::CaseInsensitive) == 0) {
QMap<int, TAction*> actions = host.getActionUnit()->getActionList();
for (auto action : actions) {
if (action->getName() == name && action->isActive()) {
++cnt;
}
}
} else if (type.compare(QLatin1String("script"), Qt::CaseInsensitive) == 0) {
QMap<int, TScript*> scripts = host.getScriptUnit()->getScriptList();
for (auto script : scripts) {
if (script->getName() == name && script->isActive()) {
++cnt;
}
}
} else {
return warnArgumentValue(L, __func__, QStringLiteral(
"invalid item type '%1' given, it should be one (case insensitive) of: 'alias', 'button', 'script', 'keybind', 'timer' or 'trigger'").arg(type));
}
lua_pushnumber(L, cnt);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#permAlias
int TLuaInterpreter::permAlias(lua_State* L)
{
QString name = getVerifiedString(L, __func__, 1, "alias name");
QString parent = getVerifiedString(L, __func__, 2, "alias group/parent");
QString regex = getVerifiedString(L, __func__, 3, "regexp pattern");
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
if (auto [validationResult, validationMessage] = pLuaInterpreter->validateLuaCodeParam(4); !validationResult) {
lua_pushfstring(L, "permAlias: bad argument #%d (%s)", 4, validationMessage.toUtf8().constData());
return lua_error(L);
}
QString script{lua_tostring(L, 4)};
auto [aliasId, message] = pLuaInterpreter->startPermAlias(name, parent, regex, script);
if (aliasId == -1) {
lua_pushfstring(L, "permAlias: cannot create alias (%s)", message.toUtf8().constData());
return lua_error(L);
}
lua_pushnumber(L, aliasId);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getScript
int TLuaInterpreter::getScript(lua_State* L)
{
int n = lua_gettop(L);
int pos = 1;
QString name = getVerifiedString(L, __func__, 1, "script name");
if (n > 1) {
pos = getVerifiedInt(L, __func__, 2, "script position");
}
Host& host = getHostFromLua(L);
auto ids = host.getScriptUnit()->findScriptId(name);
auto pS = host.getScriptUnit()->getScript(ids.value(--pos, -1));
if (!pS) {
lua_pushnumber(L, -1);
lua_pushstring(L, QStringLiteral("script \"%1\" at position \"%2\" not found").arg(name).arg(++pos).toUtf8().constData());
return 2;
}
int id = pS->getID();
lua_pushstring(L, pS->getScript().toUtf8().constData());
lua_pushnumber(L, id);
return 2;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setScript
int TLuaInterpreter::setScript(lua_State* L)
{
int n = lua_gettop(L);
int pos = 1;
QString name = getVerifiedString(L, __func__, 1, "script name");
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
if (auto [validationResult, validationMessage] = pLuaInterpreter->validateLuaCodeParam(2); !validationResult) {
lua_pushfstring(L, "setScript: bad argument #%d (%s)", 2, validationMessage.toUtf8().constData());
return lua_error(L);
}
QString luaCode{lua_tostring(L, 2)};
if (n > 2) {
pos = getVerifiedInt(L, __func__, 3, "script position");
}
auto [id, message] = pLuaInterpreter->setScriptCode(name, luaCode, --pos);
lua_pushnumber(L, id);
if (id == -1) {
lua_pushfstring(L, "permScript: cannot set script (%s)", message.toUtf8().constData());
return lua_error(L);
}
lua_pushnumber(L, id);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#permScript
int TLuaInterpreter::permScript(lua_State* L)
{
QString name = getVerifiedString(L, __func__, 1, "script name");
QString parent = getVerifiedString(L, __func__, 2, "script parent name");
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
if (auto [validationResult, validationMessage] = pLuaInterpreter->validateLuaCodeParam(3); !validationResult) {
lua_pushfstring(L, "permScript: bad argument #%d (%s)", 3, validationMessage.toUtf8().constData());
return lua_error(L);
}
QString luaCode{lua_tostring(L, 3)};
auto [id, message] = pLuaInterpreter->createPermScript(name, parent, luaCode);
if (id == -1) {
lua_pushfstring(L, "permScript: cannot create script (%s)", message.toUtf8().constData());
return lua_error(L);
}
lua_pushnumber(L, id);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#permTimer
int TLuaInterpreter::permTimer(lua_State* L)
{
QString name = getVerifiedString(L, __func__, 1, "timer name");
QString parent = getVerifiedString(L, __func__, 2, "timer parent name");
double time = getVerifiedDouble(L, __func__, 3, "time in seconds");
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
if (auto [validationResult, validationMessage] = pLuaInterpreter->validateLuaCodeParam(4); !validationResult) {
lua_pushfstring(L, "permTimer: bad argument #%d (%s)", 4, validationMessage.toUtf8().constData());
return lua_error(L);
}
QString luaCode{lua_tostring(L, 4)};
auto [id, message] = pLuaInterpreter->startPermTimer(name, parent, time, luaCode);
if (id == -1) {
lua_pushfstring(L, "permTimer: cannot create timer (%s)", message.toUtf8().constData());
return lua_error(L);
}
lua_pushnumber(L, id);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#permSubstringTrigger
int TLuaInterpreter::permSubstringTrigger(lua_State* L)
{
QString name = getVerifiedString(L, __func__, 1, "trigger name");
QString parent = getVerifiedString(L, __func__, 2, "trigger parent");
QStringList regList;
if (!lua_istable(L, 3)) {
lua_pushfstring(L, "permSubstringTrigger: bad argument #3 type (sub-strings list as table expected, got %s!)",
luaL_typename(L, 3));
return lua_error(L);
}
lua_pushnil(L);
while (lua_next(L, 3) != 0) {
// key at index -2 and value at index -1
if (lua_type(L, -1) == LUA_TSTRING) {
regList << lua_tostring(L, -1);
}
// removes value, but keeps key for next iteration
lua_pop(L, 1);
}
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
if (auto [validationResult, validationMessage] = pLuaInterpreter->validateLuaCodeParam(4); !validationResult) {
lua_pushfstring(L, "permSubstringTrigger: bad argument #%d (%s)", 4, validationMessage.toUtf8().constData());
return lua_error(L);
}
QString script{lua_tostring(L, 4)};
auto [triggerID, message] = pLuaInterpreter->startPermSubstringTrigger(name, parent, regList, script);
if(triggerID == - 1) {
lua_pushfstring(L, "permSubstringTrigger: cannot create trigger (%s)", message.toUtf8().constData());
return lua_error(L);
}
lua_pushnumber(L, triggerID);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#permPromptTrigger
int TLuaInterpreter::permPromptTrigger(lua_State* L)
{
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
QString triggerName = getVerifiedString(L, __func__, 1, "trigger name");
QString parentName = getVerifiedString(L, __func__, 2, "parent trigger name");
if (auto [validationResult, validationMessage] = pLuaInterpreter->validateLuaCodeParam(3); !validationResult) {
lua_pushfstring(L, "permPromptTrigger: bad argument #%d (%s)", 3, validationMessage.toUtf8().constData());
return lua_error(L);
}
QString luaFunction = lua_tostring(L, 3);
auto [triggerID, message] = pLuaInterpreter->startPermPromptTrigger(triggerName, parentName, luaFunction);
if(triggerID == - 1) {
lua_pushfstring(L, "permPromptTrigger: cannot create trigger (%s)", message.toUtf8().constData());
return lua_error(L);
}
lua_pushnumber(L, triggerID);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#permKey
int TLuaInterpreter::permKey(lua_State* L)
{
QString keyName = getVerifiedString(L, __func__, 1, "key name");
QString parentGroup = getVerifiedString(L, __func__, 2, "key parent group");
uint_fast8_t argIndex = 3;
int keyModifier = Qt::NoModifier;
if (lua_gettop(L) > 4) {
keyModifier = getVerifiedInt(L, __func__, 3, "key modifier", true);
argIndex++;
}
int keyCode = getVerifiedInt(L, __func__, argIndex, "key code");
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
if (auto [validationResult, validationMessage] = pLuaInterpreter->validateLuaCodeParam(++argIndex); !validationResult) {
lua_pushfstring(L, "permKey: bad argument #%d (%s)", argIndex, validationMessage.toUtf8().constData());
return lua_error(L);
}
QString luaFunction{lua_tostring(L, argIndex)};
auto [keyID, message] = pLuaInterpreter->startPermKey(keyName, parentGroup, keyCode, keyModifier, luaFunction);
if(keyID == - 1) {
lua_pushfstring(L, "permKey: cannot create key (%s)", message.toUtf8().constData());
return lua_error(L);
}
lua_pushnumber(L, keyID);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#tempKey
int TLuaInterpreter::tempKey(lua_State* L)
{
uint_fast8_t argIndex = 1;
int keyModifier = Qt::NoModifier;
if (lua_gettop(L) > 2) {
keyModifier = getVerifiedInt(L, __func__, 1, "key modifier", true);
argIndex++;
}
int keyCode = getVerifiedInt(L, __func__, argIndex, "key code");
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
if (lua_isfunction(L, ++argIndex)) {
int result = pLuaInterpreter->startTempKey(keyModifier, keyCode, QString());
if (result == -1) {
lua_pushnumber(L, -1);
return 2;
}
TKey* key = host.getKeyUnit()->getKey(result);
Q_ASSERT_X(key,
"TLuaInterpreter::tempKey(...)",
"Got a positive result from LuaInterpreter::startTempKey(...) but that failed to produce pointer to it from Host::mKeyUnit::getKey(...)");
key->mRegisteredAnonymousLuaFunction = true;
lua_pushlightuserdata(L, key);
lua_pushvalue(L, argIndex);
lua_settable(L, LUA_REGISTRYINDEX);
lua_pushnumber(L, result);
return 1;
}
if (!lua_isstring(L, argIndex)) {
lua_pushfstring(L, "tempKey: bad argument #%d type (lua script as string or function expected, got %s!)", argIndex, luaL_typename(L, argIndex));
return lua_error(L);
}
QString luaFunction{lua_tostring(L, argIndex)};
int timerID = pLuaInterpreter->startTempKey(keyModifier, keyCode, luaFunction);
lua_pushnumber(L, timerID);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#permBeginOfLineStringTrigger
int TLuaInterpreter::permBeginOfLineStringTrigger(lua_State* L)
{
QString name = getVerifiedString(L, __func__, 1, "trigger name");
QString parent = getVerifiedString(L, __func__, 2, "trigger parent");
QStringList regList;
if (!lua_istable(L, 3)) {
lua_pushfstring(L, "permBeginOfLineStringTrigger: bad argument #3 type (sub-strings list as table expected, got %s!)",
luaL_typename(L, 3));
return lua_error(L);
}
lua_pushnil(L);
while (lua_next(L, 3) != 0) {
// key at index -2 and value at index -1
if (lua_type(L, -1) == LUA_TSTRING) {
regList << lua_tostring(L, -1);
}
// removes value, but keeps key for next iteration
lua_pop(L, 1);
}
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
if (auto [validationResult, validationMessage] = pLuaInterpreter->validateLuaCodeParam(4); !validationResult) {
lua_pushfstring(L, "permBeginOfLineStringTrigger: bad argument #%d (%s)", 4, validationMessage.toUtf8().constData());
return lua_error(L);
}
QString script{lua_tostring(L, 4)};
auto [triggerId, message] = pLuaInterpreter->startPermBeginOfLineStringTrigger(name, parent, regList, script);
if (triggerId == -1) {
lua_pushfstring(L, "permRegexTrigger: cannot create trigger (%s)", message.toUtf8().constData());
return lua_error(L);
}
lua_pushnumber(L, triggerId);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#permRegexTrigger
int TLuaInterpreter::permRegexTrigger(lua_State* L)
{
QString name = getVerifiedString(L, __func__, 1, "trigger name");
QString parent = getVerifiedString(L, __func__, 2, "trigger parent");
QStringList regList;
if (!lua_istable(L, 3)) {
lua_pushfstring(L, "permRegexTrigger: bad argument #3 type (sub-strings list as table expected, got %s!)",
luaL_typename(L, 3));
return lua_error(L);
}
lua_pushnil(L);
while (lua_next(L, 3) != 0) {
// key at index -2 and value at index -1
if (lua_type(L, -1) == LUA_TSTRING) {
regList << lua_tostring(L, -1);
}
// removes value, but keeps key for next iteration
lua_pop(L, 1);
}
Host& host = getHostFromLua(L);
TLuaInterpreter* pLuaInterpreter = host.getLuaInterpreter();
if (auto [validationResult, validationMessage] = pLuaInterpreter->validateLuaCodeParam(4); !validationResult) {
lua_pushfstring(L, "permRegexTrigger: bad argument #%d (%s)", 4, validationMessage.toUtf8().constData());
return lua_error(L);
}
QString script{lua_tostring(L, 4)};
auto [triggerId, message] = pLuaInterpreter->startPermRegexTrigger(name, parent, regList, script);
if (triggerId == -1) {
lua_pushfstring(L, "permRegexTrigger: cannot create trigger (%s)", message.toUtf8().constData());
return lua_error(L);
}
lua_pushnumber(L, triggerId);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#invokeFileDialog
int TLuaInterpreter::invokeFileDialog(lua_State* L)
{
bool luaDir = getVerifiedBool(L, __func__, 1, "fileOrFolder");
QString title = getVerifiedString(L, __func__, 2, "dialogTitle");
if (!luaDir) {
QString fileName = QFileDialog::getExistingDirectory(nullptr, title, QDir::currentPath());
lua_pushstring(L, fileName.toUtf8().constData());
return 1;
} else {
QString fileName = QFileDialog::getOpenFileName(nullptr, title, QDir::currentPath());
lua_pushstring(L, fileName.toUtf8().constData());
return 1;
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getTimestamp
int TLuaInterpreter::getTimestamp(lua_State* L)
{
int n = lua_gettop(L);
int s = 1;
QString name;
if (n > 1) {
name = getVerifiedString(L, __func__, s++, "mini console, user window or buffer name {may be omitted for the \"main\" console}");
if (name == QLatin1String("main")) {
// clear it so it is treated as the main console below
name.clear();
}
}
qint64 luaLine = getVerifiedInt(L, __func__, s, "line number");
if (luaLine < 1) {
return warnArgumentValue(L, __func__, QStringLiteral(
"line number %1 invalid, it should be greater than zero").arg(luaLine));
}
Host& host = getHostFromLua(L);
if (name.isEmpty()) {
if (luaLine > 0 && luaLine < host.mpConsole->buffer.timeBuffer.size()) {
// CHECK: Lua starts counting at 1 but we are indexing into a C/C++
// structure but the previous code did not accept a zero line number
lua_pushstring(L, host.mpConsole->buffer.timeBuffer.at(luaLine).toUtf8().constData());
} else {
lua_pushstring(L, "getTimestamp: invalid line number");
}
return 1;
} else {
auto pC = host.mpConsole->mSubConsoleMap.value(name);
if (!pC) {
return warnArgumentValue(L, __func__, QStringLiteral("mini console, user window or buffer '%1' not found)").arg(name));
}
if (luaLine > 0 && luaLine < pC->buffer.timeBuffer.size()) {
lua_pushstring(L, pC->buffer.timeBuffer.at(luaLine).toUtf8().constData());
} else {
lua_pushstring(L, "getTimestamp: invalid line number");
}
return 1;
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setBorderColor
int TLuaInterpreter::setBorderColor(lua_State* L)
{
int luaRed = getVerifiedInt(L, __func__, 1, "red");
int luaGreen = getVerifiedInt(L, __func__, 2, "green");
int luaBlue = getVerifiedInt(L, __func__, 3, "blue");
Host& host = getHostFromLua(L);
QPalette framePalette;
framePalette.setColor(QPalette::Text, QColor(Qt::black));
framePalette.setColor(QPalette::Highlight, QColor(55, 55, 255));
framePalette.setColor(QPalette::Window, QColor(luaRed, luaGreen, luaBlue, 255));
host.mpConsole->mpMainFrame->setPalette(framePalette);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setRoomCoordinates
int TLuaInterpreter::setRoomCoordinates(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "roomID");
int x = getVerifiedInt(L, __func__, 2, "x");
int y = getVerifiedInt(L, __func__, 3, "y");
int z = getVerifiedInt(L, __func__, 4, "z");
Host& host = getHostFromLua(L);
lua_pushboolean(L, host.mpMap->setRoomCoordinates(id, x, y, z));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setCustomEnvColor
int TLuaInterpreter::setCustomEnvColor(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "environmentID");
int r = getVerifiedInt(L, __func__, 2, "r");
int g = getVerifiedInt(L, __func__, 3, "g");
int b = getVerifiedInt(L, __func__, 4, "b");
int alpha = getVerifiedInt(L, __func__, 5, "a");
Host& host = getHostFromLua(L);
host.mpMap->mCustomEnvColors[id] = QColor(r, g, b, alpha);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setAreaName
int TLuaInterpreter::setAreaName(lua_State* L)
{
int id = -1;
QString existingName;
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
if (lua_isnumber(L, 1)) {
id = lua_tonumber(L, 1);
if (id < 1) {
return warnArgumentValue(L, __func__, QStringLiteral(
"number %1 is not a valid area id as it is less than 1)").arg(id));
}
// Strangely, previous code allowed this command to create a NEW area's name
// with this ID, but without a TArea instance to accompany it (the latter was/is
// instantiated as needed when a room is moved to the relevent area...) and we
// need to continue to allow this - Slysven
// else if (!host.mpMap->mpRoomDB->getAreaIDList().contains(id)) {
// return warnArgumentValue(L, __func__, QStringLiteral(
// "number %1 is not a valid area id").arg(id));
// }
} else if (lua_isstring(L, 1)) {
existingName = lua_tostring(L, 1);
id = host.mpMap->mpRoomDB->getAreaNamesMap().key(existingName, 0);
if (existingName.isEmpty()) {
return warnArgumentValue(L, __func__, "area name cannot be empty");
} else if (!host.mpMap->mpRoomDB->getAreaNamesMap().values().contains(existingName)) {
return warnArgumentValue(L, __func__, QStringLiteral("area name '%1' does not exist").arg(existingName));
} else if (host.mpMap->mpRoomDB->getAreaNamesMap().value(-1).contains(existingName)) {
return warnArgumentValue(L, __func__, QStringLiteral(
"area name '%1' is reserved and protected - it cannot be changed").arg(existingName));
}
} else {
lua_pushfstring(L,
"setAreaName: bad argument #1 type (area id as number or area name as string\n"
"expected, got %s!)",
luaL_typename(L, 1));
return lua_error(L);
}
QString newName = getVerifiedString(L, __func__, 2, "area name").trimmed();
// Now allow non-Ascii names but eliminate any leading or trailing spaces
if (newName.isEmpty()) {
// Empty name not allowed (any more)
return warnArgumentValue(L, __func__, "area names may not be empty strings (and spaces are trimmed from the ends)");
} else if (host.mpMap->mpRoomDB->getAreaNamesMap().values().count(newName) > 0) {
// That name is already IN the areaNamesMap, and since we now enforce
// uniqueness there can be only one of it - so we can check if this is a
// problem or just pointless quite easily...!
if (host.mpMap->mpRoomDB->getAreaNamesMap().value(id) != newName) {
// And it isn't the trivial case, where the given areaID already IS that name
return warnArgumentValue(L, __func__, QStringLiteral(
"area names may not be duplicated and area id %1 already has the name '%2'")
.arg(QString::number(host.mpMap->mpRoomDB->getAreaNamesMap().key(newName)), newName));
}
// Renaming an area to the same name is pointlessly successful!
lua_pushboolean(L, true);
return 1;
}
bool isCurrentAreaRenamed = false;
if (host.mpMap->mpMapper) {
if (id > 0 && host.mpMap->mpRoomDB->getAreaNamesMap().value(id) == host.mpMap->mpMapper->showArea->currentText()) {
isCurrentAreaRenamed = true;
}
}
bool result = host.mpMap->mpRoomDB->setAreaName(id, newName);
if (result) {
if (host.mpMap->mpMapper) {
host.mpMap->mpMapper->updateAreaComboBox();
if (isCurrentAreaRenamed) {
host.mpMap->mpMapper->showArea->setCurrentText(newName);
}
updateMap(L);
}
}
lua_pushboolean(L, result);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getRoomAreaName
int TLuaInterpreter::getRoomAreaName(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int id;
QString name;
if (!lua_isnumber(L, 1)) {
if (!lua_isstring(L, 1)) {
lua_pushfstring(L,
"getRoomAreaName: bad argument #1 type (area id as number or area name as string\n"
"expected, got %s!)",
luaL_typename(L, 1));
return lua_error(L);
}
name = lua_tostring(L, 1);
} else {
id = lua_tonumber(L, 1);
}
if (!name.isNull()) {
int result = host.mpMap->mpRoomDB->getAreaNamesMap().key(name, -1);
lua_pushnumber(L, result);
if (result != -1) {
return 1;
} else {
lua_pushfstring(L, "getRoomAreaName: string '%s' is not a valid area name", name.toUtf8().constData());
return 2;
}
} else {
if (host.mpMap->mpRoomDB->getAreaNamesMap().contains(id)) {
lua_pushstring(L, host.mpMap->mpRoomDB->getAreaNamesMap().value(id).toUtf8().constData());
return 1;
} else {
lua_pushnumber(L, -1);
lua_pushfstring(L, "getRoomAreaName: number %d is not a valid area id", id);
return 2;
}
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#addAreaName
int TLuaInterpreter::addAreaName(lua_State* L)
{
QString name = getVerifiedString(L, __func__, 1, "area name").trimmed();
Host& host = getHostFromLua(L);
if ((!host.mpMap) || (!host.mpMap->mpRoomDB)) {
return warnArgumentValue(L, __func__, "no map present or loaded");
} else if (name.isEmpty()) {
// Empty names now not allowed
return warnArgumentValue(L, __func__, "area names may not be empty strings (and spaces are trimmed from the ends)");
} else if (host.mpMap->mpRoomDB->getAreaNamesMap().values().count(name) > 0) {
// That name is already IN the areaNamesMap
return warnArgumentValue(L, __func__, QStringLiteral("area names may not be duplicated and area id %1 already has the name '%2'")
.arg(QString::number(host.mpMap->mpRoomDB->getAreaNamesMap().key(name)), name));
}
// Note that adding an area name implicitly creates an underlying TArea instance
lua_pushnumber(L, host.mpMap->mpRoomDB->addArea(name));
// Update mapper Area names widget, using method designed for it...!
if (host.mpMap->mpMapper) {
host.mpMap->mpMapper->updateAreaComboBox();
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#deleteArea
int TLuaInterpreter::deleteArea(lua_State* L)
{
int id = 0;
QString name;
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
if (lua_isnumber(L, 1)) {
id = lua_tonumber(L, 1);
if (id < 1) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid area id greater than zero").arg(id));
}
if (!host.mpMap->mpRoomDB->getAreaIDList().contains(id) && !host.mpMap->mpRoomDB->getAreaNamesMap().contains(id)) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid area id").arg(id));
}
} else if (lua_isstring(L, 1)) {
name = lua_tostring(L, 1);
if (name.isEmpty()) {
return warnArgumentValue(L, __func__, "an empty string is not a valid area name");
} else if (!host.mpMap->mpRoomDB->getAreaNamesMap().values().contains(name)) {
return warnArgumentValue(L, __func__, QStringLiteral("string '%1' is not a valid area name").arg(name));
} else if (name == host.mpMap->getDefaultAreaName()) {
return warnArgumentValue(L, __func__, "you can't delete the default area");
}
} else {
lua_pushfstring(L,
"deleteArea: bad argument #1 type (area Id as number or area name as string\n"
"expected, got %s!)",
luaL_typename(L, 1));
return lua_error(L);
}
bool result = false;
if (!id) {
result = host.mpMap->mpRoomDB->removeArea(name);
} else {
result = host.mpMap->mpRoomDB->removeArea(id);
}
if (result) {
// Update mapper Area names widget, using method designed for it...!
if (host.mpMap->mpMapper) {
host.mpMap->mpMapper->updateAreaComboBox();
}
host.mpMap->mMapGraphNeedsUpdate = true;
}
lua_pushboolean(L, result);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#deleteRoom
int TLuaInterpreter::deleteRoom(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "roomID");
if (id <= 0) {
return 0;
}
Host& host = getHostFromLua(L);
lua_pushboolean(L, host.mpMap->mpRoomDB->removeRoom(id));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setExit
int TLuaInterpreter::setExit(lua_State* L)
{
int from = getVerifiedInt(L, __func__, 1, "from roomID");
int to = getVerifiedInt(L, __func__, 2, "to roomID");
int dir = dirToNumber(L, 3);
if (!dir) {
lua_pushfstring(L, "setExit: bad argument #3 type (direction as number or string expected, got %s!)", luaL_typename(L, 3));
return lua_error(L);
}
Host& host = getHostFromLua(L);
lua_pushboolean(L, host.mpMap->setExit(from, to, dir));
host.mpMap->mMapGraphNeedsUpdate = true;
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getRoomCoordinates
int TLuaInterpreter::getRoomCoordinates(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "roomID");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (!pR) {
lua_pushnil(L);
lua_pushnil(L);
lua_pushnil(L);
return 3;
} else {
lua_pushnumber(L, pR->x);
lua_pushnumber(L, pR->y);
lua_pushnumber(L, pR->z);
return 3;
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getRoomArea
int TLuaInterpreter::getRoomArea(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "roomID");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (!pR) {
lua_pushnil(L);
} else {
lua_pushnumber(L, pR->getArea());
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#roomExists
int TLuaInterpreter::roomExists(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "roomID");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (pR) {
lua_pushboolean(L, true);
} else {
lua_pushboolean(L, false);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#addRoom
int TLuaInterpreter::addRoom(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "room id");
Host& host = getHostFromLua(L);
bool added = host.mpMap->addRoom(id);
lua_pushboolean(L, added);
if (added) {
host.mpMap->setRoomArea(id, -1, false);
host.mpMap->mMapGraphNeedsUpdate = true;
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#createRoomID
int TLuaInterpreter::createRoomID(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
if (lua_gettop(L) > 0) {
int minId = getVerifiedInt(L, __func__, 1, "minimum room Id", true);
if (minId < 1) {
return warnArgumentValue(L, __func__, QStringLiteral(
"minimum room id %1 is an optional value but if provided it must be greater than zero").arg(minId));
}
lua_pushnumber(L, host.mpMap->createNewRoomID(lua_tointeger(L, 1)));
} else {
lua_pushnumber(L, host.mpMap->createNewRoomID());
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#unHighlightRoom
int TLuaInterpreter::unHighlightRoom(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "roomID");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (pR) {
pR->highlight = false;
if (host.mpMap) {
if (host.mpMap->mpMapper) {
host.mpMap->mpMapper->mp2dMap->update();
}
}
lua_pushboolean(L, true);
} else {
lua_pushboolean(L, false);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#highlightRoom
int TLuaInterpreter::highlightRoom(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "roomID");
int fgr = getVerifiedInt(L, __func__, 2, "color1Red");
int fgg = getVerifiedInt(L, __func__, 3, "color1Green");
int fgb = getVerifiedInt(L, __func__, 4, "color1Blue");
int bgr = getVerifiedInt(L, __func__, 5, "color2Red");
int bgg = getVerifiedInt(L, __func__, 6, "color2Green");
int bgb = getVerifiedInt(L, __func__, 7, "color2Blue");
float radius = getVerifiedFloat(L, __func__, 8, "highlightRadius");
int alpha1 = getVerifiedInt(L, __func__, 9, "color1Alpha");
int alpha2 = getVerifiedInt(L, __func__, 10, "color2Alpha");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (pR) {
auto fg = QColor(fgr, fgg, fgb, alpha1);
auto bg = QColor(bgr, bgg, bgb, alpha2);
pR->highlight = true;
pR->highlightColor = fg;
pR->highlightColor2 = bg;
pR->highlightRadius = radius;
if (host.mpMap->mpMapper) {
if (host.mpMap->mpMapper->mp2dMap) {
host.mpMap->mpMapper->mp2dMap->update();
}
}
lua_pushboolean(L, true);
} else {
lua_pushboolean(L, false);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#createMapLabel
int TLuaInterpreter::createMapLabel(lua_State* L)
{
int fontSize = 50;
float zoom = 30.0;
bool showOnTop = true;
bool noScaling = true;
int args = lua_gettop(L);
int area = getVerifiedInt(L, __func__, 1, "areaID");
QString text = getVerifiedString(L, __func__, 2, "text");
float posx = getVerifiedFloat(L, __func__, 3, "posX");
float posy = getVerifiedFloat(L, __func__, 4, "posY");
float posz = getVerifiedFloat(L, __func__, 5, "posZ");
int fgr = getVerifiedInt(L, __func__, 6, "fgRed");
int fgg = getVerifiedInt(L, __func__, 7, "fgGreen");
int fgb = getVerifiedInt(L, __func__, 8, "fgBlue");
int bgr = getVerifiedInt(L, __func__, 9, "bgRed");
int bgg = getVerifiedInt(L, __func__, 10, "bgGreen");
int bgb = getVerifiedInt(L, __func__, 11, "bgBlue");
if (args > 11) {
zoom = getVerifiedFloat(L, __func__, 12, "zoom", true);
fontSize = getVerifiedInt(L, __func__, 13, "fontSize", true);
if (args > 13) {
showOnTop = getVerifiedBool(L, __func__, 14, "showOnTop", true);
if (args > 14) {
noScaling = getVerifiedBool(L, __func__, 15, "noScaling", true);
}
}
}
Host& host = getHostFromLua(L);
lua_pushinteger(L, host.mpMap->createMapLabel(area, text, posx, posy, posz, QColor(fgr, fgg, fgb), QColor(bgr, bgg, bgb), showOnTop, noScaling, zoom, fontSize));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setMapZoom
int TLuaInterpreter::setMapZoom(lua_State* L)
{
qreal zoom = getVerifiedFloat(L, __func__, 1, "zoom");
Host& host = getHostFromLua(L);
if (host.mpMap) {
if (host.mpMap->mpMapper) {
if (host.mpMap->mpMapper->mp2dMap) {
host.mpMap->mpMapper->mp2dMap->setMapZoom(zoom);
updateMap(L);
}
}
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#createMapImageLabel
int TLuaInterpreter::createMapImageLabel(lua_State* L)
{
int area = getVerifiedInt(L, __func__, 1, "areaID");
QString imagePathFileName = getVerifiedString(L, __func__, 2, "imagePathFileName");
float posx = getVerifiedFloat(L, __func__, 3, "posX");
float posy = getVerifiedFloat(L, __func__, 4, "posY");
float posz = getVerifiedFloat(L, __func__, 5, "posZ");
float width = getVerifiedFloat(L, __func__, 6, "width");
float height = getVerifiedFloat(L, __func__, 7, "height");
float zoom = getVerifiedFloat(L, __func__, 8, "zoom");
bool showOnTop = getVerifiedBool(L, __func__, 9, "showOnTop");
Host& host = getHostFromLua(L);
lua_pushinteger(L, host.mpMap->createMapImageLabel(area, imagePathFileName, posx, posy, posz, width, height, zoom, showOnTop));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setDoor
int TLuaInterpreter::setDoor(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int roomId = getVerifiedInt(L, __func__, 1, "room id");
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomId);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid room id").arg(roomId));
}
QString exitCmd = getVerifiedString(L, __func__, 2, "door command");
if (exitCmd.compare(QStringLiteral("n")) && exitCmd.compare(QStringLiteral("e")) && exitCmd.compare(QStringLiteral("s")) && exitCmd.compare(QStringLiteral("w"))
&& exitCmd.compare(QStringLiteral("ne"))
&& exitCmd.compare(QStringLiteral("se"))
&& exitCmd.compare(QStringLiteral("sw"))
&& exitCmd.compare(QStringLiteral("nw"))
&& exitCmd.compare(QStringLiteral("up"))
&& exitCmd.compare(QStringLiteral("down"))
&& exitCmd.compare(QStringLiteral("in"))
&& exitCmd.compare(QStringLiteral("out"))) {
// One of the above WILL BE ZERO if the exitCmd is ONE of the above QStringLiterals
// So the above will be TRUE if NONE of above strings match - which
// means we must treat the exitCmd as a SPECIAL exit
if (!(pR->getSpecialExits().contains(exitCmd))) {
// And NOT a special one either
return warnArgumentValue(L, __func__, QStringLiteral(
"room with id %1 does not have a special exit in direction '%2'")
.arg(QString::number(roomId), exitCmd));
}
// else IS a valid special exit - so fall out of if and continue
} else {
// Is a normal exit so see if it is valid
if (!(((!exitCmd.compare(QStringLiteral("n"))) && (pR->getExit(DIR_NORTH) > 0 || pR->exitStubs.contains(DIR_NORTH)))
|| ((!exitCmd.compare(QStringLiteral("e"))) && (pR->getExit(DIR_EAST) > 0 || pR->exitStubs.contains(DIR_EAST)))
|| ((!exitCmd.compare(QStringLiteral("s"))) && (pR->getExit(DIR_SOUTH) > 0 || pR->exitStubs.contains(DIR_SOUTH)))
|| ((!exitCmd.compare(QStringLiteral("w"))) && (pR->getExit(DIR_WEST) > 0 || pR->exitStubs.contains(DIR_WEST)))
|| ((!exitCmd.compare(QStringLiteral("ne"))) && (pR->getExit(DIR_NORTHEAST) > 0 || pR->exitStubs.contains(DIR_NORTHEAST)))
|| ((!exitCmd.compare(QStringLiteral("se"))) && (pR->getExit(DIR_SOUTHEAST) > 0 || pR->exitStubs.contains(DIR_SOUTHEAST)))
|| ((!exitCmd.compare(QStringLiteral("sw"))) && (pR->getExit(DIR_SOUTHWEST) > 0 || pR->exitStubs.contains(DIR_SOUTHWEST)))
|| ((!exitCmd.compare(QStringLiteral("nw"))) && (pR->getExit(DIR_NORTHWEST) > 0 || pR->exitStubs.contains(DIR_NORTHWEST)))
|| ((!exitCmd.compare(QStringLiteral("up"))) && (pR->getExit(DIR_UP) > 0 || pR->exitStubs.contains(DIR_UP)))
|| ((!exitCmd.compare(QStringLiteral("down"))) && (pR->getExit(DIR_DOWN) > 0 || pR->exitStubs.contains(DIR_DOWN)))
|| ((!exitCmd.compare(QStringLiteral("in"))) && (pR->getExit(DIR_IN) > 0 || pR->exitStubs.contains(DIR_IN)))
|| ((!exitCmd.compare(QStringLiteral("out"))) && (pR->getExit(DIR_OUT) > 0 || pR->exitStubs.contains(DIR_OUT))))) {
// No there IS NOT a stub or real exit in the exitCmd direction
return warnArgumentValue(L, __func__, QStringLiteral(
"room with id %1 does not have a normal exit or a stub exit in direction '%2'")
.arg(QString::number(roomId), exitCmd));
}
// else IS a valid stub or real normal exit -fall through to continue
}
int doorStatus = getVerifiedInt(L, __func__, 3, "door type {0='none', 1='open', 2='closed' or 3='locked'}");
if (doorStatus < 0 || doorStatus > 3) {
return warnArgumentValue(L, __func__, QStringLiteral(
"door type %1 is not one of 0='none', 1='open', 2='closed' or 3='locked'").arg(doorStatus));
}
bool result = pR->setDoor(exitCmd, doorStatus);
if (result) {
if (host.mpMap->mpMapper && host.mpMap->mpMapper->mp2dMap) {
host.mpMap->mpMapper->mp2dMap->update();
}
}
lua_pushboolean(L, result);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getDoors
int TLuaInterpreter::getDoors(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int roomId = getVerifiedInt(L, __func__, 1, "room id");
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomId);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid room id").arg(roomId));
}
lua_newtable(L);
QStringList keys = pR->doors.keys();
for (unsigned int i = 0, total = keys.size(); i < total; ++i) {
lua_pushstring(L, keys.at(i).toUtf8().constData());
lua_pushnumber(L, pR->doors.value(keys.at(i)));
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setExitWeight
int TLuaInterpreter::setExitWeight(lua_State* L)
{
Host& host = getHostFromLua(L);
int roomID = getVerifiedInt(L, __func__, 1, "room id");
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomID);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("room id %1 doesn't exist").arg(roomID));
}
QString direction(dirToString(L, 2));
if (direction.isEmpty()) {
lua_pushfstring(L, "setExitWeight: bad argument #2 type (direction as string or number {between 1 and 12 inclusive} expected, got %s!)", luaL_typename(L, 2));
return lua_error(L);
}
if (!pR->hasExitOrSpecialExit(direction)) {
return warnArgumentValue(L, __func__, QStringLiteral("room id %1 does not have an exit that can be identified from '%2'")
.arg(QString::number(roomID), lua_tostring(L, 2)));
}
qint64 weight = getVerifiedInt(L, __func__, 3, "exit weight");
if (weight < 0 || weight > std::numeric_limits<int>::max()) {
return warnArgumentValue(L, __func__, QStringLiteral(
"weight %1 is outside of the usable range of 0 (which resets the weight back to that of the destination room) to %2")
.arg(QString::number(weight), QString::number(std::numeric_limits<int>::max())));
}
pR->setExitWeight(direction, weight);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#addCustomLine
int TLuaInterpreter::addCustomLine(lua_State* L)
{
Host& host = getHostFromLua(L);
//args: from id, id_to, direction, style, line color, arrow (bool)
int id_to = 0;
int r = 255;
int g = 0;
int b = 0;
Qt::PenStyle line_style(Qt::SolidLine);
QString direction;
QList<qreal> x;
QList<qreal> y;
QList<int> z;
int id_from = getVerifiedInt(L, __func__, 1, "room id");
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id_from);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("room id %1 does not exist").arg(id_from));
}
if (!lua_isnumber(L, 2) && !lua_istable(L, 2)) {
lua_pushfstring(L, "addCustomLine: bad argument #2 type (target room id as number or coordinate list as table expected, got %s!)", luaL_typename(L, 2));
return lua_error(L);
}
if (lua_isnumber(L, 2)) {
id_to = lua_tointeger(L, 2);
TRoom* pR_to = host.mpMap->mpRoomDB->getRoom(id_to);
if (!pR_to) {
return warnArgumentValue(L, __func__, QStringLiteral("target room id %1 does not exist").arg(id_to));
}
int area = pR->getArea();
int area_to = pR_to->getArea();
if (area != area_to) {
return warnArgumentValue(L, __func__, QStringLiteral(
"target room is in area '%1' (id: %2) which is not the one '%3' (id: %4) in which this custom line is to be drawn")
.arg((host.mpMap->mpRoomDB->getAreaNamesMap()).value(area_to), QString::number(area_to),
(host.mpMap->mpRoomDB->getAreaNamesMap()).value(area), QString::number(area)));
}
x.append(static_cast<qreal>(pR_to->x));
y.append(static_cast<qreal>(pR_to->y));
z.append(pR->z);
} else if (lua_istable(L, 2)) {
lua_pushnil(L);
int i = 0; // Indexes groups of coordinates in the table
while (lua_next(L, 2) != 0) {
++i;
if (lua_type(L, -1) != LUA_TTABLE) {
lua_pushfstring(L,
"addCustomLine: bad argument #2 table item index #%d type (coordinate list must be a table containing tables of three coordinates, got %s as indicated item!)",
i,
luaL_typename(L, -1));
return lua_error(L);
}
lua_pushnil(L);
int j = 0; // Indexes items (individual coordinates) in current inner table:
while (lua_next(L, -2) != 0) {
++j;
if (j <= 3) {
if (lua_type(L, -1) != LUA_TNUMBER) {
char coordinate = '\0';
switch (j) {
case 1:
coordinate = 'x';
break;
case 2:
coordinate = 'y';
break;
case 3:
coordinate = 'z';
break;
default:
Q_UNREACHABLE();
}
lua_pushfstring(L,
"addCustomLine: bad argument #2 table item index #%d inner table item #%d type (coordinates list as table containing tables of three numbers (x, y and z "
"coordinates} expected, but got a %s as the %c-coordinate at that index!)",
i,
j,
luaL_typename(L, -1),
coordinate);
return lua_error(L);
}
switch (j) {
case 1:
x.append(lua_tonumber(L, -1));
break;
case 2:
y.append(lua_tonumber(L, -1));
break;
case 3:
z.append(lua_tonumber(L, -1));
break;
default:; // No-op
}
}
lua_pop(L, 1);
}
lua_pop(L, 1);
}
}
direction = dirToString(L, 3);
if (direction.isEmpty()) {
lua_pushfstring(L, "addCustomLine: bad argument #3 type (direction as string or number (between 1 and 12 inclusive) expected, got %s!)", luaL_typename(L, 3));
return lua_error(L);
}
if (!pR->hasExitOrSpecialExit(direction)) {
return warnArgumentValue(L, __func__, QStringLiteral("room id %1 does not have an exit that can be identified from '%2'")
.arg(QString::number(id_from), lua_tostring(L, 3)));
}
QString lineStyleString = getVerifiedString(L, __func__, 4, "line style");
if (!lineStyleString.compare(QLatin1String("solid line"))) {
line_style = Qt::SolidLine;
} else if (!lineStyleString.compare(QLatin1String("dot line"))) {
line_style = Qt::DotLine;
} else if (!lineStyleString.compare(QLatin1String("dash line"))) {
line_style = Qt::DashLine;
} else if (!lineStyleString.compare(QLatin1String("dash dot line"))) {
line_style = Qt::DashDotLine;
} else if (!lineStyleString.compare(QLatin1String("dash dot dot line"))) {
line_style = Qt::DashDotDotLine;
} else {
return warnArgumentValue(L, __func__, QStringLiteral(
"invalid line style '%1', only use one of: 'solid line', 'dot line', 'dash line', 'dash dot line' or 'dash dot dot line'")
.arg(lineStyleString));
}
if (!lua_istable(L, 5)) {
lua_pushfstring(L, "addCustomLine: bad argument #5 type (RGB color components as a table expected, got %s!)", luaL_typename(L, 4));
} else {
lua_pushnil(L);
int tind = 0;
while (lua_next(L, 5) != 0) {
if (++tind <= 3) {
if (lua_type(L, -1) != LUA_TNUMBER) {
lua_pushfstring(L,
"addCustomLine: bad argument #4 table item #%d type (%s color component as a number between 0 and 255 expected, got %s!)",
tind,
(tind == 1 ? "red" : (tind == 2 ? "green" : "blue")),
luaL_typename(L, -1));
return lua_error(L);
}
qint64 component = lua_tonumber(L, -1);
if (component < 0 || component > 255) {
return warnArgumentValue(L, __func__, QStringLiteral(
"%1 color component in the table of the fourth argument is %2 which is out of the valid range (0 to 255)")
.arg((tind == 1 ? "red" : (tind == 2 ? "green" : "blue")), QString::number(component)));
}
switch (tind) {
case 1:
r = component;
break;
case 2:
g = component;
break;
case 3:
b = component;
break;
default:
Q_UNREACHABLE();
}
}
lua_pop(L, 1);
}
}
bool arrow = getVerifiedBool(L, __func__, 6, "end with arrow");
int lz = z.at(0);
QList<QPointF> points;
// TODO: make provision for 3D custom lines (and store the z coordinates and allow them to vary)
points.append(QPointF(x.at(0), y.at(0)));
for (int i = 1, total = z.size(); i < total; ++i) {
if (lz != z.at(i)) {
return warnArgumentValue(L, __func__, QStringLiteral(
"the z values are not all on the same level (first wrong value is %1 at index %2)")
.arg(QString::number(z.at(i)), QString::number(i + 1)));
}
points.append(QPointF(x.at(i), y.at(i)));
}
//Heiko: direction/line relationship must be unique
pR->customLines[direction] = points;
pR->customLinesArrow[direction] = arrow;
pR->customLinesStyle[direction] = line_style;
pR->customLinesColor[direction] = QColor(r, g, b);
// Need to update the TRoom {min|max}_{x|y} settings as they are used during
// the painting process - and not doing that here causes the new line to not
// show up properly:
pR->calcRoomDimensions();
// Better refresh the 2D map to show the new line:
if (host.mpMap->mpMapper && host.mpMap->mpMapper->mp2dMap) {
host.mpMap->mpMapper->mp2dMap->mNewMoveAction = true;
host.mpMap->mpMapper->mp2dMap->update();
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#removeCustomLine
int TLuaInterpreter::removeCustomLine(lua_State* L)
{
Host& host = getHostFromLua(L);
//args: room_id, direction
int roomId = getVerifiedInt(L, __func__, 1, "room id");
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomId);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("room id %1 doesn't exist").arg(roomId));
}
QString direction = dirToString(L, 2);
if (direction.isEmpty()) {
lua_pushfstring(L, "removeCustomLine: bad argument #2 type (direction as string or number (between 1 and 12 inclusive) expected, got %s!)", luaL_typename(L, 2));
return lua_error(L);
}
if (!pR->hasExitOrSpecialExit(direction)) {
return warnArgumentValue(L, __func__, QStringLiteral(
"room id %1 does not have an exit that can be identified from '%2'").arg(QString::number(roomId), lua_tostring(L, 2)));
}
if (0 >= (pR->customLines.remove(direction) + pR->customLinesArrow.remove(direction)
+ pR->customLinesStyle.remove(direction) + pR->customLinesColor.remove(direction))) {
return warnArgumentValue(L, __func__, QStringLiteral(
"room id %1 does not appear to have a custom exit line for the exit indentifed from '%2'")
.arg(QString::number(roomId), lua_tostring(L, 2)));
}
// Need to update the TRoom {min|max}_{x|y} settings as they are used during
// the painting process:
pR->calcRoomDimensions();
// Better refresh the 2D map to show the new line:
if (host.mpMap->mpMapper && host.mpMap->mpMapper->mp2dMap) {
host.mpMap->mpMapper->mp2dMap->mNewMoveAction = true;
host.mpMap->mpMapper->mp2dMap->update();
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getCustomLines
int TLuaInterpreter::getCustomLines(lua_State* L)
{
int roomID = getVerifiedInt(L, __func__, 1, "room id");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomID);
if (!pR) { //if the room doesnt exist return nil
return warnArgumentValue(L, __func__, QStringLiteral("room %1 doesn't exist").arg(roomID));
}
lua_newtable(L); //return table customLines[]
QStringList exits = pR->customLines.keys();
for (int i = 0, iTotal = exits.size(); i < iTotal; ++i) {
lua_pushstring(L, exits.at(i).toUtf8().constData());
lua_newtable(L); //customLines[direction]
lua_pushstring(L, "attributes");
lua_newtable(L); //customLines[attributes]
lua_pushstring(L, "style");
switch (pR->customLinesStyle.value(exits.at(i))) {
case Qt::DotLine:
lua_pushstring(L, "dot line");
break;
case Qt::DashLine:
lua_pushstring(L, "dash line");
break;
case Qt::DashDotLine:
lua_pushstring(L, "dash dot line");
break;
case Qt::DashDotDotLine:
lua_pushstring(L, "dash dot dot line");
break;
case Qt::SolidLine:
[[fallthrough]];
default:
lua_pushstring(L, "solid line");
}
lua_settable(L, -3);
lua_pushstring(L, "arrow");
lua_pushboolean(L, pR->customLinesArrow.value(exits.at(i)));
lua_settable(L, -3);
lua_pushstring(L, "color");
lua_newtable(L);
lua_pushstring(L, "r");
lua_pushinteger(L, pR->customLinesColor.value(exits.at(i)).red());
lua_settable(L, -3);
lua_pushstring(L, "g");
lua_pushinteger(L, pR->customLinesColor.value(exits.at(i)).green());
lua_settable(L, -3);
lua_pushstring(L, "b");
lua_pushinteger(L, pR->customLinesColor.value(exits.at(i)).blue());
lua_settable(L, -3);
lua_settable(L, -3); //color
lua_settable(L, -3); //attributes
lua_pushstring(L, "points");
lua_newtable(L); //customLines[points]
QList<QPointF> pointL = pR->customLines.value(exits.at(i));
for (int k = 0, kTotal = pointL.size(); k < kTotal; ++k) {
lua_pushnumber(L, k);
lua_newtable(L);
lua_pushstring(L, "x");
lua_pushnumber(L, pointL.at(k).x());
lua_settable(L, -3);
lua_pushstring(L, "y");
lua_pushnumber(L, pointL.at(k).y());
lua_settable(L, -3);
lua_settable(L, -3);
}
lua_settable(L, -3); //customLines[direction][points]
lua_settable(L, -3); //customLines[direction]
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getExitWeights
int TLuaInterpreter::getExitWeights(lua_State* L)
{
int roomID = getVerifiedInt(L, __func__, 1, "roomID");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomID);
lua_newtable(L);
if (pR) {
QStringList keys = pR->getExitWeights().keys();
for (int i = 0; i < keys.size(); i++) {
lua_pushstring(L, keys.at(i).toUtf8().constData());
lua_pushnumber(L, pR->getExitWeight(keys.at(i)));
lua_settable(L, -3);
}
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#deleteMapLabel
int TLuaInterpreter::deleteMapLabel(lua_State* L)
{
int area = getVerifiedInt(L, __func__, 1, "areaID");
int labelID = getVerifiedInt(L, __func__, 2, "labelID");
Host& host = getHostFromLua(L);
host.mpMap->deleteMapLabel(area, labelID);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getMapLabels
int TLuaInterpreter::getMapLabels(lua_State* L)
{
int area = getVerifiedInt(L, __func__, 1, "areaID");
Host& host = getHostFromLua(L);
lua_newtable(L);
auto pA = host.mpMap->mpRoomDB->getArea(area);
if (!pA->mMapLabels.isEmpty()) {
QMapIterator<int, TMapLabel> it(pA->mMapLabels);
while (it.hasNext()) {
it.next();
lua_pushnumber(L, it.key());
lua_pushstring(L, it.value().text.toUtf8().constData());
lua_settable(L, -3);
}
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getMapLabel
int TLuaInterpreter::getMapLabel(lua_State* L)
{
int area = getVerifiedInt(L, __func__, 1, "areaID");
if (!lua_isstring(L, 2) && !lua_isnumber(L, 2)) {
lua_pushfstring(L, "getMapLabel: bad argument #2 type (labelID as number or labelText as string expected, got %s!)", luaL_typename(L, 2));
return lua_error(L);
}
QString labelText;
int labelId = -1;
if (lua_type(L, 2) == LUA_TNUMBER) {
labelId = lua_tointeger(L, 2);
if (labelId < 0) {
return warnArgumentValue(L, __func__, QStringLiteral("labelID %1 is invalid, it must be zero or greater").arg(labelId));
}
} else {
labelText = lua_tostring(L, 2);
// Can be an empty string as image labels have no text!
}
Host& host = getHostFromLua(L);
auto pA = host.mpMap->mpRoomDB->getArea(area);
if (pA->mMapLabels.isEmpty()) {
// Return an empty table:
lua_newtable(L);
return 1;
}
if (labelId >= 0) {
if (!pA->mMapLabels.contains(labelId)) {
return warnArgumentValue(L, __func__, QStringLiteral("labelID %1 does not exist in area with areaID %2")
.arg(QString::number(labelId), QString::number(area)));
}
lua_newtable(L);
auto label = pA->mMapLabels.value(labelId);
pushMapLabelPropertiesToLua(L, label);
return 1;
}
lua_newtable(L);
QMapIterator<int, TMapLabel> it(pA->mMapLabels);
while (it.hasNext()) {
it.next();
if (it.value().text == labelText) {
lua_newtable(L);
pushMapLabelPropertiesToLua(L, it.value());
lua_pushnumber(L, it.key());
lua_insert(L, -2);
lua_settable(L, -3);
}
}
return 1;
}
// No documentation available - internal function
void TLuaInterpreter::pushMapLabelPropertiesToLua(lua_State* L, const TMapLabel& label)
{
lua_pushstring(L, "X");
lua_pushnumber(L, label.pos.x());
lua_settable(L, -3);
lua_pushstring(L, "Y");
lua_pushnumber(L, label.pos.y());
lua_settable(L, -3);
lua_pushstring(L, "Z");
lua_pushnumber(L, qRound(label.pos.z()));
lua_settable(L, -3);
lua_pushstring(L, "Height");
lua_pushnumber(L, label.size.height());
lua_settable(L, -3);
lua_pushstring(L, "Width");
lua_pushnumber(L, label.size.width());
lua_settable(L, -3);
lua_pushstring(L, "Text");
lua_pushstring(L, label.text.toUtf8().constData());
lua_settable(L, -3);
lua_pushstring(L, "Pixmap");
lua_pushstring(L, label.base64EncodePixmap().constData());
lua_settable(L, -3);
lua_pushstring(L, "FgColor");
{
lua_newtable(L);
lua_pushstring(L, "r");
lua_pushinteger(L, label.fgColor.red());
lua_settable(L, -3);
lua_pushstring(L, "g");
lua_pushinteger(L, label.fgColor.green());
lua_settable(L, -3);
lua_pushstring(L, "b");
lua_pushinteger(L, label.fgColor.blue());
lua_settable(L, -3);
}
lua_settable(L, -3);
lua_pushstring(L, "BgColor");
{
lua_newtable(L);
lua_pushstring(L, "r");
lua_pushinteger(L, label.bgColor.red());
lua_settable(L, -3);
lua_pushstring(L, "g");
lua_pushinteger(L, label.bgColor.green());
lua_settable(L, -3);
lua_pushstring(L, "b");
lua_pushinteger(L, label.bgColor.blue());
lua_settable(L, -3);
}
lua_settable(L, -3);
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#addSpecialExit
int TLuaInterpreter::addSpecialExit(lua_State* L)
{
Host& host = getHostFromLua(L);
int fromRoomID = getVerifiedInt(L, __func__, 1, "exit room id");
TRoom* pR_from = host.mpMap->mpRoomDB->getRoom(fromRoomID);
if (!pR_from) {
return warnArgumentValue(L, __func__, QStringLiteral("exit room id %1 does not exist").arg(fromRoomID));
}
int toRoomID = getVerifiedInt(L, __func__, 2, "entrance room id");
TRoom* pR_to = host.mpMap->mpRoomDB->getRoom(toRoomID);
if (!pR_to) {
return warnArgumentValue(L, __func__, QStringLiteral("entrance room id %1 does not exist").arg(toRoomID));
}
QString dir = getVerifiedString(L, __func__, 3, "special exit name/command");
if (dir.isEmpty()) {
return warnArgumentValue(L, __func__, "the special exit name/command cannot be empty");
}
pR_from->setSpecialExit(toRoomID, dir);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#removeSpecialExit
int TLuaInterpreter::removeSpecialExit(lua_State* L)
{
Host& host = getHostFromLua(L);
int fromRoomID = getVerifiedInt(L, __func__, 1, "exit room id");
TRoom* pR = host.mpMap->mpRoomDB->getRoom(fromRoomID);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("exit room id %1 does not exist").arg(fromRoomID));
}
QString dir = getVerifiedString(L, __func__, 2, "special exit name/command");
if (dir.isEmpty()) {
return warnArgumentValue(L, __func__, "the exit command cannot be empty");
}
if (!pR->getSpecialExits().contains(dir)) {
return warnArgumentValue(L, __func__, QStringLiteral(
"the special exit name/command '%1' does not exist in room id %2").arg(dir, QString::number(fromRoomID)));
}
pR->setSpecialExit(-1, dir);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#clearRoomUserData
int TLuaInterpreter::clearRoomUserData(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int roomId = getVerifiedInt(L, __func__, 1, "room id");
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomId);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid room id").arg(roomId));
}
if (!pR->userData.isEmpty()) {
pR->userData.clear();
lua_pushboolean(L, true);
} else {
lua_pushboolean(L, false);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#clearRoomUserDataItem
int TLuaInterpreter::clearRoomUserDataItem(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int roomId = getVerifiedInt(L, __func__, 1, "room id");
QString key = getVerifiedString(L, __func__, 2, "key");
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomId);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid room id").arg(roomId));
}
// Turns out that an empty key IS possible, but if this changes this should be uncommented
// if (key.isEmpty()) {
// // If the user accidently supplied an white-space only or empty key
// // string we don't do anything, but we, sucessfully, fail to do it... 8-)
// lua_pushboolean( L, false );
// }
/* else */ if (pR->userData.contains(key)) {
pR->userData.remove(key);
lua_pushboolean(L, true);
} else {
lua_pushboolean(L, false);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#clearAreaUserData
int TLuaInterpreter::clearAreaUserData(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int areaId = getVerifiedInt(L, __func__, 1, "area id");
TArea* pA = host.mpMap->mpRoomDB->getArea(areaId);
if (!pA) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid area id").arg(areaId));
}
if (!pA->mUserData.isEmpty()) {
pA->mUserData.clear();
lua_pushboolean(L, true);
} else {
lua_pushboolean(L, false);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#clearAreaUserDataItem
int TLuaInterpreter::clearAreaUserDataItem(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int areaId = getVerifiedInt(L, __func__, 1, "area id");
QString key = getVerifiedString(L, __func__, 2, "key");
TArea* pA = host.mpMap->mpRoomDB->getArea(areaId);
if (!pA) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid area id").arg(areaId));
}
if (key.isEmpty()) {
return warnArgumentValue(L, __func__, "key can not be an empty string");
}
lua_pushboolean(L, (pA->mUserData.remove(key) > 0));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#clearMapUserData
int TLuaInterpreter::clearMapUserData(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
if (!host.mpMap->mUserData.isEmpty()) {
host.mpMap->mUserData.clear();
lua_pushboolean(L, true);
} else {
lua_pushboolean(L, false);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#clearMapUserDataItem
int TLuaInterpreter::clearMapUserDataItem(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
QString key = getVerifiedString(L, __func__, 1, "key");
if (key.isEmpty()) {
return warnArgumentValue(L, __func__, "key can not be an empty string");
}
lua_pushboolean(L, (host.mpMap->mUserData.remove(key) > 0));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#clearSpecialExits
int TLuaInterpreter::clearSpecialExits(lua_State* L)
{
int id_from = getVerifiedInt(L, __func__, 1, "roomID");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id_from);
if (pR) {
pR->clearSpecialExits();
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getSpecialExits
// This function was slightly borked - the version in place from 2011 to 2020
// did not handle the corner case of multiple special exits that go to the same
// room, it would only show one of them at random. Each special exit was listed
// in its own table (against the key of the exit room id) and it is a key to a
// "1" or "0" depending on whether the exit is locked or not. This was not
// documented in the wiki!
int TLuaInterpreter::getSpecialExits(lua_State* L)
{
Host& host = getHostFromLua(L);
int id_from = getVerifiedInt(L, __func__, 1, "exit room id");
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id_from);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("room with id %1 does not exist").arg(id_from));
}
bool showAllExits = false;
if (lua_gettop(L) > 1) {
showAllExits = getVerifiedBool(L, __func__, 2, "show every exit to same exit room id", true);
}
QMapIterator<QString, int> itSpecialExit(pR->getSpecialExits());
QMultiMap<int, QString> specialExitsByExitId;
while (itSpecialExit.hasNext()) {
itSpecialExit.next();
specialExitsByExitId.insert(itSpecialExit.value(), itSpecialExit.key());
}
QList<int> exitRoomIdList = specialExitsByExitId.keys();
lua_newtable(L);
for (int i = 0, exitRoomIdCount = exitRoomIdList.count(); i < exitRoomIdCount; ++i) {
lua_pushnumber(L, exitRoomIdList.at(i));
lua_newtable(L);
{
QStringList exitCommandsToThisRoomId = specialExitsByExitId.values(exitRoomIdList.at(i));
int bestUnlockedExitIndex = -1;
int bestUnlockedExitWeight = -1;
int bestLockedExitIndex = -1;
int bestLockedExitWeight = -1;
int exitCommandsCount = exitCommandsToThisRoomId.count();
for (int j = 0; j < exitCommandsCount; ++j) {
if (showAllExits || exitCommandsCount == 1) {
// The simpler case - show all exits (or the only exit) to
// this room:
lua_pushstring(L, exitCommandsToThisRoomId.at(j).toUtf8().constData());
lua_pushstring(L, pR->hasSpecialExitLock(exitCommandsToThisRoomId.at(j)) ? "1" : "0");
lua_settable(L, -3);
// Go on to next exit to this room:
continue;
}
// The more complex (but highly unlikely in most MUDs) case
// - find the best exit to this room when there are more than
// one:
int thisExitWeight = pR->getExitWeight(exitCommandsToThisRoomId.at(j));
if (pR->hasSpecialExitLock(exitCommandsToThisRoomId.at(j))) {
if (bestLockedExitIndex == -1) {
bestLockedExitIndex = j;
bestLockedExitWeight = thisExitWeight;
} else if (thisExitWeight < bestLockedExitWeight) {
bestLockedExitIndex = j;
bestLockedExitWeight = thisExitWeight;
}
} else {
if (bestUnlockedExitIndex == -1) {
bestUnlockedExitIndex = j;
bestUnlockedExitWeight = thisExitWeight;
} else if (thisExitWeight < bestUnlockedExitWeight) {
bestUnlockedExitIndex = j;
bestUnlockedExitWeight = thisExitWeight;
}
}
}
if (!showAllExits && (exitCommandsCount > 1)) {
// Produce the best exit to this room given that there IS more
// than one and we haven't been asked to show them all:
int bestExitIndex = (bestUnlockedExitIndex != -1) ? bestUnlockedExitIndex : bestLockedExitIndex;
lua_pushstring(L, exitCommandsToThisRoomId.at(bestExitIndex).toUtf8().constData());
lua_pushstring(L, pR->hasSpecialExitLock(exitCommandsToThisRoomId.at(bestExitIndex)) ? "1" : "0");
lua_settable(L, -3);
}
}
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getSpecialExitsSwap
int TLuaInterpreter::getSpecialExitsSwap(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!lua_isnumber(L, 1)) {
lua_pushfstring(L, "getSpecialExitsSwap: bad argument #1 type (exit room id as number expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
int id_from = lua_tointeger(L, 1);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id_from);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("room with id %1 does not exist").arg(id_from));
}
QMapIterator<QString, int> it(pR->getSpecialExits());
lua_newtable(L);
while (it.hasNext()) {
it.next();
lua_pushstring(L, it.key().toUtf8().constData());
lua_pushnumber(L, it.value());
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getRoomEnv
int TLuaInterpreter::getRoomEnv(lua_State* L)
{
int roomID = getVerifiedInt(L, __func__, 1, "roomID");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomID);
if (pR) {
lua_pushnumber(L, pR->environment);
return 1;
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getRoomUserData
int TLuaInterpreter::getRoomUserData(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int roomId = getVerifiedInt(L, __func__, 1, "room id");
QString key = getVerifiedString(L, __func__, 2, "key");
bool isBackwardCompatibilityRequired = true;
if (lua_gettop(L) > 2) {
isBackwardCompatibilityRequired = !getVerifiedBool(L, __func__, 3, "enableFullErrorReporting {default = false}", true);
}
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomId);
if (!pR) {
if (!isBackwardCompatibilityRequired) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid room id").arg(roomId));
}
lua_pushstring(L, "");
return 1;
}
if (!pR->userData.contains(key)) {
if (!isBackwardCompatibilityRequired) {
return warnArgumentValue(L, __func__, QStringLiteral(
"no user data with key '%1' in room with id %2").arg(key, QString::number(roomId)));
}
lua_pushstring(L, "");
return 1;
}
lua_pushstring(L, pR->userData.value(key).toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getAreaUserData
int TLuaInterpreter::getAreaUserData(lua_State* L)
{
int areaId = getVerifiedInt(L, __func__, 1, "area id");
QString key = getVerifiedString(L, __func__, 2, "key");
if (key.isEmpty()) {
return warnArgumentValue(L, __func__, "key is not allowed to be an empty string");
}
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
TArea* pA = host.mpMap->mpRoomDB->getArea(areaId);
if (!pA) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid area id").arg(areaId));
}
if (!pA->mUserData.contains(key)) {
return warnArgumentValue(L, __func__, QStringLiteral(
"no user data with key '%1' in area with id %2").arg(key, QString::number(areaId)));
}
lua_pushstring(L, pA->mUserData.value(key).toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getMapUserData
int TLuaInterpreter::getMapUserData(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
QString key = getVerifiedString(L, __func__, 1, "key");
if (!host.mpMap->mUserData.contains(key)) {
return warnArgumentValue(L, __func__, QStringLiteral("no user data with key '%1' in map").arg(key));
}
lua_pushstring(L, host.mpMap->mUserData.value(key).toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setRoomUserData
int TLuaInterpreter::setRoomUserData(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int roomId = getVerifiedInt(L, __func__, 1, "room id");
QString key = getVerifiedString(L, __func__, 2, "key");
// Ideally should reject empty keys but this could break existing scripts so we can't
QString value = getVerifiedString(L, __func__, 3, "value");
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomId);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid room id").arg(roomId));
}
pR->userData[key] = value;
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setAreaUserData
int TLuaInterpreter::setAreaUserData(lua_State* L)
{
int areaId = getVerifiedInt(L, __func__, 1, "area id");
QString key = getVerifiedString(L, __func__, 2, "key");
if (key.isEmpty()) {
return warnArgumentValue(L, __func__, "key is not allowed to be an empty string");
}
QString value = getVerifiedString(L, __func__, 3, "value");
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
TArea* pA = host.mpMap->mpRoomDB->getArea(areaId);
if (!pA) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid area id").arg(areaId));
}
pA->mUserData[key] = value;
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setMapUserData
int TLuaInterpreter::setMapUserData(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
QString key = getVerifiedString(L, __func__, 1, "key");
if (key.isEmpty()) {
return warnArgumentValue(L, __func__, "key is not allowed to be an empty string");
}
QString value = getVerifiedString(L, __func__, 2, "value");
host.mpMap->mUserData[key] = value;
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getRoomUserDataKeys
int TLuaInterpreter::getRoomUserDataKeys(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int roomId = getVerifiedInt(L, __func__, 1, "room id");
QStringList keys;
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomId);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid room id").arg(roomId));
}
keys = pR->userData.keys();
lua_newtable(L);
for (int i = 0; i < keys.size(); i++) {
lua_pushnumber(L, i + 1);
lua_pushstring(L, keys.at(i).toUtf8().constData());
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getAllRoomUserData
int TLuaInterpreter::getAllRoomUserData(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int roomId = getVerifiedInt(L, __func__, 1, "room id");
QStringList keys;
QStringList values;
TRoom* pR = host.mpMap->mpRoomDB->getRoom(roomId);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid room id").arg(roomId));
}
keys = pR->userData.keys();
values = pR->userData.values();
lua_newtable(L);
for (int i = 0; i < keys.size(); i++) {
lua_pushstring(L, keys.at(i).toUtf8().constData());
lua_pushstring(L, values.at(i).toUtf8().constData());
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getAllAreaUserData
int TLuaInterpreter::getAllAreaUserData(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int areaId = getVerifiedInt(L, __func__, 1, "area id");
QStringList keys;
QStringList values;
TArea* pA = host.mpMap->mpRoomDB->getArea(areaId);
if (!pA) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid area id").arg(areaId));
}
keys = pA->mUserData.keys();
values = pA->mUserData.values();
lua_newtable(L);
for (int i = 0; i < keys.size(); i++) {
lua_pushstring(L, keys.at(i).toUtf8().constData());
lua_pushstring(L, values.at(i).toUtf8().constData());
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getAllMapUserData
int TLuaInterpreter::getAllMapUserData(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
QStringList keys;
QStringList values;
keys = host.mpMap->mUserData.keys();
values = host.mpMap->mUserData.values();
lua_newtable(L);
for (int i = 0; i < keys.size(); i++) {
lua_pushstring(L, keys.at(i).toUtf8().constData());
lua_pushstring(L, values.at(i).toUtf8().constData());
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#downloadFile
int TLuaInterpreter::downloadFile(lua_State* L)
{
Host& host = getHostFromLua(L);
QString localFile = getVerifiedString(L, __func__, 1, "local filename");
QString urlString = getVerifiedString(L, __func__, 2, "remote url");
QUrl url = QUrl::fromUserInput(urlString);
if (!url.isValid()) {
return warnArgumentValue(L, __func__, QStringLiteral("url is invalid, reason: %1").arg(url.errorString()));
}
QNetworkRequest request = QNetworkRequest(url);
mudlet::self()->setNetworkRequestDefaults(url, request);
host.updateProxySettings(host.mLuaInterpreter.mpFileDownloader);
QNetworkReply* reply = host.mLuaInterpreter.mpFileDownloader->get(request);
host.mLuaInterpreter.downloadMap.insert(reply, localFile);
connect(reply, &QNetworkReply::downloadProgress, [=](qint64 bytesDownloaded, qint64 totalBytes) {
raiseDownloadProgressEvent(L, urlString, bytesDownloaded, totalBytes);
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::blue)) << "downloadFile: " << bytesDownloaded << "/" << totalBytes
<< " bytes ready for " << reply->url().toString() << "\n" >> 0;
}
});
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::blue)) << "downloadFile: start download from " << reply->url().toString() << "\n" >> 0;
}
lua_pushboolean(L, true);
lua_pushstring(L, reply->url().toString().toUtf8().constData()); // Returns the Url that was ACTUALLY used
return 2;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setRoomArea
int TLuaInterpreter::setRoomArea(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int id = getVerifiedInt(L, __func__, 1, "room id");
if (!host.mpMap->mpRoomDB->getRoomIDList().contains(id)) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid room id").arg(id));
}
int areaId;
QString areaName;
if (lua_isnumber(L, 2)) {
areaId = lua_tonumber(L, 2);
if (areaId < 1) {
return warnArgumentValue(L, __func__, QStringLiteral(
"number %1 is not a valid area id greater than zero. "
"To remove a room's area, use resetRoomArea( roomId )").arg(areaId));
}
if (!host.mpMap->mpRoomDB->getAreaNamesMap().contains(areaId)) {
return warnArgumentValue(L, __func__, QStringLiteral("area id %1 does not exist").arg(areaId));
}
} else if (lua_isstring(L, 2)) {
areaName = lua_tostring(L, 2);
// areaId will be zero if not found!
if (areaName.isEmpty()) {
return warnArgumentValue(L, __func__, "area name cannot be empty");
}
areaId = host.mpMap->mpRoomDB->getAreaNamesMap().key(areaName, 0);
if (!areaId) {
return warnArgumentValue(L, __func__, QStringLiteral("area name '%1' does not exist").arg(areaName));
}
} else {
lua_pushfstring(L,
"setRoomArea: bad argument #2 type (area Id as number or area name as string\n"
"expected, got %s!)",
luaL_typename(L, 2));
return lua_error(L);
}
// Can set the room to an area which does not have a TArea instance but does
// appear in the TRoomDB::areaNamesMap...
bool result = host.mpMap->setRoomArea(id, areaId, false);
if (result) {
// As a sucessfull result WILL change the area a room is in then the map
// should be updated. The GUI code that modifies room(s) areas already
// includes such a call to update the mapper.
if (host.mpMap->mpMapper) {
host.mpMap->mpMapper->mp2dMap->update();
}
#if defined(INCLUDE_3DMAPPER)
if (host.mpMap->mpM) {
host.mpMap->mpM->update();
}
#endif
}
lua_pushboolean(L, result);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#resetRoomArea
int TLuaInterpreter::resetRoomArea(lua_State* L)
{
//will reset the room area to our void area
int id = getVerifiedInt(L, __func__, 1, "room id");
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
} else if (!host.mpMap->mpRoomDB->getRoomIDList().contains(id)) {
return warnArgumentValue(L, __func__, QStringLiteral("number %1 is not a valid room id").arg(id));
}
bool result = host.mpMap->setRoomArea(id, -1, false);
if (result) {
// As a sucessfull result WILL change the area a room is in then the map
// should be updated. The GUI code that modifies room(s) areas already
// includes such a call to update the mapper.
if (host.mpMap->mpMapper) {
host.mpMap->mpMapper->mp2dMap->update();
}
#if defined(INCLUDE_3DMAPPER)
if (host.mpMap->mpM) {
host.mpMap->mpM->update();
}
#endif
}
lua_pushboolean(L, result);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setRoomChar
int TLuaInterpreter::setRoomChar(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "room id");
QString symbol = getVerifiedString(L, __func__, 2, "room symbol");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("room with id %1 does not exist").arg(id));
}
if (symbol.isEmpty()) {
// Allow an empty string to be used to clear the symbol:
pR->mSymbol.clear();
} else {
// 8.0 is the maximum supported by the Qt versions (5.6 to 5.10) we
// handle/use/allow:
pR->mSymbol = symbol.normalized(QString::NormalizationForm_C, QChar::Unicode_8_0);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getRoomChar
int TLuaInterpreter::getRoomChar(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "room id");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("room with id %1 does not exist").arg(id));
}
lua_pushstring(L, pR->mSymbol.toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setRoomCharColor
int TLuaInterpreter::setRoomCharColor(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "room id");
int r = getVerifiedInt(L, __func__, 2, "red component");
if (r < 0 || r > 255) {
lua_pushfstring(L, "setRoomCharColor: bad argument #2 type (red component value %d out of range (0 to 255)", r);
return lua_error(L);
}
int g = getVerifiedInt(L, __func__, 3, "green component");
if (g < 0 || g > 255) {
lua_pushfstring(L, "setRoomCharColor: bad argument #3 type (red component value %d out of range (0 to 255)", r);
return lua_error(L);
}
int b = getVerifiedInt(L, __func__, 4, "blue component");
if (b < 0 || b > 255) {
lua_pushfstring(L, "setRoomCharColor: bad argument #4 type (blue component value %d out of range (0 to 255)", r);
return lua_error(L);
}
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("room with id %1 does not exist").arg(id));
}
pR->mSymbolColor = QColor(r, g, b);
if (host.mpMap->mpMapper && host.mpMap->mpMapper->mp2dMap) {
host.mpMap->mpMapper->mp2dMap->update();
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#unsetRoomCharColor
int TLuaInterpreter::unsetRoomCharColor(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "room id");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("room with id %1 does not exist").arg(id));
}
// Reset it to the default (and invalid) QColor:
pR->mSymbolColor = {};
if (host.mpMap->mpMapper && host.mpMap->mpMapper->mp2dMap) {
host.mpMap->mpMapper->mp2dMap->update();
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getRoomCharColor
int TLuaInterpreter::getRoomCharColor(lua_State* L)
{
int id = getVerifiedInt(L, __func__, 1, "room id");
Host& host = getHostFromLua(L);
TRoom* pR = host.mpMap->mpRoomDB->getRoom(id);
if (!pR) {
return warnArgumentValue(L, __func__, QStringLiteral("room with id %1 does not exist").arg(id));
}
lua_pushnumber(L, pR->mSymbolColor.red());
lua_pushnumber(L, pR->mSymbolColor.green());
lua_pushnumber(L, pR->mSymbolColor.blue());
return 3;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getRoomsByPosition
int TLuaInterpreter::getRoomsByPosition(lua_State* L)
{
int area = getVerifiedInt(L, __func__, 1, "areaID");
int x = getVerifiedInt(L, __func__, 2, "x");
int y = getVerifiedInt(L, __func__, 3, "y");
int z = getVerifiedInt(L, __func__, 4, "z");
Host& host = getHostFromLua(L);
TArea* pA = host.mpMap->mpRoomDB->getArea(area);
if (!pA) {
lua_pushnil(L);
return 1;
}
QList<int> rL = pA->getRoomsByPosition(x, y, z);
lua_newtable(L);
for (int i = 0; i < rL.size(); i++) {
lua_pushnumber(L, i);
lua_pushnumber(L, rL[i]);
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getGridMode
int TLuaInterpreter::getGridMode(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
int id = getVerifiedInt(L, __func__, 1, "area id");
TArea* area = host.mpMap->mpRoomDB->getArea(id);
if (!area) {
return warnArgumentValue(L, __func__, QStringLiteral("area with id %1 does not exist").arg(id));
} else {
lua_pushboolean(L, area->gridMode);
return 1;
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setGridMode
int TLuaInterpreter::setGridMode(lua_State* L)
{
int area = getVerifiedInt(L, __func__, 1, "areaID");
bool gridMode = getVerifiedBool(L, __func__, 2, "true/false");
Host& host = getHostFromLua(L);
TArea* pA = host.mpMap->mpRoomDB->getArea(area);
if (!pA) {
lua_pushboolean(L, false);
return 1;
} else {
pA->gridMode = gridMode;
pA->calcSpan();
if (host.mpMap->mpMapper) {
if (host.mpMap->mpMapper->mp2dMap) {
// Not needed IMHO - Slysven
// host.mpMap->mpMapper->mp2dMap->init();
// cout << "NEW GRID MAP: init" << endl;
// But this is:
host.mpMap->mpMapper->update();
}
}
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setFgColor
int TLuaInterpreter::setFgColor(lua_State* L)
{
int s = 0;
int n = lua_gettop(L);
auto validRange = [](int number) { return number >= 0 && number <= 255; };
QString windowName;
if (n > 3) {
windowName = WINDOW_NAME(L, ++s);
}
int luaRed = getVerifiedInt(L, __func__, ++s, "red component value");
if (!validRange(luaRed)) {
return warnArgumentValue(L, __func__, QStringLiteral("red value %1 needs to be between 0-255").arg(luaRed));
}
int luaGreen = getVerifiedInt(L, __func__, ++s, "green component value");
if (!validRange(luaGreen)) {
return warnArgumentValue(L, __func__, QStringLiteral("green value %1 needs to be between 0-255").arg(luaGreen));
}
int luaBlue = getVerifiedInt(L, __func__, ++s, "blue component value");
if (!validRange(luaBlue)) {
return warnArgumentValue(L, __func__, QStringLiteral("blue value %1 needs to be between 0-255").arg(luaBlue));
}
auto console = CONSOLE(L, windowName);
console->setFgColor(luaRed, luaGreen, luaBlue);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setBgColor
int TLuaInterpreter::setBgColor(lua_State* L)
{
QString windowName;
int r, g, b, alpha;
auto validRange = [](int number) { return number >= 0 && number <= 255; };
int s = 1;
if (lua_isstring(L, s) && !lua_isnumber(L, s)) {
windowName = WINDOW_NAME(L, s);
if (!lua_isnumber(L, ++s)) {
lua_pushfstring(L, "setBgColor: bad argument #%d type (red value 0-255 as number expected, got %s!)", s, luaL_typename(L, s));
return lua_error(L);
}
r = static_cast<int>(lua_tonumber(L, s));
if (!validRange(r)) {
return warnArgumentValue(L, __func__, QStringLiteral("red value %1 needs to be between 0-255").arg(r));
}
} else if (lua_isnumber(L, s)) {
r = static_cast<int>(lua_tonumber(L, s));
if (!validRange(r)) {
return warnArgumentValue(L, __func__, QStringLiteral("red value %1 needs to be between 0-255").arg(r));
}
} else {
lua_pushfstring(L, "setBgColor: bad argument #%d type (window name as string, or red value 0-255 as number expected, got %s!)", s, luaL_typename(L, s));
return lua_error(L);
}
g = getVerifiedInt(L, __func__, ++s, "green value 0-255");
if (!validRange(g)) {
return warnArgumentValue(L, __func__, QStringLiteral("green value %1 needs to be between 0-255").arg(g));
}
b = getVerifiedInt(L, __func__, ++s, "blue value 0-255");
if (!validRange(b)) {
return warnArgumentValue(L, __func__, QStringLiteral("blue value %1 needs to be between 0-255").arg(b));
}
// if we get nothing for the alpha value, assume it is 255. If we get a non-number value, complain.
alpha = 255;
if (lua_gettop(L) > s) {
alpha = getVerifiedInt(L, __func__, ++s, "alpha value 0-255", true);
if (!validRange(alpha)) {
return warnArgumentValue(L, __func__, QStringLiteral("alpha value %1 needs to be between 0-255").arg(alpha));
}
}
auto console = CONSOLE(L, windowName);
console->setBgColor(r, g, b, alpha);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#insertLink
int TLuaInterpreter::insertLink(lua_State* L)
{
QStringList sL;
int n = lua_gettop(L);
int s = 1;
bool b = false;
// N/U: bool gotBool = false;
for (; s <= n; s++) {
if (lua_isstring(L, s)) {
QString qs = lua_tostring(L, s);
sL << qs;
} else if (lua_isboolean(L, s)) {
// N/U: gotBool = true;
b = lua_toboolean(L, s);
}
}
if (sL.size() < 4) {
sL.prepend("main");
}
if (sL.size() < 4) {
lua_pushstring(L, "insertLink: wrong number of params or wrong type of params");
return lua_error(L);
}
QString windowName(sL[0]);
QString printScreen(sL[1]);
QStringList command;
QStringList hint;
command << sL[2];
hint << sL[3];
auto console = CONSOLE(L, windowName);
console->insertLink(printScreen, command, hint, b);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#insertPopup
int TLuaInterpreter::insertPopup(lua_State* L)
{
QString windowName;
QStringList _hintList;
QStringList _commandList;
bool customFormat = false;
int s = 1;
int n = lua_gettop(L);
// console name is an optional first argument
if (n >= 4) {
windowName = WINDOW_NAME(L, s++);
}
QString txt = getVerifiedString(L, __func__, s++, "text");
if (!lua_istable(L, s)) {
lua_pushfstring(L, "insertPopup: bad argument #%d type (commands as table expected, got %s!)", s, luaL_typename(L, s));
return lua_error(L);
}
lua_pushnil(L);
while (lua_next(L, s) != 0) {
// key at index -2 and value at index -1
if (lua_type(L, -1) == LUA_TSTRING) {
QString cmd = lua_tostring(L, -1);
_commandList << cmd;
}
// removes value, but keeps key for next iteration
lua_pop(L, 1);
}
if (!lua_istable(L, ++s)) {
lua_pushfstring(L, "insertPopup: bad argument #%d type (hints as table expected, got %s!)", s, luaL_typename(L, s));
return lua_error(L);
}
lua_pushnil(L);
while (lua_next(L, s) != 0) {
// key at index -2 and value at index -1
if (lua_type(L, -1) == LUA_TSTRING) {
QString hint = lua_tostring(L, -1);
_hintList << hint;
}
// removes value, but keeps key for next iteration
lua_pop(L, 1);
}
if (n >= ++s) {
customFormat = lua_toboolean(L, s);
}
if (_commandList.size() != _hintList.size()) {
lua_pushstring(L, "Error: command list size and hint list size do not match cannot create popup");
return lua_error(L);
}
auto console = CONSOLE(L, windowName);
console->insertLink(txt, _commandList, _hintList, customFormat);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#insertText
int TLuaInterpreter::insertText(lua_State* L)
{
QString windowName;
int s = 0;
if (lua_gettop(L) > 1) { // Have more than one argument so first must be a console name
windowName = WINDOW_NAME(L, ++s);
}
QString text = getVerifiedString(L, __func__, ++s, "text");
auto console = CONSOLE(L, windowName);
console->insertText(text);
lua_pushboolean(L, true);
return 1;
}
// No Documentation - public function but should stay undocumented -- compare https://github.com/Mudlet/Mudlet/issues/1149
int TLuaInterpreter::insertHTML(lua_State* L)
{
QString sendText = getVerifiedString(L, __func__, 1, "sendText");
Host& host = getHostFromLua(L);
host.mpConsole->insertHTML(sendText);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#addSupportedTelnetOption
int TLuaInterpreter::addSupportedTelnetOption(lua_State* L)
{
int option = getVerifiedInt(L, __func__, 1, "option");
Host& host = getHostFromLua(L);
host.mTelnet.supportedTelnetOptions[option] = true;
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#echo
int TLuaInterpreter::echo(lua_State* L)
{
Host& host = getHostFromLua(L);
QString consoleName;
int n = lua_gettop(L);
int s = 1;
if (n > 1) {
consoleName = getVerifiedString(L, __func__, s++, "console name", true);
}
QString displayText = getVerifiedString(L, __func__, s, "text to display");
if (isMain(consoleName)) {
host.mpConsole->buffer.mEchoingText = true;
host.mpConsole->echo(displayText);
host.mpConsole->buffer.mEchoingText = false;
// Writing to the main window must always succeed, but for consistent
// results, we now return a true for that
lua_pushboolean(L, true);
return 1;
}
if (!host.echoWindow(consoleName, displayText)) {
return warnArgumentValue(L, __func__, QStringLiteral("console/label '%1' does not exist").arg(consoleName));
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#echoPopup
int TLuaInterpreter::echoPopup(lua_State* L)
{
QString windowName;
QStringList hintList;
QStringList commandList;
bool customFormat = false;
int s = 1;
int n = lua_gettop(L);
// console name is an optional first argument
if (n >= 4) {
windowName = WINDOW_NAME(L, s++);
}
QString text = getVerifiedString(L, __func__, s++, "text as string");
if (!lua_istable(L, s)) {
lua_pushfstring(L, "echoPopup: bad argument #%d type (command list as table expected, got %s!)", s, luaL_typename(L, s));
return lua_error(L);
}
lua_pushnil(L);
while (lua_next(L, s) != 0) {
// key at index -2 and value at index -1
if (lua_type(L, -1) == LUA_TSTRING) {
QString cmd = lua_tostring(L, -1);
commandList << cmd;
}
// removes value, but keeps key for next iteration
lua_pop(L, 1);
}
if (!lua_istable(L, ++s)) {
lua_pushfstring(L, "echoPopup: bad argument #%d type (hint list as table expected, got %s!)", s, luaL_typename(L, s));
return lua_error(L);
}
lua_pushnil(L);
while (lua_next(L, s) != 0) {
// key at index -2 and value at index -1
if (lua_type(L, -1) == LUA_TSTRING) {
QString hint = lua_tostring(L, -1);
hintList << hint;
}
// removes value, but keeps key for next iteration
lua_pop(L, 1);
}
if (n >= ++s) {
customFormat = lua_toboolean(L, s);
}
if (commandList.size() != hintList.size()) {
lua_pushfstring(L, "echoPopup: commands and hints list aren't the same size");
return lua_error(L);
}
auto console = CONSOLE(L, windowName);
console->echoLink(text, commandList, hintList, customFormat);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#echoLink
int TLuaInterpreter::echoLink(lua_State* L)
{
QString a1;
QString a2;
QString a3;
QString a4;
bool useCurrentFormat = false;
bool gotBool = false;
int s = 0;
int n = lua_gettop(L);
if (n > 3){
if (lua_isboolean(L, 4)) gotBool = true;
}
if (!lua_isstring(L, ++s)) {
if (n == 3 || (n > 3 && n < 5 && gotBool)) {
lua_pushfstring(L, "echoLink: bad argument #%d type (text as string expected, got %s!)", s, luaL_typename(L, s));
} else {
lua_pushfstring(L, "echoLink: bad argument #%d type (optional window name as string expected, got %s!)", s, luaL_typename(L, s));
}
return lua_error(L);
}
a1 = lua_tostring(L, s);
if (n > 1) {
if (!lua_isstring(L, ++s)) {
if (n == 3 || (n > 3 && n < 5 && gotBool)) {
lua_pushfstring(L, "echoLink: bad argument #%d type (command as string expected, got %s!)", s, luaL_typename(L, s));
} else {
lua_pushfstring(L, "echoLink: bad argument #%d type (text as string expected, got %s!)", s, luaL_typename(L, s));
}
return lua_error(L);
}
a2 = lua_tostring(L, s);
}
if (n > 2) {
if (!lua_isstring(L, ++s)) {
if (n == 3 || (n > 3 && n < 5 && gotBool)) {
lua_pushfstring(L, "echoLink: bad argument #%d type (hint as string expected, got %s!)", s, luaL_typename(L, s));
} else {
lua_pushfstring(L, "echoLink: bad argument #%d type (command as string expected, got %s!)", s, luaL_typename(L, s));
}
return lua_error(L);
}
a3 = lua_tostring(L, s);
}
if (n > 3) {
if (lua_isstring(L, ++s)) {
a4 = lua_tostring(L, s);
} else if (lua_isboolean(L, s)) {
gotBool = true;
useCurrentFormat = lua_toboolean(L, s);
} else {
if (gotBool) {
lua_pushfstring(L, "echoLink: bad argument #%d type (useCurrentFormat as boolean expected, got %s!)", s, luaL_typename(L, s));
} else {
lua_pushfstring(L, "echoLink: bad argument #%d type (hint as string expected, got %s!)", s, luaL_typename(L, s));
}
return lua_error(L);
}
}
if (n > 4) {
if (!lua_isboolean(L, ++s)) {
lua_pushfstring(L, "echoLink: bad argument #%d type (useCurrentFormat as boolean expected, got %s!)", s, luaL_typename(L, s));
return lua_error(L);
}
useCurrentFormat = lua_toboolean(L, s);
gotBool = true;
}
QString text;
QString windowName;
QStringList function;
QStringList hint;
if (n == 3 || (n == 4 && gotBool)) {
text = a1;
function << a2;
hint << a3;
} else {
windowName = a1;
text = a2;
function << a3;
hint << a4;
}
auto console = CONSOLE(L, windowName);
console->echoLink(text, function, hint, useCurrentFormat);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setMergeTables
int TLuaInterpreter::setMergeTables(lua_State* L)
{
Host& host = getHostFromLua(L);
QStringList modulesList;
int n = lua_gettop(L);
for (int i = 1; i <= n; i++) {
modulesList << getVerifiedString(L, __func__, i, "module");
}
host.mGMCP_merge_table_keys = host.mGMCP_merge_table_keys + modulesList;
host.mGMCP_merge_table_keys.removeDuplicates();
return 0;
}
// No Documentation - public function but should stay undocumented -- compare https://github.com/Mudlet/Mudlet/issues/1149
int TLuaInterpreter::pasteWindow(lua_State* L)
{
if (!lua_isstring(L, 1)) {
lua_pushfstring(L, "pasteWindow: bad argument #1 type (window name as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
QString windowName {WINDOW_NAME(L, 1)};
Host& host = getHostFromLua(L);
host.pasteWindow(windowName);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#openUrl
int TLuaInterpreter::openUrl(lua_State* L)
{
QString url = getVerifiedString(L, __func__, 1, "url");
QDesktopServices::openUrl(url);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setLabelStyleSheet
int TLuaInterpreter::setLabelStyleSheet(lua_State* L)
{
std::string label = getVerifiedString(L, __func__, 1, "label").toStdString();
std::string markup = getVerifiedString(L, __func__, 2, "markup").toStdString();
Host& host = getHostFromLua(L);
host.mpConsole->setLabelStyleSheet(label, markup);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setUserWindowStyleSheet
int TLuaInterpreter::setUserWindowStyleSheet(lua_State* L)
{
QString userWindowName = getVerifiedString(L, __func__, 1, "userwindow name");
QString userWindowStyleSheet = getVerifiedString(L, __func__, 2, "StyleSheet");
Host& host = getHostFromLua(L);
if (auto [success, message] = host.mpConsole->setUserWindowStyleSheet(userWindowName, userWindowStyleSheet); !success) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getCustomEnvColorTable
int TLuaInterpreter::getCustomEnvColorTable(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap->mCustomEnvColors.empty()) {
lua_newtable(L);
QList<int> colorList = host.mpMap->mCustomEnvColors.keys();
for (auto idx : colorList) {
lua_pushnumber(L, idx);
lua_newtable(L);
// red component
{
lua_pushnumber(L, 1);
lua_pushnumber(L, host.mpMap->mCustomEnvColors.value(idx).red());
lua_settable(L, -3);
}
// green component
{
lua_pushnumber(L, 2);
lua_pushnumber(L, host.mpMap->mCustomEnvColors.value(idx).green());
lua_settable(L, -3);
}
// blue component
{
lua_pushnumber(L, 3);
lua_pushnumber(L, host.mpMap->mCustomEnvColors.value(idx).blue());
lua_settable(L, -3);
}
// alpha component
{
lua_pushnumber(L, 4);
lua_pushnumber(L, host.mpMap->mCustomEnvColors.value(idx).alpha());
lua_settable(L, -3);
}
lua_settable(L, -3);
}
} else {
lua_newtable(L);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getMudletVersion
int TLuaInterpreter::getMudletVersion(lua_State* L)
{
QByteArray version = QByteArray(APP_VERSION).trimmed();
QByteArray build = QByteArray(APP_BUILD).trimmed();
QList<QByteArray> versionData = version.split('.');
if (versionData.size() != 3) {
qWarning() << "TLuaInterpreter::getMudletVersion(): ERROR: Version data not correctly set on compilation,\n"
<< " is the VERSION value in the project file present?";
lua_pushstring(L, "getMudletVersion: sorry, version information not available.");
return lua_error(L);
}
bool ok = true;
int major = 0;
int minor = 0;
int revision = 0;
{
major = versionData.at(0).toInt(&ok);
if (ok) {
minor = versionData.at(1).toInt(&ok);
}
if (ok) {
revision = versionData.at(2).toInt(&ok);
}
}
if (!ok) {
qWarning("TLuaInterpreter::getMudletVersion(): ERROR: Version data not correctly parsed,\n"
" was the VERSION value in the project file correct at compilation time?");
lua_pushstring(L, "getMudletVersion: sorry, version information corrupted.");
return lua_error(L);
}
int n = lua_gettop(L);
if (n == 1) {
QString tidiedWhat = getVerifiedString(L, __func__, 1, "style", true).toLower().trimmed();
if (tidiedWhat.contains("major")) {
lua_pushinteger(L, major);
} else if (tidiedWhat.contains("minor")) {
lua_pushinteger(L, minor);
} else if (tidiedWhat.contains("revision")) {
lua_pushinteger(L, revision);
} else if (tidiedWhat.contains("build")) {
if (build.isEmpty()) {
lua_pushnil(L);
} else {
lua_pushstring(L, build);
}
} else if (tidiedWhat.contains("string")) {
if (build.isEmpty()) {
lua_pushstring(L, version.constData());
} else {
lua_pushstring(L, version.append(build).constData());
}
} else if (tidiedWhat.contains("table")) {
lua_pushinteger(L, major);
lua_pushinteger(L, minor);
lua_pushinteger(L, revision);
if (build.isEmpty()) {
lua_pushnil(L);
} else {
lua_pushstring(L, build);
}
return 4;
} else {
lua_pushstring(L,
"getMudletVersion: takes one (optional) argument:\n"
" \"major\", \"minor\", \"revision\", \"build\", \"string\" or \"table\".");
return lua_error(L);
}
} else if (n == 0) {
lua_newtable(L);
lua_pushstring(L, "major");
lua_pushinteger(L, major);
lua_settable(L, -3);
lua_pushstring(L, "minor");
lua_pushinteger(L, minor);
lua_settable(L, -3);
lua_pushstring(L, "revision");
lua_pushinteger(L, revision);
lua_settable(L, -3);
lua_pushstring(L, "build");
lua_pushstring(L, QByteArray(APP_BUILD).trimmed().data());
lua_settable(L, -3);
} else {
lua_pushstring(L,
"getMudletVersion: only takes one (optional) argument:\n"
" \"major\", \"minor\", \"revision\", \"build\", \"string\" or \"table\".");
return lua_error(L);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#openWebPage
int TLuaInterpreter::openWebPage(lua_State* L)
{
QString url = getVerifiedString(L, __func__, 1, "URL");
lua_pushboolean(L, mudlet::self()->openWebPage(url));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setDiscordApplicationID
int TLuaInterpreter::setDiscordApplicationID(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L, true);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
}
if (!lua_gettop(L)) {
pMudlet->mDiscord.setApplicationID(&host, QString());
lua_pushboolean(L, true);
return 1;
}
QString inputText = getVerifiedString(L, __func__, 1, "id").trimmed();
// Treat it as a UTF-8 string because although it is likely to be an
// unsigned long long integer (0 to 18446744073709551615) we want to
// be able to handle any input so we can report bad input strings back.
if (inputText.isEmpty()) {
// Empty string input - to reset to default the same as the no
// argument case:
pMudlet->mDiscord.setApplicationID(&host, QString());
// This must always succeed
lua_pushboolean(L, true);
return 1;
}
bool isOk = false;
quint64 numericEquivalent = inputText.toULongLong(&isOk);
if (numericEquivalent && isOk) {
QString appID = QString::number(numericEquivalent);
if (pMudlet->mDiscord.setApplicationID(&host, appID)) {
lua_pushboolean(L, true);
return 1;
}
return warnArgumentValue(L, __func__, QStringLiteral("'%1' does not appear to be a valid Discord application id").arg(inputText));
}
return warnArgumentValue(L, __func__, QStringLiteral("'%1' can not be converted to the expected numeric Discord application id").arg(inputText));
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#usingMudletsDiscordID
int TLuaInterpreter::usingMudletsDiscordID(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
}
lua_pushboolean(L, pMudlet->mDiscord.usingMudletsDiscordID(&host));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setDiscordLargeIcon
int TLuaInterpreter::setDiscordLargeIcon(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L, true);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetLargeIcon)) {
return warnArgumentValue(L, __func__, "access to Discord large icon is disabled in settings for privacy");
}
pMudlet->mDiscord.setLargeImage(&host, getVerifiedString(L, __func__, 1, "key").toLower());
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getDiscordLargeIcon
int TLuaInterpreter::getDiscordLargeIcon(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetLargeIcon)) {
return warnArgumentValue(L, __func__, "access to Discord large icon is disabled in settings for privacy");
}
lua_pushfstring(L, pMudlet->mDiscord.getLargeImage(&host).toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setDiscordLargeIconText
int TLuaInterpreter::setDiscordLargeIconText(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetLargeIconText)) {
return warnArgumentValue(L, __func__, "access to Discord large icon text is disabled in settings for privacy");
}
pMudlet->mDiscord.setLargeImageText(&host, getVerifiedString(L, __func__, 1, "text"));
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getDiscordLargeIconText
int TLuaInterpreter::getDiscordLargeIconText(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L, true);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetLargeIconText)) {
return warnArgumentValue(L, __func__, "access to Discord large icon text is disabled in settings for privacy");
}
lua_pushfstring(L, pMudlet->mDiscord.getLargeImageText(&host).toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setDiscordSmallIcon
int TLuaInterpreter::setDiscordSmallIcon(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetSmallIcon)) {
return warnArgumentValue(L, __func__, "access to Discord small icon is disabled in settings for privacy");
}
pMudlet->mDiscord.setSmallImage(&host, getVerifiedString(L, __func__, 1, "key").toLower());
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getDiscordSmallIcon
int TLuaInterpreter::getDiscordSmallIcon(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L, true);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetSmallIcon)) {
return warnArgumentValue(L, __func__, "access to Discord small icon is disabled in settings for privacy");
}
lua_pushfstring(L, pMudlet->mDiscord.getSmallImage(&host).toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setDiscordSmallIconText
int TLuaInterpreter::setDiscordSmallIconText(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetSmallIconText)) {
return warnArgumentValue(L, __func__, "access to Discord small icon text is disabled in settings for privacy");
}
pMudlet->mDiscord.setSmallImageText(&host, getVerifiedString(L, __func__, 1, "text"));
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getDiscordSmallIconText
int TLuaInterpreter::getDiscordSmallIconText(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L, true);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetSmallIconText)) {
return warnArgumentValue(L, __func__, "access to Discord small icon text is disabled in settings for privacy");
}
lua_pushfstring(L, pMudlet->mDiscord.getSmallImageText(&host).toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setDiscordDetail
int TLuaInterpreter::setDiscordDetail(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetDetail)) {
return warnArgumentValue(L, __func__, "access to Discord detail is disabled in settings for privacy");
}
pMudlet->mDiscord.setDetailText(&host, getVerifiedString(L, __func__, 1, "text"));
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getDiscordDetail
int TLuaInterpreter::getDiscordDetail(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetDetail)) {
return warnArgumentValue(L, __func__, "access to Discord detail is disabled in settings for privacy");
}
lua_pushfstring(L, pMudlet->mDiscord.getDetailText(&host).toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setDiscordGame
int TLuaInterpreter::setDiscordGame(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetDetail)) {
return warnArgumentValue(L, __func__, "access to Discord detail is disabled in settings for privacy");
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetLargeIcon)) {
return warnArgumentValue(L, __func__, "access to Discord large icon is disabled in settings for privacy");
}
QString gamename = getVerifiedString(L, __func__, 1, "game name");
pMudlet->mDiscord.setDetailText(&host, tr("Playing %1").arg(gamename));
pMudlet->mDiscord.setLargeImage(&host, gamename.toLower());
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setDiscordState
int TLuaInterpreter::setDiscordState(lua_State* L)
{
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetState)) {
return warnArgumentValue(L, __func__, "access to Discord state is disabled in settings for privacy");
}
mudlet::self()->mDiscord.setStateText(&host, getVerifiedString(L, __func__, 1, "text"));
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getDiscordState
int TLuaInterpreter::getDiscordState(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetState)) {
return warnArgumentValue(L, __func__, "access to Discord state is disabled in settings for privacy");
}
lua_pushfstring(L, pMudlet->mDiscord.getStateText(&host).toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setDiscordElapsedStartTime
int TLuaInterpreter::setDiscordElapsedStartTime(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetTimeInfo)) {
return warnArgumentValue(L, __func__, "access to Discord time is disabled in settings for privacy");
}
int64_t timeStamp = getVerifiedInt(L, __func__, 1, "epoch time");
if (timeStamp < 0) {
return warnArgumentValue(L, __func__, "the timestamp must be zero to clear the 'elapsed:' time or an epoch time value from the recent past");
}
pMudlet->mDiscord.setStartTimeStamp(&host, timeStamp);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setDiscordRemainingEndTime
int TLuaInterpreter::setDiscordRemainingEndTime(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetTimeInfo)) {
return warnArgumentValue(L, __func__, "access to Discord time is disabled in settings for privacy");
}
int64_t timeStamp = getVerifiedInt(L, __func__, 1, "epoch time");
if (timeStamp < 0) {
return warnArgumentValue(L, __func__, "the timestamp must be zero to clear the 'remaining:' time or an epoch time value in the recent future");
}
pMudlet->mDiscord.setEndTimeStamp(&host, timeStamp);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getDiscordTimeStamps
int TLuaInterpreter::getDiscordTimeStamps(lua_State* L)
{
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetTimeInfo)) {
return warnArgumentValue(L, __func__, "access to Discord time is disabled in settings for privacy");
}
QPair<int64_t, int64_t> timeStamps = mudlet::self()->mDiscord.getTimeStamps(&host);
lua_pushnumber(L, timeStamps.first);
lua_pushnumber(L, timeStamps.second);
return 2;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setDiscordParty
int TLuaInterpreter::setDiscordParty(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetPartyInfo)) {
return warnArgumentValue(L, __func__, "access to Discord party info is disabled in settings for privacy");
}
int64_t partySize = getVerifiedInt(L, __func__, 1, "current party size");
if (partySize < 0) {
return warnArgumentValue(L, __func__, "the current party size must be zero or more");
}
int64_t partyMax = -1;
if (lua_gettop(L) > 1) {
partyMax = getVerifiedInt(L, __func__, 2, "party maximum size", true);
if (partyMax < 0) {
return warnArgumentValue(L, __func__, "the optional party maximum size must be zero (to remove the party details) or more (to set the maximum)");
}
pMudlet->mDiscord.setParty(&host, static_cast<int>(qMin(static_cast<int64_t>(INT_MAX), partySize)), static_cast<int>(qMin(static_cast<int64_t>(INT_MAX), partyMax)));
} else {
// Only got the partySize now
pMudlet->mDiscord.setParty(&host, static_cast<int>(qMin(static_cast<int64_t>(INT_MAX), partySize)));
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getDiscordParty
int TLuaInterpreter::getDiscordParty(lua_State* L)
{
mudlet* pMudlet = mudlet::self();
auto& host = getHostFromLua(L);
auto result = discordApiEnabled(L, true);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second);
} else if (!(host.mDiscordAccessFlags & Host::DiscordSetPartyInfo)) {
return warnArgumentValue(L, __func__, "access to Discord party info is disabled in settings for privacy");
}
QPair<int, int> partyValues = pMudlet->mDiscord.getParty(&host);
lua_pushnumber(L, partyValues.first);
lua_pushnumber(L, partyValues.second);
return 2;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getTime
int TLuaInterpreter::getTime(lua_State* L)
{
int n = lua_gettop(L);
bool return_string = false;
QString format = QStringLiteral("yyyy.MM.dd hh:mm:ss.zzz");
QString tm;
if (n > 0) {
return_string = getVerifiedBool(L, __func__, 1, "return as string", true);
if (n > 1) {
format = getVerifiedString(L, __func__, 2, "custom time format");
}
}
QDateTime time = QDateTime::currentDateTime();
if (return_string) {
tm = time.toString(format);
lua_pushstring(L, tm.toUtf8().constData());
} else {
QDate dt = time.date();
QTime tm = time.time();
lua_createtable(L, 0, 4);
lua_pushstring(L, "hour");
lua_pushinteger(L, tm.hour());
lua_rawset(L, n + 1);
lua_pushstring(L, "min");
lua_pushinteger(L, tm.minute());
lua_rawset(L, n + 1);
lua_pushstring(L, "sec");
lua_pushinteger(L, tm.second());
lua_rawset(L, n + 1);
lua_pushstring(L, "msec");
lua_pushinteger(L, tm.msec());
lua_rawset(L, n + 1);
lua_pushstring(L, "year");
lua_pushinteger(L, dt.year());
lua_rawset(L, n + 1);
lua_pushstring(L, "month");
lua_pushinteger(L, dt.month());
lua_rawset(L, n + 1);
lua_pushstring(L, "day");
lua_pushinteger(L, dt.day());
lua_rawset(L, n + 1);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getEpoch
int TLuaInterpreter::getEpoch(lua_State *L)
{
lua_pushnumber(L, static_cast<double>(QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000.0));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#appendBuffer
int TLuaInterpreter::appendBuffer(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
auto console = CONSOLE(L, windowName);
console->appendBuffer();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#appendCmdLine
int TLuaInterpreter::appendCmdLine(lua_State* L)
{
int n = lua_gettop(L);
QString name = "main";
if (n > 1) {
name = CMDLINE_NAME(L, 1);
}
QString text = getVerifiedString(L, __func__, n, "text to set on command line");
auto pN = COMMANDLINE(L, name);
QString curText = pN->toPlainText();
pN->setPlainText(curText + text);
QTextCursor cur = pN->textCursor();
cur.clearSelection();
cur.movePosition(QTextCursor::EndOfLine);
pN->setTextCursor(cur);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getCmdLine
int TLuaInterpreter::getCmdLine(lua_State* L)
{
int n = lua_gettop(L);
QString name = "main";
if (n >= 1) {
name = CMDLINE_NAME(L, 1);
}
auto commandline = COMMANDLINE(L, name);
QString text = commandline->toPlainText();
lua_pushstring(L, text.toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#addCmdLineSuggestion
int TLuaInterpreter::addCmdLineSuggestion(lua_State* L)
{
int n = lua_gettop(L);
QString name = "main";
if (n > 1) {
name = CMDLINE_NAME(L, 1);
}
QString text = getVerifiedString(L, __func__, n, "suggestion text");
auto pN = COMMANDLINE(L, name);
pN->addSuggestion(text);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#removeCmdLineSuggestion
int TLuaInterpreter::removeCmdLineSuggestion(lua_State* L)
{
int n = lua_gettop(L);
QString name = "main";
if (n > 1) {
name = CMDLINE_NAME(L, 1);
}
QString text = getVerifiedString(L, __func__, n, "suggestion text");
auto pN = COMMANDLINE(L, name);
pN->removeSuggestion(text);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#clearCmdLineSuggestions
int TLuaInterpreter::clearCmdLineSuggestions(lua_State* L)
{
int n = lua_gettop(L);
QString name = "main";
if (n == 1) {
name = CMDLINE_NAME(L, 1);
}
auto pN = COMMANDLINE(L, name);
pN->clearSuggestions();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#installPackage
int TLuaInterpreter::installPackage(lua_State* L)
{
QString location = getVerifiedString(L, __func__, 1, "package location path and file name");
Host& host = getHostFromLua(L);
lua_pushboolean(L, host.installPackage(location, 0));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#uninstallPackage
int TLuaInterpreter::uninstallPackage(lua_State* L)
{
QString packageName = getVerifiedString(L, __func__, 1, "package name");
Host& host = getHostFromLua(L);
host.uninstallPackage(packageName, 0);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#installModule
int TLuaInterpreter::installModule(lua_State* L)
{
QString modName = getVerifiedString(L, __func__, 1, "module location");
Host& host = getHostFromLua(L);
QString module = QDir::fromNativeSeparators(modName);
if (host.installPackage(module, 3) && mudlet::self()->moduleTableVisible()) {
mudlet::self()->layoutModules();
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#uninstallModule
int TLuaInterpreter::uninstallModule(lua_State* L)
{
QString module = getVerifiedString(L, __func__, 1, "module name");
Host& host = getHostFromLua(L);
if (host.uninstallPackage(module, 3) && mudlet::self()->moduleTableVisible()) {
mudlet::self()->layoutModules();
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#reloadModule
int TLuaInterpreter::reloadModule(lua_State* L)
{
QString module = getVerifiedString(L, __func__, 1, "module name");
Host& host = getHostFromLua(L);
host.reloadModule(module);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#enableModuleSync
int TLuaInterpreter::enableModuleSync(lua_State* L)
{
QString module = getVerifiedString(L, __func__, 1, "module name");
Host& host = getHostFromLua(L);
if (auto [success, message] = host.changeModuleSync(module, QLatin1String("1")); !success) {
return warnArgumentValue(L, __func__, message);
}
auto moduleTable = mudlet::self()->moduleTable;
if (moduleTable && !moduleTable->findItems(module, Qt::MatchExactly).isEmpty()) {
int row = moduleTable->findItems(module, Qt::MatchExactly)[0]->row();
auto checkItem = moduleTable->item(row, 2);
checkItem->setCheckState(Qt::Checked);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#disableModuleSync
int TLuaInterpreter::disableModuleSync(lua_State* L)
{
QString module = getVerifiedString(L, __func__, 1, "module name");
Host& host = getHostFromLua(L);
if (auto [success, message] = host.changeModuleSync(module, QLatin1String("0")); !success) {
return warnArgumentValue(L, __func__, message);
}
auto moduleTable = mudlet::self()->moduleTable;
if (moduleTable && !moduleTable->findItems(module, Qt::MatchExactly).isEmpty()) {
int row = moduleTable->findItems(module, Qt::MatchExactly)[0]->row();
auto checkItem = moduleTable->item(row, 2);
checkItem->setCheckState(Qt::Unchecked);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getModuleSync
int TLuaInterpreter::getModuleSync(lua_State* L)
{
QString module = getVerifiedString(L, __func__, 1, "module name");
Host& host = getHostFromLua(L);
if (auto [success, message] = host.getModuleSync(module); !success) {
return warnArgumentValue(L, __func__, message);
} else if (message == QLatin1String("1")) {
lua_pushboolean(L, true);
return 1;
} else {
lua_pushboolean(L, false);
return 1;
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setDefaultAreaVisible
int TLuaInterpreter::setDefaultAreaVisible(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!host.mpMap || !host.mpMap->mpRoomDB) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
bool isToShowDefaultArea = getVerifiedBool(L, __func__, 1, "isToShowDefaultArea");
if (host.mpMap->mpMapper) {
// If we are reenabled the display of the default area
// AND the mapper was showing the default area
// the area widget will NOT be showing the correct area name afterwards
bool isAreaWidgetInNeedOfResetting = false;
if ((!host.mpMap->mpMapper->getDefaultAreaShown()) && (isToShowDefaultArea) && (host.mpMap->mpMapper->mp2dMap->mAreaID == -1)) {
isAreaWidgetInNeedOfResetting = true;
}
host.mpMap->mpMapper->setDefaultAreaShown(isToShowDefaultArea);
if (isAreaWidgetInNeedOfResetting) {
// Corner case fixup:
host.mpMap->mpMapper->showArea->setCurrentText(host.mpMap->getDefaultAreaName());
}
host.mpMap->mpMapper->mp2dMap->repaint();
host.mpMap->mpMapper->update();
lua_pushboolean(L, true);
} else {
lua_pushboolean(L, false);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#registerAnonymousEventHandler
// The function below is mostly unused now as it is overwritten in lua.
// The overwriting function poses as a transperant proxy and internally uses
// this function to get called events.
int TLuaInterpreter::registerAnonymousEventHandler(lua_State* L)
{
QString event = getVerifiedString(L, __func__, 1, "event name");
QString func = getVerifiedString(L, __func__, 2, "function name");
Host& host = getHostFromLua(L);
host.registerAnonymousEventHandler(event, func);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#expandAlias
int TLuaInterpreter::expandAlias(lua_State* L)
{
QString payload = getVerifiedString(L, __func__, 1, "text to parse");
bool wantPrint = true;
if (lua_gettop(L) > 1) {
// check if the 2nd argument is a 'false', but don't match if it is 'nil'
// because expandAlias("command") should be the same as expandAlias("command", nil)
if (lua_isnil(L, 2)) {
wantPrint = false;
} else {
wantPrint = getVerifiedBool(L, __func__, 2, "echo", true);
}
}
Host& host = getHostFromLua(L);
// Host::send will encode the UTF encoded data here in the wanted Server
// encoding:
host.send(payload, wantPrint, false);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#printCmdLine
int TLuaInterpreter::printCmdLine(lua_State* L)
{
int n = lua_gettop(L);
QString name = "main";
if (n > 1) {
name = CMDLINE_NAME(L, 1);
}
QString text = getVerifiedString(L, __func__, n, "text to set on command line");
auto pN = COMMANDLINE(L, name);
pN->setPlainText(text);
QTextCursor cur = pN->textCursor();
cur.clearSelection();
cur.movePosition(QTextCursor::EndOfLine);
pN->setTextCursor(cur);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#clearCmdLine
int TLuaInterpreter::clearCmdLine(lua_State* L)
{
int n = lua_gettop(L);
QString name = "main";
if (n > 1) {
name = CMDLINE_NAME(L, 1);
}
auto pN = COMMANDLINE(L, name);
pN->clear();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#send
// Note this is registered as send NOT sendRaw - see initLuaGlobals()
// It converts the bytes in the command (the first argument) from Utf-8 to be
// encoded in the required Mud Server encoding.
int TLuaInterpreter::sendRaw(lua_State* L)
{
QString text = getVerifiedString(L, __func__, 1, "command");
bool wantPrint = true;
if (lua_gettop(L) > 1) {
wantPrint = getVerifiedBool(L, __func__, 2, "showOnScreen", true);
}
Host& host = getHostFromLua(L);
// Host::send will encode the UTF encoded data here in the wanted Server encoding:
host.send(text, wantPrint, true);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#sendSocket
// The data can, theoretically, contain embedded ASCII NUL characters:
int TLuaInterpreter::sendSocket(lua_State* L)
{
if (!lua_isstring(L, 1)) {
lua_pushfstring(L, "sendSocket: bad argument #1 type (data as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
std::string data = lua_tostring(L, 1);
Host& host = getHostFromLua(L);
// msg is not in an encoded form here it is a literal set of bytes, which
// is what this usage needs:
host.mTelnet.socketOutRaw(data);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#sendIrc
int TLuaInterpreter::sendIrc(lua_State* L)
{
QString target = getVerifiedString(L, __func__, 1, "target");
QString msg = getVerifiedString(L, __func__, 2, "message");
Host* pHost = &getHostFromLua(L);
if (!pHost->mpDlgIRC) {
// create a new irc client if one isn't ready.
pHost->mpDlgIRC = new dlgIRC(pHost);
pHost->mpDlgIRC->raise();
pHost->mpDlgIRC->show();
}
// wait for our client to be ready before sending messages.
if (!pHost->mpDlgIRC->mReadyForSending) {
return warnArgumentValue(L, __func__, "not ready to send just yet");
}
QPair<bool, QString> result = pHost->mpDlgIRC->sendMsg(target, msg);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second.toUtf8().constData());
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getIrcNick
int TLuaInterpreter::getIrcNick(lua_State* L)
{
Host* pHost = &getHostFromLua(L);
QString nick;
if (pHost->mpDlgIRC) {
nick = pHost->mpDlgIRC->getNickName();
} else {
nick = dlgIRC::readIrcNickName(pHost);
}
lua_pushstring(L, nick.toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getIrcServer
int TLuaInterpreter::getIrcServer(lua_State* L)
{
Host* pHost = &getHostFromLua(L);
QString hname;
int hport;
if (pHost->mpDlgIRC) {
hname = pHost->mpDlgIRC->getHostName();
hport = pHost->mpDlgIRC->getHostPort();
} else {
hname = dlgIRC::readIrcHostName(pHost);
hport = dlgIRC::readIrcHostPort(pHost);
}
lua_pushstring(L, hname.toUtf8().constData());
lua_pushinteger(L, hport);
return 2;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getIrcChannels
int TLuaInterpreter::getIrcChannels(lua_State* L)
{
Host* pHost = &getHostFromLua(L);
QStringList channels;
if (pHost->mpDlgIRC) {
channels = pHost->mpDlgIRC->getChannels();
} else {
channels = dlgIRC::readIrcChannels(pHost);
}
lua_newtable(L);
int total = channels.count();
for (int i = 0; i < total; ++i) {
lua_pushnumber(L, i + 1);
lua_pushstring(L, channels[i].toUtf8().data());
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getIrcConnectedHost
int TLuaInterpreter::getIrcConnectedHost(lua_State* L)
{
Host* pHost = &getHostFromLua(L);
QString cHostName;
QString error = QStringLiteral("no client active");
if (pHost->mpDlgIRC) {
cHostName = pHost->mpDlgIRC->getConnectedHost();
if (cHostName.isEmpty()) {
error = QStringLiteral("not yet connected");
}
}
if (cHostName.isEmpty()) {
return warnArgumentValue(L, __func__, error, true);
} else {
lua_pushboolean(L, true);
lua_pushstring(L, cHostName.toUtf8().constData());
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setIrcNick
int TLuaInterpreter::setIrcNick(lua_State* L)
{
QString nick = getVerifiedString(L, __func__, 1, "nick");
if (nick.isEmpty()) {
return warnArgumentValue(L, __func__, "nick must not be empty");
}
Host* pHost = &getHostFromLua(L);
QPair<bool, QString> result = dlgIRC::writeIrcNickName(pHost, nick);
if (!result.first) {
return warnArgumentValue(L, __func__, QStringLiteral("unable to save nick name, reason: %1").arg(result.second));
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setIrcServer
int TLuaInterpreter::setIrcServer(lua_State* L)
{
int port = 6667;
std::string addr = getVerifiedString(L, __func__, 1, "hostname").toStdString();
if (addr.empty()) {
return warnArgumentValue(L, __func__, "hostname must not be empty");
}
if (!lua_isnoneornil(L, 2)) {
port = getVerifiedInt(L, __func__, 2, "port number {default = 6667}", true);
if (port > 65535 || port < 1) {
return warnArgumentValue(L, __func__, QStringLiteral("invalid port number %1 given, if supplied it must be in range 1 to 65535").arg(port));
}
}
Host* pHost = &getHostFromLua(L);
QPair<bool, QString> result = dlgIRC::writeIrcHostName(pHost, QString::fromStdString(addr));
if (!result.first) {
return warnArgumentValue(L, __func__, QStringLiteral("unable to save hostname, reason: %1").arg(result.second));
}
result = dlgIRC::writeIrcHostPort(pHost, port);
if (!result.first) {
return warnArgumentValue(L, __func__, QStringLiteral("unable to save port, reason: %1").arg(result.second));
}
lua_pushboolean(L, true);
lua_pushnil(L);
return 2;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setIrcChannels
int TLuaInterpreter::setIrcChannels(lua_State* L)
{
QStringList newchannels;
if (!lua_istable(L, 1)) {
lua_pushfstring(L, "setIrcChannels: bad argument #1 type (channels as table expected, got %s!)", lua_typename(L, lua_type(L, 1)));
return lua_error(L);
}
lua_pushnil(L);
while (lua_next(L, 1) != 0) {
// key at index -2 and value at index -1
if (lua_type(L, -1) == LUA_TSTRING) {
QString c = lua_tostring(L, -1);
if (!c.isEmpty() && (c.startsWith(QLatin1String("#")) || c.startsWith(QLatin1String("&")) || c.startsWith(QLatin1String("+")))) {
newchannels << c;
}
}
lua_pop(L, 1);
}
if (newchannels.empty()) {
return warnArgumentValue(L, __func__, "no (valid) channel names provided");
}
Host* pHost = &getHostFromLua(L);
QPair<bool, QString> result = dlgIRC::writeIrcChannels(pHost, newchannels);
if (!result.first) {
return warnArgumentValue(L, __func__, QStringLiteral("unable to save channels, reason: %1").arg(result.second));
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#restartIrc
int TLuaInterpreter::restartIrc(lua_State* L)
{
Host* pHost = &getHostFromLua(L);
bool rv = false;
if (pHost->mpDlgIRC) {
pHost->mpDlgIRC->ircRestart();
rv = true;
}
lua_pushboolean(L, rv);
return 1;
}
#ifdef QT_TEXTTOSPEECH_LIB
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsSpeak
int TLuaInterpreter::ttsSpeak(lua_State* L)
{
TLuaInterpreter::ttsBuild();
QString textToSay = getVerifiedString(L, __func__, 1, "text to say").trimmed();
if (textToSay.isEmpty()) { // there's nothing more to say. discussion: https://github.com/Mudlet/Mudlet/issues/4688
return warnArgumentValue(L, __func__, QStringLiteral("skipped empty text to speak (TTS)"));
}
std::vector<QString> dontSpeak = {"<", ">", "&lt;", "&gt;"}; // discussion: https://github.com/Mudlet/Mudlet/issues/4689
for (const QString dropThis : dontSpeak) {
if (textToSay.contains(dropThis)) {
textToSay.replace(dropThis, QString());
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::darkGreen)) << "LUA: removed angle-shaped brackets (<>) from text to speak (TTS)\n" >> 0;
}
}
}
speechUnit->say(textToSay);
speechCurrent = textToSay;
return 0;
}
// No documentation available in wiki - internal function
void TLuaInterpreter::ttsBuild()
{
if (bSpeechBuilt) {
return;
}
speechUnit = new QTextToSpeech();
bSpeechBuilt = true;
bSpeechQueueing = false;
connect(speechUnit, &QTextToSpeech::stateChanged, &TLuaInterpreter::ttsStateChanged);
speechUnit->setVolume(1.0);
speechUnit->setRate(0.0);
speechUnit->setPitch(0.0);
return;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsSkip
int TLuaInterpreter::ttsSkip(lua_State* L)
{
Q_UNUSED(L)
TLuaInterpreter::ttsBuild();
speechUnit->stop();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsSetRate
int TLuaInterpreter::ttsSetRate(lua_State* L)
{
TLuaInterpreter::ttsBuild();
double rate = getVerifiedDouble(L, __func__, 1, "rate");
if (rate > 1.0) {
rate = 1.0;
}
if (rate < -1.0) {
rate = -1.0;
}
speechUnit->setRate(rate);
TEvent event {};
event.mArgumentList.append(QLatin1String("ttsRateChanged"));
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
event.mArgumentList.append(QString::number(rate));
event.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER);
mudlet::self()->getHostManager().postInterHostEvent(NULL, event, true);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsSetPitch
int TLuaInterpreter::ttsSetPitch(lua_State* L)
{
TLuaInterpreter::ttsBuild();
double pitch = getVerifiedDouble(L, __func__, 1, "pitch");
if (pitch > 1.0) {
pitch = 1.0;
}
if (pitch < -1.0) {
pitch = -1.0;
}
speechUnit->setPitch(pitch);
TEvent event {};
event.mArgumentList.append(QLatin1String("ttsPitchChanged"));
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
event.mArgumentList.append(QString::number(pitch));
event.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER);
mudlet::self()->getHostManager().postInterHostEvent(NULL, event, true);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsSetVolume
int TLuaInterpreter::ttsSetVolume(lua_State* L)
{
TLuaInterpreter::ttsBuild();
double volume = getVerifiedDouble(L, __func__, 1, "volume");
if (volume > 1.0) {
volume = 1.0;
}
if (volume < 0.0) {
volume = 0.0;
}
speechUnit->setVolume(volume);
TEvent event {};
event.mArgumentList.append(QLatin1String("ttsVolumeChanged"));
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
event.mArgumentList.append(QString::number(volume));
event.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER);
mudlet::self()->getHostManager().postInterHostEvent(NULL, event, true);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsGetVolume
int TLuaInterpreter::ttsGetVolume(lua_State* L)
{
TLuaInterpreter::ttsBuild();
lua_pushnumber(L, speechUnit->volume());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsGetRate
int TLuaInterpreter::ttsGetRate(lua_State* L)
{
TLuaInterpreter::ttsBuild();
lua_pushnumber(L, speechUnit->rate());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsGetPitch
int TLuaInterpreter::ttsGetPitch(lua_State* L)
{
TLuaInterpreter::ttsBuild();
lua_pushnumber(L, speechUnit->pitch());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsGetVoices
int TLuaInterpreter::ttsGetVoices(lua_State* L)
{
TLuaInterpreter::ttsBuild();
QVector<QVoice> speechVoices = speechUnit->availableVoices();
int i = 0;
lua_newtable(L);
for (const QVoice& voice : speechVoices) {
lua_pushnumber(L, ++i);
lua_pushstring(L, voice.name().toUtf8().constData());
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsGetCurrentVoice
int TLuaInterpreter::ttsGetCurrentVoice(lua_State* L)
{
TLuaInterpreter::ttsBuild();
QString currentVoice = speechUnit->voice().name();
lua_pushstring(L, currentVoice.toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsSetVoiceByName
int TLuaInterpreter::ttsSetVoiceByName(lua_State* L)
{
TLuaInterpreter::ttsBuild();
QString nextVoice = getVerifiedString(L, __func__, 1, "voice");
QVector<QVoice> speechVoices = speechUnit->availableVoices();
for (auto voice : speechVoices) {
if (voice.name() == nextVoice) {
speechUnit->setVoice(voice);
lua_pushboolean(L, true);
TEvent event {};
event.mArgumentList.append(QLatin1String("ttsVoiceChanged"));
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
event.mArgumentList.append(voice.name());
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
mudlet::self()->getHostManager().postInterHostEvent(NULL, event, true);
return 1;
}
}
lua_pushboolean(L, false);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsSetVoiceByIndex
int TLuaInterpreter::ttsSetVoiceByIndex(lua_State* L)
{
TLuaInterpreter::ttsBuild();
int index = getVerifiedInt(L, __func__, 1, "voice as index number");
index--;
QVector<QVoice> speechVoices = speechUnit->availableVoices();
if (index < 0 || index >= speechVoices.size()) {
lua_pushboolean(L, false);
return 1;
}
speechUnit->setVoice(speechVoices.at(index));
TEvent event {};
event.mArgumentList.append(QLatin1String("ttsVoiceChanged"));
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
event.mArgumentList.append(speechVoices[index].name());
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
mudlet::self()->getHostManager().postInterHostEvent(NULL, event, true);
lua_pushboolean(L, true);
return 1;
}
// No documentation available in wiki - internal function
void TLuaInterpreter::ttsStateChanged(QTextToSpeech::State state)
{
if (state != speechState) {
speechState = state;
TEvent event {};
switch (state) {
case QTextToSpeech::Paused:
event.mArgumentList.append(QLatin1String("ttsSpeechPaused"));
break;
case QTextToSpeech::Speaking:
event.mArgumentList.append(QLatin1String("ttsSpeechStarted"));
break;
case QTextToSpeech::BackendError:
event.mArgumentList.append(QLatin1String("ttsSpeechError"));
break;
case QTextToSpeech::Ready:
event.mArgumentList.append(QLatin1String("ttsSpeechReady"));
break;
}
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
if (state == QTextToSpeech::Speaking) {
event.mArgumentList.append(speechCurrent);
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
}
mudlet::self()->getHostManager().postInterHostEvent(NULL, event, true);
}
if (state != QTextToSpeech::Ready || speechQueue.empty()) {
bSpeechQueueing = false;
return;
}
QString textToSay;
textToSay = speechQueue.takeFirst();
speechUnit->say(textToSay);
speechCurrent = textToSay;
return;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsQueue
int TLuaInterpreter::ttsQueue(lua_State* L)
{
TLuaInterpreter::ttsBuild();
QString inputText = getVerifiedString(L, __func__, 1, "input").trimmed();
if (inputText.isEmpty()) { // there's nothing more to say. discussion: https://github.com/Mudlet/Mudlet/issues/4688
return warnArgumentValue(L, __func__, QStringLiteral("skipped empty text to speak (TTS)"));
}
std::vector<QString> dontSpeak = {"<", ">", "&lt;", "&gt;"}; // discussion: https://github.com/Mudlet/Mudlet/issues/4689
for (const QString dropThis : dontSpeak) {
if (inputText.contains(dropThis)) {
inputText.replace(dropThis, QString());
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::darkGreen)) << "LUA: removed angle-shaped brackets (<>) from text to speak (TTS)\n" >> 0;
}
}
}
int index;
if (lua_gettop(L) > 1) {
index = getVerifiedInt(L, __func__, 2, "index");
index--;
if (index < 0) {
index = 0;
}
if (index > speechQueue.size()) {
index = speechQueue.size();
}
} else {
index = speechQueue.size();
}
speechQueue.insert(index, inputText);
TEvent event {};
Host& host = getHostFromLua(L);
event.mArgumentList.append(QLatin1String("ttsSpeechQueued"));
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
event.mArgumentList.append(inputText);
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
event.mArgumentList.append(QString::number(index));
event.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER);
host.raiseEvent(event);
if (speechQueue.size() == 1 && speechUnit->state() == QTextToSpeech::Ready && !bSpeechQueueing) {
bSpeechQueueing = true;
TLuaInterpreter::ttsStateChanged(speechUnit->state());
}
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsGetQueue
int TLuaInterpreter::ttsGetQueue(lua_State* L)
{
TLuaInterpreter::ttsBuild();
if (lua_gettop(L) > 0) {
int index = getVerifiedInt(L, __func__, 1, "index");
index--;
if (index < 0 || index > speechQueue.size()) {
lua_pushboolean(L, false);
return 1;
}
lua_pushstring(L, speechQueue.at(index).toUtf8().constData());
return 1;
}
lua_newtable(L);
for (int i = 0; i < speechQueue.size(); i++) {
lua_pushnumber(L, i + 1);
lua_pushstring(L, speechQueue.at(i).toUtf8().constData());
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsPause
int TLuaInterpreter::ttsPause(lua_State* L)
{
Q_UNUSED(L)
TLuaInterpreter::ttsBuild();
speechUnit->pause();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsResume
int TLuaInterpreter::ttsResume(lua_State* L)
{
Q_UNUSED(L)
TLuaInterpreter::ttsBuild();
speechUnit->resume();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsClearQueue
int TLuaInterpreter::ttsClearQueue(lua_State* L)
{
TLuaInterpreter::ttsBuild();
if (lua_gettop(L) > 0) {
int index = getVerifiedInt(L, __func__, 1, "index");
index--;
if (index < 0 || index >= speechQueue.size()) {
return warnArgumentValue(L, __func__, QStringLiteral("index %1 out of bounds for queue size %2").arg(index + 1, speechQueue.size()));
}
speechQueue.remove(index);
return 0;
}
speechQueue.clear();
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsGetCurrentLine
int TLuaInterpreter::ttsGetCurrentLine(lua_State* L)
{
TLuaInterpreter::ttsBuild();
if (speechUnit->state() == QTextToSpeech::Ready) {
return warnArgumentValue(L, __func__, "not speaking any text");
} else if (speechUnit->state() == QTextToSpeech::BackendError) {
return warnArgumentValue(L, __func__, "error with the computer's TTS engine");
}
lua_pushstring(L, speechCurrent.toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsGetState
int TLuaInterpreter::ttsGetState(lua_State* L)
{
TLuaInterpreter::ttsBuild();
switch (speechUnit->state()) {
case QTextToSpeech::Ready:
lua_pushstring(L, "ttsSpeechReady");
break;
case QTextToSpeech::Paused:
lua_pushstring(L, "ttsSpeechPaused");
break;
case QTextToSpeech::Speaking:
lua_pushstring(L, "ttsSpeechStarted");
break;
case QTextToSpeech::BackendError:
lua_pushstring(L, "ttsSpeechError");
break;
default:
lua_pushstring(L, "ttsUnknownState");
}
return 1;
}
#endif // QT_TEXTTOSPEECH_LIB
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setServerEncoding
int TLuaInterpreter::setServerEncoding(lua_State* L)
{
Host& host = getHostFromLua(L);
if (!lua_isstring(L, 1)) {
lua_pushfstring(L, "setServerEncoding: bad argument #1 type (newEncoding as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
QByteArray newEncoding = lua_tostring(L, 1);
QPair<bool, QString> results = host.mTelnet.setEncoding(newEncoding);
if (!results.first) {
return warnArgumentValue(L, __func__, results.second);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getServerEncoding
int TLuaInterpreter::getServerEncoding(lua_State* L)
{
Host& host = getHostFromLua(L);
// don't leak if we're using a Mudlet or a Qt-supplied codec to Lua
auto sanitizeEncoding = [] (auto encodingName) {
if (encodingName.startsWith("M_")) {
encodingName.remove(0, 2);
}
return encodingName;
};
QByteArray encoding = host.mTelnet.getEncoding();
if (encoding.isEmpty()) {
encoding = "ASCII";
}
lua_pushstring(L, sanitizeEncoding(encoding).constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getServerEncodingsList
int TLuaInterpreter::getServerEncodingsList(lua_State* L)
{
Host& host = getHostFromLua(L);
// don't leak if we're using a Mudlet or a Qt-supplied codec to Lua
auto sanitizeEncoding = [] (auto encodingName) {
if (encodingName.startsWith("M_")) {
encodingName.remove(0, 2);
}
return encodingName;
};
lua_newtable(L);
lua_pushnumber(L, 1);
lua_pushstring(L, "ASCII");
lua_settable(L, -3);
for (int i = 0, total = host.mTelnet.getEncodingsList().count(); i < total; ++i) {
lua_pushnumber(L, i + 2); // Lua indexes start with 1 but we already have one entry
lua_pushstring(L, sanitizeEncoding(host.mTelnet.getEncodingsList().at(i)).constData());
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getOS
int TLuaInterpreter::getOS(lua_State* L)
{
#if defined(Q_OS_CYGWIN)
// Try for this one before Q_OS_WIN32 as both are likely to be defined on
// a Cygwin platform
// CHECK: hopefully will NOT be triggered on mingw/msys
lua_pushstring(L, "cygwin");
#elif defined(Q_OS_WIN32)
lua_pushstring(L, "windows");
#elif defined(Q_OS_MACOS)
lua_pushstring(L, "mac");
#elif defined(Q_OS_LINUX)
lua_pushstring(L, "linux");
#elif defined(Q_OS_HURD)
// One can hope/dream!
lua_pushstring(L, "hurd");
#elif defined(Q_OS_FREEBSD)
// Only defined on FreeBSD but NOT Debian kFreeBSD so we should check for
// this first
lua_pushstring(L, "freebsd");
#elif defined(Q_OS_FREEBSD_KERNEL)
// Defined for BOTH Debian kFreeBSD hybrid with a GNU userland and
// main FreeBSD so it must be after Q_OS_FREEBSD check; included for Debian
// packager who may want to have this!
lua_pushstring(L, "kfreebsd");
#elif defined(Q_OS_OPENBSD)
lua_pushstring(L, "openbsd");
#elif defined(Q_OS_NETBSD)
lua_pushstring(L, "netbsd");
#elif defined(Q_OS_BSD4)
// Generic *nix - must be before unix and after other more specific results
lua_pushstring(L, "bsd4");
#elif defined(Q_OS_UNIX)
// Most generic *nix - must be after bsd4 and other more specific results
lua_pushstring(L, "unix");
#else
lua_pushstring(L, "unknown");
#endif
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getClipboardText
int TLuaInterpreter::getClipboardText(lua_State* L)
{
QClipboard* clipboard = QApplication::clipboard();
lua_pushstring(L, clipboard->text().toUtf8().constData());
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setClipboardText
int TLuaInterpreter::setClipboardText(lua_State* L)
{
QClipboard* clipboard = QApplication::clipboard();
clipboard->setText(getVerifiedString(L, __func__, 1, "text"));
lua_pushboolean(L, true);
return 1;
}
// No documentation available in wiki - internal function
bool TLuaInterpreter::compileAndExecuteScript(const QString& code)
{
if (code.size() < 1) {
return false;
}
lua_State* L = pGlobalLua;
int error = luaL_dostring(L, code.toUtf8().constData());
if (error != 0) {
std::string e = "no error message available from Lua";
if (lua_isstring(L, 1)) {
e = "Lua error:";
e += lua_tostring(L, 1);
}
if (mudlet::debugMode) {
qDebug() << "LUA ERROR: code did not compile: ERROR:" << e.c_str();
}
QString _n = "error in Lua code";
QString _n2 = "no debug data available";
logError(e, _n, _n2);
}
lua_pop(L, lua_gettop(L));
if (error == 0) {
return true;
} else {
return false;
}
}
// No documentation available in wiki - internal function
// reformats given Lua code. In case of any issues, returns the original code as-is
// issues could be invalid Lua code or the formatter code bugging out
QString TLuaInterpreter::formatLuaCode(const QString &code)
{
if (code.isEmpty()) {
return code;
}
if (!pIndenterState) {
initIndenterGlobals();
}
lua_State* L = pIndenterState.get();
if (!validLuaCode(code).first) {
return code;
}
QString escapedCode = code;
// escape backslashes so we can pass \n to the function
escapedCode.replace(QLatin1String("\\"), QLatin1String("\\\\"));
// escape quotes since we'll be using quotes to pass data to the function
escapedCode.replace(QLatin1String("\""), QLatin1String("\\\""));
// escape newlines so they don't interpreted as newlines, but instead get passed onto the function
escapedCode.replace(QLatin1String("\n"), QLatin1String("\\n"));
QString thing = QString(R"(return get_formatted_code(get_ast("%1"), {indent_chunk = ' ', right_margin = 100, max_text_width = 160, keep_comments = true}))").arg(escapedCode);
int error = luaL_dostring(L, thing.toUtf8().constData());
QString n;
if (error != 0) {
std::string e = "no error message available from Lua";
if (lua_isstring(L, 1)) {
e = "Lua error:";
e += lua_tostring(L, 1);
}
if (mudlet::debugMode) {
qDebug() << "LUA ERROR: code did not compile: ERROR:" << e.c_str();
}
QString objectName = "error in Lua code";
QString functionName = "no debug data available";
logError(e, objectName, functionName);
lua_pop(L, lua_gettop(L));
return code;
}
QString result = lua_tostring(L, 1);
lua_pop(L, lua_gettop(L));
return result;
}
// No documentation available in wiki - internal function
bool TLuaInterpreter::compileScript(const QString& code)
{
lua_State* L = pGlobalLua;
int error = luaL_dostring(L, code.toUtf8().constData());
if (error != 0) {
std::string e = "no error message available from Lua";
if (lua_isstring(L, 1)) {
e = "Lua error:";
e += lua_tostring(L, 1);
}
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::red)) << "LUA: code did not compile: ERROR:" << e.c_str() << "\n" >> 0;
}
} else {
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::darkGreen)) << "LUA: code compiled without errors. OK\n" >> 0;
}
}
lua_pop(L, lua_gettop(L));
if (error == 0) {
return true;
} else {
return false;
}
}
// No documentation available in wiki - internal function
bool TLuaInterpreter::compile(const QString& code, QString& errorMsg, const QString& name)
{
lua_State* L = pGlobalLua;
int error = (luaL_loadbuffer(L, code.toUtf8().constData(),
strlen(code.toUtf8().constData()),
name.toUtf8().constData()) || lua_pcall(L, 0, 0, 0));
QString n;
if (error != 0) {
std::string e = "Lua syntax error:";
if (lua_isstring(L, 1)) {
e.append(lua_tostring(L, 1));
}
errorMsg = "<b><font color='blue'>";
errorMsg.append(e.c_str());
errorMsg.append("</font></b>");
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::red)) << "\n " << e.c_str() << "\n" >> 0;
}
} else {
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::darkGreen)) << "\nLUA: code compiled without errors. OK\n" >> 0;
}
}
lua_pop(L, lua_gettop(L));
if (error == 0) {
return true;
} else {
return false;
}
}
// No documentation available in wiki - internal function
// returns pair where first is bool stating true the given Lua code is valid, false otherwise
// second is empty if code is valid, error message if not valid
std::pair<bool, QString> TLuaInterpreter::validateLuaCodeParam(int index)
{
lua_State* L = pGlobalLua;
if (!lua_isstring(L, index)) {
return std::make_pair(false, QStringLiteral("lua script as string expected, got %1!").arg(luaL_typename(L, index)));
}
QString script{lua_tostring(L, index)};
return validLuaCode(script);
}
// No documentation available in wiki - internal function
// returns pair where first is bool stating true the given Lua code is valid, false otherwise
// second is empty if code is valid, error message if not valid
std::pair<bool, QString> TLuaInterpreter::validLuaCode(const QString &code)
{
lua_State* L = pGlobalLua;
int error = luaL_loadbuffer(L, code.toUtf8().constData(), strlen(code.toUtf8().constData()), code.toUtf8().data());
int topElementIndex = lua_gettop(L);
QString e = "invalid Lua code: ";
if (error) {
if (lua_isstring(L, topElementIndex)) {
e += lua_tostring(L, topElementIndex);
} else {
e += "No error message available from Lua";
}
}
lua_pop(L, topElementIndex);
return std::make_pair(!error, e);
}
// No documentation available in wiki - internal function
std::pair<bool, QString> TLuaInterpreter::discordApiEnabled(lua_State* L, bool writeAccess)
{
mudlet* pMudlet = mudlet::self();
if (!pMudlet->mDiscord.libraryLoaded()) {
return std::make_pair(false, QStringLiteral("Discord API is not available"));
}
auto& host = getHostFromLua(L);
if (!(host.mDiscordAccessFlags & Host::DiscordLuaAccessEnabled)) {
return std::make_pair(false, QStringLiteral("Discord API is disabled in settings for privacy"));
}
if (writeAccess && !pMudlet->mDiscord.discordUserIdMatch(&host)) {
return std::make_pair(false, QStringLiteral("Discord API is read-only as you're logged in with a different account in Discord compared to the one you entered for this profile"));
}
return std::make_pair(true, QString());
}
// No documentation available in wiki - internal function
void TLuaInterpreter::setMultiCaptureGroups(const std::list<std::list<std::string>>& captureList, const std::list<std::list<int>>& posList, QVector<QVector<QPair<QString, QString>>>& nameGroups)
{
mMultiCaptureGroupList = captureList;
mMultiCaptureGroupPosList = posList;
mMultiCaptureNameGroups = nameGroups;
/*
* std::list< std::list<string> >::const_iterator mit = mMultiCaptureGroupList.begin();
*
* int k=1;
* for ( ; mit!=mMultiCaptureGroupList.end(); mit++, k++) {
* cout << "regex#"<<k<<" got:"<<endl;
* std::list<string>::const_iterator it = (*mit).begin();
* for ( int i=1; it!=(*mit).end(); it++, i++ ) {
* cout << i<<"#"<<"<"<<*it<<">"<<endl;
* }
* cout << "-----------------------------"<<endl;
* }
*/
}
// No documentation available in wiki - internal function
void TLuaInterpreter::setCaptureGroups(const std::list<std::string>& captureList, const std::list<int>& posList)
{
mCaptureGroupList = captureList;
mCaptureGroupPosList = posList;
/*
* std::list<string>::iterator it2 = mCaptureGroupList.begin();
* std::list<int>::iterator it1 = mCaptureGroupPosList.begin();
* int i=0;
* for ( ; it1!=mCaptureGroupPosList.end(); it1++, it2++, i++) {
* cout << "group#"<<i<<" begin="<<*it1<<" len="<<(*it2).size()<<"word="<<*it2<<endl;
* }
*/
}
void TLuaInterpreter::setCaptureNameGroups(const NameGroupMatches& nameGroups)
{
mCapturedNameGroups = nameGroups;
}
// No documentation available in wiki - internal function
void TLuaInterpreter::clearCaptureGroups()
{
mCaptureGroupList.clear();
mCaptureGroupPosList.clear();
mMultiCaptureGroupList.clear();
mMultiCaptureGroupPosList.clear();
mCapturedNameGroups.clear();
mMultiCaptureNameGroups.clear();
lua_State* L = pGlobalLua;
lua_newtable(L);
lua_setglobal(L, "matches");
lua_newtable(L);
lua_setglobal(L, "multimatches");
lua_pop(L, lua_gettop(L));
}
// No documentation available in wiki - internal function
void TLuaInterpreter::adjustCaptureGroups(int x, int a)
{
// adjust all capture group positions in line if data has been inserted by the user
for (int& it : mCaptureGroupPosList) {
if (it >= x) {
it += a;
}
}
}
// No documentation available in wiki - internal function
void TLuaInterpreter::setAtcpTable(const QString& var, const QString& arg)
{
lua_State* L = pGlobalLua;
lua_getglobal(L, "atcp"); //defined in LuaGlobal.lua
lua_pushstring(L, var.toUtf8().constData());
lua_pushstring(L, arg.toUtf8().constData());
lua_rawset(L, -3);
lua_pop(L, 1);
TEvent event {};
event.mArgumentList.append(var);
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
event.mArgumentList.append(arg);
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
Host& host = getHostFromLua(L);
host.raiseEvent(event);
}
void
TLuaInterpreter::signalMXPEvent(const QString &type, const QMap<QString, QString> &attrs, const QStringList &actions) {
lua_State *L = pGlobalLua;
lua_getglobal(L, "mxp");
if (!lua_istable(L, -1)) {
lua_newtable(L);
lua_setglobal(L, "mxp");
lua_getglobal(L, "mxp");
if (!lua_istable(L, -1)) {
qDebug() << "ERROR: mxp table not defined";
return;
}
}
lua_newtable(L);
lua_setfield(L, -2, type.toUtf8().toLower().constData());
lua_getfield(L, -1, type.toUtf8().toLower().constData());
if (!lua_istable(L, -1)) {
qDebug() << "ERROR: 'mxp." << type << "' table could not be defined";
return;
}
QMapIterator<QString, QString> itr(attrs);
while (itr.hasNext()) {
itr.next();
lua_pushstring(L, itr.value().toUtf8().constData());
lua_setfield(L, -2, itr.key().toUtf8().toLower().constData());
}
lua_newtable(L);
lua_setfield(L, -2, "actions");
lua_getfield(L, -1, "actions");
for (int i = 0; i < actions.size(); i++) {
lua_pushstring(L, actions[i].toUtf8().constData());
lua_rawseti(L, -2, i + 1);
}
lua_pop(L, lua_gettop(L));
TEvent event{};
QString token("mxp");
token.append(".");
token.append(type.toUtf8().toLower().constData());
event.mArgumentList.append(token);
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
Host &host = getHostFromLua(L);
if (mudlet::debugMode) {
QString msg = QStringLiteral("\n%1 event <%2> display(%1) to see the full content\n").arg("mxp", token);
host.mpConsole->printSystemMessage(msg);
}
host.raiseEvent(event);
}
// No documentation available in wiki - internal function
void TLuaInterpreter::setGMCPTable(QString& key, const QString& string_data)
{
lua_State* L = pGlobalLua;
lua_getglobal(L, "gmcp"); //defined in Lua init
if (!lua_istable(L, -1)) {
lua_newtable(L);
lua_setglobal(L, "gmcp");
lua_getglobal(L, "gmcp");
if (!lua_istable(L, -1)) {
qDebug() << "ERROR: gmcp table not defined";
return;
}
}
parseJSON(key, string_data, QLatin1String("gmcp"));
}
// No documentation available in wiki - internal function
void TLuaInterpreter::setMSSPTable(const QString& string_data)
{
lua_State* L = pGlobalLua;
lua_getglobal(L, "mssp"); //defined in Lua init
if (!lua_istable(L, -1)) {
lua_newtable(L);
lua_setglobal(L, "mssp");
lua_getglobal(L, "mssp");
if (!lua_istable(L, -1)) {
qDebug() << "ERROR: mssp table not defined";
return;
}
}
parseMSSP(string_data);
}
// No documentation available in wiki - internal function
void TLuaInterpreter::setMSDPTable(QString& key, const QString& string_data)
{
lua_State* L = pGlobalLua;
lua_getglobal(L, "msdp");
if (!lua_istable(L, -1)) {
lua_newtable(L);
lua_setglobal(L, "msdp");
lua_getglobal(L, "msdp");
if (!lua_istable(L, -1)) {
qDebug() << "ERROR: msdp table not defined";
return;
}
}
parseJSON(key, string_data, QLatin1String("msdp"));
}
// No documentation available in wiki - internal function
void TLuaInterpreter::parseJSON(QString& key, const QString& string_data, const QString& protocol)
{
// key is in format of Blah.Blah or Blah.Blah.Bleh - we want to push & pre-create the tables as appropriate
lua_State* L = pGlobalLua;
QStringList tokenList = key.split(QLatin1Char('.'));
if (!lua_checkstack(L, tokenList.size() + 5)) {
qCritical() << "ERROR: could not grow Lua stack by" << tokenList.size() + 5 << "elements, parsing GMCP/MSDP failed. Current stack size is" << lua_gettop(L);
return;
}
int i = 0;
for (int total = tokenList.size() - 1; i < total; ++i) {
lua_getfield(L, -1, tokenList.at(i).toUtf8().constData());
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
lua_pushstring(L, tokenList.at(i).toUtf8().constData());
lua_newtable(L);
lua_rawset(L, -3);
lua_getfield(L, -1, tokenList.at(i).toUtf8().constData());
}
lua_remove(L, -2);
}
bool __needMerge = false;
lua_getfield(L, -1, tokenList.at(i).toUtf8().constData());
Host& host = getHostFromLua(L);
if (lua_istable(L, -1)) {
// only merge tables (instead of replacing them) if the key has been registered as a need to merge key by the user default is Char.Status only
if (host.mGMCP_merge_table_keys.contains(key)) {
__needMerge = true;
}
}
lua_pop(L, 1);
if (!__needMerge) {
lua_pushstring(L, tokenList.at(i).toUtf8().constData());
} else {
lua_pushstring(L, "__needMerge");
}
lua_getglobal(L, "json_to_value");
if (!lua_isfunction(L, -1)) {
lua_settop(L, 0);
qDebug() << "CRITICAL ERROR: json_to_value not defined";
return;
}
auto dataInUtf8 = string_data.toUtf8();
lua_pushlstring(L, dataInUtf8.constData(), dataInUtf8.length());
int error = lua_pcall(L, 1, 1, 0);
if (error == 0) {
// Top of stack should now contain the lua representation of json.
lua_rawset(L, -3);
if (__needMerge) {
lua_settop(L, 0);
lua_getglobal(L, "__gmcp_merge_gmcp_sub_tables");
if (!lua_isfunction(L, -1)) {
lua_settop(L, 0);
qDebug() << "CRITICAL ERROR: __gmcp_merge_gmcp_sub_tables is not defined in lua_LuaGlobal.lua";
return;
}
lua_getglobal(L, "gmcp");
i = 0;
for (int total = tokenList.size() - 1; i < total; ++i) {
lua_getfield(L, -1, tokenList.at(i).toUtf8().constData());
lua_remove(L, -2);
}
lua_pushstring(L, tokenList.at(i).toUtf8().constData());
lua_pcall(L, 2, 0, 0);
}
} else {
{
std::string e;
if (lua_isstring(L, -1)) {
e = "Lua error:";
e += lua_tostring(L, -1);
}
QString _n = "JSON decoder error:";
QString _f = "json_to_value";
logError(e, _n, _f);
}
}
lua_settop(L, 0);
// events: for key "foo.bar.top" we raise: gmcp.foo, gmcp.foo.bar and gmcp.foo.bar.top
// with the actual key given as parameter e.g. event=gmcp.foo, param="gmcp.foo.bar"
QString token = protocol;
if (protocol == QLatin1String("msdp")) {
key.prepend(QLatin1String("msdp."));
} else {
key.prepend(QLatin1String("gmcp."));
}
for (int k = 0, total = tokenList.size(); k < total; ++k) {
TEvent event {};
token.append(".");
token.append(tokenList[k]);
event.mArgumentList.append(token);
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
event.mArgumentList.append(key);
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
if (mudlet::debugMode) {
QString msg = QStringLiteral("\n%1 event <%2> display(%1) to see the full content\n").arg(protocol, token);
host.mpConsole->printSystemMessage(msg);
}
host.raiseEvent(event);
}
// auto-detect IRE composer
if (tokenList.size() == 3 && tokenList.at(0).toLower() == "ire" && tokenList.at(1).toLower() == "composer" && tokenList.at(2).toLower() == "edit") {
QRegularExpression rx(QStringLiteral(R"lit(\{ ?"title": ?"(.*)", ?"text": ?"(.*)" ?\})lit"));
QRegularExpressionMatch match = rx.match(string_data);
if (match.capturedStart() != -1) {
QString title = match.captured(1);
QString initialText = match.captured(2);
QRegularExpression codeRegex(QStringLiteral(R"lit(\\n|\\t|\\"|\\\\|\\u[0-9a-cA-C][0-9a-fA-F]{3}|\\u[dD][0-7][0-9a-fA-F]{2}|\\u[efEF][0-9a-fA-F]{3}|\\u[dD][89abAB][0-9a-fA-F]{2}\\u[dD][c-fC-F][0-9a-fA-F]{2})lit"));
// We are about to search for 8 escape code strings within the initial text that the game gave us, patterns are:
// \n \t \" \\ - new line, tab, quote, backslash
// Then there are three patterns for \uXXXX where XXXX is a 4-digit hexadecimal value
// Characters in ranges U+0000-U+D7FF and U+E000-U+FFFD are stored as a single unit.
// 0000-CFFF
// D000-D7FF
// D800-DFFF - are reserved for surrogate pairs; will not match a pattern
// E000-FFFF - note that FFFE and FFFF match the pattern but are not valid, will skip those later
// Then one pattern for \uXXXX\uXXXX where each XXXX is a 4-digit hexadecimal value
// These are 'surrogate pairs', (U+D800-U+DBFF) followed by (U+DC00-U+DFFF).
// D800-DF00 DC00-DFFF
int j = 0;
while ((j = initialText.indexOf(codeRegex, j)) != -1) {
uint u;
switch (initialText.at(j+1).unicode()){
case 'n' : initialText.replace(j, 2, '\n'); break;
case 't' : initialText.replace(j, 2, '\t'); break;
case '\"' : initialText.replace(j, 2, '\"'); break;
case '\\' : initialText.replace(j, 2, '\\'); break;
case 'u': // handle lone code or pair of codes together
u = initialText.midRef(j+2, 4).toUShort(0, 16);
if(u > 0xFFFD){
j += 5; // FFFE and FFFF are guaranteed to not be Unicode characters. Skip it.
}
else if((u < 0xD800) || (0xDFFF < u)){
// Characters in ranges U+0000-U+D7FF and U+E000-U+FFFD are stored as a single unit.
initialText.replace(j, 6, QChar(u));
}
else if((0xD7FF < u) && (u < 0xDC00)){
// Non-BMP characters (range U+10000-U+10FFFF) are stored as "surrogate pairs".
// A 'high' surrogate (U+D800-U+DBFF) followed by 'low' surrogate (U+DC00-U+DFFF).
// Surrogates are always written in pairs, a lone one is invalid.
// The regex above should ensure second code is DCxx-DFxx
QChar code[2];
code[0] = QChar(u);
code[1] = QChar(initialText.midRef(j+8, 4).toUShort(0, 16));
initialText.replace(j, 12, code, 2);
j++; // in this case we are adding 2 code points for the character
}
// DC00-DFFF should be filtered out by the regex.
break;
}
j++;
}
Host& host = getHostFromLua(L);
if (host.mTelnet.mpComposer) {
return;
}
host.mTelnet.mpComposer = new dlgComposer(&host);
host.mTelnet.mpComposer->init(title, initialText);
host.mTelnet.mpComposer->raise();
host.mTelnet.mpComposer->show();
}
}
lua_pop(L, lua_gettop(L));
}
// No documentation available in wiki - internal function
void TLuaInterpreter::parseMSSP(const QString& string_data)
{
lua_State* L = pGlobalLua;
// string_data is in the format of MSSP_VAR "PLAYERS" MSSP_VAL "52" MSSP_VAR "UPTIME" MSSP_VAL "1234567890"
// The quote characters mean that the encased word is a string, the quotes themselves are not sent.
QStringList packageList = string_data.split(MSSP_VAR);
if (packageList.size() > 0) {
Host& host = getHostFromLua(L);
lua_getglobal(L, "mssp");
for (int i = 1; i < packageList.size(); i++) {
QStringList payloadList = packageList[i].split(MSSP_VAL);
if (payloadList.size() != 2) {
continue;
}
QString msspVAR = payloadList[0];
QString msspVAL = payloadList[1];
lua_pushstring(L, msspVAR.toUtf8().constData());
lua_pushlstring(L, msspVAL.toUtf8().constData(), msspVAL.toUtf8().length());
lua_rawset(L, -3);
// Raise an event
QString protocol = QStringLiteral("mssp");
QString token = protocol;
token.append(".");
token.append(msspVAR);
TEvent event {};
event.mArgumentList.append(token);
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
event.mArgumentList.append(token);
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
if (mudlet::debugMode) {
QString msg = QStringLiteral("\n%1 event <%2> display(%1) to see the full content\n").arg(protocol, token);
host.mpConsole->printSystemMessage(msg);
}
host.raiseEvent(event);
lua_settop (L, 1);
}
lua_pop(L, lua_gettop(L));
}
}
// No documentation available in wiki - internal function
// src is in Mud Server encoding and may need transcoding
// Includes MSDP code originally from recv_sb_msdp(...) in TinTin++'s telopt.c,
// https://tintin.sourceforge.io:
void TLuaInterpreter::msdp2Lua(const char* src)
{
Host& host = getHostFromLua(pGlobalLua);
QByteArray transcodedSrc = host.mTelnet.decodeBytes(src);
QStringList varList;
QByteArray lastVar;
int textLength = transcodedSrc.length();
int nest = 0;
quint8 last = 0;
QByteArray script;
bool no_array_marker_bug = false;
for (int i = 0; i < textLength; ++i) {
switch (transcodedSrc.at(i)) {
case MSDP_TABLE_OPEN:
script.append('{');
++nest;
last = MSDP_TABLE_OPEN;
break;
case MSDP_TABLE_CLOSE:
if (last == MSDP_VAL || last == MSDP_VAR) {
script.append('\"');
}
if (nest) {
--nest;
}
script.append('}');
last = MSDP_TABLE_CLOSE;
break;
case MSDP_ARRAY_OPEN:
script.append('[');
++nest;
last = MSDP_ARRAY_OPEN;
break;
case MSDP_ARRAY_CLOSE:
if (last == MSDP_VAL || last == MSDP_VAR) {
script.append('\"');
}
if (nest) {
--nest;
}
script.append(']');
last = MSDP_ARRAY_CLOSE;
break;
case MSDP_VAR:
if (nest) {
if (last == MSDP_VAL || last == MSDP_VAR) {
script.append('\"');
}
if (last == MSDP_VAL || last == MSDP_VAR || last == MSDP_TABLE_CLOSE || last == MSDP_ARRAY_CLOSE) {
script.append(',');
}
script.append('\"');
} else {
script.append('\"');
if (!varList.empty()) {
QString token = varList.front();
token = token.remove(QLatin1Char('\"'));
script = script.replace(0, varList.front().toUtf8().size() + 3, QByteArray());
mpHost->processDiscordMSDP(token, script);
setMSDPTable(token, script);
varList.clear();
script.clear();
}
}
last = MSDP_VAR;
lastVar.clear();
break;
case MSDP_VAL:
if (last == MSDP_VAR) {
script.append("\":");
}
if (last == MSDP_VAL) {
no_array_marker_bug = true;
script.append('\"');
}
if (last == MSDP_VAL || last == MSDP_TABLE_CLOSE || last == MSDP_ARRAY_CLOSE) {
script.append(',');
}
if (((textLength > i + 1) && transcodedSrc.at(i + 1) && transcodedSrc.at(i + 1) != MSDP_TABLE_OPEN && transcodedSrc.at(i + 1) != MSDP_ARRAY_OPEN) ||
(textLength <= i + 1)) {
script.append('\"');
}
varList.append(lastVar);
last = MSDP_VAL;
break;
case '\\':
script.append('\\');
break;
case '\"':
script.append('\"');
break;
default:
script.append(transcodedSrc.at(i));
lastVar.append(transcodedSrc.at(i));
break;
}
}
if (last != MSDP_ARRAY_CLOSE && last != MSDP_TABLE_CLOSE) {
script.append('\"');
if (!script.startsWith('\"')) {
script.prepend('\"');
}
}
if (!varList.empty()) {
QString token = varList.front();
token = token.remove(QLatin1Char('\"'));
script = script.replace(0, token.toUtf8().size() + 3, QByteArray());
if (no_array_marker_bug) {
if (!script.startsWith('[')) {
script.prepend('[');
script.append(']');
}
}
setMSDPTable(token, script);
}
}
// No documentation available in wiki - internal function
void TLuaInterpreter::setChannel102Table(int& var, int& arg)
{
lua_State* L = pGlobalLua;
lua_getglobal(L, "channel102"); //defined in LuaGlobal.lua
lua_pushnumber(L, var);
lua_pushnumber(L, arg);
lua_rawset(L, -3);
lua_pop(L, 1);
TEvent event {};
event.mArgumentList.append(QLatin1String("channel102Message"));
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
event.mArgumentList.append(QString::number(var));
event.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER);
event.mArgumentList.append(QString::number(arg));
event.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER);
Host& host = getHostFromLua(L);
host.raiseEvent(event);
}
// No documentation available in wiki - internal function
void TLuaInterpreter::setMatches(lua_State* L)
{
if (!mCaptureGroupList.empty()) {
lua_newtable(L);
// set values
int i = 1; // Lua indexes start with 1 as a general convention
for (auto it = mCaptureGroupList.begin(); it != mCaptureGroupList.end(); it++, i++) {
// if ((*it).length() < 1) continue; //have empty capture groups to be undefined keys i.e. machts[emptyCapGroupNumber] = nil otherwise it's = "" i.e. an empty string
lua_pushnumber(L, i);
lua_pushstring(L, (*it).c_str());
lua_settable(L, -3);
}
for (auto [name, capture] : mCapturedNameGroups) {
lua_pushstring(L, name.toUtf8().constData());
lua_pushstring(L, capture.toUtf8().constData());
lua_settable(L, -3);
}
lua_setglobal(L, "matches");
}
}
// No documentation available in wiki - internal function
bool TLuaInterpreter::call_luafunction(void* pT)
{
lua_State* L = pGlobalLua;
lua_pushlightuserdata(L, pT);
lua_gettable(L, LUA_REGISTRYINDEX);
if (lua_isfunction(L, -1)) {
setMatches(L);
int error = lua_pcall(L, 0, LUA_MULTRET, 0);
if (error != 0) {
int nbpossible_errors = lua_gettop(L);
for (int i = 1; i <= nbpossible_errors; i++) {
std::string e = "";
if (lua_isstring(L, i)) {
e = "Lua error:";
e += lua_tostring(L, i);
QString _n = "error in anonymous Lua function";
QString _n2 = "no debug data available";
logError(e, _n, _n2);
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::red)) << "LUA: ERROR running anonymous Lua function ERROR:" << e.c_str() >> 0;
}
}
}
} else {
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::darkGreen)) << "LUA OK anonymous Lua function ran without errors\n" >> 0;
}
}
lua_pop(L, lua_gettop(L));
//lua_settop(L, 0);
if (error == 0) {
return true;
} else {
return false;
}
} else {
QString _n = "error in anonymous Lua function";
QString _n2 = "func reference not found by Lua, func cannot be called";
std::string e = "Lua error:";
logError(e, _n, _n2);
}
return false;
}
// No documentation available in wiki - internal function
void TLuaInterpreter::delete_luafunction(void* pT)
{
lua_State* L = pGlobalLua;
lua_pushlightuserdata(L, pT);
lua_pushnil(L);
lua_rawset(L, LUA_REGISTRYINDEX);
}
// No documentation available in wiki - internal function
void TLuaInterpreter::delete_luafunction(const QString& name)
{
lua_State* L = pGlobalLua;
lua_getglobal(L, name.toUtf8().constData());
if (lua_isfunction(L, -1)) {
lua_pushnil(L);
lua_setglobal(L, name.toUtf8().constData());
lua_pop(L, lua_gettop(L));
} else if (mudlet::debugMode) {
qWarning() << "LUA: ERROR deleting " << name << ", it is not a function as expected";
}
}
// No documentation available in wiki - internal function
// returns true if function ran without errors
// as well as the boolean return value from the function
std::pair<bool, bool> TLuaInterpreter::callLuaFunctionReturnBool(void* pT)
{
lua_State* L = pGlobalLua;
lua_pushlightuserdata(L, pT);
lua_gettable(L, LUA_REGISTRYINDEX);
bool returnValue = false;
if (lua_isfunction(L, -1)) {
setMatches(L);
int error = lua_pcall(L, 0, LUA_MULTRET, 0);
if (error != 0) {
int nbpossible_errors = lua_gettop(L);
for (int i = 1; i <= nbpossible_errors; i++) {
std::string e = "";
if (lua_isstring(L, i)) {
e = "Lua error:";
e += lua_tostring(L, i);
QString _n = "error in anonymous Lua function";
QString _n2 = "no debug data available";
logError(e, _n, _n2);
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::red)) << "LUA: ERROR running anonymous Lua function ERROR:" << e.c_str() >> 0;
}
}
}
} else {
auto index = lua_gettop(L);
if (lua_isboolean(L, index)) {
returnValue = lua_toboolean(L, index);
}
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::darkGreen)) << "LUA OK anonymous Lua function ran without errors\n" >> 0;
}
}
lua_pop(L, lua_gettop(L));
//lua_settop(L, 0);
if (error == 0) {
return std::make_pair(true, returnValue);
} else {
return std::make_pair(false, returnValue);
}
} else {
QString _n = "error in anonymous Lua function";
QString _n2 = "func reference not found by Lua, func cannot be called";
std::string e = "Lua error:";
logError(e, _n, _n2);
}
return std::make_pair(false, false);
}
// No documentation available in wiki - internal function
// Third argument hides the "LUA OK" type message if it true which may be used
// to cut down on spammy output if things are okay.
bool TLuaInterpreter::call(const QString& function, const QString& mName, const bool muteDebugOutput)
{
lua_State* L = pGlobalLua;
setMatches(L);
lua_getglobal(L, function.toUtf8().constData());
int error = lua_pcall(L, 0, LUA_MULTRET, 0);
if (error != 0) {
int nbpossible_errors = lua_gettop(L);
for (int i = 1; i <= nbpossible_errors; i++) {
std::string e = "";
if (lua_isstring(L, i)) {
e += lua_tostring(L, i);
logError(e, mName, function);
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::red)) << "LUA ERROR: when running script " << mName << " (" << function << "),\nreason: " << e.c_str() << "\n" >> 0;
}
}
}
} else {
if (mudlet::debugMode && !muteDebugOutput) {
TDebug(QColor(Qt::white), QColor(Qt::darkGreen)) << "LUA OK: script " << mName << " (" << function << ") ran without errors\n" >> 0;
}
}
lua_pop(L, lua_gettop(L));
return (error);
}
// No documentation available in wiki - internal function
std::pair<bool, bool> TLuaInterpreter::callReturnBool(const QString& function, const QString& mName)
{
lua_State* L = pGlobalLua;
bool returnValue = false;
setMatches(L);
lua_getglobal(L, function.toUtf8().constData());
int error = lua_pcall(L, 0, LUA_MULTRET, 0);
if (error != 0) {
int nbpossible_errors = lua_gettop(L);
for (int i = 1; i <= nbpossible_errors; i++) {
std::string e = "";
if (lua_isstring(L, i)) {
e += lua_tostring(L, i);
logError(e, mName, function);
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::red)) << "LUA: ERROR running script " << mName << " (" << function << ") ERROR:" << e.c_str() << "\n" >> 0;
}
}
}
} else {
auto index = lua_gettop(L);
if (lua_isboolean(L, index)) {
returnValue = lua_toboolean(L, index);
}
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::darkGreen)) << "LUA OK script " << mName << " (" << function << ") ran without errors\n" >> 0;
}
}
lua_pop(L, lua_gettop(L));
if (error == 0) {
return std::make_pair(true, returnValue);
} else {
return std::make_pair(false, returnValue);
}
}
// No documentation available in wiki - internal function
void TLuaInterpreter::logError(std::string& e, const QString& name, const QString& function)
{
// Log error to Editor's Errors TConsole:
if (mpHost->mpEditorDialog) {
mpHost->mpEditorDialog->mpErrorConsole->print(QStringLiteral("[%1:]").arg(tr("ERROR")), QColor(Qt::blue), QColor(Qt::black));
mpHost->mpEditorDialog->mpErrorConsole->print(QStringLiteral(" %1:<%2> %3:<%4>\n").arg(tr("object", "object is the Mudlet alias/trigger/script, used in this sample message: object:<Alias1> function:<cure_me>"), name, tr("function", "function is the Lua function, used in this sample message: object:<Alias1> function:<cure_me>"), function), QColor(Qt::green), QColor(Qt::black));
mpHost->mpEditorDialog->mpErrorConsole->print(QStringLiteral(" <%1>\n").arg(e.c_str()), QColor(Qt::red), QColor(Qt::black));
}
// Log error to Profile's Main TConsole:
if (mpHost->mEchoLuaErrors) {
// ensure the Lua error is on a line of its own and is not prepended to the previous line
if (mpHost->mpConsole->buffer.size() > 0 && !mpHost->mpConsole->buffer.lineBuffer.at(mpHost->mpConsole->buffer.lineBuffer.size() - 1).isEmpty()) {
mpHost->postMessage(QStringLiteral("\n"));
}
mpHost->postMessage(QStringLiteral("[ LUA ] - %1: <%2> %3:<%4>\n<%5>").arg(tr("object", "object is the Mudlet alias/trigger/script, used in this sample message: object:<Alias1> function:<cure_me>"), name, tr("function", "function is the Lua function, used in this sample message: object:<Alias1> function:<cure_me>"), function, e.c_str()));
}
}
// No documentation available in wiki - internal function
void TLuaInterpreter::logEventError(const QString& event, const QString& error)
{
// Log error to Editor's Errors TConsole:
if (mpHost->mpEditorDialog) {
mpHost->mpEditorDialog->mpErrorConsole->print(QStringLiteral("[%1:]").arg(tr("ERROR")), QColor(Qt::blue), QColor(Qt::black));
mpHost->mpEditorDialog->mpErrorConsole->print(QStringLiteral(" event handler for %1:\n").arg(event), QColor(Qt::green), QColor(Qt::black));
mpHost->mpEditorDialog->mpErrorConsole->print(QStringLiteral(" <%1>\n").arg(error), QColor(Qt::red), QColor(Qt::black));
}
// Log error to Profile's Main TConsole:
if (mpHost->mEchoLuaErrors) {
// ensure the Lua error is on a line of its own and is not prepended to the previous line
if (mpHost->mpConsole->buffer.size() > 0 && !mpHost->mpConsole->buffer.lineBuffer.at(mpHost->mpConsole->buffer.lineBuffer.size() - 1).isEmpty()) {
mpHost->postMessage(QStringLiteral("\n"));
}
mpHost->postMessage(QStringLiteral("[ LUA ] - error in event handler for %1:\n<%2>").arg(event, error));
}
}
// No documentation available in wiki - internal function
bool TLuaInterpreter::callConditionFunction(std::string& function, const QString& mName)
{
lua_State* L = pGlobalLua;
lua_getfield(L, LUA_GLOBALSINDEX, function.c_str());
int error = lua_pcall(L, 0, 1, 0);
if (error != 0) {
int nbpossible_errors = lua_gettop(L);
for (int i = 1; i <= nbpossible_errors; i++) {
std::string e = "";
if (lua_isstring(L, i)) {
e += lua_tostring(L, i);
QString _f = function.c_str();
logError(e, mName, _f);
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::red)) << "LUA: ERROR running script " << mName << " (" << function.c_str() << ") ERROR:" << e.c_str() << "\n" >> 0;
}
}
}
} else {
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::darkGreen)) << "LUA OK script " << mName << " (" << function.c_str() << ") ran without errors\n" >> 0;
}
}
int ret = 0;
int returnValues = lua_gettop(L);
if (returnValues > 0) {
// Lua docs: Like all tests in Lua, lua_toboolean returns 1 for any Lua value different from false and nil; otherwise it returns 0
// This means trigger patterns don't have to strictly return true or false, as it is accepted in Lua */
ret = lua_toboolean(L, 1);
}
lua_pop(L, returnValues);
if ((error == 0) && (ret > 0)) {
return true;
} else {
return false;
}
}
// No documentation available in wiki - internal function
bool TLuaInterpreter::callMulti(const QString& function, const QString& mName)
{
lua_State* L = pGlobalLua;
if (!mMultiCaptureGroupList.empty()) {
int k = 1; // Lua indexes start with 1 as a general convention
lua_newtable(L); //multimatches
for (auto mit = mMultiCaptureGroupList.begin(); mit != mMultiCaptureGroupList.end(); mit++, k++) {
// multimatches{ trigger_idx{ table_matches{ ... } } }
lua_pushnumber(L, k);
lua_newtable(L); //regex-value => table matches
int i = 1; // Lua indexes start with 1 as a general convention
for (auto it = (*mit).begin(); it != (*mit).end(); it++, i++) {
lua_pushnumber(L, i);
lua_pushstring(L, (*it).c_str());
lua_settable(L, -3); //match in matches
}
for (auto [name, capture] : mMultiCaptureNameGroups.value(k - 1)) {
lua_pushstring(L, name.toUtf8().constData());
lua_pushstring(L, capture.toUtf8().constData());
lua_settable(L, -3);
}
lua_settable(L, -3); //matches in regex
}
lua_setglobal(L, "multimatches");
}
lua_getglobal(L, function.toUtf8().constData());
int error = lua_pcall(L, 0, LUA_MULTRET, 0);
if (error != 0) {
int nbpossible_errors = lua_gettop(L);
for (int i = 1; i <= nbpossible_errors; i++) {
std::string e = "";
if (lua_isstring(L, i)) {
e += lua_tostring(L, i);
logError(e, mName, function);
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::red)) << "LUA: ERROR running script " << mName << " (" << function << ") ERROR:" << e.c_str() << "\n" >> 0;
}
}
}
} else {
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::darkGreen)) << "LUA OK script " << mName << " (" << function << ") ran without errors\n" >> 0;
}
}
lua_pop(L, lua_gettop(L));
if (error == 0) {
return true;
} else {
return false;
}
}
// No documentation available in wiki - internal function
std::pair<bool, bool> TLuaInterpreter::callMultiReturnBool(const QString& function, const QString& mName)
{
lua_State* L = pGlobalLua;
bool returnValue = false;
if (!mMultiCaptureGroupList.empty()) {
int k = 1; // Lua indexes start with 1 as a general convention
lua_newtable(L); //multimatches
for (auto mit = mMultiCaptureGroupList.begin(); mit != mMultiCaptureGroupList.end(); mit++, k++) {
// multimatches{ trigger_idx{ table_matches{ ... } } }
lua_pushnumber(L, k);
lua_newtable(L); //regex-value => table matches
int i = 1; // Lua indexes start with 1 as a general convention
for (auto it = (*mit).begin(); it != (*mit).end(); it++, i++) {
lua_pushnumber(L, i);
lua_pushstring(L, (*it).c_str());
lua_settable(L, -3); //match in matches
}
lua_settable(L, -3); //matches in regex
}
lua_setglobal(L, "multimatches");
}
lua_getglobal(L, function.toUtf8().constData());
int error = lua_pcall(L, 0, LUA_MULTRET, 0);
if (error != 0) {
int nbpossible_errors = lua_gettop(L);
for (int i = 1; i <= nbpossible_errors; i++) {
std::string e = "";
if (lua_isstring(L, i)) {
e += lua_tostring(L, i);
logError(e, mName, function);
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::red)) << "LUA: ERROR running script " << mName << " (" << function << ") ERROR:" << e.c_str() << "\n" >> 0;
}
}
}
} else {
auto index = lua_gettop(L);
if (lua_isboolean(L, index)) {
returnValue = lua_toboolean(L, index);
}
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::darkGreen)) << "LUA OK script " << mName << " (" << function << ") ran without errors\n" >> 0;
}
}
lua_pop(L, lua_gettop(L));
if (error == 0) {
return std::make_pair(true, returnValue);
} else {
return std::make_pair(false, returnValue);
}
}
// No documentation available in wiki - internal function
bool TLuaInterpreter::callCmdLineAction(const int func, QString text)
{
lua_State* L = pGlobalLua;
lua_rawgeti(L, LUA_REGISTRYINDEX, func);
int error = 0;
lua_pushstring(L, text.toUtf8().constData());
error = lua_pcall(L, 1, LUA_MULTRET, 0);
if (error) {
std::string err = "";
if (lua_isstring(L, -1)) {
err += lua_tostring(L, -1);
}
QString function = "setCmdLineAction";
QString name = "cmd line Action";
logError(err, name, function);
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::red)) << "LUA: ERROR running script " << function << " (" << function << ")\nError: " << err.c_str() << "\n" >> 0;
}
}
lua_pop(L, lua_gettop(L));
return !error;
}
// No documentation available in wiki - internal function
bool TLuaInterpreter::callLabelCallbackEvent(const int func, const QEvent* qE)
{
lua_State* L = pGlobalLua;
lua_rawgeti(L, LUA_REGISTRYINDEX, func);
int error = 0;
if (qE) {
// Create Lua table with QEvent data if needed
switch (qE->type()) {
// This means the default argument value was used, so ignore
case (QEvent::None):
error = lua_pcall(L, 0, LUA_MULTRET, 0);
break;
// These are all QMouseEvents
case (QEvent::MouseButtonPress):
[[fallthrough]];
case (QEvent::MouseButtonDblClick):
[[fallthrough]];
case (QEvent::MouseButtonRelease):
[[fallthrough]];
case (QEvent::MouseMove): {
auto qME = static_cast<const QMouseEvent*>(qE);
lua_newtable(L);
// push button()
lua_pushstring(L, mMouseButtons.value(qME->button()).toUtf8().constData());
lua_setfield(L, -2, QStringLiteral("button").toUtf8().constData());
// push buttons()
lua_newtable(L);
QMap<Qt::MouseButton, QString>::const_iterator iter = mMouseButtons.constBegin();
int counter = 1;
while (iter != mMouseButtons.constEnd()) {
if (iter.key() & qME->buttons()) {
lua_pushnumber(L, counter);
lua_pushstring(L, iter.value().toUtf8().constData());
lua_settable(L, -3);
counter++;
}
++iter;
}
lua_setfield(L, -2, QStringLiteral("buttons").toUtf8().constData());
// Push globalX()
lua_pushnumber(L, qME->globalX());
lua_setfield(L, -2, QStringLiteral("globalX").toUtf8().constData());
// Push globalY()
lua_pushnumber(L, qME->globalY());
lua_setfield(L, -2, QStringLiteral("globalY").toUtf8().constData());
// Push x()
lua_pushnumber(L, qME->x());
lua_setfield(L, -2, QStringLiteral("x").toUtf8().constData());
// Push y()
lua_pushnumber(L, qME->y());
lua_setfield(L, -2, QStringLiteral("y").toUtf8().constData());
error = lua_pcall(L, 1, LUA_MULTRET, 0);
break;
}
// These are QEvents
case (QEvent::Enter): {
auto qME = static_cast<const QEnterEvent*>(qE);
lua_newtable(L);
// Push globalX()
lua_pushnumber(L, qME->globalX());
lua_setfield(L, -2, QStringLiteral("globalX").toUtf8().constData());
// Push globalY()
lua_pushnumber(L, qME->globalY());
lua_setfield(L, -2, QStringLiteral("globalY").toUtf8().constData());
// Push x()
lua_pushnumber(L, qME->x());
lua_setfield(L, -2, QStringLiteral("x").toUtf8().constData());
// Push y()
lua_pushnumber(L, qME->y());
lua_setfield(L, -2, QStringLiteral("y").toUtf8().constData());
error = lua_pcall(L, 1, LUA_MULTRET, 0);
break;
}
case (QEvent::Leave): {
// Seems there isn't a QLeaveEvent, so no
// extra information to be gotten
error = lua_pcall(L, 0, LUA_MULTRET, 0);
break;
}
// This is a QWheelEvent
case (QEvent::Wheel): {
auto qME = static_cast<const QWheelEvent*>(qE);
lua_newtable(L);
// push buttons()
lua_newtable(L);
QMap<Qt::MouseButton, QString>::const_iterator iter = mMouseButtons.constBegin();
int counter = 1;
while (iter != mMouseButtons.constEnd()) {
if (iter.key() & qME->buttons()) {
lua_pushnumber(L, counter);
lua_pushstring(L, iter.value().toUtf8().constData());
lua_settable(L, -3);
counter++;
}
++iter;
}
lua_setfield(L, -2, QStringLiteral("buttons").toUtf8().constData());
// Push globalX()
lua_pushnumber(L, qME->globalX());
lua_setfield(L, -2, QStringLiteral("globalX").toUtf8().constData());
// Push globalY()
lua_pushnumber(L, qME->globalY());
lua_setfield(L, -2, QStringLiteral("globalY").toUtf8().constData());
// Push x()
lua_pushnumber(L, qME->x());
lua_setfield(L, -2, QStringLiteral("x").toUtf8().constData());
// Push y()
lua_pushnumber(L, qME->y());
lua_setfield(L, -2, QStringLiteral("y").toUtf8().constData());
// Push angleDelta()
lua_pushnumber(L, qME->angleDelta().x());
lua_setfield(L, -2, QStringLiteral("angleDeltaX").toUtf8().constData());
lua_pushnumber(L, qME->angleDelta().y());
lua_setfield(L, -2, QStringLiteral("angleDeltaY").toUtf8().constData());
error = lua_pcall(L, 1, LUA_MULTRET, 0);
break;
}
default: {
// No-op - this silences warnings about unhandled QEvent types
}
}
} else {
error = lua_pcall(L, 0, LUA_MULTRET, 0);
}
if (error) {
std::string err = "";
if (lua_isstring(L, -1)) {
err += lua_tostring(L, -1);
}
QString function = "setLabelCallback";
QString name = "label callback event";
logError(err, name, function);
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::red)) << "LUA: ERROR running script " << function << " (" << function << ")\nError: " << err.c_str() << "\n" >> 0;
}
}
lua_pop(L, lua_gettop(L));
return !error;
}
// No documentation available in wiki - internal function
bool TLuaInterpreter::callEventHandler(const QString& function, const TEvent& pE)
{
if (function.isEmpty()) {
return false;
}
lua_State* L = pGlobalLua;
int error = luaL_dostring(L, QStringLiteral("return %1").arg(function).toUtf8().constData());
if (error) {
std::string err;
if (lua_isstring(L, 1)) {
err = "Lua error: ";
err += lua_tostring(L, 1);
}
QString name = "event handler function";
logError(err, name, function);
return false;
}
// Lua is limited to ~50 arguments on a function
auto maxArguments = std::min(pE.mArgumentList.size(), LUA_FUNCTION_MAX_ARGS);
for (int i = 0; i < maxArguments; i++) {
switch (pE.mArgumentTypeList.at(i)) {
case ARGUMENT_TYPE_NUMBER:
lua_pushnumber(L, pE.mArgumentList.at(i).toDouble());
break;
case ARGUMENT_TYPE_STRING:
lua_pushstring(L, pE.mArgumentList.at(i).toUtf8().constData());
break;
case ARGUMENT_TYPE_BOOLEAN:
lua_pushboolean(L, pE.mArgumentList.at(i).toInt());
break;
case ARGUMENT_TYPE_NIL:
lua_pushnil(L);
break;
case ARGUMENT_TYPE_TABLE:
lua_rawgeti(L, LUA_REGISTRYINDEX, pE.mArgumentList.at(i).toInt());
break;
case ARGUMENT_TYPE_FUNCTION:
lua_rawgeti(L, LUA_REGISTRYINDEX, pE.mArgumentList.at(i).toInt());
break;
default:
qWarning(R"(TLuaInterpreter::callEventHandler("%s", TEvent) ERROR: Unhandled ARGUMENT_TYPE: %i encountered in argument %i.)", function.toUtf8().constData(), pE.mArgumentTypeList.at(i), i);
lua_pushnil(L);
}
}
error = lua_pcall(L, maxArguments, LUA_MULTRET, 0);
if (mudlet::debugMode && pE.mArgumentList.size() > LUA_FUNCTION_MAX_ARGS) {
TDebug(QColor(Qt::white), QColor(Qt::red)) << "LUA: ERROR running script " << function << " (" << function << ")\nError: more than " << LUA_FUNCTION_MAX_ARGS
<< " arguments passed to Lua function, exceeding Lua's limit. Trimmed arguments to " << LUA_FUNCTION_MAX_ARGS << "\n"
>> 0;
}
if (error) {
std::string err = "";
if (lua_isstring(L, -1)) {
err += lua_tostring(L, -1);
}
QString name = "event handler function";
logError(err, name, function);
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::red)) << "LUA: ERROR running script " << function << " (" << function << ")\nError: " << err.c_str() << "\n" >> 0;
}
}
lua_pop(L, lua_gettop(L));
return !error;
}
// No documentation available in wiki - internal function
double TLuaInterpreter::condenseMapLoad()
{
QString luaFunction = QStringLiteral("condenseMapLoad");
double loadTime = -1.0;
lua_State* L = pGlobalLua;
lua_getfield(L, LUA_GLOBALSINDEX, "condenseMapLoad");
int error = lua_pcall(L, 0, 1, 0);
if (error != 0) {
int nbpossible_errors = lua_gettop(L);
for (int i = 1; i <= nbpossible_errors; i++) {
std::string e = "";
if (lua_isstring(L, i)) {
e += lua_tostring(L, i);
QString _f = luaFunction.toUtf8().constData();
logError(e, luaFunction, _f);
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::red)) << "LUA: ERROR running " << luaFunction << " ERROR:" << e.c_str() << "\n" >> 0;
}
}
}
} else {
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::darkGreen)) << "LUA OK " << luaFunction << " ran without errors\n" >> 0;
}
}
int returnValues = lua_gettop(L);
if (returnValues > 0 && !lua_isnoneornil(L, 1)) {
loadTime = lua_tonumber(L, 1);
}
lua_pop(L, returnValues);
return loadTime;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getAvailableFonts
int TLuaInterpreter::getAvailableFonts(lua_State* L)
{
auto fontList = mudlet::self()->getAvailableFonts();
lua_newtable(L);
for (auto& font : fontList) {
lua_pushstring(L, font.toUtf8().constData());
lua_pushboolean(L, true);
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#putHTTP
int TLuaInterpreter::putHTTP(lua_State* L)
{
auto& host = getHostFromLua(L);
QString dataToPost;
if (!lua_isstring(L, 1) && !lua_isstring(L, 4)) {
lua_pushfstring(L, "putHTTP: bad argument #1 type (data to send as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
if (lua_isstring(L, 1)) {
dataToPost = lua_tostring(L, 1);
}
QString urlString = getVerifiedString(L, __func__, 2, "remote url");
QUrl url = QUrl::fromUserInput(urlString);
if (!url.isValid()) {
return warnArgumentValue(L, __func__, QStringLiteral("url is invalid, reason: %1.").arg(url.errorString()));
}
QNetworkRequest request = QNetworkRequest(url);
mudlet::self()->setNetworkRequestDefaults(url, request);
if (!lua_istable(L, 3) && !lua_isnoneornil(L, 3)) {
lua_pushfstring(L, "putHTTP: bad argument #3 type (headers as a table expected, got %s!)", luaL_typename(L, 3));
return lua_error(L);
}
if (lua_istable(L, 3)) {
lua_pushnil(L);
while (lua_next(L, 3) != 0) {
// key at index -2 and value at index -1
if (lua_type(L, -1) == LUA_TSTRING && lua_type(L, -2) == LUA_TSTRING) {
QString cmd = lua_tostring(L, -1);
request.setRawHeader(QByteArray(lua_tostring(L, -2)), QByteArray(lua_tostring(L, -1)));
} else {
lua_pushfstring(L,
"putHTTP: bad argument #3 type (custom headers must be strings, got header: %s (should be string) and value: %s (should be string))",
luaL_typename(L, -2),
luaL_typename(L, -1));
return lua_error(L);
}
// removes value, but keeps key for next iteration
lua_pop(L, 1);
}
}
QByteArray fileToUpload;
QString fileLocation;
if (!lua_isstring(L, 4) && !lua_isnoneornil(L, 4)) {
lua_pushfstring(L, "putHTTP: bad argument #4 type (file to send as string location expected, got %s!)", luaL_typename(L, 4));
return lua_error(L);
}
if (lua_isstring(L, 4)) {
fileLocation = lua_tostring(L, 4);
}
if (!fileLocation.isEmpty()) {
QFile file(fileLocation);
if (!file.open(QFile::ReadOnly)) {
return warnArgumentValue(L, __func__, QStringLiteral("couldn't open '%1', is the location correct and do you have permissions to it?").arg(fileLocation));
}
fileToUpload = file.readAll();
file.close();
}
host.updateProxySettings(host.mLuaInterpreter.mpFileDownloader);
QNetworkReply* reply = host.mLuaInterpreter.mpFileDownloader->put(request, fileToUpload.isEmpty() ?dataToPost.toUtf8() : fileToUpload);
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::blue)) << "putHTTP: script is uploading data to " << reply->url().toString() << "\n" >> 0;
}
lua_pushboolean(L, true);
lua_pushstring(L, reply->url().toString().toUtf8().constData()); // Returns the Url that was ACTUALLY used
return 2;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getHTTP
int TLuaInterpreter::getHTTP(lua_State* L)
{
auto& host = getHostFromLua(L);
QString urlString = getVerifiedString(L, __func__, 1, "remote url");
QUrl url = QUrl::fromUserInput(urlString);
if (!url.isValid()) {
return warnArgumentValue(L, __func__, QStringLiteral("url is invalid, reason: %1").arg(url.errorString()));
}
QNetworkRequest request = QNetworkRequest(url);
mudlet::self()->setNetworkRequestDefaults(url, request);
if (!lua_istable(L, 2) && !lua_isnoneornil(L, 2)) {
lua_pushfstring(L, "getHTTP: bad argument #2 type (headers as a table expected, got %s!)", luaL_typename(L, 2));
return lua_error(L);
}
if (lua_istable(L, 2)) {
lua_pushnil(L);
while (lua_next(L, 2) != 0) {
// key at index -2 and value at index -1
if (lua_type(L, -1) == LUA_TSTRING && lua_type(L, -2) == LUA_TSTRING) {
QString cmd = lua_tostring(L, -1);
request.setRawHeader(QByteArray(lua_tostring(L, -2)), QByteArray(lua_tostring(L, -1)));
} else {
lua_pushfstring(L,
"getHTTP: bad argument #2 type (custom headers must be strings, got header: %s (should be string) and value: %s (should be string))",
luaL_typename(L, -2),
luaL_typename(L, -1));
return lua_error(L);
}
// removes value, but keeps key for next iteration
lua_pop(L, 1);
}
}
host.updateProxySettings(host.mLuaInterpreter.mpFileDownloader);
QNetworkReply* reply = host.mLuaInterpreter.mpFileDownloader->get(request);
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::blue)) << QStringLiteral("getHTTP: script is getting data from %1\n").arg(reply->url().toString()) >> 0;
}
lua_pushboolean(L, true);
lua_pushstring(L, reply->url().toString().toUtf8().constData()); // Returns the Url that was ACTUALLY used
return 2;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#postHTTP
int TLuaInterpreter::postHTTP(lua_State* L)
{
auto& host = getHostFromLua(L);
QString dataToPost;
if (!lua_isstring(L, 1) && !lua_isstring(L, 4)) {
lua_pushfstring(L, "postHTTP: bad argument #1 type (data to send as string expected, got %s!)", luaL_typename(L, 1));
return lua_error(L);
}
if (lua_isstring(L, 1)) {
dataToPost = lua_tostring(L, 1);
}
QString urlString = getVerifiedString(L, __func__, 2, "remote url");
QUrl url = QUrl::fromUserInput(urlString);
if (!url.isValid()) {
return warnArgumentValue(L, __func__, QStringLiteral("url is invalid, reason: %1").arg(url.errorString()));
}
QNetworkRequest request = QNetworkRequest(url);
mudlet::self()->setNetworkRequestDefaults(url, request);
if (!lua_istable(L, 3) && !lua_isnoneornil(L, 3)) {
lua_pushfstring(L, "postHTTP: bad argument #3 type (headers as a table expected, got %s!)", luaL_typename(L, 3));
return lua_error(L);
}
if (lua_istable(L, 3)) {
lua_pushnil(L);
while (lua_next(L, 3) != 0) {
// key at index -2 and value at index -1
if (lua_type(L, -1) == LUA_TSTRING && lua_type(L, -2) == LUA_TSTRING) {
QString cmd = lua_tostring(L, -1);
request.setRawHeader(QByteArray(lua_tostring(L, -2)), QByteArray(lua_tostring(L, -1)));
} else {
lua_pushfstring(L,
"postHTTP: bad argument #3 type (custom headers must be strings, got header: %s (should be string) and value: %s (should be string))",
luaL_typename(L, -2),
luaL_typename(L, -1));
return lua_error(L);
}
// removes value, but keeps key for next iteration
lua_pop(L, 1);
}
}
QByteArray fileToUpload;
QString fileLocation;
if (!lua_isstring(L, 4) && !lua_isnoneornil(L, 4)) {
lua_pushfstring(L, "postHTTP: bad argument #4 type (file to send as string location expected, got %s!)", luaL_typename(L, 4));
return lua_error(L);
}
if (lua_isstring(L, 4)) {
fileLocation = lua_tostring(L, 4);
}
if (!fileLocation.isEmpty()) {
QFile file(fileLocation);
if (!file.open(QFile::ReadOnly)) {
return warnArgumentValue(L, __func__, QStringLiteral("couldn't open '%1', is the location correct and do you have permissions to it?").arg(fileLocation));
}
fileToUpload = file.readAll();
file.close();
}
host.updateProxySettings(host.mLuaInterpreter.mpFileDownloader);
QNetworkReply* reply = host.mLuaInterpreter.mpFileDownloader->post(request, fileToUpload.isEmpty() ? dataToPost.toUtf8() : fileToUpload);
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::blue)) << QStringLiteral("postHTTP: script is uploading data to %1\n").arg(reply->url().toString()) >> 0;
}
lua_pushboolean(L, true);
lua_pushstring(L, reply->url().toString().toUtf8().constData()); // Returns the Url that was ACTUALLY used
return 2;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Networking_Functions#deleteHTTP
int TLuaInterpreter::deleteHTTP(lua_State *L)
{
auto& host = getHostFromLua(L);
QString urlString = getVerifiedString(L, __func__, 1, "remote url");
QUrl url = QUrl::fromUserInput(urlString);
if (!url.isValid()) {
return warnArgumentValue(L, __func__, QStringLiteral("url is invalid, reason: %1").arg(url.errorString()));
}
QNetworkRequest request = QNetworkRequest(url);
mudlet::self()->setNetworkRequestDefaults(url, request);
if (!lua_istable(L, 2) && !lua_isnoneornil(L, 2)) {
lua_pushfstring(L, "deleteHTTP: bad argument #2 type (headers as a table expected, got %s!)", luaL_typename(L, 2));
return lua_error(L);
}
if (lua_istable(L, 2)) {
lua_pushnil(L);
while (lua_next(L, 2) != 0) {
// key at index -2 and value at index -1
if (lua_type(L, -1) == LUA_TSTRING && lua_type(L, -2) == LUA_TSTRING) {
QString cmd = lua_tostring(L, -1);
request.setRawHeader(QByteArray(lua_tostring(L, -2)), QByteArray(lua_tostring(L, -1)));
} else {
lua_pushfstring(L,
"deleteHTTP: bad argument #2 type (custom headers must be strings, got header: %s (should be string) and value: %s (should be string))",
luaL_typename(L, -2),
luaL_typename(L, -1));
return lua_error(L);
}
// removes value, but keeps key for next iteration
lua_pop(L, 1);
}
}
host.updateProxySettings(host.mLuaInterpreter.mpFileDownloader);
QNetworkReply* reply = host.mLuaInterpreter.mpFileDownloader->deleteResource(request);
if (mudlet::debugMode) {
TDebug(QColor(Qt::white), QColor(Qt::blue)) << QStringLiteral("deleteHTTP: script is sending delete request for %1\n").arg(reply->url().toString()) >> 0;
}
lua_pushboolean(L, true);
lua_pushstring(L, reply->url().toString().toUtf8().constData()); // Returns the Url that was ACTUALLY used
return 2;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getConnectionInfo
int TLuaInterpreter::getConnectionInfo(lua_State *L)
{
Host& host = getHostFromLua(L);
auto [hostName, hostPort] = host.mTelnet.getConnectionInfo();
lua_pushstring(L, hostName.toUtf8().constData());
lua_pushnumber(L, hostPort);
return 2;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Networking_Functions#unzipAsync
int TLuaInterpreter::unzipAsync(lua_State *L)
{
QString zipLocation = getVerifiedString(L, __func__, 1, "zip location");
QString extractLocation = getVerifiedString(L, __func__, 2, "extract location");
QTemporaryDir temporaryDir;
if (!temporaryDir.isValid()) {
return warnArgumentValue(L, __func__, "couldn't create temporary directory to extract the zip into");
}
extractLocation = QDir::fromNativeSeparators(extractLocation);
if (!extractLocation.endsWith(QLatin1String("/"))) {
extractLocation.append(QLatin1String("/"));
}
QDir dir;
if (!dir.mkpath(extractLocation)) {
return warnArgumentValue(L, __func__, "couldn't create output directory to put the extracted files into");
}
auto future = QtConcurrent::run(mudlet::unzip, zipLocation, extractLocation, temporaryDir.path());
auto watcher = new QFutureWatcher<bool>;
QObject::connect(watcher, &QFutureWatcher<bool>::finished, [=]() {
TEvent event {};
Host& host = getHostFromLua(L);
if (future.result()) {
event.mArgumentList.append(QStringLiteral("sysUnzipDone"));
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
} else {
event.mArgumentList.append(QStringLiteral("sysUnzipError"));
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
}
event.mArgumentList.append(zipLocation);
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
event.mArgumentList.append(extractLocation);
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
host.raiseEvent(event);
});
watcher->setFuture(future);
lua_pushboolean(L, true);
return 1;
}
// No documentation available in wiki - internal function
void TLuaInterpreter::set_lua_table(const QString& tableName, QStringList& variableList)
{
lua_State* L = pGlobalLua;
lua_newtable(L);
for (int i = 0; i < variableList.size(); i++) {
lua_pushnumber(L, i + 1); // Lua indexes start with 1
lua_pushstring(L, variableList[i].toUtf8().constData());
lua_settable(L, -3);
}
lua_setglobal(L, tableName.toUtf8().constData());
lua_pop(pGlobalLua, lua_gettop(pGlobalLua));
}
// No documentation available in wiki - internal function
void TLuaInterpreter::set_lua_string(const QString& varName, const QString& varValue)
{
lua_State* L = pGlobalLua;
lua_pushstring(L, varValue.toUtf8().constData());
lua_setglobal(L, varName.toUtf8().constData());
lua_pop(pGlobalLua, lua_gettop(pGlobalLua));
}
// No documentation available in wiki - internal function
void TLuaInterpreter::set_lua_integer(const QString& varName, int varValue)
{
lua_State* L = pGlobalLua;
int top = lua_gettop(L);
lua_pushnumber(L, varValue);
lua_setglobal(L, varName.toUtf8().constData());
lua_settop(L, top);
}
// No documentation available in wiki - internal function
QString TLuaInterpreter::getLuaString(const QString& stringName)
{
lua_State* L = pGlobalLua;
int error = luaL_dostring(L, QStringLiteral("return %1").arg(stringName).toUtf8().constData());
if (!error) {
return lua_tostring(L, 1);
} else {
return QString();
}
}
// check for <whitespace><no_valid_representation> as output
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#handleWindowResizeEvent is using noop function publicly - compare initLuaGlobals()
int TLuaInterpreter::noop(lua_State* L)
{
Q_UNUSED(L)
return 0;
}
// No documentation available in wiki - internal function
int TLuaInterpreter::check_for_mappingscript()
{
lua_State* L = pGlobalLua;
lua_getglobal(L, "mudlet");
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
return 0;
}
lua_getfield(L, -1, "mapper_script");
if (!lua_isboolean(L, -1)) {
lua_pop(L, 2);
return 0;
}
int r = lua_toboolean(L, -1);
lua_pop(L, 2);
return r;
}
// No documentation available in wiki - internal function
int TLuaInterpreter::check_for_custom_speedwalk()
{
lua_State* L = pGlobalLua;
lua_getglobal(L, "mudlet");
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
return 0;
}
lua_getfield(L, -1, "custom_speedwalk");
if (!lua_isboolean(L, -1)) {
lua_pop(L, 2);
return 0;
}
int r = lua_toboolean(L, -1);
lua_pop(L, 2);
return r;
}
#if defined(_MSC_VER) && defined(_DEBUG)
// Enable leak detection for MSVC debug builds.
#define LUA_CLIENT_TYPE (_CLIENT_BLOCK | ((('L' << 8) | 'U') << 16))
// No documentation available in wiki - internal function
static void* l_alloc(void* ud, void* ptr, size_t osize, size_t nsize)
{
(void)ud;
(void)osize;
if (nsize == 0) {
::_free_dbg(ptr, LUA_CLIENT_TYPE);
return NULL;
} else {
return ::_realloc_dbg(ptr, nsize, LUA_CLIENT_TYPE, __FILE__, __LINE__);
}
}
// No documentation available in wiki - internal function
static int panic(lua_State* L)
{
fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n", lua_tostring(L, -1));
return 0;
}
// No documentation available in wiki - internal function
static lua_State* newstate()
{
lua_State* L = lua_newstate(l_alloc, NULL);
if (L) {
lua_atpanic(L, &panic);
}
return L;
}
#else
// No documentation available in wiki - internal function
static lua_State* newstate()
{
return luaL_newstate();
}
#endif // _MSC_VER && _DEBUG
// No documentation available in wiki - internal function
static void storeHostInLua(lua_State* L, Host* h);
// No documentation available in wiki - internal helper funtion
// On success will swap out any messages in the queue and replace them
// with its success message, on failure will just append...
bool TLuaInterpreter::loadLuaModule(QQueue<QString>& resultMsgsQueue, const QString& requirement, const QString& failureConsequence, const QString& description, const QString& luaModuleId)
{
int error = luaL_dostring(pGlobalLua, QStringLiteral("%1require \"%2\"")
.arg(luaModuleId.isEmpty() ? QString() : QStringLiteral("%1 =").arg(luaModuleId),
requirement).toUtf8().constData());
if (error) {
QString luaErrorMsg = tr("No error message available from Lua");
if (lua_isstring(pGlobalLua, -1)) {
luaErrorMsg = tr("Lua error: %1").arg(lua_tostring(pGlobalLua, -1));
}
resultMsgsQueue.enqueue(tr("[ ERROR ] - Cannot find Lua module %1.%2%3%4",
// Intentional comment to separate arguments
"%1 is the name of the module;"
"%2 will be a line-feed inserted to put the next argument on a new line;"
"%3 is the error message from the lua sub-system;"
"%4 can be an additional message about the expected effect (but may be blank).")
.arg((description.isEmpty() ? requirement : description),
QLatin1String("\n"),
luaErrorMsg,
(failureConsequence.isEmpty() ? QString() : QStringLiteral("\n%1").arg(failureConsequence))));
return false;
}
QQueue<QString> newMessageQueue;
newMessageQueue.enqueue(tr("[ OK ] - Lua module %1 loaded.",
"%1 is the name (may specify which variant) of the module.")
.arg(description.isEmpty() ? requirement : description));
resultMsgsQueue.swap(newMessageQueue);
return true;
}
// No documentation available in wiki - internal function
void TLuaInterpreter::insertNativeSeparatorsFunction(lua_State* L)
{
// This is likely only needed for Windows but should not be harmful for
// other OSes and it keeps things simpler if they all use it:
// clang-format off
luaL_dostring(L, R"LUA(function toNativeSeparators(rawPath)
if package.config:sub(1,1) == '\\' then
return string.gsub(rawPath, '/', '\\')
end
assert((package.config:sub(1,1) == '/'), "package path directory separator is neither '\\' nor '/' and cannot be handled")
return string.gsub(rawPath, '\\', '/')
end)LUA");
// clang-format on
}
// No documentation available in wiki - internal function
// This function initializes the main Lua Session interpreter.
// on initialization of a new session *or* in case of an interpreter reset by the user.
void TLuaInterpreter::initLuaGlobals()
{
pGlobalLua = newstate();
storeHostInLua(pGlobalLua, mpHost);
luaL_openlibs(pGlobalLua);
lua_pushstring(pGlobalLua, "SESSION");
lua_pushnumber(pGlobalLua, mHostID);
lua_settable(pGlobalLua, LUA_GLOBALSINDEX);
lua_pushstring(pGlobalLua, "SCRIPT_NAME");
lua_pushstring(pGlobalLua, "Global Lua Session Interpreter");
lua_settable(pGlobalLua, LUA_GLOBALSINDEX);
lua_pushstring(pGlobalLua, "SCRIPT_ID");
lua_pushnumber(pGlobalLua, -1); // ID 1 is used to indicate that this is the global Lua interpreter
lua_settable(pGlobalLua, LUA_GLOBALSINDEX);
lua_register(pGlobalLua, "showUnzipProgress", TLuaInterpreter::showUnzipProgress); //internal function used by the package system NOT FOR USERS
lua_register(pGlobalLua, "wait", TLuaInterpreter::Wait);
lua_register(pGlobalLua, "expandAlias", TLuaInterpreter::expandAlias);
lua_register(pGlobalLua, "echo", TLuaInterpreter::echo);
lua_register(pGlobalLua, "selectString", TLuaInterpreter::selectString);
lua_register(pGlobalLua, "selectSection", TLuaInterpreter::selectSection);
lua_register(pGlobalLua, "replace", TLuaInterpreter::replace);
lua_register(pGlobalLua, "setBgColor", TLuaInterpreter::setBgColor);
lua_register(pGlobalLua, "setFgColor", TLuaInterpreter::setFgColor);
lua_register(pGlobalLua, "tempTimer", TLuaInterpreter::tempTimer);
lua_register(pGlobalLua, "tempTrigger", TLuaInterpreter::tempTrigger);
lua_register(pGlobalLua, "tempRegexTrigger", TLuaInterpreter::tempRegexTrigger);
lua_register(pGlobalLua, "closeMudlet", TLuaInterpreter::closeMudlet);
lua_register(pGlobalLua, "loadWindowLayout", TLuaInterpreter::loadWindowLayout);
lua_register(pGlobalLua, "saveWindowLayout", TLuaInterpreter::saveWindowLayout);
lua_register(pGlobalLua, "setFont", TLuaInterpreter::setFont);
lua_register(pGlobalLua, "getFont", TLuaInterpreter::getFont);
lua_register(pGlobalLua, "setFontSize", TLuaInterpreter::setFontSize);
lua_register(pGlobalLua, "getFontSize", TLuaInterpreter::getFontSize);
lua_register(pGlobalLua, "openUserWindow", TLuaInterpreter::openUserWindow);
lua_register(pGlobalLua, "setUserWindowTitle", TLuaInterpreter::setUserWindowTitle);
lua_register(pGlobalLua, "echoUserWindow", TLuaInterpreter::echoUserWindow);
lua_register(pGlobalLua, "enableTimer", TLuaInterpreter::enableTimer);
lua_register(pGlobalLua, "disableTimer", TLuaInterpreter::disableTimer);
lua_register(pGlobalLua, "enableKey", TLuaInterpreter::enableKey);
lua_register(pGlobalLua, "disableKey", TLuaInterpreter::disableKey);
lua_register(pGlobalLua, "killKey", TLuaInterpreter::killKey);
lua_register(pGlobalLua, "clearUserWindow", TLuaInterpreter::clearUserWindow);
lua_register(pGlobalLua, "clearWindow", TLuaInterpreter::clearUserWindow);
lua_register(pGlobalLua, "killTimer", TLuaInterpreter::killTimer);
lua_register(pGlobalLua, "remainingTime", TLuaInterpreter::remainingTime);
lua_register(pGlobalLua, "moveCursor", TLuaInterpreter::moveCursor);
lua_register(pGlobalLua, "getLines", TLuaInterpreter::getLines);
lua_register(pGlobalLua, "getLineNumber", TLuaInterpreter::getLineNumber);
lua_register(pGlobalLua, "insertHTML", TLuaInterpreter::insertHTML);
lua_register(pGlobalLua, "insertText", TLuaInterpreter::insertText);
lua_register(pGlobalLua, "enableTrigger", TLuaInterpreter::enableTrigger);
lua_register(pGlobalLua, "disableTrigger", TLuaInterpreter::disableTrigger);
lua_register(pGlobalLua, "enableScript", TLuaInterpreter::enableScript);
lua_register(pGlobalLua, "disableScript", TLuaInterpreter::disableScript);
lua_register(pGlobalLua, "killTrigger", TLuaInterpreter::killTrigger);
lua_register(pGlobalLua, "getLineCount", TLuaInterpreter::getLineCount);
lua_register(pGlobalLua, "getColumnNumber", TLuaInterpreter::getColumnNumber);
lua_register(pGlobalLua, "send", TLuaInterpreter::sendRaw);
lua_register(pGlobalLua, "selectCaptureGroup", TLuaInterpreter::selectCaptureGroup);
lua_register(pGlobalLua, "tempLineTrigger", TLuaInterpreter::tempLineTrigger);
lua_register(pGlobalLua, "raiseEvent", TLuaInterpreter::raiseEvent);
lua_register(pGlobalLua, "deleteLine", TLuaInterpreter::deleteLine);
lua_register(pGlobalLua, "copy", TLuaInterpreter::copy);
lua_register(pGlobalLua, "cut", TLuaInterpreter::cut);
lua_register(pGlobalLua, "paste", TLuaInterpreter::paste);
lua_register(pGlobalLua, "pasteWindow", TLuaInterpreter::pasteWindow);
lua_register(pGlobalLua, "debugc", TLuaInterpreter::debug);
lua_register(pGlobalLua, "showHandlerError", TLuaInterpreter::showHandlerError);
lua_register(pGlobalLua, "setWindowWrap", TLuaInterpreter::setWindowWrap);
lua_register(pGlobalLua, "getWindowWrap", TLuaInterpreter::getWindowWrap);
lua_register(pGlobalLua, "setWindowWrapIndent", TLuaInterpreter::setWindowWrapIndent);
lua_register(pGlobalLua, "resetFormat", TLuaInterpreter::resetFormat);
lua_register(pGlobalLua, "moveCursorEnd", TLuaInterpreter::moveCursorEnd);
lua_register(pGlobalLua, "getLastLineNumber", TLuaInterpreter::getLastLineNumber);
lua_register(pGlobalLua, "getNetworkLatency", TLuaInterpreter::getNetworkLatency);
lua_register(pGlobalLua, "createMiniConsole", TLuaInterpreter::createMiniConsole);
lua_register(pGlobalLua, "createLabel", TLuaInterpreter::createLabel);
lua_register(pGlobalLua, "deleteLabel", TLuaInterpreter::deleteLabel);
lua_register(pGlobalLua, "setLabelToolTip", TLuaInterpreter::setLabelToolTip);
lua_register(pGlobalLua, "setLabelCursor", TLuaInterpreter::setLabelCursor);
lua_register(pGlobalLua, "setLabelCustomCursor", TLuaInterpreter::setLabelCustomCursor);
lua_register(pGlobalLua, "raiseWindow", TLuaInterpreter::raiseWindow);
lua_register(pGlobalLua, "lowerWindow", TLuaInterpreter::lowerWindow);
lua_register(pGlobalLua, "hideWindow", TLuaInterpreter::hideWindow);
lua_register(pGlobalLua, "showWindow", TLuaInterpreter::showWindow);
lua_register(pGlobalLua, "createBuffer", TLuaInterpreter::createBuffer);
lua_register(pGlobalLua, "createStopWatch", TLuaInterpreter::createStopWatch);
lua_register(pGlobalLua, "getStopWatchTime", TLuaInterpreter::getStopWatchTime);
lua_register(pGlobalLua, "stopStopWatch", TLuaInterpreter::stopStopWatch);
lua_register(pGlobalLua, "startStopWatch", TLuaInterpreter::startStopWatch);
lua_register(pGlobalLua, "resetStopWatch", TLuaInterpreter::resetStopWatch);
lua_register(pGlobalLua, "adjustStopWatch", TLuaInterpreter::adjustStopWatch);
lua_register(pGlobalLua, "deleteStopWatch", TLuaInterpreter::deleteStopWatch);
lua_register(pGlobalLua, "setStopWatchPersistence", TLuaInterpreter::setStopWatchPersistence);
lua_register(pGlobalLua, "getStopWatches", TLuaInterpreter::getStopWatches);
lua_register(pGlobalLua, "setStopWatchName", TLuaInterpreter::setStopWatchName);
lua_register(pGlobalLua, "getStopWatchBrokenDownTime", TLuaInterpreter::getStopWatchBrokenDownTime);
lua_register(pGlobalLua, "closeUserWindow", TLuaInterpreter::closeUserWindow);
lua_register(pGlobalLua, "resizeWindow", TLuaInterpreter::resizeWindow);
lua_register(pGlobalLua, "appendBuffer", TLuaInterpreter::appendBuffer);
lua_register(pGlobalLua, "setBackgroundImage", TLuaInterpreter::setBackgroundImage);
lua_register(pGlobalLua, "resetBackgroundImage", TLuaInterpreter::resetBackgroundImage);
lua_register(pGlobalLua, "setBackgroundColor", TLuaInterpreter::setBackgroundColor);
lua_register(pGlobalLua, "setCmdLineAction", TLuaInterpreter::setCmdLineAction);
lua_register(pGlobalLua, "resetCmdLineAction", TLuaInterpreter::resetCmdLineAction);
lua_register(pGlobalLua, "setCmdLineStyleSheet", TLuaInterpreter::setCmdLineStyleSheet);
lua_register(pGlobalLua, "setLabelClickCallback", TLuaInterpreter::setLabelClickCallback);
lua_register(pGlobalLua, "setLabelDoubleClickCallback", TLuaInterpreter::setLabelDoubleClickCallback);
lua_register(pGlobalLua, "setLabelReleaseCallback", TLuaInterpreter::setLabelReleaseCallback);
lua_register(pGlobalLua, "setLabelMoveCallback", TLuaInterpreter::setLabelMoveCallback);
lua_register(pGlobalLua, "setLabelWheelCallback", TLuaInterpreter::setLabelWheelCallback);
lua_register(pGlobalLua, "setLabelOnEnter", TLuaInterpreter::setLabelOnEnter);
lua_register(pGlobalLua, "setLabelOnLeave", TLuaInterpreter::setLabelOnLeave);
lua_register(pGlobalLua, "getImageSize", TLuaInterpreter::getImageSize);
lua_register(pGlobalLua, "moveWindow", TLuaInterpreter::moveWindow);
lua_register(pGlobalLua, "setWindow", TLuaInterpreter::setWindow);
lua_register(pGlobalLua, "openMapWidget", TLuaInterpreter::openMapWidget);
lua_register(pGlobalLua, "closeMapWidget", TLuaInterpreter::closeMapWidget);
lua_register(pGlobalLua, "setTextFormat", TLuaInterpreter::setTextFormat);
lua_register(pGlobalLua, "getMainWindowSize", TLuaInterpreter::getMainWindowSize);
lua_register(pGlobalLua, "getUserWindowSize", TLuaInterpreter::getUserWindowSize);
lua_register(pGlobalLua, "getMousePosition", TLuaInterpreter::getMousePosition);
lua_register(pGlobalLua, "setProfileIcon", TLuaInterpreter::setProfileIcon);
lua_register(pGlobalLua, "resetProfileIcon", TLuaInterpreter::resetProfileIcon);
lua_register(pGlobalLua, "getCurrentLine", TLuaInterpreter::getCurrentLine);
lua_register(pGlobalLua, "setMiniConsoleFontSize", TLuaInterpreter::setFontSize);
lua_register(pGlobalLua, "selectCurrentLine", TLuaInterpreter::selectCurrentLine);
lua_register(pGlobalLua, "spawn", TLuaInterpreter::spawn);
lua_register(pGlobalLua, "getButtonState", TLuaInterpreter::getButtonState);
lua_register(pGlobalLua, "showToolBar", TLuaInterpreter::showToolBar);
lua_register(pGlobalLua, "hideToolBar", TLuaInterpreter::hideToolBar);
lua_register(pGlobalLua, "loadRawFile", TLuaInterpreter::loadReplay);
lua_register(pGlobalLua, "loadReplay", TLuaInterpreter::loadReplay);
lua_register(pGlobalLua, "setBold", TLuaInterpreter::setBold);
lua_register(pGlobalLua, "setItalics", TLuaInterpreter::setItalics);
lua_register(pGlobalLua, "setOverline", TLuaInterpreter::setOverline);
lua_register(pGlobalLua, "setReverse", TLuaInterpreter::setReverse);
lua_register(pGlobalLua, "setStrikeOut", TLuaInterpreter::setStrikeOut);
lua_register(pGlobalLua, "setUnderline", TLuaInterpreter::setUnderline);
lua_register(pGlobalLua, "disconnect", TLuaInterpreter::disconnect);
lua_register(pGlobalLua, "tempButtonToolbar", TLuaInterpreter::tempButtonToolbar);
lua_register(pGlobalLua, "tempButton", TLuaInterpreter::tempButton);
lua_register(pGlobalLua, "setButtonStyleSheet", TLuaInterpreter::setButtonStyleSheet);
lua_register(pGlobalLua, "reconnect", TLuaInterpreter::reconnect);
lua_register(pGlobalLua, "getMudletHomeDir", TLuaInterpreter::getMudletHomeDir);
lua_register(pGlobalLua, "setTriggerStayOpen", TLuaInterpreter::setTriggerStayOpen);
lua_register(pGlobalLua, "wrapLine", TLuaInterpreter::wrapLine);
lua_register(pGlobalLua, "getFgColor", TLuaInterpreter::getFgColor);
lua_register(pGlobalLua, "getBgColor", TLuaInterpreter::getBgColor);
lua_register(pGlobalLua, "tempColorTrigger", TLuaInterpreter::tempColorTrigger);
lua_register(pGlobalLua, "isAnsiFgColor", TLuaInterpreter::isAnsiFgColor);
lua_register(pGlobalLua, "isAnsiBgColor", TLuaInterpreter::isAnsiBgColor);
lua_register(pGlobalLua, "stopSounds", TLuaInterpreter::stopSounds);
lua_register(pGlobalLua, "playSoundFile", TLuaInterpreter::playSoundFile);
lua_register(pGlobalLua, "setBorderSizes", TLuaInterpreter::setBorderSizes);
lua_register(pGlobalLua, "setBorderTop", TLuaInterpreter::setBorderTop);
lua_register(pGlobalLua, "setBorderRight", TLuaInterpreter::setBorderRight);
lua_register(pGlobalLua, "setBorderBottom", TLuaInterpreter::setBorderBottom);
lua_register(pGlobalLua, "setBorderLeft", TLuaInterpreter::setBorderLeft);
lua_register(pGlobalLua, "setBorderColor", TLuaInterpreter::setBorderColor);
lua_register(pGlobalLua, "getBorderTop", TLuaInterpreter::getBorderTop);
lua_register(pGlobalLua, "getBorderRight", TLuaInterpreter::getBorderRight);
lua_register(pGlobalLua, "getBorderBottom", TLuaInterpreter::getBorderBottom);
lua_register(pGlobalLua, "getBorderLeft", TLuaInterpreter::getBorderLeft);
lua_register(pGlobalLua, "getBorderSizes", TLuaInterpreter::getBorderSizes);
lua_register(pGlobalLua, "setConsoleBufferSize", TLuaInterpreter::setConsoleBufferSize);
lua_register(pGlobalLua, "enableScrollBar", TLuaInterpreter::enableScrollBar);
lua_register(pGlobalLua, "disableScrollBar", TLuaInterpreter::disableScrollBar);
lua_register(pGlobalLua, "enableHorizontalScrollBar", TLuaInterpreter::enableHorizontalScrollBar);
lua_register(pGlobalLua, "disableHorizontalScrollBar", TLuaInterpreter::disableHorizontalScrollBar);
lua_register(pGlobalLua, "enableCommandLine", TLuaInterpreter::enableCommandLine);
lua_register(pGlobalLua, "disableCommandLine", TLuaInterpreter::disableCommandLine);
lua_register(pGlobalLua, "startLogging", TLuaInterpreter::startLogging);
lua_register(pGlobalLua, "calcFontSize", TLuaInterpreter::calcFontSize);
lua_register(pGlobalLua, "permRegexTrigger", TLuaInterpreter::permRegexTrigger);
lua_register(pGlobalLua, "permSubstringTrigger", TLuaInterpreter::permSubstringTrigger);
lua_register(pGlobalLua, "permBeginOfLineStringTrigger", TLuaInterpreter::permBeginOfLineStringTrigger);
lua_register(pGlobalLua, "tempComplexRegexTrigger", TLuaInterpreter::tempComplexRegexTrigger);
lua_register(pGlobalLua, "permTimer", TLuaInterpreter::permTimer);
lua_register(pGlobalLua, "permScript", TLuaInterpreter::permScript);
lua_register(pGlobalLua, "getScript", TLuaInterpreter::getScript);
lua_register(pGlobalLua, "setScript", TLuaInterpreter::setScript);
lua_register(pGlobalLua, "permAlias", TLuaInterpreter::permAlias);
lua_register(pGlobalLua, "permKey", TLuaInterpreter::permKey);
lua_register(pGlobalLua, "tempKey", TLuaInterpreter::tempKey);
lua_register(pGlobalLua, "exists", TLuaInterpreter::exists);
lua_register(pGlobalLua, "isActive", TLuaInterpreter::isActive);
lua_register(pGlobalLua, "enableAlias", TLuaInterpreter::enableAlias);
lua_register(pGlobalLua, "tempAlias", TLuaInterpreter::tempAlias);
lua_register(pGlobalLua, "disableAlias", TLuaInterpreter::disableAlias);
lua_register(pGlobalLua, "killAlias", TLuaInterpreter::killAlias);
lua_register(pGlobalLua, "setLabelStyleSheet", TLuaInterpreter::setLabelStyleSheet);
lua_register(pGlobalLua, "setUserWindowStyleSheet", TLuaInterpreter::setUserWindowStyleSheet);
lua_register(pGlobalLua, "getTime", TLuaInterpreter::getTime);
lua_register(pGlobalLua, "getEpoch", TLuaInterpreter::getEpoch);
lua_register(pGlobalLua, "invokeFileDialog", TLuaInterpreter::invokeFileDialog);
lua_register(pGlobalLua, "getTimestamp", TLuaInterpreter::getTimestamp);
lua_register(pGlobalLua, "setLink", TLuaInterpreter::setLink);
lua_register(pGlobalLua, "deselect", TLuaInterpreter::deselect);
lua_register(pGlobalLua, "insertLink", TLuaInterpreter::insertLink);
lua_register(pGlobalLua, "echoLink", TLuaInterpreter::echoLink);
lua_register(pGlobalLua, "echoPopup", TLuaInterpreter::echoPopup);
lua_register(pGlobalLua, "insertPopup", TLuaInterpreter::insertPopup);
lua_register(pGlobalLua, "setPopup", TLuaInterpreter::setPopup);
lua_register(pGlobalLua, "sendATCP", TLuaInterpreter::sendATCP);
lua_register(pGlobalLua, "hasFocus", TLuaInterpreter::hasFocus);
lua_register(pGlobalLua, "isPrompt", TLuaInterpreter::isPrompt);
lua_register(pGlobalLua, "feedTriggers", TLuaInterpreter::feedTriggers);
lua_register(pGlobalLua, "sendTelnetChannel102", TLuaInterpreter::sendTelnetChannel102);
lua_register(pGlobalLua, "setRoomWeight", TLuaInterpreter::setRoomWeight);
lua_register(pGlobalLua, "getRoomWeight", TLuaInterpreter::getRoomWeight);
lua_register(pGlobalLua, "gotoRoom", TLuaInterpreter::gotoRoom);
lua_register(pGlobalLua, "getRoomExits", TLuaInterpreter::getRoomExits);
lua_register(pGlobalLua, "lockRoom", TLuaInterpreter::lockRoom);
lua_register(pGlobalLua, "createMapper", TLuaInterpreter::createMapper);
lua_register(pGlobalLua, "createCommandLine", TLuaInterpreter::createCommandLine);
lua_register(pGlobalLua, "getMainConsoleWidth", TLuaInterpreter::getMainConsoleWidth);
lua_register(pGlobalLua, "resetProfile", TLuaInterpreter::resetProfile);
lua_register(pGlobalLua, "printCmdLine", TLuaInterpreter::printCmdLine);
lua_register(pGlobalLua, "searchRoom", TLuaInterpreter::searchRoom);
lua_register(pGlobalLua, "clearCmdLine", TLuaInterpreter::clearCmdLine);
lua_register(pGlobalLua, "getAreaTable", TLuaInterpreter::getAreaTable);
lua_register(pGlobalLua, "getAreaTableSwap", TLuaInterpreter::getAreaTableSwap);
lua_register(pGlobalLua, "getAreaRooms", TLuaInterpreter::getAreaRooms);
lua_register(pGlobalLua, "getPath", TLuaInterpreter::getPath);
lua_register(pGlobalLua, "centerview", TLuaInterpreter::centerview);
lua_register(pGlobalLua, "denyCurrentSend", TLuaInterpreter::denyCurrentSend);
lua_register(pGlobalLua, "tempBeginOfLineTrigger", TLuaInterpreter::tempBeginOfLineTrigger);
lua_register(pGlobalLua, "tempExactMatchTrigger", TLuaInterpreter::tempExactMatchTrigger);
lua_register(pGlobalLua, "receiveMSP", TLuaInterpreter::receiveMSP);
lua_register(pGlobalLua, "purgeMediaCache", TLuaInterpreter::purgeMediaCache);
lua_register(pGlobalLua, "sendGMCP", TLuaInterpreter::sendGMCP);
lua_register(pGlobalLua, "roomExists", TLuaInterpreter::roomExists);
lua_register(pGlobalLua, "addRoom", TLuaInterpreter::addRoom);
lua_register(pGlobalLua, "setExit", TLuaInterpreter::setExit);
lua_register(pGlobalLua, "setRoomCoordinates", TLuaInterpreter::setRoomCoordinates);
lua_register(pGlobalLua, "getRoomCoordinates", TLuaInterpreter::getRoomCoordinates);
lua_register(pGlobalLua, "createRoomID", TLuaInterpreter::createRoomID);
lua_register(pGlobalLua, "getRoomArea", TLuaInterpreter::getRoomArea);
lua_register(pGlobalLua, "setRoomArea", TLuaInterpreter::setRoomArea);
lua_register(pGlobalLua, "resetRoomArea", TLuaInterpreter::resetRoomArea);
lua_register(pGlobalLua, "setAreaName", TLuaInterpreter::setAreaName);
lua_register(pGlobalLua, "roomLocked", TLuaInterpreter::roomLocked);
lua_register(pGlobalLua, "setCustomEnvColor", TLuaInterpreter::setCustomEnvColor);
lua_register(pGlobalLua, "getCustomEnvColorTable", TLuaInterpreter::getCustomEnvColorTable);
lua_register(pGlobalLua, "setRoomEnv", TLuaInterpreter::setRoomEnv);
lua_register(pGlobalLua, "setRoomName", TLuaInterpreter::setRoomName);
lua_register(pGlobalLua, "getRoomName", TLuaInterpreter::getRoomName);
lua_register(pGlobalLua, "setGridMode", TLuaInterpreter::setGridMode);
lua_register(pGlobalLua, "getGridMode", TLuaInterpreter::getGridMode);
lua_register(pGlobalLua, "addSpecialExit", TLuaInterpreter::addSpecialExit);
lua_register(pGlobalLua, "removeSpecialExit", TLuaInterpreter::removeSpecialExit);
lua_register(pGlobalLua, "getSpecialExits", TLuaInterpreter::getSpecialExits);
lua_register(pGlobalLua, "getSpecialExitsSwap", TLuaInterpreter::getSpecialExitsSwap);
lua_register(pGlobalLua, "clearSpecialExits", TLuaInterpreter::clearSpecialExits);
lua_register(pGlobalLua, "getRoomEnv", TLuaInterpreter::getRoomEnv);
lua_register(pGlobalLua, "getRoomUserData", TLuaInterpreter::getRoomUserData);
lua_register(pGlobalLua, "setRoomUserData", TLuaInterpreter::setRoomUserData);
lua_register(pGlobalLua, "searchRoomUserData", TLuaInterpreter::searchRoomUserData);
lua_register(pGlobalLua, "getRoomsByPosition", TLuaInterpreter::getRoomsByPosition);
lua_register(pGlobalLua, "clearRoomUserData", TLuaInterpreter::clearRoomUserData);
lua_register(pGlobalLua, "clearRoomUserDataItem", TLuaInterpreter::clearRoomUserDataItem);
lua_register(pGlobalLua, "downloadFile", TLuaInterpreter::downloadFile);
lua_register(pGlobalLua, "appendCmdLine", TLuaInterpreter::appendCmdLine);
lua_register(pGlobalLua, "getCmdLine", TLuaInterpreter::getCmdLine);
lua_register(pGlobalLua, "addCmdLineSuggestion", TLuaInterpreter::addCmdLineSuggestion);
lua_register(pGlobalLua, "removeCmdLineSuggestion", TLuaInterpreter::removeCmdLineSuggestion);
lua_register(pGlobalLua, "clearCmdLineSuggestions", TLuaInterpreter::clearCmdLineSuggestions);
lua_register(pGlobalLua, "openUrl", TLuaInterpreter::openUrl);
lua_register(pGlobalLua, "sendSocket", TLuaInterpreter::sendSocket);
lua_register(pGlobalLua, "setRoomIDbyHash", TLuaInterpreter::setRoomIDbyHash);
lua_register(pGlobalLua, "getRoomIDbyHash", TLuaInterpreter::getRoomIDbyHash);
lua_register(pGlobalLua, "getRoomHashByID", TLuaInterpreter::getRoomHashByID);
lua_register(pGlobalLua, "addAreaName", TLuaInterpreter::addAreaName);
lua_register(pGlobalLua, "getRoomAreaName", TLuaInterpreter::getRoomAreaName);
lua_register(pGlobalLua, "deleteArea", TLuaInterpreter::deleteArea);
lua_register(pGlobalLua, "deleteRoom", TLuaInterpreter::deleteRoom);
lua_register(pGlobalLua, "setRoomChar", TLuaInterpreter::setRoomChar);
lua_register(pGlobalLua, "getRoomChar", TLuaInterpreter::getRoomChar);
lua_register(pGlobalLua, "setRoomCharColor", TLuaInterpreter::setRoomCharColor);
lua_register(pGlobalLua, "unsetRoomCharColor", TLuaInterpreter::unsetRoomCharColor);
lua_register(pGlobalLua, "getRoomCharColor", TLuaInterpreter::getRoomCharColor);
lua_register(pGlobalLua, "registerAnonymousEventHandler", TLuaInterpreter::registerAnonymousEventHandler);
lua_register(pGlobalLua, "saveMap", TLuaInterpreter::saveMap);
lua_register(pGlobalLua, "loadMap", TLuaInterpreter::loadMap);
lua_register(pGlobalLua, "setMainWindowSize", TLuaInterpreter::setMainWindowSize);
lua_register(pGlobalLua, "setAppStyleSheet", TLuaInterpreter::setAppStyleSheet);
lua_register(pGlobalLua, "setProfileStyleSheet", TLuaInterpreter::setProfileStyleSheet);
lua_register(pGlobalLua, "sendIrc", TLuaInterpreter::sendIrc);
lua_register(pGlobalLua, "getIrcNick", TLuaInterpreter::getIrcNick);
lua_register(pGlobalLua, "getIrcServer", TLuaInterpreter::getIrcServer);
lua_register(pGlobalLua, "getIrcChannels", TLuaInterpreter::getIrcChannels);
lua_register(pGlobalLua, "getIrcConnectedHost", TLuaInterpreter::getIrcConnectedHost);
lua_register(pGlobalLua, "setIrcNick", TLuaInterpreter::setIrcNick);
lua_register(pGlobalLua, "setIrcServer", TLuaInterpreter::setIrcServer);
lua_register(pGlobalLua, "setIrcChannels", TLuaInterpreter::setIrcChannels);
lua_register(pGlobalLua, "restartIrc", TLuaInterpreter::restartIrc);
lua_register(pGlobalLua, "connectToServer", TLuaInterpreter::connectToServer);
lua_register(pGlobalLua, "getRooms", TLuaInterpreter::getRooms);
lua_register(pGlobalLua, "createMapLabel", TLuaInterpreter::createMapLabel);
lua_register(pGlobalLua, "deleteMapLabel", TLuaInterpreter::deleteMapLabel);
lua_register(pGlobalLua, "highlightRoom", TLuaInterpreter::highlightRoom);
lua_register(pGlobalLua, "unHighlightRoom", TLuaInterpreter::unHighlightRoom);
lua_register(pGlobalLua, "getMapLabels", TLuaInterpreter::getMapLabels);
lua_register(pGlobalLua, "getMapLabel", TLuaInterpreter::getMapLabel);
lua_register(pGlobalLua, "lockExit", TLuaInterpreter::lockExit);
lua_register(pGlobalLua, "hasExitLock", TLuaInterpreter::hasExitLock);
lua_register(pGlobalLua, "lockSpecialExit", TLuaInterpreter::lockSpecialExit);
lua_register(pGlobalLua, "hasSpecialExitLock", TLuaInterpreter::hasSpecialExitLock);
lua_register(pGlobalLua, "setExitStub", TLuaInterpreter::setExitStub);
lua_register(pGlobalLua, "connectExitStub", TLuaInterpreter::connectExitStub);
lua_register(pGlobalLua, "getExitStubs", TLuaInterpreter::getExitStubs);
lua_register(pGlobalLua, "getExitStubs1", TLuaInterpreter::getExitStubs1);
lua_register(pGlobalLua, "setModulePriority", TLuaInterpreter::setModulePriority);
lua_register(pGlobalLua, "getModulePriority", TLuaInterpreter::getModulePriority);
lua_register(pGlobalLua, "updateMap", TLuaInterpreter::updateMap);
lua_register(pGlobalLua, "addMapEvent", TLuaInterpreter::addMapEvent);
lua_register(pGlobalLua, "removeMapEvent", TLuaInterpreter::removeMapEvent);
lua_register(pGlobalLua, "getMapEvents", TLuaInterpreter::getMapEvents);
lua_register(pGlobalLua, "addMapMenu", TLuaInterpreter::addMapMenu);
lua_register(pGlobalLua, "removeMapMenu", TLuaInterpreter::removeMapMenu);
lua_register(pGlobalLua, "getMapMenus", TLuaInterpreter::getMapMenus);
lua_register(pGlobalLua, "installPackage", TLuaInterpreter::installPackage);
lua_register(pGlobalLua, "installModule", TLuaInterpreter::installModule);
lua_register(pGlobalLua, "uninstallModule", TLuaInterpreter::uninstallModule);
lua_register(pGlobalLua, "reloadModule", TLuaInterpreter::reloadModule);
lua_register(pGlobalLua, "enableModuleSync", TLuaInterpreter::enableModuleSync);
lua_register(pGlobalLua, "disableModuleSync", TLuaInterpreter::disableModuleSync);
lua_register(pGlobalLua, "getModuleSync", TLuaInterpreter::getModuleSync);
lua_register(pGlobalLua, "createMapImageLabel", TLuaInterpreter::createMapImageLabel);
lua_register(pGlobalLua, "setMapZoom", TLuaInterpreter::setMapZoom);
lua_register(pGlobalLua, "uninstallPackage", TLuaInterpreter::uninstallPackage);
lua_register(pGlobalLua, "setExitWeight", TLuaInterpreter::setExitWeight);
lua_register(pGlobalLua, "setDoor", TLuaInterpreter::setDoor);
lua_register(pGlobalLua, "getDoors", TLuaInterpreter::getDoors);
lua_register(pGlobalLua, "getExitWeights", TLuaInterpreter::getExitWeights);
lua_register(pGlobalLua, "addSupportedTelnetOption", TLuaInterpreter::addSupportedTelnetOption);
lua_register(pGlobalLua, "setMergeTables", TLuaInterpreter::setMergeTables);
lua_register(pGlobalLua, "getModulePath", TLuaInterpreter::getModulePath);
lua_register(pGlobalLua, "getAreaExits", TLuaInterpreter::getAreaExits);
lua_register(pGlobalLua, "auditAreas", TLuaInterpreter::auditAreas);
lua_register(pGlobalLua, "sendMSDP", TLuaInterpreter::sendMSDP);
lua_register(pGlobalLua, "handleWindowResizeEvent", TLuaInterpreter::noop);
lua_register(pGlobalLua, "addCustomLine", TLuaInterpreter::addCustomLine);
lua_register(pGlobalLua, "removeCustomLine", TLuaInterpreter::removeCustomLine);
lua_register(pGlobalLua, "getCustomLines", TLuaInterpreter::getCustomLines);
lua_register(pGlobalLua, "getMudletVersion", TLuaInterpreter::getMudletVersion);
lua_register(pGlobalLua, "openWebPage", TLuaInterpreter::openWebPage);
lua_register(pGlobalLua, "getAllRoomEntrances", TLuaInterpreter::getAllRoomEntrances);
lua_register(pGlobalLua, "getRoomUserDataKeys", TLuaInterpreter::getRoomUserDataKeys);
lua_register(pGlobalLua, "getAllRoomUserData", TLuaInterpreter::getAllRoomUserData);
lua_register(pGlobalLua, "searchAreaUserData", TLuaInterpreter::searchAreaUserData);
lua_register(pGlobalLua, "getMapUserData", TLuaInterpreter::getMapUserData);
lua_register(pGlobalLua, "getAreaUserData", TLuaInterpreter::getAreaUserData);
lua_register(pGlobalLua, "setMapUserData", TLuaInterpreter::setMapUserData);
lua_register(pGlobalLua, "setAreaUserData", TLuaInterpreter::setAreaUserData);
lua_register(pGlobalLua, "getAllAreaUserData", TLuaInterpreter::getAllAreaUserData);
lua_register(pGlobalLua, "getAllMapUserData", TLuaInterpreter::getAllMapUserData);
lua_register(pGlobalLua, "clearAreaUserData", TLuaInterpreter::clearAreaUserData);
lua_register(pGlobalLua, "clearAreaUserDataItem", TLuaInterpreter::clearAreaUserDataItem);
lua_register(pGlobalLua, "clearMapUserData", TLuaInterpreter::clearMapUserData);
lua_register(pGlobalLua, "clearMapUserDataItem", TLuaInterpreter::clearMapUserDataItem);
lua_register(pGlobalLua, "setDefaultAreaVisible", TLuaInterpreter::setDefaultAreaVisible);
lua_register(pGlobalLua, "getProfileName", TLuaInterpreter::getProfileName);
lua_register(pGlobalLua, "getCommandSeparator", TLuaInterpreter::getCommandSeparator);
lua_register(pGlobalLua, "raiseGlobalEvent", TLuaInterpreter::raiseGlobalEvent);
lua_register(pGlobalLua, "saveProfile", TLuaInterpreter::saveProfile);
#ifdef QT_TEXTTOSPEECH_LIB
lua_register(pGlobalLua, "ttsSpeak", TLuaInterpreter::ttsSpeak);
lua_register(pGlobalLua, "ttsSkip", TLuaInterpreter::ttsSkip);
lua_register(pGlobalLua, "ttsSetRate", TLuaInterpreter::ttsSetRate);
lua_register(pGlobalLua, "ttsSetPitch", TLuaInterpreter::ttsSetPitch);
lua_register(pGlobalLua, "ttsSetVolume", TLuaInterpreter::ttsSetVolume);
lua_register(pGlobalLua, "ttsGetRate", TLuaInterpreter::ttsGetRate);
lua_register(pGlobalLua, "ttsGetPitch", TLuaInterpreter::ttsGetPitch);
lua_register(pGlobalLua, "ttsGetVolume", TLuaInterpreter::ttsGetVolume);
lua_register(pGlobalLua, "ttsSetVoiceByName", TLuaInterpreter::ttsSetVoiceByName);
lua_register(pGlobalLua, "ttsSetVoiceByIndex", TLuaInterpreter::ttsSetVoiceByIndex);
lua_register(pGlobalLua, "ttsGetCurrentVoice", TLuaInterpreter::ttsGetCurrentVoice);
lua_register(pGlobalLua, "ttsGetVoices", TLuaInterpreter::ttsGetVoices);
lua_register(pGlobalLua, "ttsQueue", TLuaInterpreter::ttsQueue);
lua_register(pGlobalLua, "ttsGetQueue", TLuaInterpreter::ttsGetQueue);
lua_register(pGlobalLua, "ttsPause", TLuaInterpreter::ttsPause);
lua_register(pGlobalLua, "ttsResume", TLuaInterpreter::ttsResume);
lua_register(pGlobalLua, "ttsClearQueue", TLuaInterpreter::ttsClearQueue);
lua_register(pGlobalLua, "ttsGetCurrentLine", TLuaInterpreter::ttsGetCurrentLine);
lua_register(pGlobalLua, "ttsGetState", TLuaInterpreter::ttsGetState);
#endif // QT_TEXTTOSPEECH_LIB
lua_register(pGlobalLua, "setServerEncoding", TLuaInterpreter::setServerEncoding);
lua_register(pGlobalLua, "getServerEncoding", TLuaInterpreter::getServerEncoding);
lua_register(pGlobalLua, "getServerEncodingsList", TLuaInterpreter::getServerEncodingsList);
lua_register(pGlobalLua, "alert", TLuaInterpreter::alert);
lua_register(pGlobalLua, "tempPromptTrigger", TLuaInterpreter::tempPromptTrigger);
lua_register(pGlobalLua, "permPromptTrigger", TLuaInterpreter::permPromptTrigger);
lua_register(pGlobalLua, "getColumnCount", TLuaInterpreter::getColumnCount);
lua_register(pGlobalLua, "getRowCount", TLuaInterpreter::getRowCount);
lua_register(pGlobalLua, "getOS", TLuaInterpreter::getOS);
lua_register(pGlobalLua, "getClipboardText", TLuaInterpreter::getClipboardText);
lua_register(pGlobalLua, "setClipboardText", TLuaInterpreter::setClipboardText);
lua_register(pGlobalLua, "getAvailableFonts", TLuaInterpreter::getAvailableFonts);
lua_register(pGlobalLua, "tempAnsiColorTrigger", TLuaInterpreter::tempAnsiColorTrigger);
lua_register(pGlobalLua, "setDiscordApplicationID", TLuaInterpreter::setDiscordApplicationID);
lua_register(pGlobalLua, "usingMudletsDiscordID", TLuaInterpreter::usingMudletsDiscordID);
lua_register(pGlobalLua, "setDiscordState", TLuaInterpreter::setDiscordState);
lua_register(pGlobalLua, "setDiscordGame", TLuaInterpreter::setDiscordGame);
lua_register(pGlobalLua, "setDiscordDetail", TLuaInterpreter::setDiscordDetail);
lua_register(pGlobalLua, "setDiscordLargeIcon", TLuaInterpreter::setDiscordLargeIcon);
lua_register(pGlobalLua, "setDiscordLargeIconText", TLuaInterpreter::setDiscordLargeIconText);
lua_register(pGlobalLua, "setDiscordSmallIcon", TLuaInterpreter::setDiscordSmallIcon);
lua_register(pGlobalLua, "setDiscordSmallIconText", TLuaInterpreter::setDiscordSmallIconText);
lua_register(pGlobalLua, "getDiscordState", TLuaInterpreter::getDiscordState);
lua_register(pGlobalLua, "getDiscordDetail", TLuaInterpreter::getDiscordDetail);
lua_register(pGlobalLua, "getDiscordLargeIcon", TLuaInterpreter::getDiscordLargeIcon);
lua_register(pGlobalLua, "getDiscordLargeIconText", TLuaInterpreter::getDiscordLargeIconText);
lua_register(pGlobalLua, "getDiscordSmallIcon", TLuaInterpreter::getDiscordSmallIcon);
lua_register(pGlobalLua, "getDiscordSmallIconText", TLuaInterpreter::getDiscordSmallIconText);
lua_register(pGlobalLua, "setDiscordRemainingEndTime", TLuaInterpreter::setDiscordRemainingEndTime);
lua_register(pGlobalLua, "setDiscordElapsedStartTime", TLuaInterpreter::setDiscordElapsedStartTime);
lua_register(pGlobalLua, "getDiscordTimeStamps", TLuaInterpreter::getDiscordTimeStamps);
lua_register(pGlobalLua, "setDiscordParty", TLuaInterpreter::setDiscordParty);
lua_register(pGlobalLua, "getDiscordParty", TLuaInterpreter::getDiscordParty);
lua_register(pGlobalLua, "getPlayerRoom", TLuaInterpreter::getPlayerRoom);
lua_register(pGlobalLua, "getSelection", TLuaInterpreter::getSelection);
lua_register(pGlobalLua, "getMapSelection", TLuaInterpreter::getMapSelection);
lua_register(pGlobalLua, "enableClickthrough", TLuaInterpreter::enableClickthrough);
lua_register(pGlobalLua, "disableClickthrough", TLuaInterpreter::disableClickthrough);
lua_register(pGlobalLua, "addWordToDictionary", TLuaInterpreter::addWordToDictionary);
lua_register(pGlobalLua, "removeWordFromDictionary", TLuaInterpreter::removeWordFromDictionary);
lua_register(pGlobalLua, "spellCheckWord", TLuaInterpreter::spellCheckWord);
lua_register(pGlobalLua, "spellSuggestWord", TLuaInterpreter::spellSuggestWord);
lua_register(pGlobalLua, "getDictionaryWordList", TLuaInterpreter::getDictionaryWordList);
lua_register(pGlobalLua, "getTextFormat", TLuaInterpreter::getTextFormat);
lua_register(pGlobalLua, "getWindowsCodepage", TLuaInterpreter::getWindowsCodepage);
lua_register(pGlobalLua, "getHTTP", TLuaInterpreter::getHTTP);
lua_register(pGlobalLua, "putHTTP", TLuaInterpreter::putHTTP);
lua_register(pGlobalLua, "postHTTP", TLuaInterpreter::postHTTP);
lua_register(pGlobalLua, "deleteHTTP", TLuaInterpreter::deleteHTTP);
lua_register(pGlobalLua, "getConnectionInfo", TLuaInterpreter::getConnectionInfo);
lua_register(pGlobalLua, "unzipAsync", TLuaInterpreter::unzipAsync);
lua_register(pGlobalLua, "setMapWindowTitle", TLuaInterpreter::setMapWindowTitle);
lua_register(pGlobalLua, "getMudletInfo", TLuaInterpreter::getMudletInfo);
lua_register(pGlobalLua, "getMapBackgroundColor", TLuaInterpreter::getMapBackgroundColor);
lua_register(pGlobalLua, "setMapBackgroundColor", TLuaInterpreter::setMapBackgroundColor);
lua_register(pGlobalLua, "getMapRoomExitsColor", TLuaInterpreter::getMapRoomExitsColor);
lua_register(pGlobalLua, "setMapRoomExitsColor", TLuaInterpreter::setMapRoomExitsColor);
lua_register(pGlobalLua, "showNotification", TLuaInterpreter::showNotification);
lua_register(pGlobalLua, "exportJsonMap", TLuaInterpreter::exportJsonMap);
lua_register(pGlobalLua, "importJsonMap", TLuaInterpreter::importJsonMap);
lua_register(pGlobalLua, "registerMapInfo", TLuaInterpreter::registerMapInfo);
lua_register(pGlobalLua, "killMapInfo", TLuaInterpreter::killMapInfo);
lua_register(pGlobalLua, "enableMapInfo", TLuaInterpreter::enableMapInfo);
lua_register(pGlobalLua, "disableMapInfo", TLuaInterpreter::disableMapInfo);
lua_register(pGlobalLua, "getProfileTabNumber", TLuaInterpreter::getProfileTabNumber);
// PLACEMARKER: End of main Lua interpreter functions registration
QStringList additionalLuaPaths;
QStringList additionalCPaths;
const auto appPath{QCoreApplication::applicationDirPath()};
const auto profilePath{mudlet::getMudletPath(mudlet::profileHomePath, hostName)};
// Allow for modules or libraries placed in the profile root directory:
additionalLuaPaths << QStringLiteral("%1/?.lua").arg(profilePath);
additionalLuaPaths << QStringLiteral("%1/?/init.lua").arg(profilePath);
#if defined(Q_OS_WIN32)
additionalCPaths << QStringLiteral("%1/?.dll").arg(profilePath);
#else
additionalCPaths << QStringLiteral("%1/?.so").arg(profilePath);
#endif
#if defined(Q_OS_LINUX)
// AppInstaller on Linux would like the C search path to also be set to
// a ./lib sub-directory of the current binary directory:
additionalCPaths << QStringLiteral("%1/lib/?.so").arg(appPath);
#elif defined(Q_OS_MAC)
// macOS app bundle would like the search path to also be set to the current
// binary directory for both modules and binary libraries:
additionalCPaths << QStringLiteral("%1/?.so").arg(appPath);
additionalLuaPaths << QStringLiteral("%1/?.lua").arg(appPath);
#elif defined(Q_OS_WIN32) && defined(INCLUDE_MAIN_BUILD_SYSTEM)
// For CI builds or users/developers using the setup-windows-sdk.ps1 method:
additionalCPaths << QStringLiteral("C:\\Qt\\Tools\\mingw730_32\\lib\\lua\\5.1\\?.dll");
#endif
insertNativeSeparatorsFunction(pGlobalLua);
luaL_dostring(pGlobalLua, QStringLiteral("package.cpath = toNativeSeparators([[%1;]]) .. package.cpath").arg(additionalCPaths.join(QLatin1Char(';'))).toUtf8().constData());
luaL_dostring(pGlobalLua, QStringLiteral("package.path = toNativeSeparators([[%1;]]) .. package.path").arg(additionalLuaPaths.join(QLatin1Char(';'))).toUtf8().constData());
/*
* For uses like this where we try more than one alternative, only include
* the consequence message for the LAST one (it doesn't make sense to show
* it in multiple messages about the same functional library with alternative
* names):
* Reminder for parameters to loadLuaModule:
* 1 - Store for the result messaged (required!)
* 2 - string to pass to the Lua requires(...) function (required, will be
* different for variants of the same module/library)
* 3 - string explaining what gets broken if (one of the alternatives of)
* the module/library is not loaded (optional, may be a null/empty
* QString if needed for a placeholder for following arguments)
* 4 - string describing the module (optional, for alternatives may be
* different and reflect the luarock name for the particular
* alternative, when ommitted or a null/empty QString the second
* argument is used instead)
* 5 - string used in Lua as the identifier for the loaded module/libary,
* passed as X in the lua 'X = require("name")' (optional, in which
* case nothing is put before the "require" in that usage and the module
* assumes whatever Lua name it offers).
*/
QQueue<QString> modLoadMessageQueue;
loadLuaModule(modLoadMessageQueue, QLatin1String("lfs"), tr("Probably will not be able to access Mudlet Lua code."), QLatin1String("lfs (Lua File System)"));
while (!modLoadMessageQueue.isEmpty()) {
mpHost->postMessage(modLoadMessageQueue.dequeue());
}
bool loaded = loadLuaModule(modLoadMessageQueue, QLatin1String("brimworks.zip"), QString(), QStringLiteral("lua-zip"), QStringLiteral("zip"));
if (!loaded) {
loadLuaModule(modLoadMessageQueue, QLatin1String("zip"), QString(), QStringLiteral("luazip"));
}
while (!modLoadMessageQueue.isEmpty()) {
mpHost->postMessage(modLoadMessageQueue.dequeue());
}
loadLuaModule(modLoadMessageQueue, QLatin1String("rex_pcre"), tr("Some functions may not be available."));
while (!modLoadMessageQueue.isEmpty()) {
mpHost->postMessage(modLoadMessageQueue.dequeue());
}
loadLuaModule(modLoadMessageQueue, QLatin1String("luasql.sqlite3"), tr("Database support will not be available."), QLatin1String("sqlite3"), QLatin1String("luasql"));
while (!modLoadMessageQueue.isEmpty()) {
mpHost->postMessage(modLoadMessageQueue.dequeue());
}
loaded = loadLuaModule(modLoadMessageQueue, QLatin1String("lua-utf8"), QString(), QLatin1String("lua-utf8"), QLatin1String("utf8"));
if (!loaded) {
loadLuaModule(modLoadMessageQueue, QLatin1String("utf8"), tr("utf8.* Lua functions won't be available."));
}
while (!modLoadMessageQueue.isEmpty()) {
mpHost->postMessage(modLoadMessageQueue.dequeue());
}
loadLuaModule(modLoadMessageQueue, QLatin1String("yajl"), tr("yajl.* Lua functions won't be available."), QString(), QLatin1String("yajl"));
while (!modLoadMessageQueue.isEmpty()) {
mpHost->postMessage(modLoadMessageQueue.dequeue());
}
QString tn = "atcp";
QStringList args;
set_lua_table(tn, args);
tn = "channel102";
set_lua_table(tn, args);
lua_pop(pGlobalLua, lua_gettop(pGlobalLua));
//FIXME make function call in destructor lua_close(L);
}
// No documentation available in wiki - internal function
// Creates a 'mudlet.translations' table with directions
void TLuaInterpreter::setupLanguageData()
{
lua_State* L = pGlobalLua;
// 'mudlet' global table
lua_createtable(L, 0, 1);
// language-specific table
lua_createtable(L, 0, 1);
// language-specific directions table
lua_createtable(L, 0, 24);
// this cannot be generated programmatically since lupdate needs the explicit string at extraction time
lua_pushstring(L, QCoreApplication::translate("directions", "north", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "north");
lua_pushstring(L, QCoreApplication::translate("directions", "n", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "n");
lua_pushstring(L, QCoreApplication::translate("directions", "east", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "east");
lua_pushstring(L, QCoreApplication::translate("directions", "e", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "s");
lua_pushstring(L, QCoreApplication::translate("directions", "south", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "south");
lua_pushstring(L, QCoreApplication::translate("directions", "s", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "s");
lua_pushstring(L, QCoreApplication::translate("directions", "west", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "west");
lua_pushstring(L, QCoreApplication::translate("directions", "w", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "w");
lua_pushstring(L, QCoreApplication::translate("directions", "northeast", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "northeast");
lua_pushstring(L, QCoreApplication::translate("directions", "ne", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "ne");
lua_pushstring(L, QCoreApplication::translate("directions", "southeast", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "southeast");
lua_pushstring(L, QCoreApplication::translate("directions", "se", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "se");
lua_pushstring(L, QCoreApplication::translate("directions", "southwest", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "southwest");
lua_pushstring(L, QCoreApplication::translate("directions", "sw", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "sw");
lua_pushstring(L, QCoreApplication::translate("directions", "northwest", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "northwest");
lua_pushstring(L, QCoreApplication::translate("directions", "nw", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "nw");
lua_pushstring(L, QCoreApplication::translate("directions", "in", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "in");
lua_pushstring(L, QCoreApplication::translate("directions", "i", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "i");
lua_pushstring(L, QCoreApplication::translate("directions", "out", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "out");
lua_pushstring(L, QCoreApplication::translate("directions", "o", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "o");
lua_pushstring(L, QCoreApplication::translate("directions", "up", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "up");
lua_pushstring(L, QCoreApplication::translate("directions", "u", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "u");
lua_pushstring(L, QCoreApplication::translate("directions", "down", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "down");
lua_pushstring(L, QCoreApplication::translate("directions", "d", "Entering this direction will move the player in the game").toUtf8().constData());
lua_setfield(L, -2, "d");
// finalize language-specific directions table
lua_setfield(L, -2, mudlet::self()->getInterfaceLanguage().toUtf8().constData());
lua_pushstring(L, mudlet::self()->getInterfaceLanguage().toUtf8().constData());
lua_setfield(L, -2, "interfacelanguage");
lua_setfield(L, -2, "translations");
lua_setglobal(L, "mudlet");
lua_pop(L, lua_gettop(L));
}
// No documentation available in wiki - internal function
// Initialised a slimmed-down Lua state just to run the indenter in a separate sandbox.
// The indenter by default pollutes the global environment with some utility functions
// and we don't want to tie ourselves to it by exposing them for scripting.
void TLuaInterpreter::initIndenterGlobals()
{
Q_ASSERT_X(!pIndenterState, "TLuaInterpreter::initIndenterGlobals()", "Indenter state is already initialized - re-initializing it is very expensive!");
pIndenterState.reset(newstate());
storeHostInLua(pIndenterState.get(), mpHost);
luaL_openlibs(pIndenterState.get());
lua_pushstring(pIndenterState.get(), "SESSION");
lua_pushnumber(pIndenterState.get(), mHostID);
lua_settable(pIndenterState.get(), LUA_GLOBALSINDEX);
lua_pushstring(pIndenterState.get(), "SCRIPT_NAME");
lua_pushstring(pIndenterState.get(), "Lua Indenter Interpreter");
lua_settable(pIndenterState.get(), LUA_GLOBALSINDEX);
lua_pushstring(pIndenterState.get(), "SCRIPT_ID");
lua_pushnumber(pIndenterState.get(), -2); // ID 2 is used to indicate that this is the indenter Lua interpreter
lua_settable(pIndenterState.get(), LUA_GLOBALSINDEX);
lua_register(pIndenterState.get(), "echo", TLuaInterpreter::echo);
lua_register(pIndenterState.get(), "tempTimer", TLuaInterpreter::tempTimer);
lua_register(pIndenterState.get(), "send", TLuaInterpreter::sendRaw);
lua_register(pIndenterState.get(), "debugc", TLuaInterpreter::debug);
// PLACEMARKER: End of indenter Lua interpreter functions registration
/*
* Additional paths for the lua/C package search paths - '?' (and any '.'s,
* in the file name, apart from the one before the filename extension) are
* handled specially! See: https://stackoverflow.com/q/31904392/4805858 :
*/
QStringList additionalLuaPaths;
QStringList additionalCPaths;
const QString appPath{QCoreApplication::applicationDirPath()};
#if defined(Q_OS_MACOS)
// macOS app bundle would like the search path for the binary modules to
// also be set to the current binary directory:
additionalCPaths << QStringLiteral("%1/?.so").arg(appPath);
#elif defined (Q_OS_LINUX)
// AppInstaller on Linux would like the search path for the binary modules
// to also be set to a lib sub-directory of the application directory:
additionalCPaths << QStringLiteral("%1/lib/?.so").arg(appPath);
#endif
insertNativeSeparatorsFunction(pIndenterState.get());
// 1 installed *nix case - probably not applicable to Windows
// "LUA_DEFAULT_PATH/?.lua" (if defined and not empty)
if (!QStringLiteral(LUA_DEFAULT_PATH).isEmpty()) {
additionalLuaPaths << QStringLiteral(LUA_DEFAULT_PATH "/?.lua");
}
// 2 AppImage (directory of executable) - not needed for Wndows:
// "<applicationDirectory>/?.lua"
#if ! defined (Q_OS_WIN32)
additionalLuaPaths << QStringLiteral("%1/?.lua").arg(appPath);
#endif
// 3 QMake shadow builds without CONFIG containing "debug_and_release" but
// with "debug_and_release_target" (default on most OS but NOT Windows):
// "<applicationDirectory>/../3rdparty/?.lua"
additionalLuaPaths << QStringLiteral("%1/../3rdparty/?.lua").arg(appPath);
// 4 QMake shadow builds with CONFIG containing "debug_and_release" AND
// "debug_and_release_target" (usually Windows):
// "<applicationDirectory>/../../3rdparty/?.lua"
additionalLuaPaths << QStringLiteral("%1/../../3rdparty/?.lua").arg(appPath);
// 5 CMake shadow builds
// "<applicationDirectory>/../../mudlet/3rdparty/?.lua"
additionalLuaPaths << QStringLiteral("%1/../../mudlet/3rdparty/?.lua").arg(appPath);
int error = luaL_dostring(pIndenterState.get(), QStringLiteral("package.path = toNativeSeparators([[%1;]] .. package.path)")
.arg(additionalLuaPaths.join(QLatin1Char(';'))).toUtf8().constData());
if (!error && !additionalCPaths.isEmpty()) {
error = luaL_dostring(pIndenterState.get(), QStringLiteral("package.cpath = toNativeSeparators([[%1;]] .. package.cpath)")
.arg(additionalCPaths.join(QLatin1Char(';'))).toUtf8().constData());
}
// clang-format off
if (!error) {
error = luaL_dostring(pIndenterState.get(), R"LUA(
require('lcf.workshop.base')
get_ast = request('!.lua.code.get_ast')
get_formatted_code = request('!.lua.code.ast_as_code')
)LUA");
// clang-format on
}
if (error) {
QString e = tr("No error message available from Lua.");
if (lua_isstring(pIndenterState.get(), -1)) {
e = tr("Lua error: %1.").arg(lua_tostring(pIndenterState.get(), -1));
}
QString msg = tr("[ ERROR ] - Cannot load code formatter, indenting functionality won't be available.\n");
msg.append(e);
mpHost->postMessage(msg);
}
lua_pop(pIndenterState.get(), lua_gettop(pIndenterState.get()));
}
// No documentation available in wiki - internal function called AFTER
// initLuaGlobals() {it depends on that to load up some Lua libraries, including
// the LFS "Lua File System" one first}:
void TLuaInterpreter::loadGlobal()
{
#if defined(Q_OS_WIN32)
loadUtf8Filenames();
#endif
setupLanguageData();
const QString executablePath{QCoreApplication::applicationDirPath()};
// Initialise the list of path and file names so that
// getMudletLuaDefaultPaths() can report them later if asked:
mPossiblePaths = QStringList{
#if defined(Q_OS_MACOS)
// Load relatively to MacOS inside Resources when we're in a .app
// bundle, as mudlet-lua always gets copied in by the build script into
// the bundle for the Mac installer build:
QStringLiteral("%1/../Resources/mudlet-lua/lua/LuaGlobal.lua").arg(executablePath),
#endif
// For the installer we put the lua files under the executable's
// location. This is the case for the Windows install:
QDir::toNativeSeparators(QStringLiteral("%1/mudlet-lua/lua/LuaGlobal.lua").arg(executablePath)),
// Although a no-op for an in source build an additional "../src/"
// allows location of lua code when object code is in a directory
// alongside the src directory as occurs using Qt Creator "Shadow
// Builds":
QDir::toNativeSeparators(QStringLiteral("%1/../src/mudlet-lua/lua/LuaGlobal.lua").arg(executablePath)),
// Windows builds (or others where the qmake project file has CONFIG
// containing debug_and_release AND debug_and_release_target options)
// may be an additional sub-directory down:
QDir::toNativeSeparators(QStringLiteral("%1/../../src/mudlet-lua/lua/LuaGlobal.lua").arg(executablePath)),
// CMake builds done from Qt Creator tend to make their build directory
// be in a "out-of-source" (the more common name for what Qt calls
// "Shadow Builds") on the same level as the top level CMakeList.txt
// project file - which is one level up compared to the QMake case.
// and in a "src" subdirectory (to match the relative source file
// location to that top-level project file) of the main project
// "mudlet" directory:
QDir::toNativeSeparators(QStringLiteral("%1/../../mudlet/src/mudlet-lua/lua/LuaGlobal.lua").arg(executablePath))
};
// Although it is relatively easy to detect whether something is #define d
// it is not so easy to detect what it contains at the preprocessor stage,
// so leave checking for its contents to run-time - this one is the one
// for Linux/FreeBSD where the read-only shared Lua files go into the
// /usr/share part of the file-system:
if (!QStringLiteral(LUA_DEFAULT_PATH).isEmpty()) {
mPossiblePaths << QDir::toNativeSeparators(QStringLiteral(LUA_DEFAULT_PATH "/LuaGlobal.lua"));
};
QStringList failedMessages{};
// uncomment the following to enable some debugging texts in the LuaGlobal.lua script:
// luaL_dostring(pGlobalLua, QStringLiteral("debugLoading = true").toUtf8().constData());
#if defined(Q_OS_WIN32)
// Needed to enable permissions checks on NTFS file systems - normally
// turned off for performance reasons:
extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
#endif
int error;
for (const auto& pathFileName : qAsConst(mPossiblePaths)) {
if (!(QFileInfo::exists(pathFileName))) {
failedMessages << tr("%1 (doesn't exist)", "This file doesn't exist").arg(pathFileName);
continue;
}
if (!(QFileInfo(pathFileName).isFile())) {
failedMessages << tr("%1 (isn't a file or symlink to a file)").arg(pathFileName);
continue;
}
#if defined(Q_OS_WIN32)
// Turn on permission checking on NTFS file systems
qt_ntfs_permission_lookup++;
#endif
if (!(QFileInfo(pathFileName).isReadable())) {
failedMessages << tr("%1 (isn't a readable file or symlink to a readable file)").arg(pathFileName);
continue;
}
#if defined(Q_OS_WIN32)
// Turn off permission checking
qt_ntfs_permission_lookup--;
#endif
// Leave a global variable set to the path so we can use it to find the
// other files around it - the script will convert the directory
// separators as necessary:
Q_ASSERT_X(!pathFileName.isEmpty(), "TLuaInterpreter::loadGlobal()", "trying to call QFileInfo(path).absolutePath() when path is empty");
luaL_dostring(pGlobalLua, QStringLiteral("luaGlobalPath = \"%1\"").arg(QFileInfo(pathFileName).absolutePath()).toUtf8().constData());
// load via Qt so UTF8 paths work on Windows - Lua can't handle it
auto luaGlobal = readScriptFile(pathFileName);
if (luaGlobal.isEmpty()) {
failedMessages << tr("%1 (couldn't read file)", "This file could not be read for some reason (for example, no permission)").arg(pathFileName);
continue;
}
error = luaL_dostring(pGlobalLua, luaGlobal.toUtf8().constData());
if (error == 0) {
mpHost->postMessage(tr("[ OK ] - Mudlet-lua API & Geyser Layout manager loaded."));
return;
}
qWarning() << "TLuaInterpreter::loadGlobal() loading " << pathFileName << " failed: " << lua_tostring(pGlobalLua, -1);
failedMessages << QStringLiteral("%1 (%2)").arg(pathFileName, lua_tostring(pGlobalLua, -1));
}
mpHost->postMessage(tr("[ ERROR ] - Couldn't find, load and successfully run LuaGlobal.lua - your Mudlet is broken!\nTried these locations:\n%1").arg(failedMessages.join(QChar::LineFeed)));
}
// No documentation available in wiki - internal function
// Returns contents of the file or empty string if it couldn't be read
QString TLuaInterpreter::readScriptFile(const QString& path) const
{
QFile file(path);
if (!file.open(QFile::ReadOnly | QFile::Text)) {
return QString();
}
QTextStream in(&file);
QString text = in.readAll();
file.close();
return text;
}
#if defined(Q_OS_WIN32)
// No documentation available in wiki - internal function
// loads utf8_filenames from the resource system directly so it is not affected by
// non-ASCII characters that might be present in the users filesystem
void TLuaInterpreter::loadUtf8Filenames()
{
auto path = QStringLiteral(":/mudlet-lua/lua/utf8_filenames.lua");
auto text = readScriptFile(path);
if (text.isEmpty()) {
qWarning() << "TLuaInterpreter::loadUtf8Filenames() ERROR: couldn't read file: " << path;
return;
}
if (mpHost->getLuaInterpreter()->compileAndExecuteScript(text)) {
qDebug() << "TLuaInterpreter::loadUtf8Filenames() - patched Lua IO functions to work on Windows with UTF8";
} else {
qWarning() << "TLuaInterpreter::loadUtf8Filenames() ERROR: there was an error running the script";
}
}
#endif
// No documentation available in wiki - internal function
std::pair<int, QString> TLuaInterpreter::createPermScript(const QString& name, const QString& parent, const QString& luaCode)
{
TScript* pS;
if (parent.isEmpty()) {
pS = new TScript(QStringLiteral("newPermScriptWithoutAnId"), mpHost);
} else {
// FIXME: There can be more than one script with the same name - we will
// use only the FIRST one for now, but we really ought to enhance the
// API to handle more than one potential parent with the same name:
auto ids = mpHost->getScriptUnit()->findScriptId(parent);
auto pParentScript = mpHost->getScriptUnit()->getScript(ids.value(0, -1));
if (!pParentScript) {
return std::make_pair(-1, QStringLiteral("parent '%1' not found").arg(parent)); //parent not found
}
pS = new TScript(pParentScript, mpHost);
}
pS->setIsFolder((luaCode.isEmpty()));
pS->setName(name);
// This will lead to the generation of the id number:
mpHost->getScriptUnit()->registerScript(pS);
if (!pS->setScript(luaCode)) {
QString errMsg = pS->getError();
delete pS;
return std::make_pair(-1, QStringLiteral("unable to compile \"%1\", reason: %2").arg(luaCode, errMsg));
}
int id = pS->getID();
pS->setIsActive(false);
mpHost->mpEditorDialog->mNeedUpdateData = true;
return std::make_pair(id, QString());
}
// No documentation available in wiki - internal function
std::pair<int, QString> TLuaInterpreter::setScriptCode(QString& name, const QString& luaCode, int pos)
{
if (name.isEmpty()) {
return std::make_pair(-1, QStringLiteral("cannot have an empty string as name"));
}
auto ids = mpHost->getScriptUnit()->findScriptId(name);
TScript* pS = mpHost->getScriptUnit()->getScript(ids.value(pos, -1));
if (!pS) {
return std::make_pair(-1, QStringLiteral("script \"%1\" at position \"%2\" not found").arg(name).arg(++pos)); //script not found
}
auto oldCode = pS->getScript();
if (!pS->setScript(luaCode)) {
QString errMsg = pS->getError();
pS->setScript(oldCode);
return std::make_pair(-1, QStringLiteral("unable to compile \"%1\" at position \"%2\", reason: %3").arg(luaCode).arg(++pos).arg(errMsg));
}
pS->setScript(luaCode);
int id = pS->getID();
mpHost->mpEditorDialog->writeScript(id);
return std::make_pair(id, QString());
}
// No documentation available in wiki - internal function
std::pair<int, QString> TLuaInterpreter::startPermTimer(const QString& name, const QString& parent, double timeout, const QString& function)
{
QTime time = QTime(0, 0, 0, 0).addMSecs(qRound(timeout * 1000));
TTimer* pT;
if (parent.isEmpty()) {
pT = new TTimer(QStringLiteral("newPermTimerWithoutAnId"), time, mpHost);
} else {
// FIXME: There can be more than one timer with the same name - we will
// use only the FIRST one for now, but we really ought to enhance the
// API to handle more than one potential parent with the same name:
auto pParentTimer = mpHost->getTimerUnit()->findFirstTimer(parent);
if (!pParentTimer) {
return std::make_pair(-1, QStringLiteral("parent '%1' not found").arg(parent));
}
pT = new TTimer(pParentTimer, mpHost);
}
pT->setTime(time);
pT->setIsFolder((timeout == 0 && function.isEmpty()));
pT->setTemporary(false);
// The name should be set after isTempTimer, as that is faster.
// Also for perminent timers it is easier to debug if it is set before
// registration:
pT->setName(name);
// This will lead to the generation of the id number:
mpHost->getTimerUnit()->registerTimer(pT);
if (!pT->setScript(function)) {
QString errMsg = pT->getError();
// Apparently this will call the TTimer::unregisterTimer(...) method:
delete pT;
return std::make_pair(-1, QStringLiteral("unable to compile \"%1\", reason: %2").arg(function, errMsg));
}
pT->setIsActive(false);
mpHost->mpEditorDialog->mNeedUpdateData = true;
return std::make_pair(pT->getID(), QString());
}
// No documentation available in wiki - internal function
QPair<int, QString> TLuaInterpreter::startTempTimer(double timeout, const QString& function, const bool repeating)
{
QTime time = QTime(0,0,0,0).addMSecs(qRound(timeout * 1000));
auto* pT = new TTimer(QStringLiteral("newTempTimerWithoutAnId"), time, mpHost, repeating);
pT->setTime(time);
pT->setIsFolder(false);
pT->setTemporary(true);
// This call leads to the allocation of an ID number - and its use as the
// name for temporary timers:
mpHost->getTimerUnit()->registerTimer(pT);
if (!pT->setScript(function)) {
QString errMsg = pT->getError();
// Apparently this will call the TTimer::unregisterTimer(...) method:
delete pT;
return qMakePair(-1, QStringLiteral("unable to compile \"%1\", reason: %2").arg(function, errMsg));
}
int id = pT->getID();
pT->setIsActive(true);
pT->enableTimer(id);
return qMakePair(id, QString());
}
// No documentation available in wiki - internal function
std::pair<int, QString> TLuaInterpreter::startPermAlias(const QString& name, const QString& parent, const QString& regex, const QString& function)
{
TAlias* pT;
if (parent.isEmpty()) {
pT = new TAlias("a", mpHost);
} else {
TAlias* pP = mpHost->getAliasUnit()->findFirstAlias(parent);
if (!pP) {
return std::make_pair(-1, QStringLiteral("parent '%1' not found").arg(parent));
}
pT = new TAlias(pP, mpHost);
}
pT->setRegexCode(regex);
pT->setIsFolder((regex.isEmpty() && function.isEmpty()));
pT->setIsActive(true);
pT->setTemporary(false);
pT->registerAlias();
pT->setScript(function);
pT->setName(name);
mpHost->mpEditorDialog->mNeedUpdateData = true;
return std::make_pair(pT->getID(), QString());
}
// No documentation available in wiki - internal function
int TLuaInterpreter::startTempAlias(const QString& regex, const QString& function)
{
TAlias* pT;
pT = new TAlias("a", mpHost);
pT->setRegexCode(regex);
pT->setIsFolder(false);
pT->setIsActive(true);
pT->setTemporary(true);
pT->registerAlias();
pT->setScript(function);
int id = pT->getID();
pT->setName(QString::number(id));
return id;
}
// No documentation available in wiki - internal function
std::pair<int, QString> TLuaInterpreter::startPermKey(QString& name, QString& parent, int& keycode, int& modifier, QString& function)
{
TKey* pT;
if (parent.isEmpty()) {
pT = new TKey("a", mpHost); // The use of "a" seems a bit arbitary...!
} else {
TKey* pP = mpHost->getKeyUnit()->findFirstKey(parent);
if (!pP) {
return std::make_pair(-1, QStringLiteral("parent '%1' not found").arg(parent));
}
pT = new TKey(pP, mpHost);
}
pT->setKeyCode(keycode);
pT->setKeyModifiers(modifier);
pT->setIsFolder(keycode == -1);
pT->setIsActive(keycode != -1); // Folders (keycode == -1) start as inactive
pT->setTemporary(false);
pT->registerKey();
// CHECK: The lua code in function could fail to compile - but there is no feedback here to the caller.
pT->setScript(function);
pT->setName(name);
mpHost->mpEditorDialog->mNeedUpdateData = true;
return std::make_pair(pT->getID(), QString());
}
// No documentation available in wiki - internal function
int TLuaInterpreter::startTempKey(int& modifier, int& keycode, const QString& function)
{
TKey* pT;
pT = new TKey("a", mpHost);
pT->setKeyCode(keycode);
pT->setKeyModifiers(modifier);
pT->setIsFolder(false);
pT->setIsActive(true);
pT->setTemporary(true);
pT->registerKey();
pT->setScript(function);
int id = pT->getID();
pT->setName(QString::number(id));
return id;
}
// No documentation available in wiki - internal function
int TLuaInterpreter::startTempExactMatchTrigger(const QString& regex, const QString& function, int expiryCount)
{
TTrigger* pT;
QStringList sList;
sList << regex;
QList<int> propertyList;
propertyList << REGEX_EXACT_MATCH;
pT = new TTrigger("a", sList, propertyList, false, mpHost);
pT->setIsFolder(false);
pT->setIsActive(true);
pT->setTemporary(true);
pT->registerTrigger();
pT->setScript(function);
int id = pT->getID();
pT->setName(QString::number(id));
pT->setExpiryCount(expiryCount);
return id;
}
// No documentation available in wiki - internal function
int TLuaInterpreter::startTempBeginOfLineTrigger(const QString& regex, const QString& function, int expiryCount)
{
TTrigger* pT;
QStringList sList;
sList << regex;
QList<int> propertyList;
propertyList << REGEX_BEGIN_OF_LINE_SUBSTRING;
pT = new TTrigger("a", sList, propertyList, false, mpHost);
pT->setIsFolder(false);
pT->setIsActive(true);
pT->setTemporary(true);
pT->registerTrigger();
pT->setScript(function);
int id = pT->getID();
pT->setName(QString::number(id));
pT->setExpiryCount(expiryCount);
return id;
}
// No documentation available in wiki - internal function
int TLuaInterpreter::startTempTrigger(const QString& regex, const QString& function, int expiryCount)
{
TTrigger* pT;
QStringList sList;
sList << regex;
QList<int> propertyList;
propertyList << REGEX_SUBSTRING; // substring trigger is default
pT = new TTrigger("a", sList, propertyList, false, mpHost);
pT->setIsFolder(false);
pT->setIsActive(true);
pT->setTemporary(true);
pT->registerTrigger();
pT->setScript(function);
int id = pT->getID();
pT->setName(QString::number(id));
pT->setExpiryCount(expiryCount);
return id;
}
// No documentation available in wiki - internal function
int TLuaInterpreter::startTempPromptTrigger(const QString& function, int expiryCount)
{
TTrigger* pT;
QStringList sList = {QString()};
QList<int> propertyList = {REGEX_PROMPT};
pT = new TTrigger("a", sList, propertyList, false, mpHost);
pT->setIsFolder(false);
pT->setIsActive(true);
pT->setTemporary(true);
pT->registerTrigger();
pT->setScript(function);
int id = pT->getID();
pT->setName(QString::number(id));
pT->setExpiryCount(expiryCount);
return id;
}
// No documentation available in wiki - internal function
int TLuaInterpreter::startTempLineTrigger(int from, int howmany, const QString& function, int expiryCount)
{
TTrigger* pT;
// QStringList sList;
// QList<int> propertyList;
// propertyList << REGEX_SUBSTRING;// substring trigger is default
// pT = new TTrigger("a", sList, propertyList, false, mpHost );
pT = new TTrigger(nullptr, mpHost);
pT->setIsFolder(false);
pT->setIsActive(true);
pT->setTemporary(true);
pT->setIsLineTrigger(true);
pT->setStartOfLineDelta(from);
pT->setLineDelta(howmany);
pT->registerTrigger();
pT->setScript(function);
int id = pT->getID();
pT->setName(QString::number(id));
pT->setExpiryCount(expiryCount);
return id;
}
// No documentation available in wiki - internal function
int TLuaInterpreter::startTempColorTrigger(int fg, int bg, const QString& function, int expiryCount)
{
TTrigger* pT;
// QStringList sList;
// QList<int> propertyList;
// propertyList << REGEX_SUBSTRING;// substring trigger is default
// pT = new TTrigger("a", sList, propertyList, false, mpHost );
pT = new TTrigger(nullptr, mpHost);
pT->setIsFolder(false);
pT->setIsActive(true);
pT->setTemporary(true);
pT->setupTmpColorTrigger(fg, bg);
pT->registerTrigger();
pT->setScript(function);
int id = pT->getID();
pT->setName(QString::number(id));
pT->setExpiryCount(expiryCount);
return id;
}
// No documentation available in wiki - internal function
int TLuaInterpreter::startTempRegexTrigger(const QString& regex, const QString& function, int expiryCount)
{
TTrigger* pT;
QStringList sList;
sList << regex;
QList<int> propertyList;
propertyList << REGEX_PERL; // substring trigger is default
pT = new TTrigger("a", sList, propertyList, false, mpHost);
pT->setIsFolder(false);
pT->setIsActive(true);
pT->setTemporary(true);
pT->registerTrigger();
pT->setScript(function);
int id = pT->getID();
pT->setName(QString::number(id));
pT->setExpiryCount(expiryCount);
return id;
}
// No documentation available in wiki - internal function
std::pair<int, QString> TLuaInterpreter::startPermRegexTrigger(const QString& name, const QString& parent, QStringList& regexList, const QString& function)
{
TTrigger* pT;
QList<int> propertyList;
for (int i = 0; i < regexList.size(); i++) {
propertyList << REGEX_PERL;
}
if (parent.isEmpty()) {
pT = new TTrigger("a", regexList, propertyList, (regexList.size() > 1), mpHost);
} else {
TTrigger* pP = mpHost->getTriggerUnit()->findTrigger(parent);
if (!pP) {
return std::pair(-1, QStringLiteral("parent '%1' not found").arg(parent));
}
pT = new TTrigger(pP, mpHost);
pT->setRegexCodeList(regexList, propertyList);
}
pT->setIsFolder(regexList.empty());
pT->setIsActive(true);
pT->setTemporary(false);
pT->registerTrigger();
pT->setScript(function);
pT->setName(name);
mpHost->mpEditorDialog->mNeedUpdateData = true;
return std::pair(pT->getID(), QString());
}
// No documentation available in wiki - internal function
std::pair<int, QString> TLuaInterpreter::startPermBeginOfLineStringTrigger(const QString& name, const QString& parent, QStringList& regexList, const QString& function)
{
TTrigger* pT;
QList<int> propertyList;
for (int i = 0; i < regexList.size(); i++) {
propertyList << REGEX_BEGIN_OF_LINE_SUBSTRING;
}
if (parent.isEmpty()) {
pT = new TTrigger("a", regexList, propertyList, (regexList.size() > 1), mpHost);
} else {
TTrigger* pP = mpHost->getTriggerUnit()->findTrigger(parent);
if (!pP) {
return std::make_pair(-1, QStringLiteral("parent '%1' not found").arg(parent));
}
pT = new TTrigger(pP, mpHost);
pT->setRegexCodeList(regexList, propertyList);
}
pT->setIsFolder(regexList.empty());
pT->setIsActive(true);
pT->setTemporary(false);
pT->registerTrigger();
pT->setScript(function);
pT->setName(name);
mpHost->mpEditorDialog->mNeedUpdateData = true;
return std::pair(pT->getID(), QString());
}
// No documentation available in wiki - internal function
std::pair<int, QString> TLuaInterpreter::startPermSubstringTrigger(const QString& name, const QString& parent, const QStringList& regexList, const QString& function)
{
TTrigger* pT;
QList<int> propertyList;
for (int i = 0; i < regexList.size(); i++) {
propertyList << REGEX_SUBSTRING;
}
if (parent.isEmpty()) {
pT = new TTrigger("a", regexList, propertyList, (regexList.size() > 1), mpHost);
} else {
TTrigger* pP = mpHost->getTriggerUnit()->findTrigger(parent);
if (!pP) {
return std::make_pair(-1, QStringLiteral("parent '%1' not found").arg(parent));
}
pT = new TTrigger(pP, mpHost);
pT->setRegexCodeList(regexList, propertyList);
}
pT->setIsFolder(regexList.empty());
pT->setIsActive(true);
pT->setTemporary(false);
pT->registerTrigger();
pT->setScript(function);
pT->setName(name);
mpHost->mpEditorDialog->mNeedUpdateData = true;
return std::make_pair(pT->getID(), QString());
}
// No documentation available in wiki - internal function
std::pair<int, QString> TLuaInterpreter::startPermPromptTrigger(const QString& name, const QString& parent, const QString& function)
{
TTrigger* pT;
QList<int> propertyList = {REGEX_PROMPT};
QStringList regexList = {QString()};
if (parent.isEmpty()) {
pT = new TTrigger("a", regexList, propertyList, false, mpHost);
} else {
TTrigger* pP = mpHost->getTriggerUnit()->findTrigger(parent);
if (!pP) {
return std::make_pair(-1, QStringLiteral("parent '%1' not found").arg(parent));
}
pT = new TTrigger(pP, mpHost);
pT->setRegexCodeList(regexList, propertyList);
}
pT->setIsFolder(false);
pT->setIsActive(true);
pT->setTemporary(false);
pT->registerTrigger();
pT->setScript(function);
pT->setName(name);
mpHost->mpEditorDialog->mNeedUpdateData = true;
return std::make_pair(pT->getID(), QString());
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#alert
int TLuaInterpreter::alert(lua_State* L)
{
double luaAlertDuration = 0.0;
if (lua_gettop(L) > 0) {
luaAlertDuration = getVerifiedDouble(L, __func__, 1, "alert duration in seconds");
if (luaAlertDuration < 0.000) {
lua_pushstring(L, "alert: duration, in seconds, is optional but if given must be zero or greater.");
return lua_error(L);
}
}
// QApplication::alert expects milliseconds, not seconds
QApplication::alert(mudlet::self(), qRound(luaAlertDuration * 1000.0));
return 0;
}
static int host_key = 0;
// No documentation available in wiki - internal function
static void storeHostInLua(lua_State* L, Host* h)
{
lua_pushlightuserdata(L, &host_key); // 1 - push unique key
lua_pushlightuserdata(L, h); // 2 - push host ptr
lua_rawset(L, LUA_REGISTRYINDEX); // 0 - register[key] = host
}
// No documentation available in wiki - internal function
Host& getHostFromLua(lua_State* L)
{
lua_pushlightuserdata(L, &host_key); // 1 - push unique key
lua_rawget(L, LUA_REGISTRYINDEX); // 1 - pop key, push host ptr
auto* h = static_cast<Host*>(lua_touserdata(L, -1)); // 1 - get host ptr
lua_pop(L, 1); // 0 - pop host ptr
assert(h);
return *h;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getColumnCount
int TLuaInterpreter::getColumnCount(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
int columns;
auto console = CONSOLE(L, windowName);
columns = console->mUpperPane->getColumnCount();
lua_pushnumber(L, columns);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getRowCount
int TLuaInterpreter::getRowCount(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
int rows;
auto console = CONSOLE(L, windowName);
rows = console->mUpperPane->getRowCount();
lua_pushnumber(L, rows);
return 1;
}
// No documentation available in wiki - internal function
// Used to unref lua objects in the registry to avoid memory leaks
// i.e. Unrefing tables passed into TLabel's event parameters.
void TLuaInterpreter::freeLuaRegistryIndex(int index) {
luaL_unref(pGlobalLua, LUA_REGISTRYINDEX, index);
}
// No documentation available in wiki - internal function
// Looks for argument types in an 'event' that have stored
// data in the lua registry, and frees this data.
void TLuaInterpreter::freeAllInLuaRegistry(TEvent event)
{
for (int i = 0; i < event.mArgumentList.size(); i++) {
if (event.mArgumentTypeList.at(i) == ARGUMENT_TYPE_TABLE || event.mArgumentTypeList.at(i) == ARGUMENT_TYPE_FUNCTION)
{
freeLuaRegistryIndex(event.mArgumentList.at(i).toInt());
}
}
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getMapSelection
int TLuaInterpreter::getMapSelection(lua_State* L)
{
Host* pHost = &getHostFromLua(L);
if (!pHost || !pHost->mpMap || !pHost->mpMap->mpMapper || !pHost->mpMap->mpMapper->mp2dMap) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
lua_newtable(L);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QList<int> selectionRoomsList{pHost->mpMap->mpMapper->mp2dMap->mMultiSelectionSet.begin(),
pHost->mpMap->mpMapper->mp2dMap->mMultiSelectionSet.end()};
#else
QList<int> selectionRoomsList{pHost->mpMap->mpMapper->mp2dMap->mMultiSelectionSet.toList()};
#endif
if (!selectionRoomsList.isEmpty()) {
if (selectionRoomsList.count() > 1) {
std::sort(selectionRoomsList.begin(), selectionRoomsList.end());
}
lua_pushstring(L, "center");
lua_pushnumber(L, pHost->mpMap->mpMapper->mp2dMap->getCenterSelectedRoomId());
lua_settable(L, -3);
lua_pushstring(L, "rooms");
lua_newtable(L);
for (int i = 0, total = selectionRoomsList.size(); i < total; ++i) {
lua_pushnumber(L, i + 1);
lua_pushnumber(L, selectionRoomsList.at(i));
lua_settable(L, -3);
}
lua_settable(L, -3);
}
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#enableClickthrough
int TLuaInterpreter::enableClickthrough(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
Host& host = getHostFromLua(L);
host.setClickthrough(windowName, true);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#disableClickthrough
int TLuaInterpreter::disableClickthrough(lua_State* L)
{
QString windowName {WINDOW_NAME(L, 1)};
Host& host = getHostFromLua(L);
host.setClickthrough(windowName, false);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#addWordToDictionary
int TLuaInterpreter::addWordToDictionary(lua_State* L)
{
Host& host = getHostFromLua(L);
bool hasUserDictionary = false;
bool hasSharedDictionary = false;
host.getUserDictionaryOptions(hasUserDictionary, hasSharedDictionary);
if (!hasUserDictionary) {
return warnArgumentValue(L, __func__, "no user dictionary enabled in the preferences for this profile");
}
QString text = getVerifiedString(L, __func__, 1, "word");
QPair<bool, QString> result = host.mpConsole->addWordToSet(text);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second.toUtf8().constData());
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#removeWordFromDictionary
int TLuaInterpreter::removeWordFromDictionary(lua_State* L)
{
Host& host = getHostFromLua(L);
bool hasUserDictionary = false;
bool hasSharedDictionary = false;
host.getUserDictionaryOptions(hasUserDictionary, hasSharedDictionary);
if (!hasUserDictionary) {
return warnArgumentValue(L, __func__, "no user dictionary enabled in the preferences for this profile");
}
QString text = getVerifiedString(L, __func__, 1, "word");
QPair<bool, QString> result = host.mpConsole->removeWordFromSet(text);
if (!result.first) {
return warnArgumentValue(L, __func__, result.second.toUtf8().constData());
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#spellCheckWord
int TLuaInterpreter::spellCheckWord(lua_State* L)
{
Host& host = getHostFromLua(L);
bool hasUserDictionary = false;
bool hasSharedDictionary = false;
host.getUserDictionaryOptions(hasUserDictionary, hasSharedDictionary);
QString text = getVerifiedString(L, __func__, 1, "word");
bool useUserDictionary = false;
if (lua_gettop(L) > 1) {
useUserDictionary = getVerifiedBool(L, __func__, 2, "check profile dictionary {use 'false' or omit to check against system dictionary}", true);
if (useUserDictionary && !hasUserDictionary) {
return warnArgumentValue(L, __func__, "no user dictionary enabled in the preferences for this profile");
}
}
Hunhandle* handle = nullptr;
QByteArray encodedText;
if (useUserDictionary) {
handle = host.mpConsole->getHunspellHandle_user();
encodedText = text.toUtf8();
} else {
handle = host.mpConsole->getHunspellHandle_system();
if (!handle) {
return warnArgumentValue(L, __func__,
"no main dictionaries found: Mudlet has not been able to find any dictionary files to use so is unable to check your word");
}
encodedText = host.mpConsole->getHunspellCodec_system()->fromUnicode(text);
}
// CHECKME: Is there any danger of contention here - do we need to get mudlet::mDictionaryReadWriteLock locked for reading if we are accessing the shared user dictionary?
lua_pushboolean(L, Hunspell_spell(handle, text.toUtf8().constData()));
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#spellSuggestWord
int TLuaInterpreter::spellSuggestWord(lua_State* L)
{
Host& host = getHostFromLua(L);
bool hasUserDictionary = false;
bool hasSharedDictionary = false;
host.getUserDictionaryOptions(hasUserDictionary, hasSharedDictionary);
QString text = getVerifiedString(L, __func__, 1, "word");
bool useUserDictionary = false;
if (lua_gettop(L) > 1) {
useUserDictionary = getVerifiedBool(L, __func__, 2, "check profile dictionary {use 'false' or omit to check against system dictionary}", true);
if (useUserDictionary && !hasUserDictionary) {
return warnArgumentValue(L, __func__, "no user dictionary enabled in the preferences for this profile");
}
}
char **wordList;
size_t wordCount = 0;
Hunhandle* handle = nullptr;
QByteArray encodedText;
if (useUserDictionary) {
handle = host.mpConsole->getHunspellHandle_user();
encodedText = text.toUtf8();
} else {
handle = host.mpConsole->getHunspellHandle_system();
if (!handle) {
return warnArgumentValue(L, __func__,
"no main dictionaries found: Mudlet has not been able to find any dictionary files to use so is unable to make suggestions for your word");
}
encodedText = host.mpConsole->getHunspellCodec_system()->fromUnicode(text);
}
// CHECKME: Is there any danger of contention here - do we need to get mudlet::mDictionaryReadWriteLock locked for reading if we are accessing the shared user dictionary?
wordCount = Hunspell_suggest(handle, &wordList, encodedText.constData());
lua_newtable(L);
for (size_t i = 0; i < wordCount; ++i) {
lua_pushnumber(L, i+1);
lua_pushstring(L, wordList[i]);
lua_settable(L, -3);
}
Hunspell_free_list(handle, &wordList, wordCount);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getDictionaryWordList
int TLuaInterpreter::getDictionaryWordList(lua_State* L)
{
Host& host = getHostFromLua(L);
bool hasUserDictionary = false;
bool hasSharedDictionary = false;
host.getUserDictionaryOptions(hasUserDictionary, hasSharedDictionary);
if (!hasUserDictionary) {
return warnArgumentValue(L, __func__, "no user dictionary enabled in the preferences for this profile");
}
// This may stall if this is accessing the shared user dictionary and that
// is being updated by another profile, but it should eventually return...
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
// We must keep a local reference/copy of the value returned because the
// returned item is a deep-copy in the case of a shared dictionary and two
// calls to TConsole::getWordSet() can return two different instances which
// is fatally dangerous if used in a range based initialiser:
QSet<QString> wordSet{host.mpConsole->getWordSet()};
QStringList wordList{wordSet.begin(), wordSet.end()};
#else
QStringList wordList{host.mpConsole->getWordSet().toList()};
#endif
int wordCount = wordList.size();
if (wordCount > 1) {
QCollator sorter;
sorter.setCaseSensitivity(Qt::CaseInsensitive);
std::sort(wordList.begin(), wordList.end(), sorter);
}
lua_newtable(L);
for (int i = 0; i < wordCount; ++i) {
lua_pushinteger(L, i+1);
lua_pushstring(L, wordList.at(i).toUtf8().constData());
lua_settable(L, -3);
}
return 1;
}
// Internal function - helper for updateColorTable().
void TLuaInterpreter::insertColorTableEntry(lua_State* L, const QColor& color, const QString& name)
{
// Equivalent (when called from updateColorTable()) to Lua (where the
// '<8-bit unsigned int, i.e. 0 to 255>'s are provided from the QColor):
// color_table["name"] = { <color.red()>, <color.green()>, <color.blue()> }
// Creates a new empty table on the stack with space preallocated for 3
// array elements and 0 non-array elements:
lua_createtable(L, 3, 0);
lua_pushnumber(L, color.red());
lua_rawseti(L, -2, 1);
lua_pushnumber(L, color.green());
lua_rawseti(L, -2, 2);
lua_pushnumber(L, color.blue());
lua_rawseti(L, -2, 3);
lua_getfield(L, LUA_GLOBALSINDEX, "color_table");
lua_insert(L, -2);
lua_pushstring(L, name.toUtf8().constData());
lua_insert(L, -2);
lua_settable(L, -3);
lua_pop(L, 1);
}
// Internal function - copies current profile's 16 ANSI colors into the Lua "color_table"
void TLuaInterpreter::updateAnsi16ColorsInTable()
{
lua_State* L = pGlobalLua;
// Does the color_table already exist:
// Equivalent to Lua:
// color_table = color_table or {}
lua_getfield(L, LUA_GLOBALSINDEX, "color_table");
if (!(lua_toboolean(L,-1))) {
// no it doesn't
lua_pop(L, 1);
// So make it
lua_newtable(L);
}
// Okay so now we point ourselves at the wanted table:
lua_setfield(L, LUA_GLOBALSINDEX, "color_table");
// Now we can add/update the items we need to, though it is a bit repetative:
QColor color = mpHost->mBlack;
insertColorTableEntry(L, color, QStringLiteral("ansi_000"));
insertColorTableEntry(L, color, QStringLiteral("ansi_black"));
insertColorTableEntry(L, color, QStringLiteral("ansiBlack"));
color = mpHost->mRed;
insertColorTableEntry(L, color, QStringLiteral("ansi_001"));
insertColorTableEntry(L, color, QStringLiteral("ansi_red"));
insertColorTableEntry(L, color, QStringLiteral("ansiRed"));
color = mpHost->mGreen;
insertColorTableEntry(L, color, QStringLiteral("ansi_002"));
insertColorTableEntry(L, color, QStringLiteral("ansi_green"));
insertColorTableEntry(L, color, QStringLiteral("ansiGreen"));
color = mpHost->mYellow;
insertColorTableEntry(L, color, QStringLiteral("ansi_003"));
insertColorTableEntry(L, color, QStringLiteral("ansi_yellow"));
insertColorTableEntry(L, color, QStringLiteral("ansiYellow"));
color = mpHost->mBlue;
insertColorTableEntry(L, color, QStringLiteral("ansi_004"));
insertColorTableEntry(L, color, QStringLiteral("ansi_blue"));
insertColorTableEntry(L, color, QStringLiteral("ansiBlue"));
color = mpHost->mMagenta;
insertColorTableEntry(L, color, QStringLiteral("ansi_005"));
insertColorTableEntry(L, color, QStringLiteral("ansi_magenta"));
insertColorTableEntry(L, color, QStringLiteral("ansiMagenta"));
color = mpHost->mCyan;
insertColorTableEntry(L, color, QStringLiteral("ansi_006"));
insertColorTableEntry(L, color, QStringLiteral("ansi_cyan"));
insertColorTableEntry(L, color, QStringLiteral("ansiCyan"));
color = mpHost->mWhite;
insertColorTableEntry(L, color, QStringLiteral("ansi_007"));
insertColorTableEntry(L, color, QStringLiteral("ansi_white"));
insertColorTableEntry(L, color, QStringLiteral("ansiWhite"));
color = mpHost->mLightBlack;
insertColorTableEntry(L, color, QStringLiteral("ansi_008"));
insertColorTableEntry(L, color, QStringLiteral("ansi_light_black"));
insertColorTableEntry(L, color, QStringLiteral("ansiLightBlack"));
color = mpHost->mLightRed;
insertColorTableEntry(L, color, QStringLiteral("ansi_009"));
insertColorTableEntry(L, color, QStringLiteral("ansi_light_red"));
insertColorTableEntry(L, color, QStringLiteral("ansiLightRed"));
color = mpHost->mLightGreen;
insertColorTableEntry(L, color, QStringLiteral("ansi_010"));
insertColorTableEntry(L, color, QStringLiteral("ansi_light_green"));
insertColorTableEntry(L, color, QStringLiteral("ansiLightGreen"));
color = mpHost->mLightYellow;
insertColorTableEntry(L, color, QStringLiteral("ansi_011"));
insertColorTableEntry(L, color, QStringLiteral("ansi_light_yellow"));
insertColorTableEntry(L, color, QStringLiteral("ansiLightYellow"));
color = mpHost->mLightBlue;
insertColorTableEntry(L, color, QStringLiteral("ansi_012"));
insertColorTableEntry(L, color, QStringLiteral("ansi_light_blue"));
insertColorTableEntry(L, color, QStringLiteral("ansiLightBlue"));
color = mpHost->mLightMagenta;
insertColorTableEntry(L, color, QStringLiteral("ansi_013"));
insertColorTableEntry(L, color, QStringLiteral("ansi_light_magenta"));
insertColorTableEntry(L, color, QStringLiteral("ansiLightMagenta"));
color = mpHost->mLightCyan;
insertColorTableEntry(L, color, QStringLiteral("ansi_014"));
insertColorTableEntry(L, color, QStringLiteral("ansi_light_cyan"));
insertColorTableEntry(L, color, QStringLiteral("ansiLightCyan"));
color = mpHost->mLightWhite;
insertColorTableEntry(L, color, QStringLiteral("ansi_015"));
insertColorTableEntry(L, color, QStringLiteral("ansi_light_white"));
insertColorTableEntry(L, color, QStringLiteral("ansiLightWhite"));
}
// Internal function - copies current profile's extended ANSI colors into the
// Lua "color_table" - it might be feasible to do this entirely within an
// external lua file ("GUIUtils.lua2) as we do not provide a means to vary
// the ANSI colours 17 to 255 that this handles...
void TLuaInterpreter::updateExtendedAnsiColorsInTable()
{
lua_State* L = pGlobalLua;
// Does the color_table already exist:
// Equivalent to Lua:
// color_table = color_table or {}
lua_getfield(L, LUA_GLOBALSINDEX, "color_table");
if (!(lua_toboolean(L,-1))) {
// no it doesn't
lua_pop(L, 1);
// So make it
lua_newtable(L);
}
// Okay so now we point ourselves at the wanted table:
lua_setfield(L, LUA_GLOBALSINDEX, "color_table");
// And insert the 6x6x6 RGB colours
for (int i = 0; i < 216; ++i) {
int r = i / 36;
int g = (i - (r * 36)) / 6;
int b = (i - (r * 36)) - (g * 6);
lua_createtable(L, 3, 0);
lua_pushnumber(L, 51 * r);
lua_rawseti(L, -2, 1);
lua_pushnumber(L, 51 * g);
lua_rawseti(L, -2, 2);
lua_pushnumber(L, 51 * b);
lua_rawseti(L, -2, 3);
lua_getfield(L, LUA_GLOBALSINDEX, "color_table");
lua_insert(L, -2);
QString name = QStringLiteral("ansi_%1").arg(i + 16, 3, 10, QLatin1Char('0'));
lua_pushstring(L, name.toUtf8().constData());
lua_insert(L, -2);
lua_settable(L, -3);
lua_pop(L, 1);
}
// And insert the 24 Greyscale colours
for (int i = 232; i < 256; ++i) {
lua_createtable(L, 3, 0);
int value = 128;
// Divide the range 0 to 255 into 23 + 1 values to give a 24 value
// greyscale - this is lifted from TBuffer::decodeSGR{3|4}8() - and
// we really ought to refactor things so we only have this in one common
// place:
switch (i) {
case 232: value = 0; break; // 0.000
case 233: value = 11; break; // 11.087
case 234: value = 22; break; // 22.174
case 235: value = 33; break; // 33.261
case 236: value = 44; break; // 44.348
case 237: value = 55; break; // 55.435
case 238: value = 67; break; // 66.522
case 239: value = 78; break; // 77.609
case 240: value = 89; break; // 88.696
case 241: value = 100; break; // 99.783
case 242: value = 111; break; // 110.870
case 243: value = 122; break; // 121.957
case 244: value = 133; break; // 133.043
case 245: value = 144; break; // 144.130
case 246: value = 155; break; // 155.217
case 247: value = 166; break; // 166.304
case 248: value = 177; break; // 177.391
case 249: value = 188; break; // 188.478
case 250: value = 200; break; // 199.565
case 251: value = 211; break; // 210.652
case 252: value = 222; break; // 221.739
case 253: value = 233; break; // 232.826
case 254: value = 244; break; // 243.913
case 255: value = 255; break; // 255.000
default:
Q_UNREACHABLE(); // We should not have a case outside of the range 232 to 255
}
lua_pushnumber(L, value);
lua_rawseti(L, -2, 1);
lua_pushnumber(L, value);
lua_rawseti(L, -2, 2);
lua_pushnumber(L, value);
lua_rawseti(L, -2, 3);
lua_getfield(L, LUA_GLOBALSINDEX, "color_table");
lua_insert(L, -2);
QString name = QStringLiteral("ansi_%1").arg(i, 3, 10, QLatin1Char('0'));
lua_pushstring(L, name.toUtf8().constData());
lua_insert(L, -2);
lua_settable(L, -3);
lua_pop(L, 1);
}
}
// Internal function - Creates a table for useful information from the http
// response. It creates an empty table, calls upon other functions to
// add things to it, and then returns a key to where it is in the lua registery.
int TLuaInterpreter::createHttpResponseTable(QNetworkReply* reply)
{
lua_State* L = pGlobalLua;
// Push empty table onto stack
lua_newtable(L);
// Add "headers" table to table
createHttpHeadersTable(L, reply);
// Add "cookies" table to table
createCookiesTable(L, reply);
// Pop table from stack, store it in registry, return key to it
return luaL_ref(L, LUA_REGISTRYINDEX);
}
// Internal function - Adds an empty table to a "headers" key to the table for
// tracking useful information from the http response. If there are any headers
// in the http response, it adds them to this new empty table.
void TLuaInterpreter::createHttpHeadersTable(lua_State* L, QNetworkReply* reply)
{
// Assert table from createHttpResponseTable is where we expect it to be
if (!lua_istable(L, -1)) {
qDebug() << "Unable to find table at top of lua stack, aborting!";
return;
}
// Push "headers" key and empty table value onto stack
lua_pushstring(L, "headers");
lua_newtable(L);
// Parse headers, add them as key-value pairs to the empty table
const QList<QByteArray> headerList = reply->rawHeaderList();
for (QByteArray header : headerList) {
// Push header key onto stack
lua_pushstring(L, header.constData());
// Push header value onto stack
lua_pushstring(L, reply->rawHeader(header).constData());
// Put key-value pair into table (now 3 deep in stack), pop stack twice
lua_settable(L, -3);
}
// Put "headers" table into table (now 3 deep in stack), pop stack twice
lua_settable(L, -3);
}
// Internal function - Adds an empty table to a "cookies" key to the table for
// tracking useful information from the http response. If there are any cookies
// in the http response, it adds them to this new empty table.
void TLuaInterpreter::createCookiesTable(lua_State* L, QNetworkReply* reply)
{
// Assert table from createHttpResponseTable is where we expect it to be
if (!lua_istable(L, -1)) {
qDebug() << "Unable to find table at top of lua stack, aborting!";
return;
}
// Push "cookies" key and empty table value onto stack
lua_pushstring(L, "cookies");
lua_newtable(L);
// Parse cookies, add them as key-value pairs to the empty table
Host& host = getHostFromLua(L);
QNetworkCookieJar* cookieJar = host.mLuaInterpreter.mpFileDownloader->cookieJar();
QList<QNetworkCookie> cookies = cookieJar->cookiesForUrl(reply->url());
for (QNetworkCookie cookie : cookies) {
// Push cookie name onto stack
lua_pushstring(L, cookie.name().constData());
// Push cookie value onto stack
lua_pushstring(L, cookie.value().constData());
// Put key-value pair into table (now 3 deep in stack), pop stack twice
lua_settable(L, -3);
}
// Put "cookies" table into table (now 3 deep in stack), pop stack twice
lua_settable(L, -3);
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getMapBackgroundColor
int TLuaInterpreter::getMapBackgroundColor(lua_State* L)
{
auto& host = getHostFromLua(L);
auto color = host.mBgColor_2;
lua_pushnumber(L, color.red());
lua_pushnumber(L, color.green());
lua_pushnumber(L, color.blue());
return 3;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setMapBackgroundColor
int TLuaInterpreter::setMapBackgroundColor(lua_State* L)
{
int r = getVerifiedInt(L, __func__, 1, "red component");
if (r < 0 || r > 255) {
return warnArgumentValue(L, __func__, QStringLiteral("red value %1 needs to be between 0-255").arg(r));
}
int g = getVerifiedInt(L, __func__, 2, "green component");
if (g < 0 || g > 255) {
return warnArgumentValue(L, __func__, QStringLiteral("green value %1 needs to be between 0-255").arg(g));
}
int b = getVerifiedInt(L, __func__, 3, "blue component");
if (b < 0 || b > 255) {
return warnArgumentValue(L, __func__, QStringLiteral("blue value %1 needs to be between 0-255").arg(b));
}
auto& host = getHostFromLua(L);
host.mBgColor_2 = QColor(r, g, b);
updateMap(L);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#getMapRoomExitsColor
int TLuaInterpreter::getMapRoomExitsColor(lua_State* L)
{
auto& host = getHostFromLua(L);
auto color = host.mFgColor_2;
lua_pushnumber(L, color.red());
lua_pushnumber(L, color.green());
lua_pushnumber(L, color.blue());
return 3;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setMapRoomExitsColor
int TLuaInterpreter::setMapRoomExitsColor(lua_State* L)
{
int r = getVerifiedInt(L, __func__, 1, "red component");
if (r < 0 || r > 255) {
return warnArgumentValue(L, __func__, QStringLiteral("red value %1 needs to be between 0-255").arg(r));
}
int g = getVerifiedInt(L, __func__, 2, "green component");
if (g < 0 || g > 255) {
return warnArgumentValue(L, __func__, QStringLiteral("green value %1 needs to be between 0-255").arg(g));
}
int b = getVerifiedInt(L, __func__, 3, "blue component");
if (b < 0 || b > 255) {
return warnArgumentValue(L, __func__, QStringLiteral("blue value %1 needs to be between 0-255").arg(b));
}
auto& host = getHostFromLua(L);
host.mFgColor_2 = QColor(r, g, b);
updateMap(L);
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#showNotification
int TLuaInterpreter::showNotification(lua_State* L)
{
int n = lua_gettop(L);
QString title = getVerifiedString(L, __func__, 1, "title");
QString text = title;
if (n >= 2) {
QString text = getVerifiedString(L, __func__, 2, "message");
}
int notificationExpirationTime = 1;
if (n >= 3) {
notificationExpirationTime = qMax(qRound(getVerifiedDouble(L, __func__, 3, "expiration time in seconds") / 1000), 1);
}
mudlet::self()->mTrayIcon.show();
mudlet::self()->mTrayIcon.showMessage(title, text, mudlet::self()->mTrayIcon.icon(), notificationExpirationTime);
return 0;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#exportJsonMap
int TLuaInterpreter::exportJsonMap(lua_State* L)
{
Host* pHost = &getHostFromLua(L);
if (!pHost || !pHost->mpMap || !pHost->mpMap->mpMapper || !pHost->mpMap->mpMapper->mp2dMap) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
auto dest = getVerifiedString(L, __func__, 1, "export pathFileName");
if (dest.isEmpty()) {
return warnArgumentValue(L, __func__, "a non-empty path and file name to write to must be provided");
}
if (auto [result, message] = pHost->mpMap->writeJsonMapFile(dest); !result) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#importJsonMap
int TLuaInterpreter::importJsonMap(lua_State* L)
{
Host* pHost = &getHostFromLua(L);
if (!pHost || !pHost->mpMap || !pHost->mpMap->mpMapper || !pHost->mpMap->mpMapper->mp2dMap) {
return warnArgumentValue(L, __func__, "no map present or loaded");
}
auto source = getVerifiedString(L, __func__, 1, "import pathFileName");
if (source.isEmpty()) {
return warnArgumentValue(L, __func__, "a non-empty path and file name to read to must be provided");
}
if (auto [result, message] = pHost->mpMap->readJsonMapFile(source); !result) {
return warnArgumentValue(L, __func__, message);
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#registerMapInfo
int TLuaInterpreter::registerMapInfo(lua_State* L)
{
auto name = getVerifiedString(L, __func__, 1, "label");
if (!lua_isfunction(L, 2)) {
lua_pushfstring(L, "registerMapInfo: bad argument #2 type (callback as function expected, got %s!)", luaL_typename(L, 2));
return lua_error(L);
}
int callback = luaL_ref(L, LUA_REGISTRYINDEX);
auto& host = getHostFromLua(L);
host.mpMap->mMapInfoContributorManager->registerContributor(name, [=](int roomID, int selectionSize, int areaId, int displayAreaId, QColor& infoColor) {
lua_rawgeti(L, LUA_REGISTRYINDEX, callback);
if (roomID > 0) {
lua_pushinteger(L, roomID);
} else {
lua_pushnil(L);
}
lua_pushinteger(L, selectionSize);
lua_pushinteger(L, areaId);
lua_pushinteger(L, displayAreaId);
int error = lua_pcall(L, 4, 6, 0);
if (error) {
int errorCount = lua_gettop(L);
if (mudlet::debugMode) {
for (int i = 1; i <= errorCount; i++) {
if (lua_isstring(L, i)) {
auto errorMessage = lua_tostring(L, i);
TDebug(QColor(Qt::white), QColor(Qt::red)) << "LUA ERROR: when running map info callback for '" << name << "\nreason: " << errorMessage << "\n" >> 0;
}
}
}
lua_pop(L, errorCount);
return MapInfoProperties{};
}
auto nResult = lua_gettop(L);
auto index = -nResult;
QString text = lua_tostring(L, index);
bool isBold = lua_toboolean(L, ++index);
bool isItalic = lua_toboolean(L, ++index);
int r = -1;
int g = -1;
int b = -1;
if (!lua_isnil(L, ++index)) {
r = lua_tonumber(L, index);
}
if (!lua_isnil(L, ++index)) {
g = lua_tonumber(L, index);
}
if (!lua_isnil(L, ++index)) {
b = lua_tonumber(L, index);
}
QColor color;
if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {
color = QColor(r, g, b);
}
lua_pop(L, nResult);
return MapInfoProperties{ isBold, isItalic, text, color };
});
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#killMapInfo
int TLuaInterpreter::killMapInfo(lua_State* L)
{
auto& host = getHostFromLua(L);
auto name = getVerifiedString(L, __func__, 1, "label");
if (!host.mpMap->mMapInfoContributorManager->removeContributor(name)) {
return warnArgumentValue(L, __func__, QStringLiteral("map info '%1' does not exist").arg(name));
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#enableMapInfo
int TLuaInterpreter::enableMapInfo(lua_State* L)
{
auto name = getVerifiedString(L, __func__, 1, "label");
auto& host = getHostFromLua(L);
if (!host.mpMap->mMapInfoContributorManager->enableContributor(name)) {
return warnArgumentValue(L, __func__, QStringLiteral("map info '%1' does not exist").arg(name));
}
lua_pushboolean(L, true);
return 1;
}
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#disableMapInfo
int TLuaInterpreter::disableMapInfo(lua_State* L)
{
auto name = getVerifiedString(L, __func__, 1, "label");
auto& host = getHostFromLua(L);
if (!host.mpMap->mMapInfoContributorManager->disableContributor(name)) {
return warnArgumentValue(L, __func__, QStringLiteral("map info '%1' does not exist").arg(name));
}
lua_pushboolean(L, true);
return 1;
}