Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/clock/mumeclock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,31 @@ void MumeClock::parseClockTime(const QString &clockTime, const int64_t secsSince
m_mumeStartEpoch = newStartEpoch;
}

void MumeClock::parseMSSP(const int year, const int month, const int day, const int hour)
{
// We should not parse the fuzzy MSSP time if we already have a greater precision.
if (getPrecision() > MumeClockPrecisionEnum::DAY)
return;

const int64_t secsSinceEpoch = QDateTime::QDateTime::currentDateTimeUtc().toSecsSinceEpoch();

auto moment = getMumeMoment();
moment.year = year;
moment.month = month;
moment.day = day;
moment.hour = hour;
// Don't override minute, since we don't get the from the MSSP time.

const int64_t newStartEpoch = secsSinceEpoch - moment.toSeconds();
m_mumeStartEpoch = newStartEpoch;

// Update last sync timestamp
setLastSyncEpoch(secsSinceEpoch);

setPrecision(MumeClockPrecisionEnum::HOUR);
log("Synchronized clock using MSSP");
}

// TODO: move this somewhere useful?
NODISCARD static const char *getOrdinalSuffix(const int day)
{
Expand Down Expand Up @@ -410,3 +435,12 @@ MumeClock::DawnDusk MumeClock::getDawnDusk(const int month)
const auto m = static_cast<uint32_t>(month);
return DawnDusk{s_dawnHour.at(m), s_duskHour.at(m)};
}

int MumeClock::getMumeMonth(const QString &monthName)
Comment thread
Mattias-Viklund marked this conversation as resolved.
{
const int month = s_westronMonthNames.keyToValue(monthName.toLatin1().data());
if (month == static_cast<int>(WestronMonthNamesEnum::UnknownWestronMonth)) {
return s_sindarinMonthNames.keysToValue(monthName.toLatin1().data());
}
return month;
}
4 changes: 3 additions & 1 deletion src/clock/mumeclock.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,11 @@ public slots:

public:
void setPrecision(const MumeClockPrecisionEnum state);

void setLastSyncEpoch(int64_t epoch) { m_lastSyncEpoch = epoch; }

int getMumeMonth(const QString &monthName);
void parseMSSP(const int year, const int month, const int day, const int hour);

protected:
void parseMumeTime(const QString &mumeTime, int64_t secsSinceEpoch);

Expand Down
5 changes: 3 additions & 2 deletions src/clock/mumeclockwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ void MumeClockWidget::slot_updateLabel()
QString statusTip = "";
if (precision <= MumeClockPrecisionEnum::UNSET) {
styleSheet = "padding-left:1px;padding-right:1px;color:white;background:grey";
statusTip = "The clock has not synced with MUME! Click to override at your own risk.";
} else if (time == MumeTimeEnum::DAWN) {
styleSheet = "padding-left:1px;padding-right:1px;color:white;background:red";
statusTip = "Ticks left until day";
Expand All @@ -150,11 +149,13 @@ void MumeClockWidget::slot_updateLabel()
styleSheet = "padding-left:1px;padding-right:1px;color:black;background:yellow";
statusTip = "Ticks left until night";
}
if (precision != MumeClockPrecisionEnum::MINUTE)
statusTip = "The clock has not synced with MUME! Click to override at your own risk.";
timeLabel->setStyleSheet(styleSheet);
timeLabel->setStatusTip(statusTip);
updateMoonStyleSheet = true;
}
if (precision <= MumeClockPrecisionEnum::DAY) {
if (precision <= MumeClockPrecisionEnum::HOUR) {
// Prepend warning emoji to countdown
timeLabel->setText(QString::fromUtf8("\xe2\x9a\xa0").append(m_clock->toCountdown(moment)));
} else
Expand Down
2 changes: 2 additions & 0 deletions src/parser/abstractparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ protected slots:

void showHeader(const QString &s);

void receiveMudServerStatus(const QByteArray &);

NODISCARD ExitDirEnum tryGetDir(StringView &words);

void parseSetCommand(StringView view);
Expand Down
13 changes: 13 additions & 0 deletions src/proxy/AbstractTelnet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,17 @@ void AbstractTelnet::sendGmcpMessage(const GmcpMessage &msg)
s.addSubnegEnd();
}

void AbstractTelnet::sendMudServerStatus(const QByteArray &data)
{
if (debug)
qDebug() << "Sending MSSP:" << data;

TelnetFormatter s{*this};
s.addSubnegBegin(OPT_MSSP);
s.addEscapedBytes(data);
s.addSubnegEnd();
}

