|
From: <xer...@us...> - 2026-07-24 20:22:40
|
Revision: 1680
http://sourceforge.net/p/seq/svn/1680
Author: xerxes00
Date: 2026-07-24 20:22:38 +0000 (Fri, 24 Jul 2026)
Log Message:
-----------
Add command-line options to select data locations for multiple configs
ShowEQ locates its user config, opcode files, EQ client data files, and
maps relative to a single per-user directory ($HOME/.showeq). Running
several independent configurations (e.g. one per server or game version)
therefore meant juggling $HOME, which also affects everything else the
process resolves from home. These options let a location be chosen
explicitly instead, so multiple configs can coexist from one install
without repurposing $HOME.
New options:
--user-dir=DIR Use DIR as the per-user data directory instead of
$HOME/.showeq (resolved before the config file is
read, so it must be scanned ahead of getopt_long).
--game-files=DIR Read spells_us.txt, dbstr_us.txt and eqstr_us.txt
from DIR (these differ per game version).
--maps-dir=DIR Shared maps directory, searched (read and write)
ahead of the user/pkg maps dirs. Also settable as
the MapDir preference under [MapMgr].
--zone-opcodes=FILE Override the zone/world opcodes file for this run.
--world-opcodes=FILE
--dump-config Print every effective preference with its value and
source (cmdline/user/default), then exit.
DataLocationMgr::findExistingFile() now uses an absolute filename as-is,
so any file preference (SpellsFile, ZoneOPCodes, ...) can point at a file
shared between configurations rather than copied into each user dir.
Command-line overrides are applied as Runtime prefs (not written back to
showeq.xml). At startup a short summary of any active location overrides
is printed. All options are optional; with none given, behavior is
unchanged. Documented in --help, showeq.1, and README.
Modified Paths:
--------------
showeq/branches/xerxes-trunk-dev/README
showeq/branches/xerxes-trunk-dev/showeq.1
showeq/branches/xerxes-trunk-dev/src/datalocationmgr.cpp
showeq/branches/xerxes-trunk-dev/src/datalocationmgr.h
showeq/branches/xerxes-trunk-dev/src/main.cpp
showeq/branches/xerxes-trunk-dev/src/xmlpreferences.cpp
showeq/branches/xerxes-trunk-dev/src/xmlpreferences.h
Modified: showeq/branches/xerxes-trunk-dev/README
===================================================================
--- showeq/branches/xerxes-trunk-dev/README 2026-07-24 02:40:56 UTC (rev 1679)
+++ showeq/branches/xerxes-trunk-dev/README 2026-07-24 20:22:38 UTC (rev 1680)
@@ -52,6 +52,41 @@
The file name each feature looks for can be overridden in the config
(e.g. the DBStrFile setting under [Interface]).
+CHOOSING DATA LOCATIONS
+-----------------------
+Several command-line options select where ShowEQ reads and writes, which
+makes it easy to run more than one independent configuration (for example
+one per server or game version) from a single install:
+
+ --user-dir=DIR Use DIR as the per-user data directory (config, logs,
+ opcodes, filters) instead of $HOME/.showeq. Each config
+ gets its own DIR; nothing is shared through $HOME.
+ --game-files=DIR Read the EverQuest client data files (spells_us.txt,
+ dbstr_us.txt, eqstr_us.txt) from DIR. These differ per
+ game version, so a directory per version keeps one copy
+ of each rather than duplicating them.
+ --maps-dir=DIR Use DIR as a shared maps directory (maps are usually the
+ same across configurations). Also settable as the MapDir
+ preference under [MapMgr]; the option overrides the pref.
+ --zone-opcodes=FILE Override the zone opcodes file for this run. FILE may be
+ --world-opcodes=FILE a bare name (found in the data dir) or an absolute path
+ to a file shared elsewhere on disk.
+
+Any file preference (SpellsFile, DBStrFile, ZoneOPCodes, ...) may be set to
+an absolute path in the config, in which case that exact file is used - a
+convenient way to share one copy of a data file between configurations
+instead of copying it into each user directory.
+
+Command-line overrides apply for that run only and are not written back to
+your config file. Preference lookup order is: command line, then your config
+file (showeq.xml), then the installed defaults (seqdef.xml). Run
+
+ ./showeq --dump-config
+
+to print every effective preference, its value, and which of those three it
+comes from - useful for seeing settings that are supplied by the defaults
+file rather than your own config.
+
ALTERNATE ADVANCEMENT WINDOW
----------------------------
View -> Alternate Advancement lists the AAs your character owns, with the
Modified: showeq/branches/xerxes-trunk-dev/showeq.1
===================================================================
--- showeq/branches/xerxes-trunk-dev/showeq.1 2026-07-24 02:40:56 UTC (rev 1679)
+++ showeq/branches/xerxes-trunk-dev/showeq.1 2026-07-24 20:22:38 UTC (rev 1680)
@@ -17,9 +17,40 @@
.PP
.SH "OPTIONS"
.PP
-.IP "\fB-i <device>\fP"
+.IP "\fB-i <device>\fP"
Specify which network device to bind to
-.IP
+.IP
+.IP "\fB--user-dir <dir>\fP"
+Use <dir> as the user data directory (per-user config, logs, maps and
+filters) instead of the default ~/.showeq. Lets several independent
+ShowEQ configurations coexist without relying on $HOME.
+.IP
+.IP "\fB--zone-opcodes <file>\fP"
+Use <file> as the zone opcodes file for this session, overriding the
+ZoneOPCodes preference. <file> may be a bare name (resolved in the data
+directory) or an absolute path to a file shared elsewhere on disk.
+.IP
+.IP "\fB--world-opcodes <file>\fP"
+As --zone-opcodes, but for the world opcodes file (WorldOPCodes).
+.IP
+.IP "\fB--game-files <dir>\fP"
+Read the EverQuest client data files (spells_us.txt, dbstr_us.txt and
+eqstr_us.txt) from <dir>. These differ per game version, so a directory
+per version keeps one copy of each instead of duplicating them in every
+user directory.
+.IP
+.IP "\fB--maps-dir <dir>\fP"
+Use <dir> as a shared maps directory, searched (for both reading and
+writing) ahead of the per-user and package maps directories. Lets a
+single set of maps be shared between ShowEQ configurations. Overrides the
+MapDir preference (section MapMgr) if both are given.
+.IP
+.IP "\fB--dump-config\fP"
+Print every effective preference, its value, and the tier it comes from
+(cmdline / user / default), then exit. Command-line overrides are applied
+first, so this shows exactly what ShowEQ would use. Useful for seeing
+settings that come from the defaults file rather than your own config.
+.IP
.IP "\fB-b\fP"
Broken Decode option to skip spawn packet handling
when Verant changes the coding.
Modified: showeq/branches/xerxes-trunk-dev/src/datalocationmgr.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/datalocationmgr.cpp 2026-07-24 02:40:56 UTC (rev 1679)
+++ showeq/branches/xerxes-trunk-dev/src/datalocationmgr.cpp 2026-07-24 20:22:38 UTC (rev 1680)
@@ -30,13 +30,19 @@
#include <QFileInfo>
#include <QRegExp>
-DataLocationMgr::DataLocationMgr(const QString& homeSubDir)
+DataLocationMgr::DataLocationMgr(const QString& homeSubDir,
+ const QString& configDir)
{
// create package directory object
m_pkgData = PKGDATADIR;
- // create the user directory object
- m_userData = QDir::homePath() + "/" + homeSubDir;
+ // create the user directory object; an explicit user directory (from the
+ // --user-dir command-line option) is used verbatim, otherwise it defaults
+ // to homeSubDir under the user's home directory (i.e. ~/.showeq).
+ if (!configDir.isEmpty())
+ m_userData = configDir;
+ else
+ m_userData = QDir::homePath() + "/" + homeSubDir;
}
DataLocationMgr::~DataLocationMgr()
@@ -63,11 +69,32 @@
return true;
}
-QFileInfo DataLocationMgr::findExistingFile(const QString& subdir,
+QFileInfo DataLocationMgr::findExistingFile(const QString& subdir,
const QString& filename,
- bool caseSensitive,
+ bool caseSensitive,
bool preferUser) const
{
+ // an absolute filename is used as-is, bypassing the data-directory search.
+ // This lets a preference or command-line option (e.g. --zone-opcodes) point
+ // at a file anywhere on disk, including one shared between several ShowEQ
+ // configurations rather than copied into each user directory.
+ if (QDir::isAbsolutePath(filename))
+ return QFileInfo(filename);
+
+ // a shared maps directory (--maps-dir), when set, is searched ahead of the
+ // per-user/pkg maps dirs; fall through to the normal search if the file is
+ // not found there.
+ if (subdir == "maps" && !m_mapsDir.isEmpty())
+ {
+ QDir mapsDir(m_mapsDir);
+ if (mapsDir.exists())
+ {
+ QFileInfo fi = findFile(mapsDir, filename, caseSensitive, false);
+ if (fi.exists())
+ return fi;
+ }
+ }
+
// find the file using the preferred search ordering
if (preferUser)
return findFile(m_userData, m_pkgData, subdir, filename, caseSensitive);
@@ -80,6 +107,14 @@
bool caseSensitive,
bool preferUser) const
{
+ // writes to the maps subdir go to the shared maps directory when one is set
+ if (subdir == "maps" && !m_mapsDir.isEmpty())
+ {
+ QFileInfo mapsDirInfo(m_mapsDir);
+ if (mapsDirInfo.exists() && mapsDirInfo.isDir() && mapsDirInfo.isWritable())
+ return QFileInfo(QDir(m_mapsDir), filename);
+ }
+
if (preferUser)
return findWriteFile(m_userData, m_pkgData, subdir, filename,
caseSensitive);
Modified: showeq/branches/xerxes-trunk-dev/src/datalocationmgr.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/datalocationmgr.h 2026-07-24 02:40:56 UTC (rev 1679)
+++ showeq/branches/xerxes-trunk-dev/src/datalocationmgr.h 2026-07-24 20:22:38 UTC (rev 1680)
@@ -31,9 +31,14 @@
class DataLocationMgr
{
public:
- DataLocationMgr(const QString& homeSubDir);
+ DataLocationMgr(const QString& homeSubDir, const QString& configDir = QString());
~DataLocationMgr();
bool setupUserDirectory();
+
+ // Set a shared maps directory (from --maps-dir). When set, map files are
+ // looked up here ahead of the per-user/pkg maps dirs, so a single copy of
+ // the maps can be shared between ShowEQ configurations.
+ void setMapsDir(const QString& dir) { m_mapsDir = dir; }
QFileInfo findExistingFile(const QString& subdir, const QString& filename,
bool caseSensitive = false,
bool preferUser = true) const;
@@ -57,6 +62,7 @@
QString m_pkgData;
QString m_userData;
+ QString m_mapsDir;
};
#endif // _DATALOCATIONMGR_H_
Modified: showeq/branches/xerxes-trunk-dev/src/main.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/main.cpp 2026-07-24 02:40:56 UTC (rev 1679)
+++ showeq/branches/xerxes-trunk-dev/src/main.cpp 2026-07-24 20:22:38 UTC (rev 1680)
@@ -99,6 +99,12 @@
#define RESTORE_ZONE_STATE 7
#define RESTORE_SPAWNS 8
#define RESTORE_ALL 9
+#define USER_DIR_OPTION 128
+#define ZONE_OPCODES_OPTION 129
+#define WORLD_OPCODES_OPTION 130
+#define GAME_FILES_OPTION 131
+#define MAPS_DIR_OPTION 132
+#define DUMP_CONFIG_OPTION 133
/* Note that ASCII 32 is a space, best to stop at 31 and pick up again
at 128 or higher
@@ -142,6 +148,12 @@
{"default-class", required_argument, NULL, 'Z'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
+ {"user-dir", required_argument, NULL, USER_DIR_OPTION},
+ {"zone-opcodes", required_argument, NULL, ZONE_OPCODES_OPTION},
+ {"world-opcodes", required_argument, NULL, WORLD_OPCODES_OPTION},
+ {"game-files", required_argument, NULL, GAME_FILES_OPTION},
+ {"maps-dir", required_argument, NULL, MAPS_DIR_OPTION},
+ {"dump-config", no_argument, NULL, DUMP_CONFIG_OPTION},
{"ip-address", required_argument, NULL, IPADDR_OPTION},
{"mac-address", required_argument, NULL, MACADDR_OPTION},
{"global-log-filename", required_argument, NULL, GLOBAL_LOG_FILENAME_OPTION},
@@ -183,6 +195,7 @@
int option_index = 0;
bool bOptionHelp = false;
+ bool bDumpConfig = false;
#undef OFFTEST
#ifdef OFFTEST
@@ -207,9 +220,31 @@
/* Print the version number */
displayVersion();
- // create the data location manager (with user data under ~/.showeq
- DataLocationMgr dataLocMgr(".showeq");
+ // --user-dir must be resolved before the DataLocationMgr is constructed,
+ // because the configuration file is read (below) before getopt_long()
+ // parses the command line further down. Do a minimal early scan for it
+ // here; the option is still listed in option_list so it is accepted (as a
+ // no-op) by the main parse loop and appears in the usage output.
+ QString userDir;
+ // additional data-location overrides captured during option parsing, for
+ // the startup summary printed once the command line has been processed
+ QString mapsDirOverride, gameFilesOverride, zoneOpcodesOverride, worldOpcodesOverride;
+ for (int i = 1; i < argc; i++)
+ {
+ const QString arg = QString::fromLocal8Bit(argv[i]);
+ if (arg == "--user-dir")
+ {
+ if (i + 1 < argc)
+ userDir = QString::fromLocal8Bit(argv[++i]);
+ }
+ else if (arg.startsWith("--user-dir="))
+ userDir = arg.section('=', 1);
+ }
+ // create the data location manager (with user data under ~/.showeq,
+ // unless --user-dir provided an explicit directory)
+ DataLocationMgr dataLocMgr(".showeq", userDir);
+
// setup the user directory
dataLocMgr.setupUserDirectory();
@@ -292,6 +327,14 @@
showeq_params->saveRestoreBaseFilename = dataLocMgr.findWriteFile("tmp", pSEQPrefs->getPrefString("BaseFilename", section, "last")).absoluteFilePath();
showeq_params->filterZoneDataLog = 0;
+ // a shared maps directory may be set in the config file (MapDir); the
+ // --maps-dir command-line option below overrides it.
+ {
+ QString mapDirPref = pSEQPrefs->getPrefString("MapDir", "MapMgr", "");
+ if (!mapDirPref.isEmpty())
+ dataLocMgr.setMapsDir(mapDirPref);
+ }
+
/* Parse the commandline for commandline parameters */
while ((opt = getopt_long( argc,
argv,
@@ -716,6 +759,53 @@
}
+ /* Override the zone/world opcode file for this session. The value may
+ be a bare filename (resolved in the user/pkg data dir) or an
+ absolute path to a file anywhere on disk. */
+ case ZONE_OPCODES_OPTION:
+ zoneOpcodesOverride = QString::fromLocal8Bit(optarg);
+ pSEQPrefs->setPrefString("ZoneOPCodes", "Network", optarg,
+ XMLPreferences::Runtime);
+ break;
+
+ case WORLD_OPCODES_OPTION:
+ worldOpcodesOverride = QString::fromLocal8Bit(optarg);
+ pSEQPrefs->setPrefString("WorldOPCodes", "Network", optarg,
+ XMLPreferences::Runtime);
+ break;
+
+ /* --game-files DIR: point the EQ client data files (spells, dbstr,
+ eqstr) at DIR. These differ per game version, so grouping them under
+ one directory keeps a single copy per version. */
+ case GAME_FILES_OPTION:
+ gameFilesOverride = QString::fromLocal8Bit(optarg);
+ pSEQPrefs->setPrefString("SpellsFile", "Interface",
+ QString::fromLocal8Bit(optarg) + "/spells_us.txt",
+ XMLPreferences::Runtime);
+ pSEQPrefs->setPrefString("DBStrFile", "Interface",
+ QString::fromLocal8Bit(optarg) + "/dbstr_us.txt",
+ XMLPreferences::Runtime);
+ pSEQPrefs->setPrefString("FormatFile", "Interface",
+ QString::fromLocal8Bit(optarg) + "/eqstr_us.txt",
+ XMLPreferences::Runtime);
+ break;
+
+ /* --maps-dir DIR: use DIR as a shared maps directory, searched ahead
+ of the per-user/pkg maps dirs (maps are the same across versions). */
+ case MAPS_DIR_OPTION:
+ mapsDirOverride = QString::fromLocal8Bit(optarg);
+ dataLocMgr.setMapsDir(mapsDirOverride);
+ break;
+
+ /* Print the effective configuration and exit (handled after the loop) */
+ case DUMP_CONFIG_OPTION:
+ bDumpConfig = true;
+ break;
+
+ /* Handled by the early --user-dir pre-scan above; accept as no-op */
+ case USER_DIR_OPTION:
+ break;
+
/* Spit out the help */
case 'h': /* Fall through */
default:
@@ -732,6 +822,33 @@
exit (0);
}
+ // Report any data-location overrides given on the command line, so it is
+ // clear at startup which non-default directories/files are in use.
+ if (!userDir.isEmpty() || !gameFilesOverride.isEmpty() ||
+ !mapsDirOverride.isEmpty() || !zoneOpcodesOverride.isEmpty() ||
+ !worldOpcodesOverride.isEmpty())
+ {
+ printf("ShowEQ location overrides:\n");
+ if (!userDir.isEmpty())
+ printf(" user dir: %s\n", userDir.toLatin1().data());
+ if (!gameFilesOverride.isEmpty())
+ printf(" game files: %s\n", gameFilesOverride.toLatin1().data());
+ if (!mapsDirOverride.isEmpty())
+ printf(" maps dir: %s\n", mapsDirOverride.toLatin1().data());
+ if (!zoneOpcodesOverride.isEmpty())
+ printf(" zone opcodes: %s\n", zoneOpcodesOverride.toLatin1().data());
+ if (!worldOpcodesOverride.isEmpty())
+ printf(" world opcodes: %s\n", worldOpcodesOverride.toLatin1().data());
+ }
+
+ // dump the effective configuration (with command-line overrides applied)
+ // and exit, for config discoverability.
+ if (bDumpConfig)
+ {
+ pSEQPrefs->dumpEffectivePreferences();
+ exit (0);
+ }
+
/* Set up individual files for logging selected packet types based on
a common filename base. The types to log were found by following
where pre_worked was a precondition for further analysis.
@@ -830,6 +947,18 @@
printf (" -h, --help Shows this help\n");
printf (" -o CONFIGFILE Alternate showeq.xml pathname\n");
+ printf (" --user-dir=DIR User data directory for config, logs,\n");
+ printf (" maps and filters (default: ~/.showeq)\n");
+ printf (" --zone-opcodes=FILE Use FILE as the zone opcodes file\n");
+ printf (" (bare name in the data dir, or abs path)\n");
+ printf (" --world-opcodes=FILE Use FILE as the world opcodes file\n");
+ printf (" (bare name in the data dir, or abs path)\n");
+ printf (" --game-files=DIR Read spells_us.txt, dbstr_us.txt and\n");
+ printf (" eqstr_us.txt from DIR (per game version)\n");
+ printf (" --maps-dir=DIR Shared maps directory, searched ahead of\n");
+ printf (" the user/pkg maps dirs\n");
+ printf (" --dump-config Print every effective preference and its\n");
+ printf (" source (cmdline/user/default), then exit\n");
printf (" -V, --version Prints ShowEQ version number\n");
printf (" -i, --net-interface=DEVICE Specify which network device to bind to\n");
printf (" -r, --realtime Set the network thread realtime\n");
Modified: showeq/branches/xerxes-trunk-dev/src/xmlpreferences.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/xmlpreferences.cpp 2026-07-24 02:40:56 UTC (rev 1679)
+++ showeq/branches/xerxes-trunk-dev/src/xmlpreferences.cpp 2026-07-24 20:22:38 UTC (rev 1680)
@@ -37,6 +37,7 @@
#include <QRegExp>
#include <QKeySequence>
#include <QTextStream>
+#include <QSet>
const float seqPrefVersion = 1.0;
@@ -148,7 +149,54 @@
loadPreferences(m_filename, m_userSections);
}
-void XMLPreferences::loadPreferences(const QString& filename,
+void XMLPreferences::dumpEffectivePreferences() const
+{
+ // union of all section names across the three tiers
+ QSet<QString> sectionSet;
+ for (PrefSectionDict::const_iterator it = m_runtimeSections.constBegin();
+ it != m_runtimeSections.constEnd(); ++it)
+ sectionSet.insert(it.key());
+ for (PrefSectionDict::const_iterator it = m_userSections.constBegin();
+ it != m_userSections.constEnd(); ++it)
+ sectionSet.insert(it.key());
+ for (PrefSectionDict::const_iterator it = m_defaultsSections.constBegin();
+ it != m_defaultsSections.constEnd(); ++it)
+ sectionSet.insert(it.key());
+
+ QStringList sections = sectionSet.values();
+ sections.sort();
+
+ printf("Effective ShowEQ preferences (name = value <- source):\n");
+ for (const QString& section : sections)
+ {
+ printf("[%s]\n", section.toLatin1().data());
+
+ PreferenceDict* rd = m_runtimeSections.value(section, nullptr);
+ PreferenceDict* ud = m_userSections.value(section, nullptr);
+ PreferenceDict* dd = m_defaultsSections.value(section, nullptr);
+
+ // union of preference names in this section across the three tiers
+ QSet<QString> nameSet;
+ if (rd) for (PreferenceDict::const_iterator it = rd->constBegin(); it != rd->constEnd(); ++it) nameSet.insert(it.key());
+ if (ud) for (PreferenceDict::const_iterator it = ud->constBegin(); it != ud->constEnd(); ++it) nameSet.insert(it.key());
+ if (dd) for (PreferenceDict::const_iterator it = dd->constBegin(); it != dd->constEnd(); ++it) nameSet.insert(it.key());
+
+ QStringList names = nameSet.values();
+ names.sort();
+ for (const QString& name : names)
+ {
+ QVariant* v = nullptr;
+ const char* src = "?";
+ if (rd && (v = rd->value(name, nullptr)) != nullptr) src = "cmdline";
+ else if (ud && (v = ud->value(name, nullptr)) != nullptr) src = "user";
+ else if (dd && (v = dd->value(name, nullptr)) != nullptr) src = "default";
+ printf(" %-32s = %-26s <- %s\n", name.toLatin1().data(),
+ v ? v->toString().toLatin1().data() : "", src);
+ }
+ }
+}
+
+void XMLPreferences::loadPreferences(const QString& filename,
PrefSectionDict& dict)
{
QDomDocument doc(seqPrefName);
Modified: showeq/branches/xerxes-trunk-dev/src/xmlpreferences.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/xmlpreferences.h 2026-07-24 02:40:56 UTC (rev 1679)
+++ showeq/branches/xerxes-trunk-dev/src/xmlpreferences.h 2026-07-24 20:22:38 UTC (rev 1680)
@@ -196,7 +196,12 @@
void save(); // Saves all modified values to the appropriate file
void load(); // Loads values from files into the appropriate caches
void revert(); // Reloads values from files into the appropriate caches
-
+
+ // print every effective preference (across all sections) to stdout, showing
+ // its value and which tier it comes from (cmdline / user / default). Used by
+ // the --dump-config command-line option for config discoverability.
+ void dumpEffectivePreferences() const;
+
protected:
// loads a preference dictionary from the specified filename
void loadPreferences(const QString& filename, PrefSectionDict& dict);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|