mudlet/src/TMxpExpireTagHandler.cpp
Mike Conley 50dc0b2c95
Add: MXP EXPIRE tag support for expiring links (#8383)
<!-- Keep the title short & concise so anyone non-technical can
understand it,
     the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions

Implements support for the MXP (Mud eXtension Protocol) EXPIRE tag,
which allows MUD servers to automatically deactivate clickable links
when they're no longer relevant. For example, when you move to a new
room, the exit links from the previous room are deactivated so you can't
accidentally click them.

The EXPIRE tag can be used in two ways:
- Standalone: `<EXPIRE name>` deactivates all links tagged with that
name
- As an attribute: `<SEND href="..." EXPIRE="name">` tags a link to be
expired later

Expired links remain visible in scrollback for historical reference but
become non-functional when clicked.

#### Motivation for adding to Mudlet

This feature was requested by @mfontani in [Discord
comments](https://discord.com/channels/279748146316312576/1393998251786768384/1429244386541178911)
to improve the MUD playing experience by preventing accidental clicks on
outdated navigation links. The MXP v1.0 specification includes EXPIRE
tag support, and implementing it brings Mudlet closer to full MXP
compliance.

StickMUD (stickmud.com:7680) has already implemented server-side support
for this feature and uses it to expire room exit links when players move
to new rooms.

#### Other info (issues closed, discussion etc)

- Tested extensively with StickMUD's MXP implementation
- All existing unit tests pass
- No breaking changes to existing functionality
- Links are deactivated but remain visually unchanged in scrollback to
preserve historical context

---

Video of MXP tags expiring. Clicking on prior room links does nothing,
while clicking on existing room links functions.


https://github.com/user-attachments/assets/417bb1e7-458a-459b-9529-ca7749744cc5
2025-10-21 10:02:34 +01:00

84 lines
3.3 KiB
C++

/***************************************************************************
* Copyright (C) 2025 by Mike Conley - mike.conley@stickmud.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 "TMxpExpireTagHandler.h"
#include "TMxpClient.h"
#include "TMxpContext.h"
#ifdef DEBUG_MXP_PROCESSING
#include <QDebug>
#endif
// Override supports() to add debug output
bool TMxpExpireTagHandler::supports(TMxpContext& ctx, TMxpClient& client, MxpTag* tag)
{
#ifdef DEBUG_MXP_PROCESSING
qDebug() << "TMxpExpireTagHandler::supports() called for tag:" << tag->getName();
#endif
// Call parent class supports() which checks if tag->isNamed("EXPIRE")
bool result = TMxpSingleTagHandler::supports(ctx, client, tag);
#ifdef DEBUG_MXP_PROCESSING
qDebug() << "TMxpExpireTagHandler::supports() returning:" << result;
#endif
return result;
}
// <EXPIRE name>
// Removes all links tagged with the given expire name
TMxpTagHandlerResult TMxpExpireTagHandler::handleStartTag(TMxpContext& ctx, TMxpClient& client, MxpStartTag* tag)
{
Q_UNUSED(ctx)
QString expireName;
#ifdef DEBUG_MXP_PROCESSING
qDebug() << "TMxpExpireTagHandler: Processing EXPIRE tag";
qDebug() << " Attribute count:" << tag->getAttributesCount();
for (int i = 0; i < tag->getAttributesCount(); i++) {
auto attr = tag->getAttribute(i);
qDebug() << " Attribute" << i << ":" << attr.getName() << "=" << attr.getValue();
}
#endif
// The expire name can be specified in different ways:
// <EXPIRE name> or <EXPIRE name="name">
if (tag->getAttributesCount() > 0) {
if (tag->hasAttribute(qsl("name"))) {
expireName = tag->getAttributeValue(qsl("name"));
} else {
// First attribute without a value is the name
expireName = tag->getAttribute(0).getName();
}
}
if (expireName.isEmpty()) {
#ifdef DEBUG_MXP_PROCESSING
qDebug() << "TMxpExpireTagHandler: No expire name found, not handling";
#endif
return MXP_TAG_NOT_HANDLED;
}
#ifdef DEBUG_MXP_PROCESSING
qDebug() << "TMxpExpireTagHandler: Expiring links with name:" << expireName;
#endif
client.expireLinks(expireName);
return MXP_TAG_HANDLED;
}