void AbstractTelnet::sendLineModeEdit()
{
if (debug)
Expand Down Expand Up @@ -704,6 +715,8 @@ void AbstractTelnet::processTelnetSubnegotiation(const AppendBuffer &payload)
if (hisOptionState[OPT_MSSP]) {
if (debug)
qDebug() << "Received MSSP message" << payload;

receiveMudServerStatus(payload);
}
break;

Expand Down
5 changes: 5 additions & 0 deletions src/proxy/AbstractTelnet.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ static constexpr const uint8_t TNSB_SEND = 1;
static constexpr const uint8_t TNSB_REQUEST = 1;
static constexpr const uint8_t TNSB_MODE = 1;
static constexpr const uint8_t TNSB_EDIT = 1;
static constexpr const uint8_t TNSB_MSSP_VAR = 1;
static constexpr const uint8_t TNSB_MSSP_VAL = 2;
static constexpr const uint8_t TNSB_ACCEPTED = 2;
static constexpr const uint8_t TNSB_REJECTED = 3;
static constexpr const uint8_t TNSB_TTABLE_IS = 4;
Expand Down Expand Up @@ -122,6 +124,7 @@ class AbstractTelnet : public QObject
void sendWindowSizeChanged(int, int);
void sendTerminalTypeRequest();
void sendGmcpMessage(const GmcpMessage &msg);
void sendMudServerStatus(const QByteArray &);
void sendLineModeEdit();
void requestTelnetOption(unsigned char type, unsigned char subnegBuffer);

Expand All @@ -134,6 +137,7 @@ class AbstractTelnet : public QObject
virtual void virt_receiveEchoMode(bool) {}
virtual void virt_receiveGmcpMessage(const GmcpMessage &) {}
virtual void virt_receiveTerminalType(const QByteArray &) {}
virtual void virt_receiveMudServerStatus(const QByteArray &) {}
virtual void virt_receiveWindowSize(int, int) {}
/// Send out the data. Does not double IACs, this must be done
/// by caller if needed. This function is suitable for sending
Expand All @@ -146,6 +150,7 @@ class AbstractTelnet : public QObject
void receiveEchoMode(bool b) { virt_receiveEchoMode(b); }
void receiveGmcpMessage(const GmcpMessage &msg) { virt_receiveGmcpMessage(msg); }
void receiveTerminalType(const QByteArray &ba) { virt_receiveTerminalType(ba); }
void receiveMudServerStatus(const QByteArray &ba) { virt_receiveMudServerStatus(ba); }
void receiveWindowSize(int x, int y) { virt_receiveWindowSize(x, y); }

/// Send out the data. Does not double IACs, this must be done
Expand Down
127 changes: 127 additions & 0 deletions src/proxy/MudTelnet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ void MudTelnet::virt_receiveGmcpMessage(const GmcpMessage &msg)
emit sig_relayGmcp(msg);
}

void MudTelnet::virt_receiveMudServerStatus(const QByteArray &ba)
{
parseMudServerStatus(ba);
emit sig_sendMSSPToUser(ba);
}

void MudTelnet::virt_onGmcpEnabled()
{
if (debug)
Expand Down Expand Up @@ -215,3 +221,124 @@ void MudTelnet::sendCoreSupports()

sendGmcpMessage(GmcpMessage(GmcpMessageTypeEnum::CORE_SUPPORTS_SET, set));
}

void MudTelnet::parseMudServerStatus(const QByteArray &data)
{
std::map<std::string, std::list<std::string>> map;

std::optional<std::string> varName = std::nullopt;
std::list<std::string> vals;

enum class NODISCARD MSSPStateEnum {
///
BEGIN,
/// VAR
IN_VAR,
/// VAL
IN_VAL
} state
= MSSPStateEnum::BEGIN;

AppendBuffer buffer;

const auto addValue([&map, &vals, &varName, &buffer, this]() {
// Put it into the map.
if (debug)
qDebug() << "MSSP received value" << ::toQByteArrayLatin1(buffer.toStdString())
<< "for variable" << ::toQByteArrayLatin1(varName.value());

vals.push_back(buffer.toStdString());
map[varName.value()] = vals;

buffer.clear();
});

for (int i = 0; i < data.size(); i++) {
switch (state) {
case MSSPStateEnum::BEGIN:
if (data.at(i) != TNSB_MSSP_VAR)
continue;
state = MSSPStateEnum::IN_VAR;
break;

case MSSPStateEnum::IN_VAR:
switch (data.at(i)) {
case TNSB_MSSP_VAR:
case TN_IAC:
case 0:
continue;

case TNSB_MSSP_VAL: {
if (buffer.isEmpty()) {
if (debug)
qDebug() << "MSSP received variable without any name; ignoring it";
continue;
}

if (debug)
qDebug() << "MSSP received variable"
<< ::toQByteArrayLatin1(buffer.toStdString());

varName = buffer.toStdString();
state = MSSPStateEnum::IN_VAL;

vals.clear(); // Which means this is a new value, so clear the list.
buffer.clear();
} break;

default:
buffer.append(static_cast<uint8_t>(data.at(i)));
}
break;

case MSSPStateEnum::IN_VAL: {
assert(varName.has_value());

switch (data.at(i)) {
case TN_IAC:
case 0:
continue;

case TNSB_MSSP_VAR:
state = MSSPStateEnum::IN_VAR;
FALLTHROUGH;
case TNSB_MSSP_VAL:
addValue();
break;

default:
buffer.append(static_cast<uint8_t>(data.at(i)));
break;
}
break;

} break;
}
}

if (varName.has_value() && !buffer.isEmpty())
addValue();

// Parse game time from MSSP
const auto firstElement(
[](const std::list<std::string> &elements) -> std::optional<std::string> {
return elements.empty() ? std::nullopt : std::optional<std::string>{elements.front()};
});
const auto yearStr = firstElement(map["GAME YEAR"]);
const auto monthStr = firstElement(map["GAME MONTH"]);
const auto dayStr = firstElement(map["GAME DAY"]);
const auto hourStr = firstElement(map["GAME HOUR"]);

qInfo() << "MSSP game time received with"
<< "year:" << ::toQByteArrayLatin1(yearStr.value_or("unknown"))
<< "month:" << ::toQByteArrayLatin1(monthStr.value_or("unknown"))
<< "day:" << ::toQByteArrayLatin1(dayStr.value_or("unknown"))
<< "hour:" << ::toQByteArrayLatin1(hourStr.value_or("unknown"));

if (yearStr.has_value() && monthStr.has_value() && dayStr.has_value() && hourStr.has_value()) {
const int year = stoi(yearStr.value());
const int day = stoi(dayStr.value());
const int hour = stoi(hourStr.value());
emit sig_sendGameTimeToClock(year, monthStr.value(), day, hour);
}
}
7 changes: 7 additions & 0 deletions src/proxy/MudTelnet.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,24 @@ public slots:
void sig_sendToSocket(const QByteArray &);
void sig_relayEchoMode(bool);
void sig_relayGmcp(const GmcpMessage &);
void sig_sendMSSPToUser(const QByteArray &);
void sig_sendGameTimeToClock(const int year,
const std::string &month,
const int day,
const int hour);

