mirror of
https://github.com/brazilofmux/tinymux
synced 2026-08-13 00:23:11 -04:00
Every classic telnet connect waited a hardcoded 500ms in total silence before the server sent its first byte. Measured over 30 connects to an idle server: min 500.6ms, p50 501.0ms, max 504.5ms -- the distribution is the grace window itself. Base command latency on the same setup is ~51us. The window exists so telnet negotiation cannot corrupt a WebSocket handshake (#1074), and the incentives are inverted: WebSocket and TLS clients speak first and are served immediately, while the classic MUD client -- which waits for the server to speak, and is the primary audience -- is the only kind that always pays in full. Now `proto_detect_window`, in milliseconds, default 500. Nothing changes for a site that does not touch it. 0 disables detection: the banner goes out at accept, as in 2.13. That is the correct setting for a port that never serves WebSocket -- it has nothing to detect and no reason to wait. On keeping 500 as the default rather than shaving it: the window is NOT covering a round trip, which is what the original issue text assumed. A WebSocket client's GET rides directly behind the handshake's final ACK, so the healthy case needs ~0ms however distant the client -- the RTT is already spent by the time the window opens. What the window must survive is that first packet being LOST, where the retransmit arrives on an RTO that Linux floors at 200ms. 500 covers one retransmit with headroom. 100 would sit in the dead zone -- past every healthy client, short of every retransmit -- and would break real WebSocket handshakes intermittently on a lossy link. A slow banner is a far better failure mode than that. The value was a literal in two places (the age-out sweep and the main-loop timeout clamp) with nothing tying them together, so they could drift and the clamp would silently stop bounding the sweep. One accessor now. 0 is expressed as arithmetic rather than a second finalize call site: with the window at 0, `age >= 0` is true on the first sweep and the clamp drives processEvents to a 0ms timeout, so the connection finalizes in the same main-loop iteration it was accepted in. A shortcut around the sweep would have had to duplicate both the #2018 exception barrier and the #1800 partial-preface replay. IID_IGameEngine bumped C9D2 -> C9D3. The vtable is unchanged, but GetConfig() memsets and fills sizeof(DRIVER_CONFIG) as the ENGINE sees it into storage the DRIVER sized, so a size disagreement is an out-of-bounds write rather than a wrong answer. tests/scenario/proto_detect.py asserts it against a live server: ok 1 - silent client waits the configured proto-detect window # 500 ms ok 2 - client that speaks first is served without waiting # 0 ms ok 3 - engine accepted the runtime @admin to 0 # config='0' ok 4 - #2193 window 0 serves a silent client at accept # 0 ms ok 5 - restoring the window restores the wait (live push, both ways) Catch-verified by reverting only the driver's USE of the knob, leaving the config plumbing intact -- the #1222 shape, where the engine reports the change and the driver ignores it until restart: ok 3 - engine accepted the runtime @admin to 0 # config='0' not ok 4 - #2193 window 0 serves a silent client at accept (500 ms) Clean rebuild, since DRIVER_CONFIG's layout changed (#2107). make test: 36 targets, 34 passed, 2 skipped (NLS), 0 failed config: jit=yes stubslave=yes nls=no realitylvls=yes wodrealms=yes Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2706 lines
108 KiB
C++
2706 lines
108 KiB
C++
/*! \file conf.cpp
|
||
* \brief Set up configuration information and static data.
|
||
*
|
||
* This parses the configuration files and controls configuration options used
|
||
* to control the server and its behavior.
|
||
*/
|
||
|
||
#include "copyright.h"
|
||
#include "autoconf.h"
|
||
#include "config.h"
|
||
#include "externs.h"
|
||
#include "alloc.h"
|
||
|
||
#define PCRE2_CODE_UNIT_WIDTH 8
|
||
#include <pcre2.h>
|
||
|
||
using namespace std;
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// CONFPARM: Data used to find fields in CONFDATA.
|
||
//
|
||
typedef struct confparm
|
||
{
|
||
const UTF8 *pname; // parm name
|
||
int (*interpreter)(int *vp, UTF8 *str, void *pExtra, uint32_t nExtra,
|
||
dbref player, UTF8 *cmd); // routine to interp parameter
|
||
int flags; // control flags
|
||
int rperms; // read permission flags.
|
||
int *loc; // where to store value
|
||
void *pExtra; // extra pointer for interpreter
|
||
uint32_t nExtra; // extra data for interpreter
|
||
} CONFPARM;
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// External symbols.
|
||
//
|
||
CONFDATA mudconf;
|
||
STATEDATA mudstate;
|
||
int cf_set(UTF8 *, UTF8 *, dbref);
|
||
CF_HAND(cf_cf_access);
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_init: Initialize mudconf to default values.
|
||
//
|
||
void cf_init(void)
|
||
{
|
||
int i;
|
||
|
||
mudconf.indb = StringClone(T("netmux.db"));
|
||
mudconf.outdb = StringClone(T(""));
|
||
mudconf.crashdb = StringClone(T(""));
|
||
mudconf.mail_db = StringClone(T("mail.db"));
|
||
mudconf.comsys_db = StringClone(T("comsys.db"));
|
||
|
||
mudconf.status_file = StringClone(T("shutdown.status"));
|
||
mudconf.max_cache_size = 256LL*1024*1024;
|
||
mudconf.cache_preload_depth = 1;
|
||
|
||
mudconf.ip_address = nullptr;
|
||
mudconf.ports.push_back(2860);
|
||
|
||
#if defined(UNIX_SSL) || defined(_WIN32)
|
||
mudconf.sslPorts.clear();
|
||
mudconf.ssl_certificate_file[0] = '\0';
|
||
mudconf.ssl_certificate_key[0] = '\0';
|
||
mudconf.ssl_certificate_password[0] = '\0';
|
||
#endif
|
||
|
||
mudconf.init_size = 1000;
|
||
mudconf.guest_char = -1;
|
||
mudconf.guest_nuker = GOD;
|
||
mudconf.number_guests = 30;
|
||
mudconf.min_guests = 1;
|
||
mux_strncpy(mudconf.guest_prefix, T("Guest"), sizeof(mudconf.guest_prefix)-1);
|
||
mudconf.guest_file = StringClone(T("text/guest.txt"));
|
||
mudconf.conn_file = StringClone(T("text/connect.txt"));
|
||
mudconf.conn_help_file = StringClone(T("text/connect_help.txt"));
|
||
mudconf.creg_file = StringClone(T("text/register.txt"));
|
||
mudconf.regf_file = StringClone(T("text/create_reg.txt"));
|
||
mudconf.motd_file = StringClone(T("text/motd.txt"));
|
||
mudconf.wizmotd_file = StringClone(T("text/wizmotd.txt"));
|
||
mudconf.quit_file = StringClone(T("text/quit.txt"));
|
||
mudconf.down_file = StringClone(T("text/down.txt"));
|
||
mudconf.full_file = StringClone(T("text/full.txt"));
|
||
mudconf.site_file = StringClone(T("text/badsite.txt"));
|
||
mudconf.crea_file = StringClone(T("text/newuser.txt"));
|
||
mudconf.crash_msg[0] = '\0';
|
||
mudconf.motd_msg[0] = '\0';
|
||
mudconf.wizmotd_msg[0] = '\0';
|
||
mudconf.downmotd_msg[0] = '\0';
|
||
mudconf.fullmotd_msg[0] = '\0';
|
||
mudconf.dump_msg[0] = '\0';
|
||
mudconf.postdump_msg[0] = '\0';
|
||
mux_strncpy(mudconf.fixed_home_msg, T("You are fixed in place and cannot move."), sizeof(mudconf.fixed_home_msg)-1);
|
||
mux_strncpy(mudconf.fixed_tel_msg, T("You are fixed in place and cannot teleport."), sizeof(mudconf.fixed_tel_msg)-1);
|
||
mux_strncpy(mudconf.public_channel, T("Public"), sizeof(mudconf.public_channel)-1);
|
||
mux_strncpy(mudconf.public_channel_alias, T("pub"), sizeof(mudconf.public_channel_alias)-1);
|
||
mudconf.player_channels[0] = '\0';
|
||
mux_strncpy(mudconf.guests_channel, T("Guests"), sizeof(mudconf.guests_channel)-1);
|
||
mux_strncpy(mudconf.guests_channel_alias, T("g"), sizeof(mudconf.guests_channel_alias)-1);
|
||
mux_strncpy(mudconf.pueblo_msg, T("</xch_mudtext><img xch_mode=html>"), sizeof(mudconf.pueblo_msg)-1);
|
||
#if defined(INLINESQL)
|
||
mudconf.sql_server[0] = '\0';
|
||
mudconf.sql_user[0] = '\0';
|
||
mudconf.sql_password[0] = '\0';
|
||
mudconf.sql_database[0] = '\0';
|
||
#endif // INLINESQL
|
||
|
||
mudconf.mail_server[0] = '\0';
|
||
mudconf.mail_ehlo[0] = '\0';
|
||
mudconf.mail_sendaddr[0]= '\0';
|
||
mudconf.mail_sendname[0]= '\0';
|
||
mudconf.mail_subject[0] = '\0';
|
||
|
||
mudconf.indent_desc = false;
|
||
g_no_flash = false;
|
||
mudconf.terse_nospoof = false;
|
||
mudconf.name_spaces = true;
|
||
#if defined(HAVE_WORKING_FORK)
|
||
mudconf.fork_dump = true;
|
||
mudstate.dumping = false;
|
||
mudstate.dumper = 0;
|
||
mudstate.dumped = 0;
|
||
mudstate.write_protect = false;
|
||
#endif // HAVE_WORKING_FORK
|
||
mudconf.restrict_home = false;
|
||
mudconf.have_zones = true;
|
||
mudconf.paranoid_alloc = false;
|
||
mudconf.sig_action = SA_DFLT;
|
||
mudconf.max_name_protect = 5;
|
||
mudconf.max_players = -1;
|
||
mudconf.dump_interval = 3600;
|
||
mudconf.check_interval = 600;
|
||
mudconf.events_daily_hour = 7;
|
||
mudconf.dump_offset = 0;
|
||
mudconf.check_offset = 300;
|
||
mudconf.idle_timeout = 3600;
|
||
mudconf.conn_timeout = 120;
|
||
mudconf.idle_interval = 60;
|
||
mudconf.keepalive_interval = 60;
|
||
mudconf.retry_limit = 3;
|
||
// Failed-login budget per source address. Generous relative to how often
|
||
// real players mistype (and dorms/NAT put many unrelated players behind
|
||
// one address), but 10/minute makes brute-force-by-reconnect -- currently
|
||
// unbounded -- useless.
|
||
mudconf.login_fail_limit = 10;
|
||
mudconf.login_fail_period = 60;
|
||
// Collapse a consecutive run of refusals from one address rather than
|
||
// logging each. Rhost ships the equivalent off (0); we default to 1
|
||
// because the collapse loses no signal -- you still get the first line
|
||
// and an exact count -- while closing the refused-flood-fills-the-disk
|
||
// path by default.
|
||
mudconf.nospam_connect = 1;
|
||
// Connection churn per source. Rhost ships 20/60s but gates it behind
|
||
// lastsite_paranoia (off by default) and counts only CONSECUTIVE
|
||
// connections, so one interleaved connection from elsewhere resets them.
|
||
// Ours is a true per-source bucket -- it cannot be reset that way -- so
|
||
// the same number would bite considerably harder. 40/60s still cuts
|
||
// churn from unlimited to 40/minute while clearing a whole dorm
|
||
// reconnecting at once after a reboot, which is the burst that matters.
|
||
mudconf.max_lastsite_cnt = 40;
|
||
mudconf.min_con_attempt = 60;
|
||
mudconf.output_limit = 2 * LBUF_SIZE;
|
||
// Pre-auth connections from one source address. Legit multi-play
|
||
// (dorm/NAT, household, one player on 5+ alts) AUTHENTICATES promptly,
|
||
// so it does not accumulate pre-auth sockets; a slowloris holds many
|
||
// half-open ones. Refusal is transient and self-healing (a slot frees
|
||
// the moment a peer authenticates or conn_timeout reaps it).
|
||
mudconf.max_preauth_sitecons = 2;
|
||
// Protocol-detect grace (#1074, #2193). 500ms is not a guess at an RTT
|
||
// -- a WebSocket client's GET rides right behind the handshake's final
|
||
// ACK, so the healthy case needs ~0ms regardless of distance. What the
|
||
// window has to survive is that first packet being LOST: the retransmit
|
||
// comes back on an RTO that Linux floors at 200ms. 500 covers one such
|
||
// retransmit with headroom. 100 would sit in the dead zone -- past every
|
||
// healthy client, short of every retransmit -- and would break real
|
||
// WebSocket handshakes intermittently on a lossy link, which is a far
|
||
// worse failure than a slow banner. Set 0 on a port that never serves
|
||
// WebSocket: nothing to detect, banner at accept.
|
||
mudconf.proto_detect_window = 500;
|
||
// Pool memory budget: 0 = unlimited (off) by default. No non-zero
|
||
// default is safe across deployments (a 256MB VPS and a 32GB host want
|
||
// very different ceilings), so the admin sizes it per host; enabling it
|
||
// converts a fatal OutOfMemory under a runaway command into a graceful
|
||
// per-command abort. (#pool-oom)
|
||
mudconf.pool_memory_limit = 0;
|
||
g_pool_limit_bytes = 0;
|
||
// Input backlog is drop-SENSITIVE (a dropped line silently corrupts a
|
||
// user's paste — a code attribute is one legit ~32KB line, and @edit /
|
||
// multi-attribute uploads are legit bursts), unlike output. So this is
|
||
// a high anti-runaway BACKSTOP, not a primary throttle: 16 max-size
|
||
// lines, far above any interactive burst, but bounded. In practice the
|
||
// read/drain cadence + TCP flow control already bound the app queue
|
||
// (docs/survey-resource-defenses.md); the proper form is read-side
|
||
// backpressure, deferred. <=0 disables.
|
||
mudconf.input_limit = 16 * LBUF_SIZE;
|
||
mudconf.paycheck = 0;
|
||
mudconf.paystart = 0;
|
||
mudconf.paylimit = 10000;
|
||
#ifdef REALITY_LVLS
|
||
mudconf.no_levels = 0;
|
||
mudconf.def_room_rx = 1;
|
||
mudconf.def_room_tx = ~static_cast<RLEVEL>(0);
|
||
mudconf.def_player_rx = 1;
|
||
mudconf.def_player_tx = 1;
|
||
mudconf.def_exit_rx = 1;
|
||
mudconf.def_exit_tx = 1;
|
||
mudconf.def_thing_rx = 1;
|
||
mudconf.def_thing_tx = 1;
|
||
#endif // REALITY_LVLS
|
||
mudconf.start_quota = 20;
|
||
mudconf.site_chars = 25;
|
||
mudconf.payfind = 0;
|
||
mudconf.digcost = 10;
|
||
mudconf.linkcost = 1;
|
||
mudconf.opencost = 1;
|
||
mudconf.createmin = 10;
|
||
mudconf.createmax = 505;
|
||
mudconf.killmin = 10;
|
||
mudconf.killmax = 100;
|
||
mudconf.killguarantee = 100;
|
||
mudconf.robotcost = 1000;
|
||
mudconf.pagecost = 10;
|
||
mudconf.searchcost = 100;
|
||
mudconf.waitcost = 10;
|
||
mudconf.machinecost = 64;
|
||
mudconf.exit_quota = 1;
|
||
mudconf.player_quota = 1;
|
||
mudconf.room_quota = 1;
|
||
mudconf.thing_quota = 1;
|
||
mudconf.mail_expiration = 14;
|
||
mudconf.queuemax = 100;
|
||
mudconf.queue_chunk = 10;
|
||
mudconf.active_q_chunk = 10;
|
||
mudconf.sacfactor = 5;
|
||
mudconf.sacadjust = -1;
|
||
mudconf.trace_limit = 200;
|
||
mudconf.float_precision = -1;
|
||
|
||
mudconf.autozone = true;
|
||
mudconf.jit_code_slots = 7; // all slots (#2129); 1 = old behaviour
|
||
mudconf.jit_compile_cache_max = 2048; // #2130: cover the measured
|
||
// ~1500-program live working set
|
||
mudconf.jit_eval_brackets = true;
|
||
// Default ON (#1325, 2026-07-28). The gates: the differential
|
||
// harness executes every supported shape (140 chunks, declines all
|
||
// deliberate), loops run under an aborting instruction budget with
|
||
// the interpreter's error surface, the compiled path wins or ties
|
||
// every benched shape (#1741), and the intermittent interpreter
|
||
// crash (#1433) closed unreproduced on three machines including at
|
||
// its original commit. A chunk the compiler cannot honestly run
|
||
// declines to the interpreter, so OFF buys nothing but the slower
|
||
// path; set lua_jit=0 to diagnose a suspected compiled-path bug.
|
||
mudconf.lua_jit = true;
|
||
mudconf.use_hostname = true;
|
||
mudconf.clone_copy_cost = false;
|
||
mudconf.dark_sleepers = true;
|
||
mudconf.ex_flags = true;
|
||
mudconf.exam_public = true;
|
||
mudconf.fascist_tport = false;
|
||
mudconf.idle_wiz_dark = false;
|
||
mudconf.match_mine = true;
|
||
mudconf.match_mine_pl = true;
|
||
mudconf.pemit_players = false;
|
||
mudconf.pemit_any = false;
|
||
mudconf.player_listen = false;
|
||
mudconf.pub_flags = true;
|
||
mudconf.quiet_look = true;
|
||
mudconf.quiet_whisper = true;
|
||
mudconf.quotas = false;
|
||
mudconf.read_rem_desc = false;
|
||
mudconf.read_rem_name = false;
|
||
mudconf.reset_players = false;
|
||
mudconf.robot_speak = true;
|
||
mudconf.safe_unowned = false;
|
||
mudconf.safer_passwords = false;
|
||
mudconf.safer_iter = false;
|
||
mudconf.see_own_dark = true;
|
||
mudconf.sweep_dark = false;
|
||
mudconf.switch_df_all = true;
|
||
mudconf.talk_mode_default = false;
|
||
mudconf.terse_contents = true;
|
||
mudconf.terse_exits = true;
|
||
mudconf.terse_look = true;
|
||
mudconf.terse_movemsg = true;
|
||
mudconf.trace_topdown = true;
|
||
|
||
// -- ??? Running SC on a non-SC DB may cause problems.
|
||
//
|
||
mudconf.space_compress = true;
|
||
mudconf.start_room = 0;
|
||
mudconf.start_home = NOTHING;
|
||
mudconf.default_home = NOTHING;
|
||
mudconf.master_room = NOTHING;
|
||
|
||
mudconf.exit_parent = NOTHING;
|
||
mudconf.player_parent = NOTHING;
|
||
mudconf.room_parent = NOTHING;
|
||
mudconf.thing_parent = NOTHING;
|
||
|
||
for (i = FLAG_WORD1; i <= FLAG_WORD3; i++)
|
||
{
|
||
mudconf.player_flags.word[i] = 0;
|
||
mudconf.room_flags.word[i] = 0;
|
||
mudconf.exit_flags.word[i] = 0;
|
||
mudconf.thing_flags.word[i] = 0;
|
||
mudconf.robot_flags.word[i] = 0;
|
||
mudconf.stripped_flags.word[i] = 0;
|
||
}
|
||
mudconf.robot_flags.word[FLAG_WORD1] |= ROBOT;
|
||
mudconf.stripped_flags.word[FLAG_WORD1] = IMMORTAL | INHERIT | ROYALTY | WIZARD;
|
||
mudconf.stripped_flags.word[FLAG_WORD2] = BLIND | CONNECTED | GAGGED
|
||
| HEAD_FLAG | SLAVE | STAFF | SUSPECT;
|
||
|
||
mudconf.vattr_flags = AF_ODARK;
|
||
mux_strncpy(mudconf.mud_name, T("MUX"), sizeof(mudconf.mud_name)-1);
|
||
// Empty means "follow the environment" -- the behaviour every existing
|
||
// netmux.conf gets, since none of them set this (#1702).
|
||
//
|
||
mudconf.language[0] = '\0';
|
||
mux_strncpy(mudconf.one_coin, T("penny"), sizeof(mudconf.one_coin)-1);
|
||
mux_strncpy(mudconf.many_coins, T("pennies"), sizeof(mudconf.many_coins)-1);
|
||
mudconf.timeslice.SetSeconds(1);
|
||
mudconf.cmd_quota_max = 100;
|
||
mudconf.cmd_quota_incr = 1;
|
||
mudconf.rpt_cmdsecs.SetSeconds(120);
|
||
mudconf.max_cmdsecs.SetSeconds(60);
|
||
mudconf.cache_tick_period.SetSeconds(30);
|
||
mudconf.control_flags = 0xffffffff; // Everything for now...
|
||
mudconf.log_options = LOG_ALWAYS | LOG_BUGS | LOG_SECURITY |
|
||
LOG_NET | LOG_LOGIN | LOG_DBSAVES | LOG_CONFIGMODS |
|
||
LOG_SHOUTS | LOG_STARTUP | LOG_WIZARD | LOG_SUSPECTCMDS |
|
||
LOG_PROBLEMS | LOG_PCREATES | LOG_TIMEUSE;
|
||
mudconf.log_info = LOGOPT_TIMESTAMP | LOGOPT_LOC;
|
||
mudconf.markdata[0] = 0x01;
|
||
mudconf.markdata[1] = 0x02;
|
||
mudconf.markdata[2] = 0x04;
|
||
mudconf.markdata[3] = 0x08;
|
||
mudconf.markdata[4] = 0x10;
|
||
mudconf.markdata[5] = 0x20;
|
||
mudconf.markdata[6] = 0x40;
|
||
mudconf.markdata[7] = 0x80;
|
||
mudconf.lua_instruction_limit = 100000;
|
||
mudconf.lua_memory_limit = 1048576;
|
||
mudconf.func_nest_lim = 500;
|
||
mudconf.func_invk_lim = 25000;
|
||
mudconf.wild_invk_lim = 100000;
|
||
mudconf.ntfy_nest_lim = 20;
|
||
mudconf.lock_nest_lim = 20;
|
||
mudconf.parent_nest_lim = 10;
|
||
mudconf.zone_nest_lim = 20;
|
||
mudconf.cache_names = true;
|
||
mudconf.toad_recipient = -1;
|
||
mudconf.eval_comtitle = true;
|
||
mudconf.run_startup = true;
|
||
mudconf.safe_wipe = false;
|
||
mudconf.destroy_going_now = false;
|
||
mudconf.nStackLimit = 10000;
|
||
mudconf.hook_obj = NOTHING;
|
||
mudconf.help_executor = NOTHING;
|
||
mudconf.global_error_obj = NOTHING;
|
||
mudconf.mail_per_hour = 50;
|
||
mudconf.mail_max_per_player = 500;
|
||
mudconf.email_per_hour = 50;
|
||
mudconf.vattr_per_hour = 5000;
|
||
mudconf.vlimit = 1000;
|
||
mudconf.references_per_hour = 500;
|
||
mudconf.pcreate_per_hour = 100;
|
||
mudconf.lbuf_size = LBUF_SIZE;
|
||
mudconf.attr_name_charset = 0;
|
||
mudconf.exit_name_charset = 0;
|
||
mudconf.player_name_charset = 0;
|
||
mudconf.room_name_charset = 0;
|
||
mudconf.thing_name_charset = 0;
|
||
mudconf.password_methods = CRYPT_DEFAULT;
|
||
|
||
// Work factor for newly generated $5$/$6$ password hashes (sha-crypt
|
||
// rounds=; clamped to [1000, 999999999] at use). The default targets a
|
||
// ~50 ms per-hash budget on server-class hardware (measured ~50 ms at
|
||
// 50,000 rounds on a 2.5 GHz Xeon) -- chosen against the single-threaded
|
||
// login-path DoS surface described below rather than to match a raw
|
||
// OWASP iteration count, and still 10x the sha-crypt spec default.
|
||
// Changing this propagates automatically: the stored rounds ride in each
|
||
// hash, and a hash whose rounds differ from this policy is re-encoded on
|
||
// the player's next successful login.
|
||
//
|
||
// What password hashing here does and does not buy: it protects the
|
||
// passwords themselves in at-rest copies of the database (backups,
|
||
// flatfiles, decommissioned disks) against offline guessing -- players
|
||
// reuse passwords elsewhere, and a hash must not leak them to whoever
|
||
// can read a copy of the file, operators included. It does not and
|
||
// cannot protect against a hostile operator of the RUNNING game, who
|
||
// sits in the authentication path and sees cleartext at every connect.
|
||
//
|
||
// Verification recomputes at the STORED rounds, so every login costs
|
||
// that work in the single-threaded server: at the default, a burst of
|
||
// 100 reconnects after a restart serializes ~5 s of hashing. Raising it
|
||
// buys offline-guessing margin at the cost of that latency (~50 ms per
|
||
// 50,000 rounds); the spec default 5000 (~5 ms) is the low end. Any
|
||
// standard value is equally portable across platforms.
|
||
//
|
||
mudconf.password_hash_rounds = 50000;
|
||
mudconf.default_charset = CHARSET_LATIN1;
|
||
|
||
mudstate.events_flag = 0;
|
||
mudstate.bReadingConfiguration = false;
|
||
mudstate.bCanRestart = false;
|
||
mudstate.panicking = false;
|
||
mudstate.asserting = 0;
|
||
mudstate.logging = 0;
|
||
mudstate.epoch = 0;
|
||
mudstate.generation = 0;
|
||
mudstate.curr_executor = NOTHING;
|
||
mudstate.curr_enactor = NOTHING;
|
||
mudstate.shutdown_flag = false;
|
||
mudstate.attr_next = A_USER_START;
|
||
g_debug_cmd = T("< init >");
|
||
mudstate.curr_cmd = T("< none >");
|
||
mux_strncpy(mudstate.doing_hdr, T("Doing"), sizeof(mudstate.doing_hdr)-1);
|
||
mudstate.badname_head = nullptr;
|
||
mudstate.attrperm_list = nullptr;
|
||
mudstate.mstat_ixrss[0] = 0;
|
||
mudstate.mstat_ixrss[1] = 0;
|
||
mudstate.mstat_idrss[0] = 0;
|
||
mudstate.mstat_idrss[1] = 0;
|
||
mudstate.mstat_isrss[0] = 0;
|
||
mudstate.mstat_isrss[1] = 0;
|
||
mudstate.mstat_secs[0] = 0;
|
||
mudstate.mstat_secs[1] = 0;
|
||
mudstate.mstat_curr = 0;
|
||
mudstate.attr_iter_ctx.attrs.clear();
|
||
mudstate.attr_iter_ctx.pos = 0;
|
||
mudstate.attr_iter_stack.clear();
|
||
mudstate.olist = ObjectListStack();
|
||
mudstate.min_size = 0;
|
||
mudstate.db_top = 0;
|
||
mudstate.db_size = 0;
|
||
mudstate.mail_db_top = 0;
|
||
mudstate.mail_db_size = 0;
|
||
mudstate.freelist = NOTHING;
|
||
mudstate.markbits = nullptr;
|
||
mudstate.func_nest_lev = 0;
|
||
mudstate.func_invk_ctr = 0;
|
||
mudstate.wild_invk_ctr = 0;
|
||
mudstate.ntfy_nest_lev = 0;
|
||
mudstate.include_nest_lev = 0;
|
||
mudstate.train_nest_lev = 0;
|
||
mudstate.lock_nest_lev = 0;
|
||
mudstate.zone_nest_num = 0;
|
||
mudstate.pipe_nest_lev = 0;
|
||
mudstate.inpipe = false;
|
||
mudstate.pout = nullptr;
|
||
mudstate.poutnew = nullptr;
|
||
mudstate.poutbufc = nullptr;
|
||
mudstate.poutobj = NOTHING;
|
||
for (i = 0; i < MAX_GLOBAL_REGS; i++)
|
||
{
|
||
mudstate.global_regs[i] = nullptr;
|
||
}
|
||
mudstate.named_regs = nullptr;
|
||
mudstate.vattr_numbers.clear();
|
||
#if defined(STUB_SLAVE)
|
||
mudstate.pResultsSet = nullptr;
|
||
mudstate.iRow = RS_TOP;
|
||
#endif // STUB_SLAVE
|
||
mudstate.nObjEvalNest = 0;
|
||
mudstate.in_loop = 0;
|
||
mudstate.switch_token = nullptr;
|
||
mudstate.bStackLimitReached = false;
|
||
mudstate.nStackNest = 0;
|
||
mudstate.nHearNest = 0;
|
||
mudstate.aHelpDesc = nullptr;
|
||
mudstate.mHelpDesc = 0;
|
||
mudstate.nHelpDesc = 0;
|
||
mudstate.pIQueryControl = nullptr;
|
||
mudstate.pIComsysControl = nullptr;
|
||
mudstate.pIMailControl = nullptr;
|
||
mudstate.pILuaControl = nullptr;
|
||
// Note: pIGameEngine and pIPlayerSession are set by the driver before
|
||
// LoadGame() calls cf_init(). Do not clear them here.
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_log_notfound: Log a 'parameter not found' error.
|
||
//
|
||
void cf_log_notfound(dbref player, const UTF8 *cmd, const UTF8 *thingname, const UTF8 *thing)
|
||
{
|
||
if (mudstate.bReadingConfiguration)
|
||
{
|
||
STARTLOG(LOG_STARTUP, "CNF", "NFND");
|
||
Log.tinyprintf(T("%s: %s %s not found"), cmd, thingname, thing);
|
||
ENDLOG;
|
||
}
|
||
else
|
||
{
|
||
notify(player, tprintf(M_("%s %s not found"), thingname, thing));
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_log_alreadyexists: Log a 'already exists' error.
|
||
//
|
||
void cf_log_alreadyexists(dbref player, const UTF8* cmd, const UTF8* thingname, const UTF8* thing)
|
||
{
|
||
if (mudstate.bReadingConfiguration)
|
||
{
|
||
STARTLOG(LOG_STARTUP, "CNF", "NFND");
|
||
Log.tinyprintf(T("%s: %s %s already exists"), cmd, thingname, thing);
|
||
ENDLOG;
|
||
}
|
||
else
|
||
{
|
||
notify(player, tprintf(M_("%s %s already exists"), thingname, thing));
|
||
}
|
||
}
|
||
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_log_syntax: Log a syntax error.
|
||
//
|
||
void DCL_CDECL cf_log_syntax(dbref player, UTF8 *cmd, const UTF8 *fmt, ...)
|
||
{
|
||
va_list ap;
|
||
va_start(ap, fmt);
|
||
|
||
LBuf buf = LBuf_Src("cf_log_syntax");
|
||
mux_vsnprintf(buf, LBUF_SIZE, fmt, ap);
|
||
if (mudstate.bReadingConfiguration)
|
||
{
|
||
STARTLOG(LOG_STARTUP, "CNF", "SYNTX")
|
||
log_text(cmd);
|
||
log_text(T(": "));
|
||
log_text(buf);
|
||
ENDLOG;
|
||
}
|
||
else
|
||
{
|
||
notify(player, buf);
|
||
}
|
||
va_end(ap);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_status_from_succfail: Return command status from succ and fail info
|
||
//
|
||
static int cf_status_from_succfail(dbref player, UTF8 *cmd, int success, int failure)
|
||
{
|
||
// If any successes, return SUCCESS(0) if no failures or
|
||
// PARTIAL_SUCCESS(1) if any failures.
|
||
//
|
||
if (success > 0)
|
||
return ((failure == 0) ? 0 : 1);
|
||
|
||
// No successes. If no failures indicate nothing done. Always return
|
||
// FAILURE(-1)
|
||
//
|
||
if (failure == 0)
|
||
{
|
||
if (mudstate.bReadingConfiguration)
|
||
{
|
||
STARTLOG(LOG_STARTUP, "CNF", "NDATA")
|
||
LBuf buff = LBuf_Src("cf_status_from_succfail.LOG");
|
||
mux_sprintf(buff, LBUF_SIZE, T("%s: Nothing to set"), cmd);
|
||
log_text(buff);
|
||
ENDLOG
|
||
}
|
||
else
|
||
{
|
||
notify(player, M_("Nothing to set"));
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
// cf_rlevel
|
||
//
|
||
|
||
#ifdef REALITY_LVLS
|
||
|
||
CF_HAND(cf_rlevel)
|
||
{
|
||
CONFDATA *mc = reinterpret_cast<CONFDATA *>(vp);
|
||
int i;
|
||
|
||
if (mc->no_levels >= 32)
|
||
{
|
||
return 1;
|
||
}
|
||
for (i=0; *str && !mux_isspace[*str]; ++str)
|
||
{
|
||
if (i < 8)
|
||
{
|
||
mc->reality_level[mc->no_levels].name[i++] = *str;
|
||
}
|
||
}
|
||
mc->reality_level[mc->no_levels].name[i] = '\0';
|
||
mc->reality_level[mc->no_levels].value = 1;
|
||
strcpy(reinterpret_cast<char *>(mc->reality_level[mc->no_levels].attr), "DESC");
|
||
for (; *str && mux_isspace[*str]; ++str)
|
||
{
|
||
; // Nothing.
|
||
}
|
||
for (i=0; *str && mux_isdigit[*str]; ++str)
|
||
{
|
||
i = i * 10 + (*str - '0');
|
||
}
|
||
if (i)
|
||
{
|
||
mc->reality_level[mc->no_levels].value = static_cast<RLEVEL>(i);
|
||
}
|
||
for (; *str && mux_isspace[*str]; ++str)
|
||
{
|
||
; // Nothing.
|
||
}
|
||
if (*str)
|
||
{
|
||
strncpy(reinterpret_cast<char *>(mc->reality_level[mc->no_levels].attr), reinterpret_cast<char *>(str), 32);
|
||
}
|
||
mc->no_levels++;
|
||
return 0;
|
||
}
|
||
#endif // REALITY_LVLS
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_int_array: Setup array of integers.
|
||
//
|
||
static CF_HAND(cf_int_array)
|
||
{
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(player);
|
||
UNUSED_PARAMETER(cmd);
|
||
|
||
int *aPorts = nullptr;
|
||
try
|
||
{
|
||
aPorts = new int[nExtra];
|
||
}
|
||
catch (...)
|
||
{
|
||
; // Nothing.
|
||
}
|
||
|
||
if (nullptr == aPorts)
|
||
{
|
||
cf_log_syntax(player, cmd, T("Out of memory."));
|
||
return -1;
|
||
}
|
||
|
||
unsigned int nPorts = 0;
|
||
UTF8 *p;
|
||
string_token st(str, T(" \t\n\r"));
|
||
while ( (p = st.parse()) != nullptr
|
||
&& nPorts < nExtra)
|
||
{
|
||
int unused;
|
||
if (is_integer(p, &unused))
|
||
{
|
||
aPorts[nPorts++] = mux_atoi64(p);
|
||
}
|
||
}
|
||
|
||
auto *pvec = reinterpret_cast<std::vector<int>*>(vp);
|
||
if (nPorts)
|
||
{
|
||
pvec->assign(aPorts, aPorts + nPorts);
|
||
}
|
||
delete [] aPorts;
|
||
return 0;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_int: Set integer parameter.
|
||
//
|
||
static CF_HAND(cf_int)
|
||
{
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
UNUSED_PARAMETER(player);
|
||
UNUSED_PARAMETER(cmd);
|
||
|
||
// Copy the numeric value to the parameter.
|
||
//
|
||
*vp = mux_atoi64(str);
|
||
return 0;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_live_driver_int: Set an integer that the driver reads from g_dc on the
|
||
// accept/login hot path. Plain cf_int only updates mudconf; g_dc is a
|
||
// boot-time snapshot via GetConfig. After a runtime @admin, re-pull the
|
||
// basket so defense/session knobs (rate limits, pre-auth cap, nospam,
|
||
// input/output_limit, timeouts, max_players, cmd_quota_max, retry_limit)
|
||
// take effect immediately — matching pool_memory_limit's live push.
|
||
//
|
||
static CF_HAND(cf_live_driver_int)
|
||
{
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
UNUSED_PARAMETER(player);
|
||
UNUSED_PARAMETER(cmd);
|
||
|
||
*vp = mux_atoi64(str);
|
||
if ( !mudstate.bReadingConfiguration
|
||
&& nullptr != g_driver_config_sync_fn)
|
||
{
|
||
g_driver_config_sync_fn();
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
// cf_live_lua_int: Set an integer the Lua module holds a private copy of.
|
||
//
|
||
// Same defect as #1222 was for the driver basket, in a different subsystem:
|
||
// SetLimits is pushed once, during module discovery in engine_com.cpp, and
|
||
// CLuaMod keeps m_nInsnLimit / m_nMemLimit thereafter. A plain cf_int writes
|
||
// mudconf, config() reads the new value back, @admin answers "Set." -- and
|
||
// InsnCountHook goes on comparing against the boot-time number until a
|
||
// restart. Nothing warns (#1613).
|
||
//
|
||
// These are the containment knobs for a sandbox softcode can enter, so the
|
||
// realistic moment to change one is during an incident: somebody is hammering
|
||
// lua(), staff tightens the limit, the server says Set., and nothing happens
|
||
// until the restart the incident is trying to avoid.
|
||
//
|
||
// Push both, not just the one that changed: SetLimits takes the pair and
|
||
// mudconf holds the current value of each, so re-sending both is free and
|
||
// cannot leave the module holding a mix of old and new.
|
||
//
|
||
static CF_HAND(cf_live_lua_int)
|
||
{
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
UNUSED_PARAMETER(player);
|
||
UNUSED_PARAMETER(cmd);
|
||
|
||
*vp = mux_atoi64(str);
|
||
|
||
// bReadingConfiguration gates the boot-time parse, which runs before
|
||
// module discovery -- pILuaControl is still null there, and discovery
|
||
// does the initial push itself.
|
||
//
|
||
if ( !mudstate.bReadingConfiguration
|
||
&& nullptr != mudstate.pILuaControl)
|
||
{
|
||
mudstate.pILuaControl->SetLimits(
|
||
mudconf.lua_instruction_limit,
|
||
mudconf.lua_memory_limit);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_size: Set a size parameter with optional K/M/G suffix.
|
||
// -1 means unlimited.
|
||
//
|
||
static CF_HAND(cf_size)
|
||
{
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
UNUSED_PARAMETER(player);
|
||
UNUSED_PARAMETER(cmd);
|
||
|
||
int64_t *pSize = reinterpret_cast<int64_t *>(vp);
|
||
|
||
// Handle -1 (unlimited) directly.
|
||
//
|
||
UTF8 *p = str;
|
||
while (mux_isspace(*p)) p++;
|
||
|
||
if (p[0] == '-' && p[1] == '1' && (p[2] == '\0' || mux_isspace(p[2])))
|
||
{
|
||
*pSize = -1;
|
||
return 0;
|
||
}
|
||
|
||
// Reject negative values other than -1.
|
||
//
|
||
if (*p == '-')
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
// mux_atoi64, not mux_atol: long is 32-bit on LLP64 (Windows), so a raw
|
||
// byte count at or above 2^31 was truncated before the suffix multiplier
|
||
// could be applied. Values in [2^31, 2^32) came out NEGATIVE, and every
|
||
// consumer reads a negative size as "unlimited" (attrcache.cpp:506) --
|
||
// so `max_cache_size 3000000000` silently uncapped the cache instead of
|
||
// capping it at 3 GB (#1373).
|
||
//
|
||
int64_t val = mux_atoi64(p);
|
||
|
||
// Find the suffix character.
|
||
//
|
||
while (mux_isdigit(*p) || *p == '+') p++;
|
||
while (mux_isspace(*p)) p++;
|
||
|
||
switch (*p)
|
||
{
|
||
case 'k': case 'K': val *= 1024LL; break;
|
||
case 'm': case 'M': val *= 1024LL * 1024; break;
|
||
case 'g': case 'G': val *= 1024LL * 1024 * 1024; break;
|
||
case '\0': break;
|
||
default:
|
||
return -1;
|
||
}
|
||
|
||
*pSize = val;
|
||
return 0;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_pool_limit: Set the pool memory budget (bytes, K/M/G suffixes; 0 or -1
|
||
// = unlimited) and push it to the libmux allocator (g_pool_limit_bytes) so it
|
||
// stays in sync on initial load and @readcache reload. (#pool-oom)
|
||
//
|
||
static CF_HAND(cf_pool_limit)
|
||
{
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
|
||
int64_t v = 0;
|
||
int rc = cf_size(reinterpret_cast<int *>(&v), str, nullptr, 0,
|
||
player, cmd);
|
||
if (rc != 0)
|
||
{
|
||
return rc;
|
||
}
|
||
if (v < 0)
|
||
{
|
||
v = 0; // -1 (unlimited) normalizes to 0 = off
|
||
}
|
||
*reinterpret_cast<int64_t *>(vp) = v;
|
||
g_pool_limit_bytes = static_cast<size_t>(v);
|
||
return 0;
|
||
}
|
||
|
||
// Defined below, next to cf_bool/cf_string/cf_option which also use it.
|
||
// Declared here because cf_dbref and cf_seconds precede that definition.
|
||
//
|
||
static void live_sync_driver_config(void);
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_dbref: Set dbref parameter....looking for an ignoring the leading '#'.
|
||
//
|
||
static CF_HAND(cf_dbref)
|
||
{
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
UNUSED_PARAMETER(player);
|
||
UNUSED_PARAMETER(cmd);
|
||
|
||
UTF8 *p = str;
|
||
while (mux_isspace(*p))
|
||
{
|
||
p++;
|
||
}
|
||
if (*p == '#')
|
||
{
|
||
p++;
|
||
}
|
||
|
||
// Copy the numeric value to the parameter.
|
||
//
|
||
*vp = mux_atoi64(p);
|
||
|
||
// guest_char_num and player_starting_room are both in the driver basket
|
||
// and read live (the guest-login gate and where a newly created player is
|
||
// moved), so re-pull it here for the same reason as cf_seconds (#1222).
|
||
//
|
||
live_sync_driver_config();
|
||
return 0;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_seconds: Set CLinearTimeDelta in units of seconds.
|
||
//
|
||
static CF_HAND(cf_seconds)
|
||
{
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
UNUSED_PARAMETER(player);
|
||
UNUSED_PARAMETER(cmd);
|
||
|
||
auto* pltd = reinterpret_cast<CLinearTimeDelta*>(vp);
|
||
pltd->SetSecondsString(str);
|
||
|
||
// lag_limit, lag_maximum and timeslice all land in the driver basket and
|
||
// are read live by net.cpp (the alarm-clock watchdog, the lag report
|
||
// threshold, and command deferral). Without this the runtime @admin
|
||
// updated mudconf -- so config() reported the new value -- while the
|
||
// driver kept using the boot-time one until restart (#1222).
|
||
//
|
||
live_sync_driver_config();
|
||
return 0;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_bool: Set boolean parameter.
|
||
//
|
||
static NAMETAB bool_names[] =
|
||
{
|
||
{T("true"), 1, 0, true},
|
||
{T("false"), 1, 0, false},
|
||
{T("yes"), 1, 0, true},
|
||
{T("no"), 1, 0, false},
|
||
{T("1"), 1, 0, true},
|
||
{T("0"), 1, 0, false},
|
||
{static_cast<UTF8*>(nullptr), 0, 0, 0}
|
||
};
|
||
|
||
// #1085: after runtime conf changes that may affect g_dc, re-pull the
|
||
// driver basket (same as cf_live_driver_int).
|
||
//
|
||
static void live_sync_driver_config(void)
|
||
{
|
||
if ( !mudstate.bReadingConfiguration
|
||
&& nullptr != g_driver_config_sync_fn)
|
||
{
|
||
g_driver_config_sync_fn();
|
||
}
|
||
}
|
||
|
||
static CF_HAND(cf_bool)
|
||
{
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
|
||
int i;
|
||
if (!search_nametab(GOD, bool_names, str, &i))
|
||
{
|
||
cf_log_notfound(player, cmd, T("Value"), str);
|
||
return -1;
|
||
}
|
||
const auto pb = reinterpret_cast<bool*>(vp);
|
||
*pb = isTRUE(i);
|
||
live_sync_driver_config();
|
||
return 0;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_option: Select one option from many choices.
|
||
//
|
||
static CF_HAND(cf_option)
|
||
{
|
||
UNUSED_PARAMETER(nExtra);
|
||
|
||
int i;
|
||
if (!search_nametab(GOD, static_cast<NAMETAB*>(pExtra), str, &i))
|
||
{
|
||
cf_log_notfound(player, cmd, T("Value"), str);
|
||
return -1;
|
||
}
|
||
*vp = i;
|
||
live_sync_driver_config();
|
||
return 0;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_string: Set string parameter.
|
||
//
|
||
static CF_HAND(cf_string)
|
||
{
|
||
UNUSED_PARAMETER(pExtra);
|
||
|
||
auto pc = reinterpret_cast<UTF8*>(vp);
|
||
|
||
// The following should never happen because extra is always a non-zero
|
||
// constant in the config table.
|
||
//
|
||
if (nExtra <= 0)
|
||
{
|
||
return 1;
|
||
}
|
||
|
||
// Copy the string to the buffer if it is not too big.
|
||
//
|
||
int retval = 0;
|
||
size_t nStr = strlen(reinterpret_cast<char*>(str));
|
||
if (nStr >= nExtra)
|
||
{
|
||
nStr = nExtra - 1;
|
||
if (mudstate.bReadingConfiguration)
|
||
{
|
||
STARTLOG(LOG_STARTUP, "CNF", "NFND");
|
||
Log.tinyprintf(T("%s: String truncated"), cmd);
|
||
ENDLOG;
|
||
}
|
||
else
|
||
{
|
||
notify(player, M_("String truncated"));
|
||
}
|
||
retval = 1;
|
||
}
|
||
memcpy(pc, str, nStr+1);
|
||
pc[nStr] = '\0';
|
||
|
||
if (pc == mudconf.mud_name)
|
||
{
|
||
// We are changing the name of the MUD. Form a prefix from the
|
||
// mudname and let the logger know.
|
||
//
|
||
UTF8 *buff = alloc_sbuf("cf_string.prefix");
|
||
UTF8 *p = buff;
|
||
UTF8 *q = strip_color(mudconf.mud_name);
|
||
size_t nLen = 0;
|
||
while ( *q
|
||
&& nLen < SBUF_SIZE)
|
||
{
|
||
if (mux_isalnum(*q))
|
||
{
|
||
*p++ = *q;
|
||
nLen++;
|
||
}
|
||
q++;
|
||
}
|
||
*p = '\0';
|
||
Log.SetPrefix(buff);
|
||
free_sbuf(buff);
|
||
}
|
||
live_sync_driver_config();
|
||
return retval;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_string_dyn: Set string parameter using dynamically allocated memory.
|
||
//
|
||
static CF_HAND(cf_string_dyn)
|
||
{
|
||
UNUSED_PARAMETER(pExtra);
|
||
|
||
const auto ppc = reinterpret_cast<UTF8**>(vp);
|
||
|
||
// Allocate memory for buffer and copy string to it. If nExtra is non-zero,
|
||
// then there is a size limitation as well.
|
||
//
|
||
int retval = 0;
|
||
size_t nStr = strlen(reinterpret_cast<char*>(str));
|
||
if (nExtra && nStr >= nExtra)
|
||
{
|
||
nStr = nExtra - 1;
|
||
if (mudstate.bReadingConfiguration)
|
||
{
|
||
STARTLOG(LOG_STARTUP, "CNF", "NFND");
|
||
Log.tinyprintf(T("%s: String truncated"), cmd);
|
||
ENDLOG;
|
||
}
|
||
else
|
||
{
|
||
notify(player, M_("String truncated"));
|
||
}
|
||
retval = 1;
|
||
}
|
||
UTF8 *confbuff = StringCloneLen(str, nStr);
|
||
|
||
// Free previous memory for buffer.
|
||
//
|
||
if (*ppc != nullptr)
|
||
{
|
||
MEMFREE(*ppc);
|
||
}
|
||
*ppc = confbuff;
|
||
|
||
return retval;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_alias: define a generic hash table alias.
|
||
//
|
||
static CF_HAND(cf_alias)
|
||
{
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
|
||
string_token st(str, T(" \t=,"));
|
||
const UTF8* alias = st.parse();
|
||
UTF8* orig = st.parse();
|
||
|
||
if (orig)
|
||
{
|
||
size_t nCased;
|
||
const UTF8 *pCased = mux_strupr(orig, nCased);
|
||
auto pMap = reinterpret_cast<StringPtrMap*>(vp);
|
||
auto it = pMap->find(std::vector<UTF8>(pCased, pCased + nCased));
|
||
if (it == pMap->end())
|
||
{
|
||
cf_log_notfound(player, cmd, T("Entry"), orig);
|
||
return -1;
|
||
}
|
||
void *cp = it->second;
|
||
|
||
pCased = mux_strupr(alias, nCased);
|
||
if (pMap->find(std::vector<UTF8>(pCased, pCased + nCased)) == pMap->end())
|
||
{
|
||
pMap->emplace(std::vector<UTF8>(pCased, pCased + nCased), cp);
|
||
}
|
||
return 0;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
static CF_HAND(cf_attr_name_alias)
|
||
{
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
|
||
string_token st(str, T(" \t=,"));
|
||
const UTF8* alias = st.parse();
|
||
UTF8* orig = st.parse();
|
||
|
||
if (orig)
|
||
{
|
||
size_t nCased;
|
||
const UTF8* pCased = mux_strupr(orig, nCased);
|
||
const vector<UTF8> v(pCased, pCased + nCased);
|
||
const auto it = mudstate.builtin_attribute_names.find(v);
|
||
if (it == mudstate.builtin_attribute_names.end())
|
||
{
|
||
cf_log_notfound(player, cmd, T("Entry"), orig);
|
||
return -1;
|
||
}
|
||
|
||
pCased = mux_strupr(alias, nCased);
|
||
const vector <UTF8> alias_vector(pCased, pCased + nCased);
|
||
const auto it2 = mudstate.builtin_attribute_names.find(alias_vector);
|
||
if (it == mudstate.builtin_attribute_names.end())
|
||
{
|
||
mudstate.builtin_attribute_names.insert(make_pair(alias_vector, it->second));
|
||
}
|
||
return 0;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
// cf_pronoun_group: Define a custom pronoun set.
|
||
//
|
||
// Syntax: pronoun_group <name> <subj>,<obj>,<poss>,<aposs> [plural]
|
||
//
|
||
static CF_HAND(cf_pronoun_group)
|
||
{
|
||
UNUSED_PARAMETER(vp);
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
|
||
string_token st(str, T(" \t"));
|
||
UTF8 *pName = st.parse();
|
||
UTF8 *pPronouns = st.parse();
|
||
|
||
if ( !pName
|
||
|| !pPronouns)
|
||
{
|
||
cf_log_syntax(player, cmd, T("Expected: pronoun_group <name> <subj>,<obj>,<poss>,<aposs> [plural]"));
|
||
return -1;
|
||
}
|
||
|
||
// Parse comma-separated pronouns.
|
||
//
|
||
string_token st2(pPronouns, T(","));
|
||
UTF8 *pSubj = st2.parse();
|
||
UTF8 *pObj = st2.parse();
|
||
UTF8 *pPoss = st2.parse();
|
||
UTF8 *pAposs = st2.parse();
|
||
|
||
if ( !pSubj
|
||
|| !pObj
|
||
|| !pPoss
|
||
|| !pAposs)
|
||
{
|
||
cf_log_syntax(player, cmd, T("Expected four comma-separated pronouns: <subj>,<obj>,<poss>,<aposs>"));
|
||
return -1;
|
||
}
|
||
|
||
// Check for optional 'plural' keyword.
|
||
//
|
||
UTF8 *pPlural = st.parse();
|
||
bool bPlural = false;
|
||
if ( pPlural
|
||
&& mux_stricmp(pPlural, T("plural")) == 0)
|
||
{
|
||
bPlural = true;
|
||
}
|
||
|
||
// Uppercase the name for lookup key.
|
||
//
|
||
size_t nCased;
|
||
const UTF8 *pCased = mux_strupr(pName, nCased);
|
||
vector<UTF8> vKey(pCased, pCased + nCased);
|
||
|
||
// Check if this group name already exists.
|
||
//
|
||
auto it = mudstate.pronoun_groups.find(vKey);
|
||
PRONOUN_SET *ps;
|
||
if (it != mudstate.pronoun_groups.end())
|
||
{
|
||
ps = it->second;
|
||
}
|
||
else
|
||
{
|
||
try
|
||
{
|
||
ps = new PRONOUN_SET;
|
||
}
|
||
catch (...)
|
||
{
|
||
return -1;
|
||
}
|
||
mudstate.pronoun_groups.insert(make_pair(vKey, ps));
|
||
}
|
||
|
||
mux_strncpy(ps->subjective, pSubj, sizeof(ps->subjective) - 1);
|
||
mux_strncpy(ps->objective, pObj, sizeof(ps->objective) - 1);
|
||
mux_strncpy(ps->possessive, pPoss, sizeof(ps->possessive) - 1);
|
||
mux_strncpy(ps->absolute, pAposs, sizeof(ps->absolute) - 1);
|
||
ps->plural = bPlural;
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int function_name_helper(const dbref player, const UTF8* cmd, const UTF8* existing_name, const UTF8* new_name, bool delete_existing)
|
||
{
|
||
// Existing function name should exist.
|
||
//
|
||
size_t uppercase_length;
|
||
const UTF8* uppercase = mux_strupr(existing_name, uppercase_length);
|
||
const vector<UTF8> existing_name_vector(uppercase, uppercase + uppercase_length);
|
||
const auto it_existing = mudstate.builtin_functions.find(existing_name_vector);
|
||
if (it_existing == mudstate.builtin_functions.end())
|
||
{
|
||
cf_log_notfound(player, cmd, T("Entry"), existing_name);
|
||
return -1;
|
||
}
|
||
|
||
// New function name should not exist.
|
||
//
|
||
uppercase = mux_strupr(new_name, uppercase_length);
|
||
vector<UTF8> new_function_name(uppercase, uppercase + uppercase_length);
|
||
const auto it_new = mudstate.builtin_functions.find(new_function_name);
|
||
if (it_new != mudstate.builtin_functions.end())
|
||
{
|
||
cf_log_alreadyexists(player, cmd, T("Entry"), new_name);
|
||
return -1;
|
||
}
|
||
mudstate.builtin_functions.insert(make_pair(new_function_name, it_existing->second));
|
||
if (delete_existing)
|
||
{
|
||
mudstate.builtin_functions.erase(it_existing);
|
||
}
|
||
jit_gate_note_function_table_change(); // (#2068)
|
||
return 0;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_function_name: rename a function
|
||
//
|
||
static CF_HAND(cf_function_name)
|
||
{
|
||
UNUSED_PARAMETER(vp);
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
|
||
string_token st(str, T(" \t=,"));
|
||
const UTF8* old_name = st.parse();
|
||
UTF8* new_name = st.parse();
|
||
|
||
if (!new_name)
|
||
{
|
||
cf_log_syntax(player, cmd, T("function_name oldname newname : %s"), str);
|
||
return -1;
|
||
}
|
||
return function_name_helper(player, cmd, old_name, new_name, true);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_function_alias: create an alias for function
|
||
//
|
||
static CF_HAND(cf_function_alias)
|
||
{
|
||
UNUSED_PARAMETER(vp);
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
|
||
string_token st(str, T(" \t=,"));
|
||
const UTF8* function_alias = st.parse();
|
||
UTF8* function_name = st.parse();
|
||
|
||
if (!function_name)
|
||
{
|
||
cf_log_syntax(player, cmd, T("function_alias alias funcname : %s"), str);
|
||
return -1;
|
||
}
|
||
return function_name_helper(player, cmd, function_name, function_alias, false);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_flagalias: define a flag alias.
|
||
//
|
||
static CF_HAND(cf_flagalias)
|
||
{
|
||
UNUSED_PARAMETER(vp);
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
|
||
string_token st(str, T(" \t=,"));
|
||
UTF8 *alias = st.parse();
|
||
UTF8 *orig = st.parse();
|
||
|
||
if ( nullptr == alias
|
||
|| '\0' == alias[0]
|
||
|| nullptr == orig
|
||
|| '\0' == orig[0])
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
bool success = false;
|
||
int name_length;
|
||
bool valid;
|
||
UTF8 *name = MakeCanonicalFlagName(orig, &name_length, &valid);
|
||
if (valid)
|
||
{
|
||
const vector<UTF8> original_name(name, name + name_length);
|
||
const auto it_original = mudstate.flag_names_map.find(original_name);
|
||
if (it_original != mudstate.flag_names_map.end())
|
||
{
|
||
name = MakeCanonicalFlagName(alias, &name_length, &valid);
|
||
vector<UTF8> alias_name(name, name + name_length);
|
||
if (valid)
|
||
{
|
||
const auto it_alias_name = mudstate.flag_names_map.find(alias_name);
|
||
if (it_alias_name == mudstate.flag_names_map.end())
|
||
{
|
||
mudstate.flag_names_map.insert(make_pair(alias_name, it_original->second));
|
||
success = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (!success)
|
||
{
|
||
cf_log_notfound(player, cmd, T("Flag"), orig);
|
||
}
|
||
return (success ? 0 : -1);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_poweralias: define a power alias.
|
||
//
|
||
static CF_HAND(cf_poweralias)
|
||
{
|
||
UNUSED_PARAMETER(vp);
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
|
||
string_token st(str, T(" \t=,"));
|
||
UTF8 *alias = st.parse();
|
||
UTF8* orig = st.parse();
|
||
|
||
if ( nullptr == alias
|
||
|| '\0' == alias[0]
|
||
|| nullptr == orig
|
||
|| '\0' == orig[0])
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
bool success = false;
|
||
int nName;
|
||
bool bValid;
|
||
void *cp;
|
||
UTF8 *pName = MakeCanonicalFlagName(orig, &nName, &bValid);
|
||
if (bValid)
|
||
{
|
||
auto it = mudstate.powers_htab.find(std::vector<UTF8>(pName, pName + nName));
|
||
if (it != mudstate.powers_htab.end())
|
||
{
|
||
cp = it->second;
|
||
pName = MakeCanonicalFlagName(alias, &nName, &bValid);
|
||
if (bValid)
|
||
{
|
||
mudstate.powers_htab.emplace(std::vector<UTF8>(pName, pName + nName), cp);
|
||
success = true;
|
||
}
|
||
}
|
||
}
|
||
if (!success)
|
||
{
|
||
cf_log_notfound(player, cmd, T("Power"), orig);
|
||
}
|
||
return (success ? 0 : -1);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_modify_bits: set or clear bits in a flag word from a namelist.
|
||
//
|
||
CF_HAND(cf_modify_bits)
|
||
{
|
||
UNUSED_PARAMETER(nExtra);
|
||
|
||
int f, success, failure;
|
||
bool negate;
|
||
|
||
// Walk through the tokens.
|
||
//
|
||
success = failure = 0;
|
||
string_token st(str, T(" \t"));
|
||
UTF8 *sp = st.parse();
|
||
while (sp != nullptr)
|
||
{
|
||
// Check for negation.
|
||
//
|
||
negate = false;
|
||
if (*sp == '!')
|
||
{
|
||
negate = true;
|
||
sp++;
|
||
}
|
||
|
||
// Set or clear the appropriate bit.
|
||
//
|
||
if (search_nametab(GOD, reinterpret_cast<NAMETAB *>(pExtra), sp, &f))
|
||
{
|
||
if (negate)
|
||
*vp &= ~f;
|
||
else
|
||
*vp |= f;
|
||
success++;
|
||
}
|
||
else
|
||
{
|
||
cf_log_notfound(player, cmd, T("Entry"), sp);
|
||
failure++;
|
||
}
|
||
|
||
// Get the next token.
|
||
//
|
||
sp = st.parse();
|
||
}
|
||
return cf_status_from_succfail(player, cmd, success, failure);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_set_flags: Clear flag word and then set from a flags htab.
|
||
//
|
||
static CF_HAND(cf_set_flags)
|
||
{
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
|
||
int failure;
|
||
int success = failure = 0;
|
||
|
||
// Walk through the tokens.
|
||
//
|
||
string_token st(str, T(" \t"));
|
||
UTF8 *sp = st.parse();
|
||
const auto flag_set = reinterpret_cast<FLAGSET*>(vp);
|
||
|
||
while (sp != nullptr)
|
||
{
|
||
// Canonical Flag Name.
|
||
//
|
||
int flag_name_length;
|
||
bool valid;
|
||
UTF8 *flag_name = MakeCanonicalFlagName(sp, &flag_name_length, &valid);
|
||
if (valid)
|
||
{
|
||
vector<UTF8> flagname(flag_name, flag_name + flag_name_length);
|
||
auto it = mudstate.flag_names_map.find(flagname);
|
||
if (it != mudstate.flag_names_map.end())
|
||
{
|
||
const FLAGNAMEENT* flag_name_entry = it->second;
|
||
|
||
// Set the appropriate bit.
|
||
//
|
||
if (success == 0)
|
||
{
|
||
for (int i = FLAG_WORD1; i <= FLAG_WORD3; i++)
|
||
{
|
||
(*flag_set).word[i] = 0;
|
||
}
|
||
}
|
||
FLAGBITENT* flag_bit_entity = flag_name_entry->fbe;
|
||
if (flag_name_entry->bPositive)
|
||
{
|
||
(*flag_set).word[flag_bit_entity->flagflag] |= flag_bit_entity->flagvalue;
|
||
}
|
||
else
|
||
{
|
||
(*flag_set).word[flag_bit_entity->flagflag] &= ~(flag_bit_entity->flagvalue);
|
||
}
|
||
success++;
|
||
}
|
||
else
|
||
{
|
||
cf_log_notfound(player, cmd, T("Entry"), sp);
|
||
failure++;
|
||
}
|
||
}
|
||
|
||
// Get the next token
|
||
//
|
||
sp = st.parse();
|
||
}
|
||
if ((success == 0) && (failure == 0))
|
||
{
|
||
for (int i = FLAG_WORD1; i <= FLAG_WORD3; i++)
|
||
{
|
||
(*flag_set).word[i] = 0;
|
||
}
|
||
return 0;
|
||
}
|
||
if (success > 0)
|
||
{
|
||
return ((failure == 0) ? 0 : 1);
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_badname: Disallow use of player name/alias.
|
||
//
|
||
static CF_HAND(cf_badname)
|
||
{
|
||
UNUSED_PARAMETER(vp);
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(player);
|
||
UNUSED_PARAMETER(cmd);
|
||
|
||
if (nExtra)
|
||
{
|
||
badname_remove(str);
|
||
}
|
||
else
|
||
{
|
||
badname_add(str);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_site: Update site information
|
||
|
||
static CF_HAND(cf_site)
|
||
{
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(vp);
|
||
|
||
// The access list lives in the driver. Route the update through COM.
|
||
//
|
||
return site_update(str, player, cmd, nExtra);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_helpfile, cf_raw_helpfile: Add help files and their corresponding
|
||
// command.
|
||
//
|
||
static int add_helpfile(dbref player, UTF8 *cmd, UTF8 *str, bool bEval)
|
||
{
|
||
// Parse the two arguments.
|
||
//
|
||
string_token st(str, T(" \t\n\r"));
|
||
|
||
UTF8 *pCmdName = st.parse();
|
||
UTF8 *pBase = st.parse();
|
||
if (pBase == nullptr)
|
||
{
|
||
cf_log_syntax(player, cmd, T("Missing path for helpfile %s"), pCmdName);
|
||
return -1;
|
||
}
|
||
if ( pCmdName[0] == '_'
|
||
&& pCmdName[1] == '_')
|
||
{
|
||
cf_log_syntax(player, cmd,
|
||
T("Helpfile %s would conflict with the use of @addcommand."),
|
||
pCmdName);
|
||
return -1;
|
||
}
|
||
if (SBUF_SIZE <= strlen(reinterpret_cast<char *>(pBase)))
|
||
{
|
||
cf_log_syntax(player, cmd, T("Helpfile ‘%s’ filename too long"), pBase);
|
||
return -1;
|
||
}
|
||
|
||
// Allocate an empty place in the table of help file hashes.
|
||
//
|
||
if (mudstate.aHelpDesc == nullptr)
|
||
{
|
||
mudstate.mHelpDesc = 4;
|
||
mudstate.nHelpDesc = 0;
|
||
try
|
||
{
|
||
mudstate.aHelpDesc = new HELP_DESC[mudstate.mHelpDesc];
|
||
}
|
||
catch (...)
|
||
{
|
||
; // Nothing.
|
||
}
|
||
|
||
if (nullptr == mudstate.aHelpDesc)
|
||
{
|
||
cf_log_syntax(player, cmd, T("Out of memory."));
|
||
return -1;
|
||
}
|
||
}
|
||
else if (mudstate.mHelpDesc <= mudstate.nHelpDesc)
|
||
{
|
||
int newsize = mudstate.mHelpDesc + 4;
|
||
HELP_DESC *q = nullptr;
|
||
try
|
||
{
|
||
q = new HELP_DESC[newsize];
|
||
}
|
||
catch (...)
|
||
{
|
||
; // Nothing.
|
||
}
|
||
|
||
if (nullptr == mudstate.aHelpDesc)
|
||
{
|
||
cf_log_syntax(player, cmd, T("Out of memory."));
|
||
return -1;
|
||
}
|
||
|
||
memset(q, 0, sizeof(HELP_DESC)*newsize);
|
||
memcpy(q, mudstate.aHelpDesc, sizeof(HELP_DESC)*mudstate.mHelpDesc);
|
||
delete [] mudstate.aHelpDesc;
|
||
mudstate.aHelpDesc = q;
|
||
mudstate.mHelpDesc = newsize;
|
||
}
|
||
|
||
// Build HELP_DESC
|
||
//
|
||
HELP_DESC *pDesc = mudstate.aHelpDesc + mudstate.nHelpDesc;
|
||
pDesc->CommandName = StringClone(pCmdName);
|
||
pDesc->ht = nullptr;
|
||
pDesc->pBaseFilename = StringClone(pBase);
|
||
pDesc->bEval = bEval;
|
||
|
||
// Build up Command Entry.
|
||
//
|
||
CMDENT_ONE_ARG *cmdp = nullptr;
|
||
try
|
||
{
|
||
cmdp = new CMDENT_ONE_ARG;
|
||
}
|
||
catch (...)
|
||
{
|
||
; // Nothing.
|
||
}
|
||
|
||
if (nullptr != cmdp)
|
||
{
|
||
cmdp->callseq = CS_ONE_ARG;
|
||
cmdp->cmdname = StringClone(pCmdName);
|
||
cmdp->extra = mudstate.nHelpDesc;
|
||
cmdp->handler = do_help;
|
||
cmdp->flags = CEF_ALLOC;
|
||
cmdp->perms = CA_PUBLIC;
|
||
cmdp->switches = nullptr;
|
||
|
||
// Free any previously-allocated command at the same key.
|
||
//
|
||
const UTF8 *p = cmdp->cmdname;
|
||
size_t nLen = strlen(reinterpret_cast<const char *>(p));
|
||
std::vector<UTF8> key(p, p + nLen);
|
||
auto it = mudstate.command_htab.find(key);
|
||
if (it != mudstate.command_htab.end())
|
||
{
|
||
CMDENT *old = static_cast<CMDENT *>(it->second);
|
||
if (old && (old->flags & CEF_ALLOC))
|
||
{
|
||
MEMFREE(old->cmdname);
|
||
old->cmdname = nullptr;
|
||
MEMFREE(old);
|
||
}
|
||
}
|
||
mudstate.command_htab.erase(key);
|
||
mudstate.command_htab.emplace(key, cmdp);
|
||
|
||
p = tprintf(T("__%s"), cmdp->cmdname);
|
||
nLen = strlen(reinterpret_cast<const char *>(p));
|
||
std::vector<UTF8> key2(p, p + nLen);
|
||
it = mudstate.command_htab.find(key2);
|
||
if (it != mudstate.command_htab.end())
|
||
{
|
||
CMDENT *old = static_cast<CMDENT *>(it->second);
|
||
if (old && (old->flags & CEF_ALLOC))
|
||
{
|
||
MEMFREE(old->cmdname);
|
||
old->cmdname = nullptr;
|
||
MEMFREE(old);
|
||
}
|
||
}
|
||
mudstate.command_htab.erase(key2);
|
||
mudstate.command_htab.emplace(key2, cmdp);
|
||
}
|
||
else
|
||
{
|
||
STARTLOG(LOG_PROBLEMS, "HLP", "MEM");
|
||
log_printf(T("cf_helpfile_add: out of memory registering help command."));
|
||
ENDLOG;
|
||
return -1;
|
||
}
|
||
|
||
mudstate.nHelpDesc++;
|
||
|
||
return 0;
|
||
}
|
||
|
||
static CF_HAND(cf_helpfile)
|
||
{
|
||
UNUSED_PARAMETER(vp);
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
|
||
return add_helpfile(player, cmd, str, true);
|
||
}
|
||
|
||
static CF_HAND(cf_raw_helpfile)
|
||
{
|
||
UNUSED_PARAMETER(vp);
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
|
||
return add_helpfile(player, cmd, str, false);
|
||
}
|
||
|
||
// @hook: run softcode before or after running a hardcode command, or softcode access.
|
||
// Original idea from TinyMUSH 3, code from RhostMUSH.
|
||
// Used with express permission of RhostMUSH developers.
|
||
// Bludgeoned into MUX by Jake Nelson 7/2002.
|
||
//
|
||
static NAMETAB hook_names[] =
|
||
{
|
||
{T("after"), 3, 0, CEF_HOOK_AFTER},
|
||
{T("before"), 3, 0, CEF_HOOK_BEFORE},
|
||
{T("fail"), 3, 0, CEF_HOOK_AFAIL},
|
||
{T("ignore"), 3, 0, CEF_HOOK_IGNORE},
|
||
{T("igswitch"), 3, 0, CEF_HOOK_IGSWITCH},
|
||
{T("permit"), 3, 0, CEF_HOOK_PERMIT},
|
||
{T("args"), 3, 0, CEF_HOOK_ARGS},
|
||
{nullptr, 0, 0, 0}
|
||
};
|
||
|
||
static CF_HAND(cf_hook)
|
||
{
|
||
UNUSED_PARAMETER(vp);
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
UNUSED_PARAMETER(player);
|
||
UNUSED_PARAMETER(cmd);
|
||
|
||
UTF8 *hookcmd, *hookptr, playbuff[201];
|
||
int hookflg;
|
||
CMDENT *cmdp;
|
||
|
||
int retval = -1;
|
||
memset(playbuff, '\0', sizeof(playbuff));
|
||
mux_strncpy(playbuff, str, sizeof(playbuff)-1);
|
||
string_token st(playbuff, T(" \t"));
|
||
hookcmd = st.parse();
|
||
if (hookcmd != nullptr)
|
||
{
|
||
{
|
||
size_t nHookCmd = strlen(reinterpret_cast<char *>(hookcmd));
|
||
auto it = mudstate.command_htab.find(std::vector<UTF8>(hookcmd, hookcmd + nHookCmd));
|
||
cmdp = (it != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it->second) : nullptr;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
return retval;
|
||
}
|
||
if (!cmdp)
|
||
{
|
||
return retval;
|
||
}
|
||
|
||
int t = cmdp->flags;
|
||
mux_strncpy(playbuff, str, sizeof(playbuff)-1);
|
||
hookptr = st.parse();
|
||
while (hookptr != nullptr)
|
||
{
|
||
if ( hookptr[0] == '!'
|
||
&& hookptr[1] != '\0')
|
||
{
|
||
if (search_nametab(GOD, hook_names, hookptr+1, &hookflg))
|
||
{
|
||
retval = 0;
|
||
t = t & ~hookflg;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (search_nametab(GOD, hook_names, hookptr, &hookflg))
|
||
{
|
||
retval = 0;
|
||
t = t | hookflg;
|
||
}
|
||
}
|
||
hookptr = st.parse();
|
||
}
|
||
cmdp->flags = t;
|
||
return retval;
|
||
}
|
||
|
||
static CF_HAND(cf_module)
|
||
{
|
||
UNUSED_PARAMETER(vp);
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
|
||
string_token st(str, T(" \t"));
|
||
UTF8 *modname = st.parse();
|
||
UTF8 *modtype = st.parse();
|
||
|
||
enum MODTYPE
|
||
{
|
||
eInProc = 0,
|
||
eLocal
|
||
} eType = eInProc;
|
||
|
||
if (nullptr == modname)
|
||
{
|
||
cf_log_syntax(player, cmd, T("Module name is missing."));
|
||
return -1;
|
||
}
|
||
|
||
if (nullptr == modtype)
|
||
{
|
||
eType = eInProc;
|
||
}
|
||
else if (strcmp(reinterpret_cast<char *>(modtype), "inproc") == 0)
|
||
{
|
||
eType = eInProc;
|
||
}
|
||
else if ( strcmp(reinterpret_cast<char *>(modtype), "local") == 0
|
||
|| strcmp(reinterpret_cast<char *>(modtype), "slave") == 0)
|
||
{
|
||
eType = eLocal;
|
||
}
|
||
else
|
||
{
|
||
cf_log_syntax(player, cmd, T("load type is invalid."));
|
||
return -1;
|
||
}
|
||
|
||
#if !defined(STUB_SLAVE)
|
||
if (eLocal == eType)
|
||
{
|
||
cf_log_syntax(player, cmd, T("StubSlave is not enabled."));
|
||
return -1;
|
||
}
|
||
#endif // !STUB_SLAVE
|
||
|
||
#if defined(STUB_SLAVE)
|
||
mux_ISlaveControl *pISlaveControl = nullptr;
|
||
if (eLocal == eType)
|
||
{
|
||
MUX_RESULT mrSlave = mux_CreateInstance(CID_StubSlave, nullptr,
|
||
UseSlaveProcess, IID_ISlaveControl,
|
||
(void **)&pISlaveControl);
|
||
if (MUX_FAILED(mrSlave) || nullptr == pISlaveControl)
|
||
{
|
||
cf_log_syntax(player, cmd, T("No StubSlave management interface available."));
|
||
return -1;
|
||
}
|
||
}
|
||
#endif // STUB_SLAVE
|
||
|
||
MUX_RESULT mr = MUX_S_OK;
|
||
if ('!' == str[0])
|
||
{
|
||
if (eInProc == eType)
|
||
{
|
||
mr = mux_RemoveModule(str+1);
|
||
}
|
||
#if defined(STUB_SLAVE)
|
||
else
|
||
{
|
||
mr = pISlaveControl->RemoveModule(str+1);
|
||
}
|
||
#endif // STUB_SLAVE
|
||
}
|
||
else
|
||
{
|
||
LBuf buffer = LBuf_Src("cf_module");
|
||
bool bHaveFilename = true;
|
||
#if defined(WINDOWS_FILES)
|
||
size_t n;
|
||
mux_sprintf(buffer, LBUF_SIZE, T(".\\bin\\%s.dll"), str);
|
||
const UTF16 *filename = ConvertFromUTF8ToUTF16(buffer, n);
|
||
if (nullptr == filename)
|
||
{
|
||
bHaveFilename = false;
|
||
mr = MUX_E_FAIL;
|
||
}
|
||
#elif defined(UNIX_FILES)
|
||
mux_sprintf(buffer, LBUF_SIZE, T("./bin/%s.so"), str);
|
||
UTF8 *filename = buffer;
|
||
#endif
|
||
if ( bHaveFilename
|
||
&& eInProc == eType)
|
||
{
|
||
mr = mux_AddModule(str, filename);
|
||
}
|
||
#if defined(STUB_SLAVE)
|
||
else if (bHaveFilename)
|
||
{
|
||
mr = pISlaveControl->AddModule(str, filename);
|
||
}
|
||
#endif // STUB_SLAVE
|
||
}
|
||
|
||
#if defined(STUB_SLAVE)
|
||
if (nullptr != pISlaveControl)
|
||
{
|
||
pISlaveControl->Release();
|
||
pISlaveControl = nullptr;
|
||
}
|
||
#endif // STUB_SLAVE
|
||
|
||
if (MUX_FAILED(mr))
|
||
{
|
||
cf_log_notfound(player, cmd, T("Module"), str);
|
||
return -1;
|
||
}
|
||
else
|
||
{
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_include: Read another config file. Only valid during startup.
|
||
//
|
||
static CF_HAND(cf_include)
|
||
{
|
||
UNUSED_PARAMETER(vp);
|
||
UNUSED_PARAMETER(pExtra);
|
||
UNUSED_PARAMETER(nExtra);
|
||
|
||
if (!mudstate.bReadingConfiguration)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
FILE *fp;
|
||
if (!mux_fopen(&fp, str, T("rb")))
|
||
{
|
||
cf_log_notfound(player, cmd, T("Config file"), str);
|
||
return -1;
|
||
}
|
||
LBuf buf = LBuf_Src("cf_include");
|
||
if (nullptr == fgets(reinterpret_cast<char *>(buf.get()), LBUF_SIZE, fp))
|
||
{
|
||
// Distinguish "nothing to read" from "could not read" (#1601).
|
||
// fopen succeeds on a directory on both Linux and macOS, and the
|
||
// read then fails with EISDIR -- so `-c somedir` used to look
|
||
// exactly like an empty file and be silently accepted, leaving the
|
||
// game on compiled-in defaults. An empty regular file sets feof
|
||
// with no error and stays deliberately valid: it is the supported
|
||
// way to ask for the defaults on purpose.
|
||
//
|
||
bool bReadFailed = (0 != ferror(fp));
|
||
|
||
// The original early return here skipped the fclose that the normal
|
||
// path below does, leaking the handle for every empty include.
|
||
//
|
||
mux_fclose(fp);
|
||
if (bReadFailed)
|
||
{
|
||
cf_log_notfound(player, cmd, T("Readable config file"), str);
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
while (!feof(fp))
|
||
{
|
||
UTF8 *zp = buf;
|
||
|
||
// Remove comments. Anything after the '#' is a comment except if it
|
||
// matches: whitespace + '#' + digit.
|
||
//
|
||
while (*zp != '\0')
|
||
{
|
||
if ( *zp == '#'
|
||
&& ( zp <= buf
|
||
|| !mux_isspace(zp[-1])
|
||
|| !mux_isdigit(zp[1])))
|
||
{
|
||
// Found a comment.
|
||
//
|
||
*zp = '\0';
|
||
}
|
||
else
|
||
{
|
||
zp++;
|
||
}
|
||
}
|
||
|
||
// Trim trailing spaces.
|
||
//
|
||
while ( buf < zp
|
||
&& mux_isspace(zp[-1]))
|
||
{
|
||
*(--zp) = '\0';
|
||
}
|
||
|
||
// Process line.
|
||
//
|
||
UTF8 *cp = buf;
|
||
|
||
// Trim leading spaces.
|
||
//
|
||
while (mux_isspace(*cp))
|
||
{
|
||
cp++;
|
||
}
|
||
|
||
// Skip over command.
|
||
//
|
||
UTF8 *ap;
|
||
for (ap = cp; *ap && !mux_isspace(*ap); ap++)
|
||
{
|
||
; // Nothing.
|
||
}
|
||
|
||
// Terminate command.
|
||
//
|
||
if (*ap)
|
||
{
|
||
*ap++ = '\0';
|
||
}
|
||
|
||
// Skip spaces between command and argument.
|
||
//
|
||
while (mux_isspace(*ap))
|
||
{
|
||
ap++;
|
||
}
|
||
|
||
if (*cp)
|
||
{
|
||
cf_set(cp, ap, player);
|
||
}
|
||
|
||
if (nullptr == fgets(reinterpret_cast<char *>(buf.get()), LBUF_SIZE, fp))
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
mux_fclose(fp);
|
||
return 0;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// conftable: Table for parsing the configuration file.
|
||
|
||
static CONFPARM conftable[] =
|
||
{
|
||
{T("access"), cf_access, CA_GOD, CA_DISABLED, nullptr, access_nametab, 0},
|
||
{T("alias"), cf_cmd_alias, CA_GOD, CA_DISABLED, reinterpret_cast<int *>(&mudstate.command_htab), 0, 0},
|
||
{T("attr_access"), cf_attr_access, CA_GOD, CA_DISABLED, reinterpret_cast<int *>(&mudstate.attrperm_list), attraccess_nametab, 0},
|
||
{T("attr_alias"), cf_attr_name_alias,CA_GOD, CA_DISABLED, nullptr, nullptr, 0},
|
||
{T("attr_cmd_access"), cf_acmd_access, CA_GOD, CA_DISABLED, nullptr, access_nametab, 0},
|
||
{T("attr_name_charset"), cf_modify_bits, CA_GOD, CA_PUBLIC, &mudconf.attr_name_charset, allow_charset_nametab, 0},
|
||
{T("autozone"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.autozone), nullptr, 0},
|
||
{T("bad_name"), cf_badname, CA_GOD, CA_DISABLED, nullptr, nullptr, 0},
|
||
{T("badsite_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.site_file), nullptr, SIZEOF_PATHNAME},
|
||
{T("cache_names"), cf_bool, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.cache_names), nullptr, 0},
|
||
{T("cache_tick_period"), cf_seconds, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(&mudconf.cache_tick_period), nullptr, 0},
|
||
{T("cache_max_size"), cf_size, CA_GOD, CA_GOD, reinterpret_cast<int *>(&mudconf.max_cache_size), nullptr, 0},
|
||
{T("cache_preload_depth"), cf_int, CA_GOD, CA_GOD, &mudconf.cache_preload_depth, nullptr, 0},
|
||
{T("check_interval"), cf_int, CA_GOD, CA_WIZARD, &mudconf.check_interval, nullptr, 0},
|
||
{T("check_offset"), cf_int, CA_GOD, CA_WIZARD, &mudconf.check_offset, nullptr, 0},
|
||
{T("clone_copies_cost"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.clone_copy_cost), nullptr, 0},
|
||
{T("command_quota_increment"), cf_int, CA_GOD, CA_WIZARD, &mudconf.cmd_quota_incr, nullptr, 0},
|
||
{T("command_quota_max"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.cmd_quota_max, nullptr, 0},
|
||
{T("comsys_database"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.comsys_db), nullptr, SIZEOF_PATHNAME},
|
||
{T("config_access"), cf_cf_access, CA_GOD, CA_DISABLED, nullptr, access_nametab, 0},
|
||
{T("conn_timeout"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.conn_timeout, nullptr, 0},
|
||
{T("connect_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.conn_file), nullptr, SIZEOF_PATHNAME},
|
||
{T("connect_help_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.conn_help_file), nullptr, SIZEOF_PATHNAME},
|
||
{T("connect_reg_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.creg_file), nullptr, SIZEOF_PATHNAME},
|
||
{T("lag_limit"), cf_seconds, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(&mudconf.max_cmdsecs), nullptr, 0},
|
||
{T("crash_database"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.crashdb), nullptr, SIZEOF_PATHNAME},
|
||
{T("crash_message"), cf_string, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(mudconf.crash_msg), nullptr, GBUF_SIZE},
|
||
{T("create_max_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.createmax, nullptr, 0},
|
||
{T("create_min_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.createmin, nullptr, 0},
|
||
{T("dark_sleepers"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(&mudconf.dark_sleepers), nullptr, 0},
|
||
{T("default_charset"), cf_option, CA_GOD, CA_PUBLIC, &mudconf.default_charset, default_charset_nametab, 0},
|
||
{T("default_home"), cf_dbref, CA_GOD, CA_PUBLIC, &mudconf.default_home, nullptr, 0},
|
||
{T("destroy_going_now"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(&mudconf.destroy_going_now), nullptr, 0},
|
||
{T("dig_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.digcost, nullptr, 0},
|
||
{T("down_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.down_file), nullptr, SIZEOF_PATHNAME},
|
||
{T("down_motd_message"), cf_string, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(mudconf.downmotd_msg), nullptr, GBUF_SIZE},
|
||
{T("dump_interval"), cf_int, CA_GOD, CA_WIZARD, &mudconf.dump_interval, nullptr, 0},
|
||
{T("dump_message"), cf_string, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(mudconf.dump_msg), nullptr, 256},
|
||
{T("dump_offset"), cf_int, CA_GOD, CA_WIZARD, &mudconf.dump_offset, nullptr, 0},
|
||
{T("earn_limit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.paylimit, nullptr, 0},
|
||
{T("eval_comtitle"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.eval_comtitle), nullptr, 0},
|
||
{T("events_daily_hour"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.events_daily_hour, nullptr, 0},
|
||
{T("examine_flags"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.ex_flags), nullptr, 0},
|
||
{T("examine_public_attrs"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.exam_public), nullptr, 0},
|
||
{T("exit_flags"), cf_set_flags, CA_GOD, CA_DISABLED, reinterpret_cast<int *>(&mudconf.exit_flags), nullptr, 0},
|
||
{T("exit_name_charset"), cf_modify_bits, CA_GOD, CA_PUBLIC, &mudconf.exit_name_charset, allow_charset_nametab, 0},
|
||
{T("exit_quota"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.exit_quota, nullptr, 0},
|
||
{T("exit_parent"), cf_dbref, CA_GOD, CA_PUBLIC, &mudconf.exit_parent, nullptr, 0},
|
||
{T("fascist_teleport"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.fascist_tport), nullptr, 0},
|
||
{T("find_money_chance"), cf_int, CA_GOD, CA_WIZARD, &mudconf.payfind, nullptr, 0},
|
||
{T("fixed_home_message"), cf_string, CA_STATIC, CA_PUBLIC, reinterpret_cast<int *>(mudconf.fixed_home_msg), nullptr, 128},
|
||
{T("fixed_tel_message"), cf_string, CA_STATIC, CA_PUBLIC, reinterpret_cast<int *>(mudconf.fixed_tel_msg), nullptr, 128},
|
||
{T("flag_access"), cf_flag_access, CA_GOD, CA_DISABLED, nullptr, nullptr, 0},
|
||
{T("flag_alias"), cf_flagalias, CA_GOD, CA_DISABLED, nullptr, nullptr, 0},
|
||
{T("flag_name"), cf_flag_name, CA_GOD, CA_DISABLED, nullptr, nullptr, 0},
|
||
{T("float_precision"), cf_int, CA_STATIC, CA_PUBLIC, &mudconf.float_precision, nullptr, 0},
|
||
{T("forbid_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_FORBID},
|
||
#if defined(HAVE_WORKING_FORK)
|
||
{T("fork_dump"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(&mudconf.fork_dump), nullptr, 0},
|
||
#endif // HAVE_WORKING_FORK
|
||
{T("full_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.full_file), nullptr, SIZEOF_PATHNAME},
|
||
{T("full_motd_message"), cf_string, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(mudconf.fullmotd_msg), nullptr, GBUF_SIZE},
|
||
{T("function_access"), cf_func_access, CA_GOD, CA_DISABLED, nullptr, access_nametab, 0},
|
||
{T("function_alias"), cf_function_alias,CA_GOD, CA_DISABLED, nullptr, nullptr, 0},
|
||
{T("function_invocation_limit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.func_invk_lim, nullptr, 0},
|
||
{T("function_name"), cf_function_name, CA_GOD, CA_DISABLED, nullptr, nullptr, 0},
|
||
{T("function_recursion_limit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.func_nest_lim, nullptr, 0},
|
||
{T("global_error_obj"), cf_dbref, CA_GOD, CA_GOD, &mudconf.global_error_obj, nullptr, 0},
|
||
{T("good_name"), cf_badname, CA_GOD, CA_DISABLED, nullptr, nullptr, 1},
|
||
{T("guest_char_num"), cf_dbref, CA_STATIC, CA_WIZARD, &mudconf.guest_char, nullptr, 0},
|
||
{T("guest_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.guest_file), nullptr, SIZEOF_PATHNAME},
|
||
{T("guest_nuker"), cf_dbref, CA_GOD, CA_WIZARD, &mudconf.guest_nuker, nullptr, 0},
|
||
{T("guest_prefix"), cf_string, CA_STATIC, CA_PUBLIC, reinterpret_cast<int *>(mudconf.guest_prefix), nullptr, 32},
|
||
{T("guest_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_GUEST},
|
||
{T("guests_channel"), cf_string, CA_STATIC, CA_PUBLIC, reinterpret_cast<int *>(mudconf.guests_channel), nullptr, 32},
|
||
{T("guests_channel_alias"), cf_string, CA_STATIC, CA_PUBLIC, reinterpret_cast<int *>(mudconf.guests_channel_alias), nullptr, 32},
|
||
{T("have_zones"), cf_bool, CA_STATIC, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.have_zones), nullptr, 0},
|
||
{T("help_executor"), cf_dbref, CA_GOD, CA_WIZARD, &mudconf.help_executor, nullptr, 0},
|
||
{T("helpfile"), cf_helpfile, CA_STATIC, CA_DISABLED, nullptr, nullptr, 0},
|
||
{T("hook_cmd"), cf_hook, CA_GOD, CA_GOD, nullptr, nullptr, 0},
|
||
{T("hook_obj"), cf_dbref, CA_GOD, CA_GOD, &mudconf.hook_obj, nullptr, 0},
|
||
{T("hostnames"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(&mudconf.use_hostname), nullptr, 0},
|
||
{T("idle_interval"), cf_int, CA_GOD, CA_WIZARD, &mudconf.idle_interval, nullptr, 0},
|
||
{T("idle_timeout"), cf_live_driver_int, CA_GOD, CA_PUBLIC, &mudconf.idle_timeout, nullptr, 0},
|
||
{T("idle_wiz_dark"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(&mudconf.idle_wiz_dark), nullptr, 0},
|
||
{T("include"), cf_include, CA_STATIC, CA_DISABLED, nullptr, nullptr, 0},
|
||
{T("indent_desc"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.indent_desc), nullptr, 0},
|
||
{T("initial_size"), cf_int, CA_STATIC, CA_WIZARD, &mudconf.init_size, nullptr, 0},
|
||
{T("input_database"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.indb), nullptr, SIZEOF_PATHNAME},
|
||
{T("ip_address"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.ip_address), nullptr, LBUF_SIZE},
|
||
{T("jit_code_slots"), cf_int, CA_GOD, CA_WIZARD, &mudconf.jit_code_slots, nullptr, 0},
|
||
{T("jit_compile_cache_max"), cf_int, CA_GOD, CA_WIZARD, &mudconf.jit_compile_cache_max, nullptr, 0},
|
||
{T("jit_eval_brackets"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(&mudconf.jit_eval_brackets), nullptr, 0},
|
||
{T("lua_jit"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(&mudconf.lua_jit), nullptr, 0},
|
||
{T("keepalive_interval"), cf_int, CA_GOD, CA_WIZARD, &mudconf.keepalive_interval, nullptr, 0},
|
||
{T("kill_guarantee_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.killguarantee, nullptr, 0},
|
||
{T("kill_max_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.killmax, nullptr, 0},
|
||
{T("kill_min_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.killmin, nullptr, 0},
|
||
{T("lag_maximum"), cf_seconds, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(&mudconf.rpt_cmdsecs), nullptr, 0},
|
||
{T("lbuf_size"), cf_int, CA_DISABLED, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.lbuf_size), nullptr, 0},
|
||
{T("link_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.linkcost, nullptr, 0},
|
||
{T("list_access"), cf_ntab_access, CA_GOD, CA_DISABLED, reinterpret_cast<int *>(list_names), access_nametab, 0},
|
||
{T("lock_recursion_limit"), cf_int, CA_WIZARD, CA_PUBLIC, &mudconf.lock_nest_lim, nullptr, 0},
|
||
{T("log"), cf_modify_bits, CA_GOD, CA_DISABLED, &mudconf.log_options, logoptions_nametab, 0},
|
||
{T("log_options"), cf_modify_bits, CA_GOD, CA_DISABLED, &mudconf.log_info, logdata_nametab, 0},
|
||
{T("logout_cmd_access"), cf_ntab_access, CA_GOD, CA_DISABLED, reinterpret_cast<int *>(logout_cmdtable), access_nametab, 0},
|
||
{T("logout_cmd_alias"), cf_alias, CA_GOD, CA_DISABLED, reinterpret_cast<int *>(&mudstate.logout_cmd_htab),nullptr, 0},
|
||
{T("look_obey_terse"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.terse_look), nullptr, 0},
|
||
{T("lua_instruction_limit"), cf_live_lua_int, CA_GOD, CA_PUBLIC, &mudconf.lua_instruction_limit, nullptr, 0},
|
||
{T("lua_memory_limit"), cf_live_lua_int, CA_GOD, CA_PUBLIC, &mudconf.lua_memory_limit, nullptr, 0},
|
||
{T("machine_command_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.machinecost, nullptr, 0},
|
||
{T("mail_database"), cf_string_dyn, CA_GOD, CA_GOD, reinterpret_cast<int *>(&mudconf.mail_db), nullptr, SIZEOF_PATHNAME},
|
||
{T("mail_expiration"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.mail_expiration, nullptr, 0},
|
||
{T("mail_per_hour"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.mail_per_hour, nullptr, 0},
|
||
{T("mail_max_per_player"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.mail_max_per_player, nullptr, 0},
|
||
{T("email_per_hour"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.email_per_hour, nullptr, 0},
|
||
{T("master_room"), cf_dbref, CA_GOD, CA_WIZARD, &mudconf.master_room, nullptr, 0},
|
||
{T("match_own_commands"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.match_mine), nullptr, 0},
|
||
{T("max_cache_size"), cf_size, CA_GOD, CA_GOD, reinterpret_cast<int *>(&mudconf.max_cache_size), nullptr, 0},
|
||
{T("max_name_protect"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.max_name_protect, nullptr, 0},
|
||
{T("max_players"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.max_players, nullptr, 0},
|
||
{T("min_guests"), cf_int, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.min_guests), nullptr, 0},
|
||
{T("money_name_plural"), cf_string, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(mudconf.many_coins), nullptr, 32},
|
||
{T("money_name_singular"), cf_string, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(mudconf.one_coin), nullptr, 32},
|
||
{T("motd_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.motd_file), nullptr, SIZEOF_PATHNAME},
|
||
{T("motd_message"), cf_string, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(mudconf.motd_msg), nullptr, GBUF_SIZE},
|
||
{T("module"), cf_module, CA_GOD, CA_WIZARD, nullptr, nullptr, 0},
|
||
{T("mud_name"), cf_string, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(mudconf.mud_name), nullptr, 32},
|
||
{T("language"), cf_string, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(mudconf.language), nullptr, 32},
|
||
{T("newuser_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.crea_file), nullptr, SIZEOF_PATHNAME},
|
||
{T("no_flash"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&g_no_flash), nullptr, 0},
|
||
{T("noguest_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_NOGUEST},
|
||
{T("nositemon_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_NOSITEMON},
|
||
{T("notify_recursion_limit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.ntfy_nest_lim, nullptr, 0},
|
||
{T("number_guests"), cf_int, CA_STATIC, CA_WIZARD, &mudconf.number_guests, nullptr, 0},
|
||
{T("open_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.opencost, nullptr, 0},
|
||
{T("output_database"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.outdb), nullptr, SIZEOF_PATHNAME},
|
||
{T("input_limit"), cf_live_driver_int, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(&mudconf.input_limit), nullptr, 0},
|
||
{T("max_preauth_sitecons"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.max_preauth_sitecons, nullptr, 0},
|
||
{T("pool_memory_limit"), cf_pool_limit, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(&mudconf.pool_memory_limit), nullptr, 0},
|
||
{T("proto_detect_window"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.proto_detect_window, nullptr, 0},
|
||
{T("output_limit"), cf_live_driver_int, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(&mudconf.output_limit), nullptr, 0},
|
||
{T("page_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.pagecost, nullptr, 0},
|
||
{T("paranoid_allocate"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(&mudconf.paranoid_alloc), nullptr, 0},
|
||
{T("parent_recursion_limit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.parent_nest_lim, nullptr, 0},
|
||
{T("password_hash_rounds"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.password_hash_rounds, nullptr, 0},
|
||
{T("password_methods"), cf_modify_bits, CA_GOD, CA_PUBLIC, &mudconf.password_methods, method_nametab, 0},
|
||
{T("paycheck"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.paycheck, nullptr, 0},
|
||
{T("pemit_any_object"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.pemit_any), nullptr, 0},
|
||
{T("pemit_far_players"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.pemit_players), nullptr, 0},
|
||
{T("permit_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_PERMIT},
|
||
{T("player_flags"), cf_set_flags, CA_GOD, CA_DISABLED, reinterpret_cast<int *>(&mudconf.player_flags), nullptr, 0},
|
||
{T("player_listen"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.player_listen), nullptr, 0},
|
||
{T("player_match_own_commands"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.match_mine_pl), nullptr, 0},
|
||
{T("player_name_spaces"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.name_spaces), nullptr, 0},
|
||
{T("player_name_charset"), cf_modify_bits, CA_GOD, CA_PUBLIC, &mudconf.player_name_charset, allow_charset_nametab, 0},
|
||
{T("player_parent"), cf_dbref, CA_GOD, CA_PUBLIC, &mudconf.player_parent, nullptr, 0},
|
||
{T("player_queue_limit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.queuemax, nullptr, 0},
|
||
{T("player_quota"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.player_quota, nullptr, 0},
|
||
{T("player_starting_home"), cf_dbref, CA_GOD, CA_PUBLIC, &mudconf.start_home, nullptr, 0},
|
||
{T("player_starting_room"), cf_dbref, CA_GOD, CA_PUBLIC, &mudconf.start_room, nullptr, 0},
|
||
{T("port"), cf_int_array, CA_STATIC, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.ports), nullptr, MAX_LISTEN_PORTS},
|
||
#if defined(UNIX_SSL) || defined(_WIN32)
|
||
{T("port_ssl"), cf_int_array, CA_STATIC, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.sslPorts), nullptr, MAX_LISTEN_PORTS},
|
||
#endif
|
||
{T("postdump_message"), cf_string, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(mudconf.postdump_msg), nullptr, 256},
|
||
{T("power_alias"), cf_poweralias, CA_GOD, CA_DISABLED, nullptr, nullptr, 0},
|
||
{T("pronoun_group"), cf_pronoun_group, CA_GOD, CA_DISABLED, nullptr, nullptr, 0},
|
||
{T("pcreate_per_hour"), cf_int, CA_STATIC, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.pcreate_per_hour),nullptr, 0},
|
||
{T("public_channel"), cf_string, CA_STATIC, CA_PUBLIC, reinterpret_cast<int *>(mudconf.public_channel), nullptr, 32},
|
||
{T("public_channel_alias"), cf_string, CA_STATIC, CA_PUBLIC, reinterpret_cast<int *>(mudconf.public_channel_alias), nullptr, 32},
|
||
{T("player_channels"), cf_string, CA_STATIC, CA_PUBLIC, reinterpret_cast<int *>(mudconf.player_channels), nullptr, LBUF_SIZE},
|
||
{T("public_flags"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.pub_flags), nullptr, 0},
|
||
{T("pueblo_message"), cf_string, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(mudconf.pueblo_msg), nullptr, GBUF_SIZE},
|
||
{T("queue_active_chunk"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.active_q_chunk, nullptr, 0},
|
||
{T("queue_idle_chunk"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.queue_chunk, nullptr, 0},
|
||
{T("quiet_look"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.quiet_look), nullptr, 0},
|
||
{T("quiet_whisper"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.quiet_whisper), nullptr, 0},
|
||
{T("quit_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.quit_file), nullptr, SIZEOF_PATHNAME},
|
||
{T("quotas"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.quotas), nullptr, 0},
|
||
{T("raw_helpfile"), cf_raw_helpfile,CA_STATIC, CA_DISABLED, nullptr, nullptr, 0},
|
||
{T("read_remote_desc"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.read_rem_desc), nullptr, 0},
|
||
{T("read_remote_name"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.read_rem_name), nullptr, 0},
|
||
{T("references_per_hour"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.references_per_hour, nullptr, 0},
|
||
{T("register_create_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.regf_file), nullptr, SIZEOF_PATHNAME},
|
||
{T("register_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_REGISTER},
|
||
{T("reset_players"), cf_bool, CA_GOD, CA_DISABLED, reinterpret_cast<int *>(&mudconf.reset_players), nullptr, 0},
|
||
{T("reset_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_RESET},
|
||
{T("restrict_home"), cf_bool, CA_GOD, CA_DISABLED, reinterpret_cast<int *>(&mudconf.restrict_home), nullptr, 0},
|
||
{T("max_lastsite_cnt"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.max_lastsite_cnt, nullptr, 0},
|
||
{T("min_con_attempt"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.min_con_attempt, nullptr, 0},
|
||
{T("nospam_connect"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.nospam_connect, nullptr, 0},
|
||
{T("login_fail_limit"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.login_fail_limit, nullptr, 0},
|
||
{T("login_fail_period"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.login_fail_period, nullptr, 0},
|
||
{T("retry_limit"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.retry_limit, nullptr, 0},
|
||
{T("robot_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.robotcost, nullptr, 0},
|
||
{T("robot_flags"), cf_set_flags, CA_GOD, CA_DISABLED, reinterpret_cast<int *>(&mudconf.robot_flags), nullptr, 0},
|
||
{T("robot_speech"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.robot_speak), nullptr, 0},
|
||
{T("room_flags"), cf_set_flags, CA_GOD, CA_DISABLED, reinterpret_cast<int *>(&mudconf.room_flags), nullptr, 0},
|
||
{T("room_name_charset"), cf_modify_bits, CA_GOD, CA_PUBLIC, &mudconf.room_name_charset, allow_charset_nametab, 0},
|
||
{T("room_parent"), cf_dbref, CA_GOD, CA_PUBLIC, &mudconf.room_parent, nullptr, 0},
|
||
{T("room_quota"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.room_quota, nullptr, 0},
|
||
{T("run_startup"), cf_bool, CA_STATIC, CA_WIZARD, reinterpret_cast<int *>(&mudconf.run_startup), nullptr, 0},
|
||
{T("sacrifice_adjust"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.sacadjust, nullptr, 0},
|
||
{T("sacrifice_factor"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.sacfactor, nullptr, 0},
|
||
{T("safe_wipe"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(&mudconf.safe_wipe), nullptr, 0},
|
||
{T("safer_iter"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.safer_iter), nullptr, 0},
|
||
{T("safer_passwords"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.safer_passwords), nullptr, 0},
|
||
{T("search_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.searchcost, nullptr, 0},
|
||
{T("see_owned_dark"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.see_own_dark), nullptr, 0},
|
||
{T("signal_action"), cf_option, CA_STATIC, CA_GOD, &mudconf.sig_action, sigactions_nametab, 0},
|
||
{T("site_chars"), cf_live_driver_int, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(&mudconf.site_chars), nullptr, 0},
|
||
{T("sitemon_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_SITEMON},
|
||
{T("space_compress"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.space_compress), nullptr, 0},
|
||
#if defined(UNIX_SSL) || defined(_WIN32)
|
||
{T("ssl_certificate_file"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast<int *>(mudconf.ssl_certificate_file),nullptr, 128},
|
||
{T("ssl_certificate_key"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast<int *>(mudconf.ssl_certificate_key), nullptr, 128},
|
||
{T("ssl_certificate_password"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast<int *>(mudconf.ssl_certificate_password), nullptr, 128},
|
||
#endif
|
||
{T("starting_money"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.paystart, nullptr, 0},
|
||
{T("starting_quota"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.start_quota, nullptr, 0},
|
||
{T("status_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.status_file), nullptr, SIZEOF_PATHNAME},
|
||
{T("stripped_flags"), cf_set_flags, CA_GOD, CA_DISABLED, reinterpret_cast<int *>(&mudconf.stripped_flags), nullptr, 0},
|
||
{T("suspect_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_SUSPECT},
|
||
{T("sweep_dark"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.sweep_dark), nullptr, 0},
|
||
{T("switch_default_all"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.switch_df_all), nullptr, 0},
|
||
{T("talk_mode_default"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.talk_mode_default), nullptr, 0},
|
||
{T("terse_nospoof"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.terse_nospoof), nullptr, 0},
|
||
{T("terse_shows_contents"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.terse_contents), nullptr, 0},
|
||
{T("terse_shows_exits"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.terse_exits), nullptr, 0},
|
||
{T("terse_shows_move_messages"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.terse_movemsg), nullptr, 0},
|
||
{T("thing_flags"), cf_set_flags, CA_GOD, CA_DISABLED, reinterpret_cast<int *>(&mudconf.thing_flags), nullptr, 0},
|
||
{T("thing_name_charset"), cf_modify_bits, CA_GOD, CA_PUBLIC, &mudconf.thing_name_charset, allow_charset_nametab, 0},
|
||
{T("thing_parent"), cf_dbref, CA_GOD, CA_PUBLIC, &mudconf.thing_parent, nullptr, 0},
|
||
{T("thing_quota"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.thing_quota, nullptr, 0},
|
||
{T("timeslice"), cf_seconds, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.timeslice), nullptr, 0},
|
||
{T("toad_recipient"), cf_dbref, CA_GOD, CA_WIZARD, &mudconf.toad_recipient, nullptr, 0},
|
||
{T("trace_output_limit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.trace_limit, nullptr, 0},
|
||
{T("trace_topdown"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.trace_topdown), nullptr, 0},
|
||
{T("trust_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_TRUST},
|
||
{T("unowned_safe"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.safe_unowned), nullptr, 0},
|
||
{T("user_attr_access"), cf_modify_bits, CA_GOD, CA_DISABLED, &mudconf.vattr_flags, attraccess_nametab, 0},
|
||
{T("user_attr_per_hour"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.vattr_per_hour, nullptr, 0},
|
||
{T("vlimit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.vlimit, nullptr, 0},
|
||
{T("wait_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.waitcost, nullptr, 0},
|
||
{T("wizard_motd_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf.wizmotd_file), nullptr, SIZEOF_PATHNAME},
|
||
{T("wizard_motd_message"), cf_string, CA_GOD, CA_WIZARD, reinterpret_cast<int *>(mudconf.wizmotd_msg), nullptr, GBUF_SIZE},
|
||
{T("zone_recursion_limit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.zone_nest_lim, nullptr, 0},
|
||
#ifdef REALITY_LVLS
|
||
{T("reality_level"), cf_rlevel, CA_STATIC, CA_GOD, reinterpret_cast<int *>(&mudconf), nullptr, 0},
|
||
{T("def_room_rx"), cf_int, CA_WIZARD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.def_room_rx), nullptr, 0},
|
||
{T("def_room_tx"), cf_int, CA_WIZARD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.def_room_tx), nullptr, 0},
|
||
{T("def_player_rx"), cf_int, CA_WIZARD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.def_player_rx), nullptr, 0},
|
||
{T("def_player_tx"), cf_int, CA_WIZARD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.def_player_tx), nullptr, 0},
|
||
{T("def_exit_rx"), cf_int, CA_WIZARD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.def_exit_rx), nullptr, 0},
|
||
{T("def_exit_tx"), cf_int, CA_WIZARD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.def_exit_tx), nullptr, 0},
|
||
{T("def_thing_rx"), cf_int, CA_WIZARD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.def_thing_rx), nullptr, 0},
|
||
{T("def_thing_tx"), cf_int, CA_WIZARD, CA_PUBLIC, reinterpret_cast<int *>(&mudconf.def_thing_tx), nullptr, 0},
|
||
#endif // REALITY_LVLS
|
||
|
||
#if defined(INLINESQL)
|
||
{T("sql_server"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast<int *>(mudconf.sql_server), nullptr, 128},
|
||
{T("sql_user"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast<int *>(mudconf.sql_user), nullptr, 128},
|
||
{T("sql_password"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast<int *>(mudconf.sql_password), nullptr, 128},
|
||
{T("sql_database"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast<int *>(mudconf.sql_database), nullptr, 128},
|
||
#endif
|
||
{T("mail_server"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast<int *>(mudconf.mail_server), nullptr, 128},
|
||
{T("mail_ehlo"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast<int *>(mudconf.mail_ehlo), nullptr, 128},
|
||
{T("mail_sendaddr"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast<int *>(mudconf.mail_sendaddr), nullptr, 128},
|
||
{T("mail_sendname"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast<int *>(mudconf.mail_sendname), nullptr, 128},
|
||
{T("mail_subject"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast<int *>(mudconf.mail_subject), nullptr, 128},
|
||
{ nullptr, nullptr, 0, 0, nullptr, nullptr, 0}
|
||
};
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_cf_access: Set access on config directives
|
||
//
|
||
CF_HAND(cf_cf_access)
|
||
{
|
||
UNUSED_PARAMETER(vp);
|
||
|
||
CONFPARM *tp;
|
||
UTF8 *ap;
|
||
|
||
for (ap = str; *ap && !mux_isspace(*ap); ap++)
|
||
{
|
||
; // Nothing
|
||
}
|
||
if (*ap)
|
||
{
|
||
*ap++ = '\0';
|
||
}
|
||
|
||
for (tp = conftable; tp->pname; tp++)
|
||
{
|
||
if (!strcmp(reinterpret_cast<const char *>(tp->pname), reinterpret_cast<const char *>(str)))
|
||
{
|
||
// Cannot modify parameters set CA_STATIC.
|
||
//
|
||
if ( tp->flags & CA_STATIC
|
||
&& !mudstate.bReadingConfiguration)
|
||
{
|
||
notify(player, NOPERM_MESSAGE);
|
||
STARTLOG(LOG_CONFIGMODS, "CFG", "PERM");
|
||
log_name(player);
|
||
log_text(T(" tried to change access to static param: "));
|
||
log_text(tp->pname);
|
||
ENDLOG;
|
||
return -1;
|
||
}
|
||
return cf_modify_bits(&tp->flags, ap, pExtra, nExtra, player, cmd);
|
||
}
|
||
}
|
||
cf_log_notfound(player, cmd, T("Config directive"), str);
|
||
return -1;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_set: Set config parameter.
|
||
//
|
||
int cf_set(UTF8 *cp, UTF8 *ap, dbref player)
|
||
{
|
||
CONFPARM *tp;
|
||
|
||
// Search the config parameter table for the command. If we find
|
||
// it, call the handler to parse the argument.
|
||
//
|
||
for (tp = conftable; tp->pname; tp++)
|
||
{
|
||
if (!strcmp(reinterpret_cast<const char *>(tp->pname), reinterpret_cast<const char *>(cp)))
|
||
{
|
||
int i = -1;
|
||
if ( (tp->flags & CA_DISABLED) == 0
|
||
&& ( mudstate.bReadingConfiguration
|
||
|| check_access(player, tp->flags)))
|
||
{
|
||
if (!mudstate.bReadingConfiguration)
|
||
{
|
||
LBuf buff = LBuf_Src("cf_set");
|
||
mux_strncpy(buff, ap, LBUF_SIZE-1);
|
||
|
||
i = tp->interpreter(tp->loc, ap, tp->pExtra, tp->nExtra, player, cp);
|
||
|
||
STARTLOG(LOG_CONFIGMODS, "CFG", "UPDAT");
|
||
log_name(player);
|
||
log_text(T(" entered config directive: "));
|
||
log_text(cp);
|
||
log_text(T(" with args ‘"));
|
||
log_text(buff);
|
||
log_text(T("’. Status: "));
|
||
switch (i)
|
||
{
|
||
case 0:
|
||
log_text(T("Success."));
|
||
break;
|
||
|
||
case 1:
|
||
log_text(T("Partial success."));
|
||
break;
|
||
|
||
case -1:
|
||
log_text(T("Failure."));
|
||
break;
|
||
|
||
default:
|
||
log_text(T("Strange."));
|
||
}
|
||
ENDLOG;
|
||
}
|
||
else
|
||
{
|
||
i = tp->interpreter(tp->loc, ap, tp->pExtra, tp->nExtra, player, cp);
|
||
}
|
||
}
|
||
else if (!mudstate.bReadingConfiguration)
|
||
{
|
||
notify(player, NOPERM_MESSAGE);
|
||
}
|
||
return i;
|
||
}
|
||
}
|
||
|
||
// Config directive not found. Complain about it.
|
||
//
|
||
cf_log_notfound(player, T("Set"), T("Config directive"), cp);
|
||
return -1;
|
||
}
|
||
|
||
// Validate important dbrefs.
|
||
//
|
||
void ValidateConfigurationDbrefs(void)
|
||
{
|
||
static struct
|
||
{
|
||
dbref *ploc;
|
||
dbref dflt;
|
||
} Table[] =
|
||
{
|
||
{ &mudconf.default_home, NOTHING },
|
||
{ &mudconf.exit_parent, NOTHING },
|
||
{ &mudconf.global_error_obj, NOTHING },
|
||
{ &mudconf.guest_char, NOTHING },
|
||
{ &mudconf.guest_nuker, GOD },
|
||
{ &mudconf.help_executor, NOTHING },
|
||
{ &mudconf.hook_obj, NOTHING },
|
||
{ &mudconf.master_room, NOTHING },
|
||
{ &mudconf.player_parent, NOTHING },
|
||
{ &mudconf.start_home, NOTHING },
|
||
{ &mudconf.start_room, 0 },
|
||
{ &mudconf.room_parent, NOTHING },
|
||
{ &mudconf.thing_parent, NOTHING },
|
||
{ &mudconf.toad_recipient, GOD },
|
||
{ nullptr, NOTHING }
|
||
};
|
||
|
||
for (int i = 0; nullptr != Table[i].ploc; i++)
|
||
{
|
||
if (*Table[i].ploc != Table[i].dflt)
|
||
{
|
||
if (!Good_obj(*Table[i].ploc))
|
||
{
|
||
*Table[i].ploc = Table[i].dflt;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// do_admin: Command handler to set config params at runtime
|
||
//
|
||
void do_admin
|
||
(
|
||
dbref executor,
|
||
dbref caller,
|
||
dbref enactor,
|
||
int eval,
|
||
int key,
|
||
int nargs,
|
||
UTF8 *kw,
|
||
UTF8 *value,
|
||
const UTF8 *cargs[],
|
||
int ncargs
|
||
)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(key);
|
||
UNUSED_PARAMETER(nargs);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
int i = cf_set(kw, value, executor);
|
||
if ((i >= 0) && !Quiet(executor))
|
||
{
|
||
notify(executor, M_("Set."));
|
||
}
|
||
ValidateConfigurationDbrefs();
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_read: Read in config parameters from named file
|
||
//
|
||
static struct
|
||
{
|
||
UTF8 **pFilename;
|
||
const UTF8 *pSuffix;
|
||
} DefaultSuffixes[]
|
||
=
|
||
{
|
||
{ &mudconf.outdb, T(".out") },
|
||
{ &mudconf.crashdb, T(".CRASH") },
|
||
{ 0, 0 }
|
||
};
|
||
|
||
int cf_read(void)
|
||
{
|
||
int retval;
|
||
|
||
mudstate.bReadingConfiguration = true;
|
||
retval = cf_include(nullptr, mudconf.config_file, nullptr, 0, 0, const_cast<UTF8 *>(T("init")));
|
||
mudstate.bReadingConfiguration = false;
|
||
|
||
// Fill in missing DB file names.
|
||
//
|
||
size_t nInDB = strlen(reinterpret_cast<char *>(mudconf.indb));
|
||
for (int i = 0; DefaultSuffixes[i].pFilename; i++)
|
||
{
|
||
UTF8 **p = DefaultSuffixes[i].pFilename;
|
||
if (**p == '\0')
|
||
{
|
||
// The filename is an empty string so we should construct
|
||
// a default filename.
|
||
//
|
||
const UTF8 *pSuffix = DefaultSuffixes[i].pSuffix;
|
||
size_t nSuffix = strlen(reinterpret_cast<const char *>(pSuffix));
|
||
UTF8 *buff = reinterpret_cast<UTF8 *>(MEMALLOC(nInDB + nSuffix + 1));
|
||
if (nullptr == buff)
|
||
{
|
||
STARTLOG(LOG_PROBLEMS, "CNF", "MEM");
|
||
log_printf(T("cf_verify: out of memory constructing default filename."));
|
||
ENDLOG;
|
||
}
|
||
else
|
||
{
|
||
memcpy(buff, mudconf.indb, nInDB);
|
||
memcpy(buff + nInDB, pSuffix, nSuffix+1);
|
||
MEMFREE(*p);
|
||
*p = buff;
|
||
}
|
||
}
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// list_cf_access: List access to config directives.
|
||
//
|
||
void list_cf_access(dbref player)
|
||
{
|
||
for (CONFPARM *tp = conftable; tp->pname; tp++)
|
||
{
|
||
if (God(player) || check_access(player, tp->flags))
|
||
{
|
||
listset_nametab(player, access_nametab, tp->flags, tp->pname, true);
|
||
}
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_display: Given a config parameter by name, return its value in some
|
||
// sane fashion.
|
||
//
|
||
void cf_display(dbref player, const UTF8 *param_name, UTF8 *buff, UTF8 **bufc)
|
||
{
|
||
CONFPARM *tp;
|
||
|
||
for (tp = conftable; tp->pname; tp++)
|
||
{
|
||
if (!mux_stricmp(tp->pname, param_name))
|
||
{
|
||
if (check_access(player, tp->rperms))
|
||
{
|
||
// The live-push handlers store a plain int at tp->loc exactly
|
||
// as cf_int does; they differ only in re-pushing the value to
|
||
// a subsystem holding its own copy. They have to be listed
|
||
// here anyway, because this dispatch is by handler IDENTITY:
|
||
// an unrecognised interpreter falls all the way through to
|
||
// safe_noperm below and answers "#-1 PERMISSION DENIED".
|
||
//
|
||
// That answer is a lie -- check_access() has already passed
|
||
// by this point -- and it is silent, so converting a knob to
|
||
// a live handler makes config() stop reading it with no
|
||
// build or test complaining. #1222 did exactly that to all
|
||
// fourteen cf_live_driver_int knobs (max_players,
|
||
// idle_timeout, retry_limit, output_limit, nospam_connect,
|
||
// ...), which have been unreadable since; #1613 was about to
|
||
// do it to the two Lua limits.
|
||
//
|
||
if ( tp->interpreter == cf_int
|
||
|| tp->interpreter == cf_live_driver_int
|
||
|| tp->interpreter == cf_live_lua_int)
|
||
{
|
||
safe_ltoa(*(tp->loc), buff, bufc);
|
||
return;
|
||
}
|
||
else if (tp->interpreter == cf_dbref)
|
||
{
|
||
safe_chr('#', buff, bufc);
|
||
safe_ltoa(*(tp->loc), buff, bufc);
|
||
return;
|
||
}
|
||
else if (tp->interpreter == cf_bool)
|
||
{
|
||
bool *pb = reinterpret_cast<bool *>(tp->loc);
|
||
safe_bool(*pb, buff, bufc);
|
||
return;
|
||
}
|
||
else if (tp->interpreter == cf_string)
|
||
{
|
||
safe_str(reinterpret_cast<UTF8 *>(tp->loc), buff, bufc);
|
||
return;
|
||
}
|
||
else if (tp->interpreter == cf_string_dyn)
|
||
{
|
||
safe_str(*reinterpret_cast<UTF8 **>(tp->loc), buff, bufc);
|
||
return;
|
||
}
|
||
else if (tp->interpreter == cf_int_array)
|
||
{
|
||
auto *pvec = reinterpret_cast<std::vector<int> *>(tp->loc);
|
||
ITL itl;
|
||
ItemToList_Init(&itl, buff, bufc);
|
||
for (size_t i = 0; i < pvec->size(); i++)
|
||
{
|
||
if (!ItemToList_AddInteger(&itl, (*pvec)[i]))
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
ItemToList_Final(&itl);
|
||
return;
|
||
}
|
||
else if (tp->interpreter == cf_size)
|
||
{
|
||
int64_t *pSize = reinterpret_cast<int64_t *>(tp->loc);
|
||
safe_i64toa(*pSize, buff, bufc);
|
||
return;
|
||
}
|
||
else if (tp->interpreter == cf_seconds)
|
||
{
|
||
CLinearTimeDelta *pltd = reinterpret_cast<CLinearTimeDelta *>(tp->loc);
|
||
safe_str(pltd->ReturnSecondsString(7), buff, bufc);
|
||
return;
|
||
}
|
||
}
|
||
safe_noperm(buff, bufc);
|
||
return;
|
||
}
|
||
}
|
||
safe_nomatch(buff, bufc);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// cf_list: List all config options the player can read.
|
||
//
|
||
void cf_list(dbref player, UTF8 *buff, UTF8 **bufc)
|
||
{
|
||
CONFPARM *tp;
|
||
ITL itl;
|
||
ItemToList_Init(&itl, buff, bufc);
|
||
|
||
for (tp = conftable; tp->pname; tp++)
|
||
{
|
||
if (check_access(player, tp->rperms))
|
||
{
|
||
if (!ItemToList_AddString(&itl, tp->pname))
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
ItemToList_Final(&itl);
|
||
return;
|
||
}
|