Stacked on #9799, so the base is `fix-db-index-string` and this
retargets to development once that merges.
- `table.contains()` keeps a set of the tables it has walked, so a
self-referential one (every Geyser object holds its container, which
holds it back) answers instead of overflowing the stack, and
`Geyser.Label:setDoubleClickCallback()` stores the `doubleClickCallback`
key the label's own re-registration reads rather than one nothing reads.
- db: a `UNIQUE` with no `ON CONFLICT` clause is now seen, so a change
in uniqueness rebuilds the sheet; a sheet given as a list of column
names takes the sheet options instead of swallowing `_index` as a
phantom column; and an `_index` naming a column the sheet does not have
is refused rather than quietly dropping the indexes the sheet already
had.
- `saveMap()` resolves a relative location against the profile directory
the way `importMap()` does instead of against the directory Mudlet was
started in, `loadMap()` looks in the same place, and a format version
below the oldest one Mudlet can write is refused the way one that is too
new already was.
Test case: `lua local t = {} t.self = t display(table.contains(t, "x"))`
answers `false` instead of raising, and `lua saveMap(42)` writes into
`getMudletHomeDir()` rather than the directory Mudlet was started in.
Worth knowing: `db:create` now hard-errors on an `_index` naming a
column the sheet does not declare, where it used to load and silently
lose the sheet's indexes.
Closes#9777, Closes#9779, Closes#9780, Closes#9781, Closes#9782,
Closes#9800, Closes#9801
Assisted-by: Claude:claude-opus-5
#### Brief overview of PR changes/additions
- Closing a profile no longer frees the map out from under a running
import, export or download. `TMap` counts the operations that pump
`qApp->processEvents()`, and `mudlet::closeHost()` - which one of those
pumps is what delivers it - stops the operation and destroys the `Host`
once it has unwound, instead of half way through it.
- Discord presence fields keep their last character and are only ever
cut between characters: each buffer is now the documented limit plus
room for its terminator, and a new `utils::copyUtf8String()` walks the
cut back to a character boundary.
- An interrupting `ttsSpeak()` announces the utterance it starts, and
the `Ready` an engine reports for the utterance it cut off no longer
drains `ttsQueue()` over the top of the one the script asked for.
#### Motivation for adding to Mudlet
Each is a filed defect, and each was reproduced before it was fixed. The
map one is a use-after-free: ASan reports `heap-use-after-free` inside
`TMap::readJsonMapFile()`, freed by `~TMap` <- `~Host` <-
`HostManager::deleteHost` <- `mudlet::closeHost` delivered by the
import's own `processEvents()`. The Discord one is worse than one field
looking wrong: a single over-long non-ASCII field makes the whole
`SET_ACTIVITY` payload undecodable, so the entire presence update is
discarded - the fake Discord client recorded exactly that. The TTS one
silently drops speech: `ttsQueue()` plus an interrupting `ttsSpeak()`
speaks the queued line and never speaks the requested one.
#### Other info (issues closed, discussion etc)
Closes#9520, closes#9634, closes#9659.
`MapCloseDuringImportTest` stages the close through
`mudlet::slot_closeProfileByName()` and lets the map operation's own
pump deliver it; the functional tests build with ASan, so the pre-fix
run is a sanitizer report rather than an inference.
`TtsInterruptingSpeakTest` hands `ttsStateChanged()` the `Ready` a real
engine sends, which Qt's mock engine never does - the mock-visible half
is pinned in `Media_spec.lua`, where the two specs that recorded the old
behaviour are updated. `Discord_spec.lua` gains four end-to-end specs
against `CI/discord-ipc-fixture.py` asserting that the captured frame
still decodes as JSON and that a field is cut on a character boundary,
and `DiscordTest.cpp` covers the same at unit level. Every new or
changed test was confirmed to fail without its fix.
Two things deliberately left alone, both older than this PR:
`Host::requestClose()` still runs nested inside the map operation's pump
(it saves the profile there), and an XML import or a map download has no
cancel to poll, so a close waits for it rather than stopping it.
**Test case:** Export a large map with `exportJsonMap()` and close the
profile's tab while it runs; then `setDiscordDetail(string.rep("ä",
65))` and confirm the presence still updates; then `ttsQueue("queued
line") ttsSpeak("first")` followed immediately by `ttsSpeak("second")`
and confirm "second" is what gets spoken.
Assisted-by: Claude:claude-opus-5
#### Brief overview of PR changes/additions
Saving a map at format <= 19 was leaking internal `system.fallback_*`
keys into the live map's user-visible userData, permanently. A format-19
save polluted room userData; a save at format < 19 tainted map userData
forever.
- The first commit serialises a locally-augmented copy, so saves never
mutate the live map. Old-format **file** output stays byte-identical, so
older Mudlets still receive their compatibility keys.
- The second commit strips stale leaked keys on format >= 19 loads, so
already-tainted maps self-clean.
#### Motivation for adding to Mudlet
Users' maps were silently accumulating internal keys they never set, and
those keys survived across save/load cycles. This stops new pollution
and cleans up existing damage.
#### Other info (issues closed, discussion etc)
Stacked on top of #9468 (base branch `add-persistence-roundtrip-tests`);
GitHub will auto-retarget to `development` once that PR merges. Please
merge after #9468. These are the two bugs the round-trip tests caught,
so this PR also flips their `QEXPECT_FAIL` markers to hard assertions.
Human build/test pending; DCO sign-off to be added at squash time.
https://github.com/user-attachments/assets/3931edb7-4daf-4277-bac4-0c6680d5bfe3
#### Brief overview of PR changes/additions
- Removes all raw Qt Widgets usage from the map engine: `TMap.{h,cpp}`
no longer owns a `QProgressDialog` (and drops a dead `QFileDialog`
include), and `XMLimport.{h,cpp}` no longer pulls in `QApplication` (the
clipboard read now uses `QGuiApplication::clipboard()`, which lives in
Qt Gui).
- The standalone map-progress dialog (shown when the mapper is not
visible, for map download / XML import and JSON export/import) is now
driven by Qt signals carrying pre-translated payloads; the frontend
(`TMainConsole`) owns the actual `QProgressDialog`, and a user cancel
returns to the engine through `TMap::slot_mapProgressDialogCancelled()`.
- Adds `MapProgressDialogSeamTest` covering the transfer-progress state
machine, a JSON export/import round trip driving the new signals, a
mid-import cancel delivered through the seam (the highest-risk change,
since the JSON reader used to poll `QProgressDialog::wasCanceled()`
synchronously), and an XML map import re-entered from inside a running
JSON operation.
#### Motivation for adding to Mudlet
Second concrete step of the re-scoped libmudlet plan (a Qt-Widgets-free
`mudlet_core` for headless use, testability and WASM). It copies the
seam template established in #9507: core emits a pre-translated payload
-> frontend owns the widget -> a callback slot returns the answer. The
Qt Widgets dependency audit (`cmake/audit-core-widgets.sh`) drops from
**151 to 147** offending files; `TMap.cpp`, `TMap.h`, `XMLimport.cpp`
and `XMLimport.h` are all now clean. The mapper-owned inline progress
path (`dlgMapper`/`T2DMap`, Mudlet's own widgets) is deliberately
untouched here - those move wholesale in the later target-split phase.
#### Other info (issues closed, discussion etc)
Part of #8681 / #9011. Existing translations are unaffected: every
progress string keeps its `TMap` `tr()` context, so current translations
carry straight over. Two new strings do arrive, both with `//:`
translator comments - the warnings shown when a map download or an XML
map import is refused because a JSON import/export is already running.
The JSON dialog stays non-modal and the download/import dialog keeps its
modeless styling, each applied by the frontend. The engine keeps its own
`mMapProgressStandalone` / `mMapProgressCancelRequested` /
`mMapProgressStandaloneMaximum` state to replace the widget read-backs
it used to do (`!= nullptr`, `wasCanceled()`, `maximum()`). If a map
operation ever reaches the engine before a console is wired (checked via
`isSignalConnected`), `TMap::warnIfMapProgressUnwired()` logs a loud
`qWarning` rather than silently running with no progress UI.
It also closes a latent null-dereference that exists on `development`
today. With the mapper visible a map download takes the inline-progress
path, leaving `mpProgressDialog` null - so a JSON export started
meanwhile sails past the `if (mpProgressDialog)` "already in progress"
check and creates a dialog of its own. When the download then finishes
inside the `processEvents()` pump the export is running,
`clearTransferProgress()` deletes and nulls *that* dialog, and the
export's next `incrementJsonProgressDialog()` dereferences null. The
engine now records whose dialog is up (`mMapProgressIsTransfer`) so a
transfer only ever closes its own, and `importMap()` refuses to start
while a JSON operation holds the progress - the mirror of the guard
`downloadMap()` has.
Two review-driven details worth flagging: the frontend only wires the
dialog's cancel to the engine when the operation is actually cancelable,
so a non-cancelable local XML import no longer turns a window-close into
a spurious "Map download was canceled" message; and the standalone
download/import dialog is now parented to the console (like the JSON one
always was, and like #9507's package-download dialog), so it centres on
and dies with the profile window. The three `#include <QApplication>`
additions to `Host.cpp` / `dlgTriggerEditor.cpp` /
`dlgConnectionProfiles.cpp` replace the transitive include they used to
get from `XMLimport.h`; all three are already Qt Widgets consumers, so
the audit count is unaffected.
Assisted-by: Claude:claude-opus-4-8
Assisted-by: Claude:claude-opus-5
**Test case:** With a mapper window open, use a game that supports map
download (or call `downloadMap()`) and confirm the progress dialog
shows, updates, and its Abort cancels the download. Then with the mapper
window closed, run `exportJsonMap()` and `importJsonMap()` on a large
map and confirm the non-modal JSON progress dialog appears, updates its
Areas/Rooms/Labels counts, and that clicking Abort during an import
stops it with an "aborted by user" result. Load a local XML map
(Settings -> Map -> load) and confirm closing its progress window does
not print a "Map download was canceled" line. Everything should behave
exactly as on `development`.
#### Demo (before & after)
https://github.com/user-attachments/assets/f1c62580-2d03-4e5b-a6ee-f6e2b78d113d
#### Brief overview of PR changes/additions
Clears five open CodeQL "warning"-level alerts in `src/`, each with a
minimal, behavior-preserving fix:
- **`src/TMap.cpp`** (`cpp/catch-by-value`): the A* search catches the
`found_goal` sentinel by value. `found_goal` (in `src/TAstar.h`) is an
empty struct thrown by the goal visitor purely as a control-flow signal,
and the handler never touches the caught object, so it is now caught as
`const found_goal&`.
- **`src/exitstreewidget.h`** (`cpp/integer-used-for-enum`, two alerts):
the special-exit column indices were `static const int` constants, so
the two `switch` statements in `dlgRoomExits::slot_editSpecialExit()`
dispatched an `int` over const-int case labels. They are now an unscoped
`enum ExitsTreeColumn : int` with identical values (0-8). Unscoped keeps
the implicit `int` conversions, so every `ExitsTreeWidget::colIndex_*`
call site (all passed to Qt column-index `int` parameters) is unchanged.
- **`src/TMatchState.h`** (`cpp/rule-of-two`): the class had a
user-defined copy constructor but only an implicit copy assignment.
Added an explicit `= default` copy assignment. The defaulted assignment
reproduces the previous implicit one exactly (full member-wise copy);
the existing, deliberately partial copy constructor is untouched.
- **`src/TConsole.h`** (`cpp/rule-of-two`): `TFontAttributes` had a `=
default` copy assignment but only an implicit copy constructor. Added an
explicit `= default` copy constructor. Move operations were already
suppressed by the existing user-declared copy assignment, so nothing
about copy/move behavior changes.
#### Motivation for adding to Mudlet
Reduces the open CodeQL alert backlog with small, low-risk hygiene fixes
that also make the affected types' intent clearer (explicit special
members, a named column enum) without altering any runtime behavior.
#### Other info (issues closed, discussion etc)
CodeQL alerts cleared:
- `cpp/catch-by-value` - `src/TMap.cpp` (alert #140)
- `cpp/integer-used-for-enum` - `src/dlgRoomExits.cpp` switch at ~318
(alert #1070)
- `cpp/integer-used-for-enum` - `src/dlgRoomExits.cpp` switch at ~399
(alert #1071)
- `cpp/rule-of-two` - `src/TMatchState.h` (alert #185)
- `cpp/rule-of-two` - `src/TConsole.h` / `TFontAttributes` (alert #2148)
Each fix is behavior-preserving. Verified with a full Ninja build (Qt
6.12.0, ASan) and the adjacent functional tests: `MapRoundTripTest`,
`TAreaZLevelIndexTest`, `TAreaGridIndexTest`,
`TriggerSameLineMatchTest`, `TFeedTriggersRecursionTest`,
`ColorTriggerFilterChildTest`, `EnableDisableByNameTest`,
`MainConsoleSelectionTest` - all pass.
#### Brief overview of PR changes/additions
- Include QJson/QtCore headers directly in the 16 files that only
reached those types through transitive includes; headers forward-declare
where a reference suffices
- Pass the MMCP command bytes to QString::arg() as explicit QChar (wire
bytes unchanged)
#### Motivation for adding to Mudlet
An audit comment in #9011 reported that development does not build at
the declared Qt 6.8.2 lower bound; building against official Qt 6.8.2
binaries shows it actually builds cleanly today, but only via transitive
includes and arg() overload-set details that shift between Qt releases -
this PR makes both explicit so the lower bound keeps working.
#### Other info (issues closed, discussion etc)
Relates to #9011 (not closed by this PR).
Verification of the audit's two Qt 6.8.2 claims, done against official
Qt 6.8.2 gcc_64 binaries:
- Unmodified development configures and builds completely (387/387
targets, all functional tests link) at 6.8.2, so the reported QJson
include errors and MMCP arg() compile failures did not reproduce. This
branch builds green at 6.8.2 as well.
- On the arg() claim specifically: 6.8.2's qstring.h declares
`arg(char)` unconditionally, so the two live single-arg sites in
MMCPServer.cpp already compiled. The genuinely 6.8.2-problematic pattern
- multi-arg `.arg(char, ...)`, which on 6.8.2 resolves to `arg(char a,
int fieldWidth)` and would silently eat the second byte as a field width
- only occurs in commented-out code (MMCPServer.cpp around lines 534 and
1054). If that code is ever revived, it needs the QChar wrapping.
- MMCP byte-equivalence: a small harness compiled against Qt 6.12 shows
the old and new forms produce identical toLatin1() frames (0x06 for
TextGroup, 0xFF for End). On 6.8.2, `arg(char)` forwards to QLatin1Char
- the same Latin-1 mapping `QChar(char)` uses - so frames are
byte-identical there too.
Assisted-by: Claude:claude-opus-4-8
Assisted-by: Claude:claude-fable-5
**Test case:**
- Build against Qt 6.8.x: done for this PR with official 6.8.2 binaries
on Linux - full build green on this branch and on unmodified
development.
- At minimum: the current build stays green (CI covers newer Qt only).
#### Brief overview of PR changes/additions
Convert raw millisecond integer literals at time-duration call sites to
`std::chrono` literals, and add `#include <chrono>` to each touched
translation unit. Examples:
- `QTimer::singleShot(0, ...)` → `QTimer::singleShot(0ms, ...)`
- `mpTimerReplay->setInterval(1000)` → `setInterval(1s)`
- `mPendingTimer.start(60000)` → `start(1min)`
- `QObject::startTimer(50)` → `startTimer(50ms)`
- `QTest::qWait(100)` → `QTest::qWait(100ms)`
- `QThread::msleep(10)` → `QThread::sleep(10ms)`
This is a semantics-preserving refactor - every duration is kept exactly
equal to before (e.g. `1000` ms becomes `1s`, `60000` ms becomes
`1min`). No behavioural change.
#### Motivation for adding to Mudlet
Chrono literals make time durations self-documenting and type-safe. `1s`
/ `100ms` read unambiguously where a bare `1000` / `100` forces the
reader to remember each API's unit, and the compiler now rejects unit
mismatches. Only genuine duration arguments were converted - loop
counts, scroll-line counts, sizes, ports and the like were deliberately
left as plain integers.
All targeted APIs provide `std::chrono` overloads in the minimum
supported Qt (6.8.2): `QTimer::singleShot`/`start`/`setInterval` (5.8),
`QObject::startTimer` (5.9), `QThread::sleep(std::chrono::nanoseconds)`
(6.6) and `QTest::qWait(std::chrono::milliseconds)` (6.7).
#### Other info (issues closed, discussion etc)
Test case: the full application builds cleanly and the entire functional
`ctest` suite passes. The only failing test is the known, pre-existing
`PasswordMigrationTest` LSan exit-leak (GTK3/fontconfig noise), which is
unrelated to this change.
Assisted-by: Claude:claude-opus-4-8
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Remove unnecessary else/else-if after return, break, continue, and throw
statements - and in places where fixing them is more trouble than its
worth, added NOLINT.
#### Motivation for adding to Mudlet
https://clang.llvm.org/extra/clang-tidy/checks/readability/else-after-return.html,
so it doesn't pop up in PR reviews.
#### Other info (issues closed, discussion etc)
---------
Co-authored-by: Stephen Lyons <slysven@virginmedia.com>
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
### Refactor: replace raw pointer ownership with smart pointers across
core subsystems
#### Brief overview of PR changes/additions
Replaces raw pointer ownership patterns with `std::unique_ptr` and
`std::map`
across several core subsystems:
- **Host**: `mStopWatchMap` (`QMap<int, stopWatch*>` → `std::map<int,
unique_ptr<stopWatch>>`), `profileShortcuts` (`QMap<QString,
QKeySequence*>` → `std::map<QString, unique_ptr<QKeySequence>>`).
Removes `qDeleteAll` in destructor and `delete mMMCPServer`.
- **TMap**: `mpRoomDB` raw pointer → `unique_ptr`
- **VarUnit**: `base` raw pointer → `unique_ptr`
- **TTrigger**: condition map storage converted to `unique_ptr`,
destructor simplified
- **discord**: handler and presence maps converted from raw pointer
`QMap` to `unique_ptr` + `std::map`
- **Updater**: `mFeed` and `mUpdateDialog` converted to `unique_ptr`
#### Motivation for adding to Mudlet
These patterns were identified as sources of memory leaks and potential
use-after-free bugs. Using smart pointers makes ownership explicit,
eliminates manual cleanup code, and ensures correct destruction even on
early-exit paths.
#### Other info (issues closed, discussion etc)
sorry this one is still pretty big, but most of the changes are the same
for each thing so reviewing them together probably makes sense. sadly
there isn't much to see here other than no slow uptick of heap size :-[
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
#### Brief overview of PR changes/additions
One of a series of PRs, each addressing a type of issue reported by
Clazy.
This is the: "Use isEmpty() instead [-Wclazy-isempty-vs-count]" one.
#### Motivation for adding to Mudlet
Remove warnings detected by the Clazy tool - either when explicitly run
on the Mudlet code-base or detected by the background scanner/analyser
that Qt Creator offers.
#### Other info (issues closed, discussion etc)
A summary I found for this is:
> Using isEmpty() is preferred over comparing count to zero because it
is more efficient and improves code readability. This is especially
important for collections that do not implement
`RandomAccessCollection`, as counting elements can be costly.
However a conflicting alternative merely suggests that only the
semantics are better reflect by the use of `isEmpty` - there is no
difference in performance - and the first bit is certainly the basis of
our (Mudlet Core Devs) existing preference for it.
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Inline map download dialog to remove an additional pop-up when the map
is opened.
#### Motivation for adding to Mudlet
Less pop-ups in desktop UX, the better
#### Other info (issues closed, discussion etc)
[Screencast from 2026-04-18
09-43-26.webm](https://github.com/user-attachments/assets/e5df051a-db20-47a9-b62d-6f59a15eb17b)
---------
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
Co-authored-by: Zooka <136661366+ZookaOnGit@users.noreply.github.com>
#### Brief overview of PR changes/additions
Add TAreaZLevelIndex and TAreaGridIndex - spatial indices maintained
alongside TArea::rooms that allow the renderer to query only rooms on
the current Z level or within the visible viewport rectangle, instead of
iterating all rooms in the area on every paint event.
Key changes:
- TAreaZLevelIndex: Z -> set<roomId> index, O(1) getRoomsForZ()
- TAreaGridIndex: Z -> X -> Y -> set<roomId> viewport index for grid
mode
- T2DMap::drawGridModeRooms: new batch renderer for grid mode using the
spatial index; includes LOD fast-path for sub-pixel zoom levels that
writes directly to QImage scanlines (~700x speedup vs QPainterPath)
- T2DMap::scheduleRender: throttled repaint via QTimer (~60fps cap) for
continuous input events (pan drag, scroll-wheel zoom)
- paintEvent: uses getRoomsForZ() for upper/lower level shadows and
exits, eliminating per-frame O(N-total) scans
- dlgMapper: fix setPen before boundingRect to prevent invisible info
box
- TMap::setRoomCoordinates: keeps both indices consistent on room moves
#### Motivation for adding to Mudlet
As grid mode maps scale, interacting with the map (specifically zooming
and panning) becomes sluggish to unusable in the extreme.
#### Other info (issues closed, discussion etc)
#### Brief overview of PR changes/additions
Revise the text of some map related error messages that get shown in the
Editor's error window. Including marking one of them for translation
when it wasn't before. Also make translatable the `"[MAP ERROR:] "` text
prepended to such texts and put in a space that was missing in the
Engineering English for that.
#### Motivation for adding to Mudlet
Better constructed (and more localised) map error messages.
#### Other info (issues closed, discussion etc)
`(void) TMap::logError(const QString& msg)` uses the
`TConsole::print(const QString&, const QColor, const QColor)` method to
put error messages into the "Errors" window in the editor and that is
also used in other places to insert other messages however many of them
are constructed from fragments of text in different colours such that
some parts are NOT put through the translation system but others are -
and that isn't even consistent for different messages in the same form.
Whilst it might look colourful and pretty - it makes things virtually
impossible to translate them properly IMHO.
IIRC it is considered that text to be translated should not be broken up
into anything less than whole sentences.
---------
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
#### Brief overview of PR changes/additions
One of a series of PRs, each addressing a type of issue reported by
Clazy.
This is the: "C+11 range-loop might detach Qt container
[clazy-range-loop-detach]" one.
#### Motivation for adding to Mudlet
Remove warnings detected by the Clazy tool - either when explicitly run
on the Mudlet code-base or detected by the background scanner/analyser
that Qt Creator offers.
#### Other info (issues closed, discussion etc)
There was over 100 of this particular item - some of them also could
have `const` applied to the iterator.
---------
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
#### Brief overview of PR changes/additions
Adds a fall-back mechanism to store the hidden room detail in binary map
formats less than 22.
#### Motivation for adding to Mudlet
Improve the handling of the newly added ability to hide rooms.
#### Other info (issues closed, discussion etc)
---------
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
Co-authored-by: Zooka <136661366+ZookaOnGit@users.noreply.github.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
If there was an error in saving the downloaded map, the player would
have had no idea.
#### Motivation for adding to Mudlet
A bit better error reporting.
#### Other info (issues closed, discussion etc)
#### Brief overview of PR changes/additions
Add missing reads for mMapSymbolFont, mMapSymbolFontFudgeFactor, and
mIsOnlyMapSymbolFontToBeUsed in retrieveMapFileStats() for map format
v>=19, matching what restore() already does correctly
#### Motivation for adding to Mudlet
Copying a map to inactive profiles via Preferences silently corrupts the
player's saved room position in those profiles because
retrieveMapFileStats() reads garbage data from a desynchronized
QDataStream.
#### Other info (issues closed, discussion etc)
The bug was introduced in 2016 (94dd41bfb7) when retrieveMapFileStats
was written, and became active when map format v19 added three new
fields (mMapSymbolFont, mMapSymbolFontFudgeFactor,
mIsOnlyMapSymbolFontToBeUsed) to the serialization in
serialize()/restore() but retrieveMapFileStats was never updated to
match.
The serialize() function writes in this order for v>=19:
1. mUserData
2. mMapSymbolFont (QFont)
3. mMapSymbolFontFudgeFactor (double)
4. mIsOnlyMapSymbolFontToBeUsed (bool)
5. area count + area data
6. room data
7. userRoomHash (contains the player room ID per profile)
The restore() function reads all of these correctly (lines 1664-1677).
But retrieveMapFileStats() read mUserData and then jumped straight to
reading area count, interpreting the QFont bytes as an int. This
desynchronized the entire QDataStream, causing all subsequent reads
(area count, room count, player room ID) to return garbage.
The only caller is dlgProfilePreferences.cpp:2741, the Copy Map feature
for inactive profiles. It uses the returned roomId to update mRoomIdHash
for the target profile. With the desync, this was either a garbage room
ID (silently teleporting the player to a wrong room) or 0 (losing their
position entirely).
Since the default map save version is 20, this affected every map file
written by current Mudlet. Active profiles were unaffected because their
room ID is read from memory, not from disk.
**Test case:** Create two profiles (A and B) each with a map. Close
profile B. In profile A, go to Preferences, Mapper tab, click Copy Map.
Check the debug output for profile B's stats -- area count and room
count should now show correct values instead of garbage numbers. Reopen
profile B and verify the player is in the correct room.
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
%d should be used [for
int](https://www.lua.org/manual/5.1/manual.html#lua_pushfstring),
#### Motivation for adding to Mudlet
Small bugfix.
#### Other info (issues closed, discussion etc)
#### Brief overview of PR changes/additions
Persist map label outline color in userData alongside font info during
map serialization.
#### Motivation for adding to Mudlet
Fixing report in
https://discord.com/channels/283581582550237184/427919962561052673/1467980966122492015
#### Other info (issues closed, discussion etc)
N/A
**Test case:** Create a map label with a custom outline color (or
transparent outline via `createMapLabel`), save the map, reload the
profile, and verify the label doesn't appear bolded/outlined in black.
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Add hidden rooms as a feature, so the mapper can be aware of a room but
not necessarily draw it.
#### Motivation for adding to Mudlet
Closes#783
#### Other info (issues closed, discussion etc)
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Zooka <136661366+ZookaOnGit@users.noreply.github.com>
#### Brief overview of PR changes/additions
Fixes bugs in the small area centering feature (#8766) that caused maps
not centered around 0,0 to display incorrectly.
#### Motivation for adding to Mudlet
The centering feature from #8766 had two bugs: Y coordinates weren't
negated for the first room in area bounds calculation, and the center Y
calculation had wrong sign, causing areas to appear off-screen when
zoomed out.
#### Other info (issues closed, discussion etc)
Fixes the issues that led to #8813. This PR can replace that revert once
verified.
**Test case:** Load a map with areas not centered around 0,0, zoom out
past the point where the area fits in viewport - map should stay
centered correctly.
#### Brief overview of PR changes/additions
Ran clang-format on all 134 CPP files in src/ using the project's
.clang-format config
#### Motivation for adding to Mudlet
Ensures consistent code formatting across the codebase.
#### Other info (issues closed, discussion etc)
None
**Test case:** Build the project and verify it compiles successfully.
#### Brief overview of PR changes/additions
Text labels are now re-rendered at the current zoom level with dynamic
font sizing, eliminating blurriness when zoomed.
#### Motivation for adding to Mudlet
Map labels were blurry when zoomed because they used fast scaling
instead of re-rendering.
#### Other info (issues closed, discussion etc)
Font size selection removed from label dialog since it's now
auto-calculated to fit.
**Test case:** Create a text label on the map, set it to scale, zoom
in/out, verify text remains crisp at all zoom levels.
https://github.com/user-attachments/assets/14308b15-706a-4d73-a881-bb57416fecae
#### Brief overview of PR changes/additions
Open additional 2D map windows showing different areas of the same map.
Views are dockable and controlled via menu or Lua.
New Lua functions:
- `createMapView([areaId])` - open a new map window
- `closeMapView(viewId)` - close a specific window
- `closeAllMapViews()` - close all extra windows
- `getMapViewIds()` - list open windows
- `getMapViewInfo(viewId)` - get window state
Extended with optional `viewId` parameter:
- `centerview(roomId, [viewId])`
- `setMapZoom(zoom, [areaId], [viewId])`
- `getMapZoom([areaId], [viewId])`
#### Motivation for adding to Mudlet
Allows viewing multiple map areas simultaneously - useful for navigation
planning, comparing areas, or keeping a zoomed-out overview while
exploring.
Upvoted suggestion in
https://discord.com/channels/283581582550237184/792073945922142259/1402147549585866854
#### Other info (issues closed, discussion etc)
**Test case:**
1. Load a profile with a map
2. Window → New map window (or run `createMapView()`)
3. Select different area in new window's dropdown
4. Verify both views update when moving rooms
5. Dock/undock the new window
6. Close via X button or `closeMapView(id)`
https://github.com/user-attachments/assets/1cd38f37-5b3e-4ae2-83e4-ea9478b9f68c
#### Brief overview of PR changes/additions
Adds per-room border color and thickness settings for the 2D mapper.
Rooms can now have custom borders to visually distinguish them (e.g.,
indoor vs outdoor).
New Lua functions:
- `setRoomBorderColor(roomID, r, g, b[, a])` / `getRoomBorderColor()` /
`clearRoomBorderColor()`
- `setRoomBorderThickness(roomID, thickness)` /
`getRoomBorderThickness()` / `clearRoomBorderThickness()`
UI controls added to the room properties dialog.
#### Motivation for adding to Mudlet
[User
request](https://discord.com/channels/283581582550237184/792073945922142259/1457314371184365569)
to visually distinguish room types on maps, previously only possible in
CMUD.
#### Other info (issues closed, discussion etc)
**Test case:**
1. Open a map with rooms
2. Run: `setRoomBorderColor(1, 255, 0, 0)` and
`setRoomBorderThickness(1, 3)`
3. Room 1 should display with a thick red border
4. Run: `clearRoomBorderColor(1)` - border returns to global default
color
5. Right-click a room → Properties → Border section allows setting
color/thickness via UI
https://github.com/user-attachments/assets/1261b84f-0ba1-4719-9f56-1870ba51e4d1
I looked into bumping the map format to 21 and storing the data
natively, not using userdata - but then my map grew from 7.9MB to 8.7MB
without me having changed any room's data at all! Just the extra
structures, now empty, per room, added this might weight. In the end,
going with a "sparse" storage solution of using room userdata is more
space-efficient and less of an issue for backwards compatibility.
---------
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Reduce Mudlet build times
#### Motivation for adding to Mudlet
Address #6765, and give developers a better experience.
#### Other info (issues closed, discussion etc)
Benchmark Results
=== Testing: development (before) (development) ===
Run 1 of 3...
Time: 226.071397436s
Run 2 of 3...
Time: 216.059263209s
Run 3 of 3...
Time: 221.592760908s
=== Testing: PR #8403 (after) (pr-8403) ===
Run 1 of 3...
Time: 191.112400957s
Run 2 of 3...
Time: 193.975717783s
Run 3 of 3...
Time: 196.569316252s
[benchmark-mudlet-build.sh](https://github.com/user-attachments/files/24466717/benchmark-mudlet-build.sh)
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
New Lua API: setRoomBorderColor, getRoomBorderColor, clearRoomBorderColor,
setRoomBorderThickness, getRoomBorderThickness, clearRoomBorderThickness
Adds UI controls in room properties dialog. Rooms without custom
borders fall back to global settings. Map format version bumped to 21.
#### Brief overview of PR changes/additions
When the map fails to save automatically, a warning icon (⚠) now appears
next to the mapper's menu button. Clicking it lets you retry saving or
dismiss the warning. The icon disappears once the map saves
successfully.
#### Motivation for adding to Mudlet
Previously, if map autosave failed, users had no indication their map
changes might be lost. This provides a clear, non-intrusive visual
warning so users can take action before closing their profile.
#### Other info (issues closed, discussion etc)
Closes#6316
**Testing instructions:**
1. Open a profile with a map
2. Make the map directory read-only (or simulate a save failure)
3. Wait for autosave interval or trigger map changes
4. Verify ⚠ appears next to mapper menu (≡)
5. Click ⚠ → "Retry save" should attempt save
6. Click ⚠ → "Dismiss warning" should hide the icon
7. Restore write permissions, save map manually, verify icon clears
---------
Co-authored-by: Claude <noreply@anthropic.com>
#### Brief overview of PR changes/additions
Adds explicit type casts to fix "lossy function result cast" compiler
warnings across 7 files. These occur when wider numeric types (like
`double`) are assigned to narrower types (like `int`).
#### Motivation for adding to Mudlet
Cleaner build output with fewer warnings, making it easier to spot real
issues during development.
#### Other info (issues closed, discussion etc)
All casts are safe - values are either validated after conversion
(colors 0-255, expiry counts ≥1) or truncation is intentional (map grid
coordinates).
Co-authored-by: Claude <noreply@anthropic.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Fix a few places where miss checking for null (but usually do).
Discovered by static analysis ([link for Mudlet
devs](https://github.com/Mudlet/Mudlet/security/code-scanning))
#### Motivation for adding to Mudlet
Better code quality
#### Other info (issues closed, discussion etc)
---------
Co-authored-by: Claude <noreply@anthropic.com>
#### Brief overview of PR changes/additions
add a helper method that encapsulates directional exit validation and
weight selection
update initGraph to use the helper for all standard exits, removing
repeated code
add setExitWeightFilter that will accept callback function that will be
called for each room exit when creating pathfinding graph, so user can
alter values written in the map based on conditions of his choosing
#### Motivation for adding to Mudlet
so exit weight can be dynamically modified without modification of map
file
#### Other info (issues closed, discussion etc)
---------
Co-authored-by: Zooka <136661366+ZookaOnGit@users.noreply.github.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Remove support for leak detection using MSVC - this functionality hasn't
been in use in a decade, and we're switching to to using
[LeakSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer)
instead.
#### Motivation for adding to Mudlet
Cleaner code.
#### Other info (issues closed, discussion etc)
---------
Co-authored-by: Claude <noreply@anthropic.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Clear and hide room list combo box after deleteMap()
#### Motivation for adding to Mudlet
Clears and hides incorrect information. Better user experience.
#### Other info (issues closed, discussion etc)
closes#6860https://github.com/user-attachments/assets/f71700b7-f526-4158-b78f-7c786b740ea2
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Remove two messages. Lua API and map loaded messages.
<img width="2213" height="369" alt="Screenshot_20250907_123044"
src="https://github.com/user-attachments/assets/fa60e291-4a66-48ae-a112-dc7689a85f35"
/>
(first two lines)
#### Motivation for adding to Mudlet
Mudlet should just work, showing that it's working seems redundant.
Better user experience.
Excess messages are confusing for [brand new
players](https://wiki.mudlet.org/w/Mudlet:February_2021_Userstudy).
#### Other info (issues closed, discussion etc)
Mudlet will fail spectacularly with plenty of error messages if the Lua
API isn't present.
A corrupted map will also present a helpful message to the user.
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
This adds an experimental, new 3D mapper that uses shaders, more modern
openGL, and a far better code reorganization that makes it an easier
foundation to build upon.
The new 3D mapper is here side by side with the original and can be
toggled on for experimentation. There's a lot of work to be done, so I'd
rather merge it early instead of making a mega-PR.
#### Motivation for adding to Mudlet
So we have a new foundation to build upon and improve.
#### Other info (issues closed, discussion etc)
Old and new mapper can be toggled dynamically with:
```lua
-- this can be a keybinding
setConfig("experiment.3dmap.modernmapper", not getConfig("experiment.3dmap.modernmapper"))
```
Smooth movement is one experiment in the new mapper, and it can be
enabled with:
```lua
lua setConfig("experiment.rendering.smooth-camera", true)
```
As you notice an experiments system has been added so we can implement
things at once and experiment to choose the one that works best. This
system can be used in other places in Mudlet as well.
<details><summary>Details</summary>
<p>
## Experiments System
### Overview
Allows enabling/disabling experimental features via
`setConfig`/`getConfig` with validation against a predefined
whitelist.
### Usage
```lua
-- Enable experiment
setConfig("experiment.rendering.more-transparent", true)
-- Check if enabled
local enabled = getConfig("experiment.rendering.more-transparent") --
returns true/false
-- Get active experiment in group
local active = getConfig("experiment.rendering.active") -- returns
"more-transparent"
-- List all valid experiments
local experiments = getConfig("experiment.list") -- returns table of
valid keys
```
### Behavior
- Grouped experiments: Mutually exclusive (enabling one disables others in same group)
- Validation: Only predefined experiments allowed, invalid keys return errors
- Persistence: Experiment states saved/loaded with profiles
### Adding New Experiments
Edit Host::mValidExperiments in src/Host.cpp:
```cpp
const QSet<QString> Host::mValidExperiments = {
qsl("experiment.rendering.originalish"),
qsl("experiment.rendering.more-transparent"),
qsl("experiment.newfeature.option1"), // Add here
};
```
### Current Experiments
- experiment.rendering.originalish
- experiment.rendering.more-transparent
</p>
</details>
---------
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
#### Brief overview of PR changes/additions
In all places in our code where we use `Q_UNUSED(...)` to silence a
warning about an argument to a method/function that isn't used this PR
removes any following `;` as this macro does NOT need it.
#### Motivation for adding to Mudlet
For consistency across the entirety of our code, so that all our usage
of this macro is "correct" and doesn't include something that is
unneeded.
#### Other info (issues closed, discussion etc)
There are places in third-party code that does include it but it isn't
our job to clean those up!
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Attempt to remove all obsolete qt5 checks.
#### Motivation for adding to Mudlet
Migrate to qt6.
#### Other info (issues closed, discussion etc)
---------
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Add `enums.h` where we can add enums and flags to be used throughout the
application that don't have a more specific place to be, without
dragging in giant classes such as mudlet.h or Host.h.
Also added `enums::PackageModuleType` to track the magic numbers used
throughout package/module code 🚀
#### Motivation for adding to Mudlet
Simplify code organisation.
#### Other info (issues closed, discussion etc)
---------
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Apply fixes from clazy - mostly add const and references.
#### Motivation for adding to Mudlet
Better code quality and a potentially quicker Mudlet!
#### Other info (issues closed, discussion etc)
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Add an outline colour chooser to map label creation window in mapper.
Update createMapLabel Lua function to reflect this as well. Adds three
optional arguments to the end of createMapLabel( ..., outlineRed,
outlineGreen, outlineBlue), default to the same colour as foreground if
not specified.
Also, chosen colours weren't being saved from one dialog to the next
forcing unnecessary typing to replicate a colour scheme when creating
multiple labels, so fix this and allow chosen colours to also save
across Mudlet restarts as well.
#### Motivation for adding to Mudlet
Better user experience, more map design options, prettier maps.
#### Other info (issues closed, discussion etc)
- The outline width is only 1 pixel, but as fonts get smaller this
starts to become increasingly larger compared to the width of the small
font. Zoomed in fonts (or larger than 16 on my display) typically look
better.
- Based on this work I think it would be fairly trivial to add drop
shadows, mirroring and other text effects.

Zoomed in;

closes#2861
/claim #2861
#### Summary of PR Changes/Additions
Makes the coordinate members of the `TRoom` class private so that access
to them can be tracked via methods to set and get them.
#### Motivation for Adding to Mudlet
This is so that the setters can then subsequently include any extra code
that needs to be aware when the room is moved. I intend to improve the
detection of rooms being placed in the same position but realised this
would be a good preliminary step.
#### Additional Information (related issues, discussions, etc.)
Removes some dead code setting but not using `(int) quads` and `(int)
verts` in `(void) GLWidget::paintGL()`
Also using the mouse to drag and thus move selected rooms when those
rooms were on different levels would squash them all down to be on the
same z-coordinate as the "highlighted centre of the selection" room.
This is not as helpful it might seem and instead increased the
likelihood of causing room collisions - so now each room will retain
it's z coordinate if it is not on the same level as the centre of the
multiple room selection.
Also move code that likely needs to be run whenever rooms are
added/removed/moved within an area to a common block of code (`(void)
TArea::clean()`) to help keep things DRY. I intend to put code to update
a per area record of rooms that are in the same place within that block
in the future - so that the record can be reused without having to be
repeatedly recalculated, especially in the paint event for the 2D
mapper.
---------
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Speed up bulk room creation so people can make million-map rooms
quickly:
* addRoom now accepts the area to place the room in right away
* setRoomArea accepts an optional table of rooms instead of one room to
work in bulk
* setRoomArea will no longer update the mapper view automatically - but
this is fixed by https://github.com/Mudlet/Mudlet/pull/7187
#### Motivation for adding to Mudlet
https://discord.com/channels/283581582550237184/1215109133150199889/1217359192835358741
#### Other info (issues closed, discussion etc)
Somewhere along the line we forgot that
https://wiki.mudlet.org/w/Manual:Mapper_Functions#updateMap needs to be
called from scripts to update the map for this very reason - performance
#### Brief overview of PR changes/additions
Whilst working on getting the Windows CI process to run in a
MSYS2+Mingw-w64 environment on AppVeyor in both Qt 5 and 6 and both
32-bits and 64-bits (Qt6 only supports 64-Bit builds). I ran into a
number of warnings, some of them about things deprecated in Qt 6.0 or
later. This PR should eliminate all of them for our code (though there
are a couple in upstream things).
#### Motivation for adding to Mudlet
Make the build process cleaner all around, especially with moving
forward to Qt 6.
#### Other info (issues closed, discussion etc)
The use of `std::as_const(...)` requires C++17 but we have already
mandated that. `qAsConst(...)` is deprecated in Qt 6.
Some of the places where the above was being done also were missing the
use of a `const` reference rather than the making of a constant copy of
the iterated values; these have been fixed as well.
A couple of Mudlet classes that I haven't yet cleaned up to move as much
of the class initialisation to the header as possible were reporting
initialisation ordering issue (`Host` and `TTimer`). I have fixed those
but only in the region of the issues, more work there is desirable to
clean up every remaining class - but I'm not allowed to leave "TODO:"
comments around nowadays! 😀
`(void) zip_error_to_str(char*, size_t, int, int))` has been obsoleted
for a long time now, and I've finally put in something in a couple of
places that will use the recommended replacement
`(zip_error_t*) zip_get_error(zip*)` and dump the error message out to
the OS console - which was not happening in the past.
`(QString) QString::fromUtf16(...)` has been obsoleted and alternatives
are suggested within the Qt documentation. I've used
`QString::fromWCharArray(...)`. Whilst this compiles ***I am not 100%
sure I have this correct and a second opinion on this change in
`./src/mudlet.cpp` is desirable!***
Qt is renaming in Qt6 a few methods that otherwise function as before:
* `(Qt::KeyboardModifiers) QDragEnterEvent::keyboardModifiers()` ==>
`QDragEnterEvent::modifiers()`
* `(Qt::KeyboardModifiers) QDragMoveEvent::keyboardModifiers()` ==>
`QDragMoveEvent::modifiers()`
* `(bool) QColor::isValidColor(const QString&)` ==> `(bool)
QColor::isValidColorName(QAnyStringView)`
* `(void) QColor::setNamedColor(const QString&)` ==> `(QColor)
QColor::fromString(QAnyStringView)`
* `(QString) QLocale::countryToString(Country)` ==> `(QString)
QLocale::territoryToString(Territory)`
Windows NTFS permissions checking was being done with a really low-level
procedure which has been deprecated in Qt 6.6 and replaced with a
slightly better (but also low-level) pair of functions:
* `(bool) qEnableNtfsPermissionChecks()`
* `(bool) qEnableNtfsPermissionChecks()`
to do the same thing in almost the same way with a lesser risk of a
"race-condition". There is a higher-level procedure involving the use of
a new class `QNtfsPermissionCheckGuard` but that is a different way of
doing things that is not a drop-in replacement AFAICT.
There was an unhandled `case` (for `QTextToSpeech::State::Synthesizing`)
in `(void) TLuaInterpreter::ttsStateChanged(QTextToSpeech::State)` -
I've put in something to report that state but it is not clear that
this, seemingly, transient state, needs anything extra than that. For
instance, given that it looks to be associated with preparing a text to
be spoken it might be reasonable to report the text involved as the
`Speaking` state does... The point at which it was introduced is also
unclear as that isn't documented!
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
Co-authored-by: Vadim Peretokin <vperetokin@hey.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Automatically update map on all map changes via API while not losing
performance.
#### Motivation for adding to Mudlet
Better user experience.
#### Other info (issues closed, discussion etc)
Alleviates the need for calling
https://wiki.mudlet.org/w/Manual:Mapper_Functions#updateMap and fixes
the inconsistency where some functions were updating the map
automatically and others haven't.
---------
Co-authored-by: Vadim Peretokin <vadim.peretokin@carasent.com>
Co-authored-by: Kebap <kebap_spam@gmx.net>
#### Brief overview of PR changes/additions
This PR tries to put all `const`s before the type (class).
#### Motivation for adding to Mudlet
There is a mix of positioning of `const`s where it is used to indicate
that a variable is not to be modified by program code, we tend to put it
before the type but a prior PR (which looks to have been done with an
automated tool) has resulted in a mix of cases some with the const
adjacent to the variable. This lack of consistency can be confusing.
#### Other info (issues closed, discussion etc)
It looks like this came about in #6843.
This will upset the Danger detector because of the number of files
modified but it should be fairly straightforward to review.
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Pass Mudlet version in an embedded resource file instead of compiler
argument to every single file. Passing it as a compiler argument
invalidates ccache's caching, because the commit ID is different every
time.
Now, ideally, only the actually changed files on disk will invalidate
ccache's cache.
#### Motivation for adding to Mudlet
More cache-friendly PTB and test builds, helping get those out the door
quicker! A Windows appveyor build takes 31min right now and is not
affected by caching much at all. With this improvement they are sped up
by 70% and only take 8min.
#### Other info (issues closed, discussion etc)
Won't affect local development builds (those are sped up by ccache
already).
It does seem to be working! Windows builds normally take 30min:

With quite a lot of cache misses:

But the builds in this branch take 8min:

With quite a lot of cache hits:

#### Brief overview of PR changes/additions
`static_cast<qint32>(...)` all Qt container `size()`/`count()`s when
writing them to a binary map file.
#### Motivation for adding to Mudlet
The return type from a Qt "container" type (well in fact ALL classes)
`size()`/`count()` operation is now defined as `qsizetype` which just
happens to effectively be a `int64_t` (`qint64`, 8 bytes) unfortunately
in the past - for Qt5 it was an `int`, which for all the platforms we
compile Mudlet on corresponds to an `int32_t` (`qint32`, 4 bytes). The
differences in size means that writing them to a `QDataStream` produces
incompatible files. The simplest fix, given that we are not expecting
*any* value so produced to overflow the prior `int32_t` type is to
explicitly `static_cast` the values in all cases.
#### Other info (issues closed, discussion etc)
This will close#7003
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
#### Brief overview of PR changes/additions
This PR removes the (unhelpful) translation to user's locale.
#### Motivation for adding to Mudlet
Whilst describing how the SpeedWalking code operated last night on
Discord in the **#help** channel:
https://www.discord.com/channels/283581582550237184/283582068334526464/1142680496254484561
I realised that we had been passing the normal exit directions through
the translation system for the end-users GUI language. This is wrong
because there is not necessarily a match between the language the
end-user prefers and that used by any MUD they might play. As such it
means that the normal exit "abbreviations" that the speedwalking code
uses to fill in the `speedWalkDir` table change depending on which GUI
language is selected and this means that the "mapper" package for any
MUD has to know that and then translate them to the language that the
MUD uses - as they need not be the same.
#### Other info (issues closed, discussion etc)
Examining the various `translations/translated/mudlet_xx_YY.ts` files I
have determined that this bogus translation in the user's GUI language
happens for the following language/country codes (the code that is used
for the others and for all cases when this PR is place without the bogus
translation being done AND the "direction codes" also used within the
Lua sub-system are also shown):
|code|Language
(Country)|North|North-east|North-west|East|West|South|South-east|South-west|Up|Down|In|out|
|-|-|-|-|-|-|-|-|-|-|-|-|-|-|
||Direction code|`1`|`2`|`3`|`4`|`5`|`6`|`7`|`8`|`9`|`10`|`11`|`12`|
||All others (or after this
PR)|`n`|`ne`|`nw`|`e`|`w`|`s`|`se`|`sw`|`up`|`down`|`in`|`out`|
|ar_SA|Arabic (Saudi
Arabia)|`n`(?)|`ne`(?)|`nw`(?)|`e`(?)|`ص`|`s`(?)|`se`(?)|`sw`(?)|`فوق`|`تحت`|`للداخل`|`للخارج`
|de_DE|German
(Germany)|`n`|`no`|`nw`|`o`|`w`|`s`|`so`|`sw`|`oben`|`unten`|`rein`|`raus`|
|es_ES|Spanish
(Spain)|`n`|`ne`|`no`|`e`|`o`|`s`|`se`|`so`|`arriba`|`abajo`|`adentro`|`afuera`|
|fr_FR|French
(France)|`n`|`ne`|`no`|`e`|`o`|`s`|`se`|`so`|`haut`|`bas`|`entrer`|`sortir`|
|it_IT|Italian
(Italy)|`n`|`ne`|`no`|`e`|`o`|`s`|`se`|`so`|`alto`|`basso`|`dentro`|`fuori`|
|nl_NL|Dutch
(Netherlands)|`n`|`no`|`nw`|`o`|`w`|`s`(?)|`zo`|`zw`|`omhoog`|`omlaag`|`in`|`uit`|
|pl_PL|Polish (Poland)|`n`|`ne`|`nw`|`e`|`w`|`s`|`se`|`sw`|`u`|`d`|`do
środka`|`na zewnątrz`|
|pt_BR|Portuguese
(Brazil)|`n`|`ne`(?)|`nw`(?)|`e`(?)|`w`(?)|`s`|`se`(?)|`sw`(?)|`cima`|`baixo`|`dentro`|`fora`|
|pt_PT|Portuguese
(Portugal)|`n`|`ne`(?)|`no`|`e`(?)|`o`|`s`|`se`(?)|`so`|`cima`|`baixo`|`dentro`|`fora`|
|ru_RU|Russian
(Russia)|`n`|`ne`|`nw`|`e`|`w`|`s`|`se`|`sw`|`up`|`down`|`in`|`out`|
|tr_TR|Turkish
(Türkiye)|`k`|`kd`|`kb`|`d`|`b`|`g`|`gd`|`gb`|`y`|`a`|`i`|`d`(!)|
|zh_TW|Chinese
(Traditional)|`n`|`ne`|`nw`|`e`|`w`|`s`|`se`|`sw`|`上`|`下`|`入口`|`出口`|
Note that there are some suspect strings in there (?) and also one case
where the same code is produced for two different exit directions -
which is not just suspect, but probably wrong!
---------
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>