MUD Events & Meetups with In-game event scripting
Running scheduled events in MUDs with global playerbases requires handling timezone math, preventing exploit farming, and avoiding event fatigue in tight-knit communities. This guide implements a production-ready event system using UTC-normalized scheduling, automated state machines, and non-blocking external notifications. You will modify core event handlers, implement anti-cheat reward distribution, and build spectator streaming buffers that operate without lagging the main game loop.

Audit your temporal trigger architecture
Locate your main game loop (comm.c or update.c) and identify whether events fire on pulse counters (PULSE_PER_SECOND) or real-time calendar checks. Document the exact file where global event updates occur—typically update_handler() or heartbeat()—and determine if your codebase uses DG Scripts, MobProgs, or hardcoded C events. Check for existing global event queues that might conflict with your new system.
⚠ Common Pitfalls
- •Assuming all MUDs use real-time clocks; many rely on tick-based pulses
- •Modifying core pulse counts without checking combat timing dependencies
Normalize event schedules to UTC storage
Define an event_data struct that stores trigger timestamps as time_t (seconds since epoch) in UTC. Never store local time. Create a conversion function that applies player-specific timezone offsets (stored in pcdata->timezone_offset) only when displaying event times to the character. This prevents daylight saving transition bugs when your playerbase spans multiple hemispheres.
struct event_data {
time_t trigger_time;
int event_id;
int state;
int max_participants;
};
void display_event_time(struct char_data *ch, struct event_data *ev) {
time_t local_time = ev->trigger_time + ch->pcdata->timezone_offset;
strftime(buf, sizeof(buf), "%H:%M %Z", localtime(&local_time));
send_to_char(ch, buf);
}⚠ Common Pitfalls
- •Storing local time in the database causing DST shift bugs
- •Forgetting to handle timezone offsets for international players
Implement the event state machine
Define explicit constants: EVENT_IDLE, EVENT_ANNOUNCE (15 min warning), EVENT_ACTIVE, EVENT_COOLDOWN. Use a switch statement in your event_update() function that checks current time against trigger_time and transitions states only when thresholds are crossed. Require explicit state flags rather than implicit time comparisons to prevent race conditions during server lag spikes.
⚠ Common Pitfalls
- •Using implicit time range checks instead of explicit state flags
- •Missing state transitions during unexpected server lag or crashes
Script automated reward distribution with exploit checks
Create distribute_event_reward() that validates EVENT_PARTICIPANT flags before issuing items. Check IS_NPC to prevent mob farming, verify ch->pcdata->event_rewards < MAX_REWARDS to stop alts, and ensure ch->desc is not NULL to skip link-dead characters. Log all distributions to a separate file (event_audit.log) with timestamps and IP addresses for post-event analysis.
void distribute_event_reward(struct char_data *ch, int event_id) {
if (IS_NPC(ch) || !IS_SET(ch->act, PLR_EVENT_PARTICIPANT))
return;
if (ch->pcdata->event_rewards >= MAX_EVENT_REWARDS) {
log_event_cheat(ch, "reward_cap_exceeded", event_id);
return;
}
if (!ch->desc) return; /* Link-dead check */
obj_to_char(create_object(get_obj_index(EVENT_PRIZE_VNUM), 0), ch);
ch->pcdata->event_rewards++;
log_event_audit(ch, event_id, "reward_issued");
}⚠ Common Pitfalls
- •Distributing rewards to link-dead characters allowing reconnect exploits
- •Failing to cap per-player rewards causing economic inflation
Configure Discord webhook bridge for cross-platform announcements
Implement a non-blocking notification by writing event state changes to a spool file (events/spool.txt) that a separate Python script monitors and POSTs to Discord. Do not make HTTP calls directly from the main game thread. Include event title, current UTC time, and online player count in the JSON payload. Rate limit to one message per 5 seconds to avoid Discord API bans.
#!/usr/bin/env python3
import requests, json, time, os
WEBHOOK_URL = 'https://discord.com/api/webhooks/YOUR/URL'
SPOOL_FILE = '../events/spool.txt'
while True:
if os.path.exists(SPOOL_FILE):
with open(SPOOL_FILE, 'r') as f:
payload = json.load(f)
requests.post(WEBHOOK_URL, json=payload)
os.remove(SPOOL_FILE)
time.sleep(5)⚠ Common Pitfalls
- •Blocking the main game thread with synchronous HTTP calls
- •Leaking webhook URLs in public code repositories or crash logs
Enable spectator streaming for non-participants
Implement a 'watch' command that subscribes observers to a ring buffer of room events. Copy fight messages and emotes to spectators without processing combat calculations for them. Limit the buffer to 1000 messages and implement a spectator flag that excludes them from area-effect spells or room-wide damage to prevent observation exploits.
⚠ Common Pitfalls
- •Broadcasting sensitive information (player locations, hidden items) to spectators
- •Memory leaks from unbounded buffers during extended events
Stress test with simulated time compression
Create an IMP-only command 'timejump' that advances the game clock by 24-hour increments via settimeofday() or by modifying your internal time tracking. Monitor that event triggers fire exactly once during jumps, that state transitions don't skip phases, and that Discord hooks don't fire duplicate messages. Verify timezone math by jumping across DST transition dates.
⚠ Common Pitfalls
- •Double-firing events during time skips
- •Timezone math errors causing events to fire at wrong hours for specific regions
Deploy with manual circuit breakers and participation logging
Add a global bool event_kill_switch accessible to IMP level that immediately transitions all active events to EVENT_COOLDOWN state when toggled. Log every state transition to syslog with player counts, timestamp, and current load average. Monitor the event_audit.log for participation drop-off rates; if attendance drops below 20% of average, trigger the kill switch to prevent community fatigue.
⚠ Common Pitfalls
- •Missing kill switches causing endless event loops during logic errors
- •Inadequate logging making debugging timezone issues impossible
What you built
Event systems in MUDs fail silently when timezone math drifts or when reward exploits inflate economies. After deployment, monitor event_audit.log for duplicate reward hashes and verify that your Discord bridge process remains running outside the game loop. Schedule quarterly reviews of player timezone distribution to adjust peak event hours, and keep the circuit breaker accessible during live events to prevent catastrophic loops.