private:
void virt_sendToMapper(const QByteArray &data, bool goAhead) final;
void virt_receiveEchoMode(bool toggle) final;
void virt_receiveGmcpMessage(const GmcpMessage &) final;
void virt_receiveMudServerStatus(const QByteArray &) final;
void virt_onGmcpEnabled() final;
void virt_sendRawData(const std::string_view data) final;

private:
void receiveGmcpModule(const GmcpModule &, bool);
void resetGmcpModules();
void parseMudServerStatus(const QByteArray &);

private:
void sendCoreSupports();
Expand Down
5 changes: 5 additions & 0 deletions src/proxy/UserTelnet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ void UserTelnet::slot_onGmcpToUser(const GmcpMessage &msg)
}
}

void UserTelnet::slot_onSendMSSPToUser(const QByteArray &data)
{
sendMudServerStatus(data);
}

void UserTelnet::virt_sendToMapper(const QByteArray &data, const bool goAhead)
{
// MMapper requires all data to be Latin-1 internally
Expand Down
1 change: 1 addition & 0 deletions src/proxy/UserTelnet.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public slots:
void slot_onConnected();
void slot_onRelayEchoMode(bool);
void slot_onGmcpToUser(const GmcpMessage &);
void slot_onSendMSSPToUser(const QByteArray &);

signals:
void sig_analyzeUserStream(const QByteArray &, bool goAhead);
Expand Down
19 changes: 19 additions & 0 deletions src/proxy/proxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,15 @@ void Proxy::slot_start()
connect(mudTelnet, &MudTelnet::sig_sendToSocket, this, &Proxy::slot_onSendToMudSocket);
connect(mudTelnet, &MudTelnet::sig_relayEchoMode, userTelnet, &UserTelnet::slot_onRelayEchoMode);
connect(mudTelnet, &MudTelnet::sig_relayGmcp, userTelnet, &UserTelnet::slot_onGmcpToUser);
connect(mudTelnet,
&MudTelnet::sig_sendGameTimeToClock,
this,
&Proxy::slot_onSendGameTimeToClock);
connect(mudTelnet,
&MudTelnet::sig_sendMSSPToUser,
userTelnet,
&UserTelnet::slot_onSendMSSPToUser);

connect(mudTelnet,
&MudTelnet::sig_relayGmcp,
&m_groupManager,
Expand Down Expand Up @@ -565,3 +574,13 @@ bool Proxy::isGmcpModuleEnabled(const GmcpModuleTypeEnum &module) const
{
return m_userTelnet->isGmcpModuleEnabled(module);
}

void Proxy::slot_onSendGameTimeToClock(const int year,
const std::string &monthStr,
const int day,
const int hour)
{
// Month from MSSP comes as a string, so fetch the month index.
const int month = m_mumeClock.getMumeMonth(::toQStringLatin1(monthStr));
m_mumeClock.parseMSSP(year, month, day, hour);
}
5 changes: 5 additions & 0 deletions src/proxy/proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ public slots:
void slot_onMudError(const QString &);
void slot_onMudConnected();

void slot_onSendGameTimeToClock(const int year,
const std::string &month,
const int day,
const int hour);

signals:
void sig_log(const QString &, const QString &);

Expand Down
Loading