Nah, module heartbeat for this one, so long as your mod isn't so bloated that it's barely firing. Even if it is, the pseudo will only make it worse. Rough rule of thumb is that pseudos use about 5 times the cpu as a functionally identical heartbeat. If it's for something you want running more than 20% of the time, use a normal heartbeat.
As for heartbeats, Shad is correct. Sure, they add load, but compared to everything else going on, it's negligible. Here's ours, by way of comparison. Our mod still runs as smoothly as it did without - smoother, thanks to other optimizations, actually, but that's missing the point.
'>
#include "hg_inc"
#include "fky_chat_inc"
#include "hg_client_inc"
#include "ac_effect_inc"
#include "ac_itemprop_inc"
void SendResetBroadcast (string sMessage, int bTell, object oMessenger) {
sMessage = "<cþ þ>" + sMessage + "</c>";
object oPC = GetFirstPC();
while (GetIsObjectValid(oPC)) {
SendChatLogMessage(oPC, sMessage, oMessenger, (bTell ? 4 : 5));
oPC = GetNextPC();
}
}
void main() {
object oMod = GetModule();
object oMes = GetMessenger();
int nUptime = GetLocalInt(oMod, "uptime");
int nMemory = GetProcessMemoryUsage();
int nMessages = 0, nPlayers = 0;
string sServer = GetLocalString(oMod, "ServerNumber");
string sBootTime = IntToString(GetLocalInt(oMod, "boottime"));
{
object oPC;
for (oPC = GetFirstPC(); GetIsObjectValid(oPC); oPC = GetNextPC()) {
nPlayers++;
RecalculateMovementRate(oPC);
RecalculateDexModifier(oPC);
int nAlarm = GetLocalInt(oPC, "AlarmUptime");
if (nAlarm > 0 && nAlarm <= nUptime) {
DeleteLocalInt(oPC, "AlarmUptime");
SendChatLogMessage(oPC, C_PINK + "[Alarm] " + GetLocalString(oPC, "AlarmMessage") + C_END, oMes, 4);
}
}
SetLocalInt(oMod, "ServerPlayers", nPlayers);
}
SQLExecDirect("SELECT UNIX_TIMESTAMP() - " + sBootTime + ", UTC_TIMESTAMP(), UNIX_TIMESTAMP(), " +
"COUNT(*) FROM user_messages WHERE um_recipient = '*" + sServer + "'");
if (SQLFetch() == SQL_SUCCESS) {
nUptime = StringToInt(SQLGetData(1));
nMessages = StringToInt(SQLGetData(4));
SetLocalInt(oMod, "uptime", nUptime);
SetLocalInt(oMod, "realtime", StringToInt(SQLGetData(3)));
SetLocalString(oMod, "utctime", SQLGetData(2));
SQLExecDirect("UPDATE server_list SET srv_utime = " + IntToString(nUptime) + ", srv_memory = " +
IntToString(nMemory) + ", srv_players = " + IntToString(nPlayers) + " WHERE srv_id = '" + sServer + "'");
SQLExecDirect("SELECT COUNT(*) FROM user_list WHERE u_active > 0");
if (SQLFetch() == SQL_SUCCESS)
SetLocalInt(oMod, "GlobalPlayers", StringToInt(SQLGetData(1)));
SQLExecDirect("SELECT COUNT(*) FROM user_list WHERE u_active > 0 AND u_server_name REGEXP '" + GetStringLeft(sServer, 1) + ".[1-8]'");
if (SQLFetch() == SQL_SUCCESS) {
int nHubPlayers = StringToInt(SQLGetData(1));
SetLocalInt(oMod, "HubPlayers", nHubPlayers);
if (GetStringLeft(sServer, 1) != "1" && GetStringRight(sServer, 1) == "1")
SetNumberOfPlayers(nHubPlayers);
}
}
/* check for server messages if available */
if (nMessages > 0) {
int nGuild;
string sMessage;
string sSQL = "SELECT um_id, um_guild, um_text FROM user_messages WHERE um_recipient = '*" + sServer + "'";
SQLExecDirect(sSQL);
sSQL = "";
while (SQLFetch() != SQL_ERROR) {
if (sSQL == "")
sSQL = "um_id = " + SQLGetData(1);
else
sSQL += " OR um_id = " + SQLGetData(1);
nGuild = StringToInt(SQLGetData(2));
sMessage = SQLDecodeSpecialChars(SQLGetData(3));
if (nGuild == -2) {
object oPC = GetFirstPC();
while (GetIsObjectValid(oPC)) {
if (!(GetPCFilter(oPC, PCFILTER_CHANNELS) & 1))
SendChatLogMessage(oPC, sMessage, oMes);
oPC = GetNextPC();
}
} else if (nGuild == -3) {
object oPC = GetFirstPC();
while (GetIsObjectValid(oPC)) {
if (!(GetPCFilter(oPC, PCFILTER_CHANNELS) & 2))
SendChatLogMessage(oPC, sMessage, oMes);
oPC = GetNextPC();
}
} else if (nGuild == -4) {
object oPC = GetFirstPC();
while (GetIsObjectValid(oPC)) {
if (!(GetPCFilter(oPC, PCFILTER_CHANNELS) & 4))
SendChatLogMessage(oPC, sMessage, oMes);
oPC = GetNextPC();
}
} else if (nGuild < 0) {
SendMessageToDMDMs(sMessage);
SendMessageToPCDMs(sMessage);
SendMessageToDMAdmins(sMessage);
SendMessageToPCAdmins(sMessage);
} else if (nGuild > 0) {
object oPC = GetFirstPC();
while (GetIsObjectValid(oPC)) {
if (GetLocalInt(oPC, "Guild") == nGuild && !(GetPCFilter(oPC, PCFILTER_GUILD) & 1))
SendChatLogMessage(oPC, sMessage, oMes);
oPC = GetNextPC();
}
} else
AssignCommand(oMod, SpeakString(sMessage, TALKVOLUME_SHOUT));
}
if (sSQL != "")
SQLExecDirect("DELETE FROM user_messages WHERE " + sSQL);
}
/* scan players for inter-server messages every minute; also check for auto-reset */
if (nUptime >= GetLocalInt(oMod, "LastMessageCheck") + 60) {
SetLocalInt(oMod, "LastMessageCheck", nUptime);
if (nMemory > GetLocalInt(oMod, "resetmemory") && !GetLocalInt(oMod, "resetforce")) {
SetLocalInt(oMod, "resetforce", 1);
if (GetLocalInt(oMod, "resetuptime") > nUptime)
SetLocalInt(oMod, "resetuptime", nUptime - 1);
}
/* update PCs in the user list */
UpdateAllClients(sServer, sBootTime, oMes);
/* check for automatic reset */
int nResetUptime = GetLocalInt(oMod, "resetuptime");
if (nResetUptime > 0 && nUptime > nResetUptime) {
if (!GetIsObjectValid(GetFirstPC()))
SetLocalString(oMod, "NWNX!RESETPLUGIN!SHUTDOWN", "1");
if (nUptime > nResetUptime + 900) {
SendResetBroadcast("SERVER RESET IN 10 SECONDS - SERVER REBOOT IS NOW COMMITTED - CANCELLATION " +
"IS NO LONGER POSSIBLE - PLEASE STAY OUT OF BANK CHESTS - HAVE A NICE DAY!", 1, oMes);
DelayCommand(10.0, SetLocalString(oMod, "NWNX!RESETPLUGIN!SHUTDOWN", "1"));
} else {
int bTell = 0;
int nSeconds = (nResetUptime + 900) - nUptime;
string sMinutes = IntToString((nSeconds / 60) + 1);
if (sMinutes == "1" || sMinutes == "5" || sMinutes == "10" || sMinutes == "15")
bTell = 1;
if (GetLocalInt(oMod, "resetforce"))
SendResetBroadcast("AUTOMATIC SERVER RESET IN " + sMinutes + " MINUTE" +
(sMinutes != "1" ? "S" : "") + " - THIS CANNOT BE ABORTED DUE TO A LOW MEMORY CONDITION", bTell, oMes);
else
SendResetBroadcast("AUTOMATIC SERVER RESET IN " + sMinutes + " MINUTE" +
(sMinutes != "1" ? "S" : "") + " - USE !delayreset TO DELAY THE RESET", bTell, oMes);
}
}
}
/* scan players for epic buffs every minute */
if (nUptime >= GetLocalInt(oMod, "LastImmunityBuffCheck") + 60) {
int i;
object oCreator, oArea, oItem, oPC = GetFirstPC();
while (GetIsObjectValid(oPC)) {
oArea = GetArea(oPC);
if (GetIsDead(oPC) ||
!GetIsObjectValid(oArea) ||
GetHasEffectOfType(EFFECT_TYPE_CONFUSED, oPC)) {
oPC = GetNextPC();
continue;
}
if (GetHasSpellEffect(HGSPELL_EPIC_AEGIS, oPC)) {
oCreator = GetLocalObject(oPC, "EpicCreator_Aegis");
if (!GetIsObjectValid(oCreator) || GetArea(oCreator) != oArea || !GetFactionEqual(oCreator, oPC)) {
if (GetLocalInt(oPC, "EpicCollapse_Aegis") > 1) {
RemoveEffectsFromSpell(HGSPELL_EPIC_AEGIS, oPC);
FloatingTextStringOnCreature(
"* The Aegis spell on you collapses without the support of its caster! *",
oPC, FALSE);
} else
AddLocalInt(oPC, "EpicCollapse_Aegis", 1);
} else
DeleteLocalInt(oPC, "EpicCollapse_Aegis");
}
if (GetHasSpellEffect(HGSPELL_EPIC_CHANT_OF_WARDING, oPC)) {
oCreator = GetLocalObject(oPC, "EpicCreator_Chant");
if (!GetIsObjectValid(oCreator) || GetArea(oCreator) != oArea || !GetFactionEqual(oCreator, oPC)) {
if (GetLocalInt(oPC, "EpicCollapse_Chant") > 1) {
RemoveEffectsFromSpell(HGSPELL_EPIC_CHANT_OF_WARDING, oPC);
FloatingTextStringOnCreature(
"* The Chant of Warding spell on you collapses without the support of its caster! *",
oPC, FALSE);
} else
AddLocalInt(oPC, "EpicCollapse_Chant", 1);
} else
DeleteLocalInt(oPC, "EpicCollapse_Chant");
}
if (GetHasSpellEffect(HGSPELL_EPIC_ELEMENTAL_SHUNT, oPC)) {
oCreator = GetLocalObject(oPC, "EpicCreator_Shunt");
if (!GetIsObjectValid(oCreator) || GetArea(oCreator) != oArea || !GetFactionEqual(oCreator, oPC)) {
if (GetLocalInt(oPC, "EpicCollapse_Shunt") > 1) {
RemoveEffectsFromSpell(HGSPELL_EPIC_ELEMENTAL_SHUNT, oPC);
FloatingTextStringOnCreature(
"* The Elemental Shunt spell on you collapses without the support of its caster! *",
oPC, FALSE);
} else
AddLocalInt(oPC, "EpicCollapse_Shunt", 1);
} else
DeleteLocalInt(oPC, "EpicCollapse_Shunt");
}
if (GetHasSpellEffect(HGSPELL_EPIC_SHROUD_OF_NATURE, oPC)) {
oCreator = GetLocalObject(oPC, "EpicCreator_Shroud");
if (!GetIsObjectValid(oCreator) || GetArea(oCreator) != oArea || !GetFactionEqual(oCreator, oPC)) {
if (GetLocalInt(oPC, "EpicCollapse_Shroud") > 1) {
RemoveEffectsFromSpell(HGSPELL_EPIC_SHROUD_OF_NATURE, oPC);
FloatingTextStringOnCreature(
"* The Shroud of Nature spell on you collapses without the support of its caster! *",
oPC, FALSE);
} else
AddLocalInt(oPC, "EpicCollapse_Shroud", 1);
} else
DeleteLocalInt(oPC, "EpicCollapse_Shroud");
}
oPC = GetNextPC();
}
SetLocalInt(oMod, "LastImmunityBuffCheck", (nUptime - 5) + Random(11));
}
if (nUptime >= GetLocalInt(oMod, "LastEquipmentBuffCheck") + 60) {
int i;
object oCreator, oArea, oItem, oPC = GetFirstPC();
while (GetIsObjectValid(oPC)) {
oArea = GetArea(oPC);
if (GetIsDead(oPC) ||
!GetIsObjectValid(oArea) ||
GetHasEffectOfType(EFFECT_TYPE_POLYMORPH, oPC) ||
GetHasEffectOfType(EFFECT_TYPE_CONFUSED, oPC)) {
oPC = GetNextPC();
continue;
}
if ((GetLevelByclass(class_TYPE_BLACKGUARD, oPC) + GetLevelByclass(class_TYPE_PALADIN, oPC) + GetLevelByclass(class_TYPE_DIVINE_CHAMPION, oPC)) >= 40) {
if (GetKnowsFeat(FEAT_SMITE_EVIL, oPC))
IncrementRemainingFeatUses(oPC, FEAT_SMITE_EVIL);
if (GetKnowsFeat(FEAT_SMITE_GOOD, oPC))
IncrementRemainingFeatUses(oPC, FEAT_SMITE_GOOD);
// Greater Smite failing to recharge fix.
int nIndex = GetLocalInt(oPC, "GreaterSmiteAbility") - 1;
struct SpecialAbilitySlot sa = GetSpecialAbility(oPC, nIndex);
if ((sa.id == SPELLABILITY_GAZE_DESTROY_EVIL ||
sa.id == SPELLABILITY_GAZE_DESTROY_GOOD ||
sa.id == SPELLABILITY_PULSE_HOLY) && sa.ready == 0 && !GetLocalInt(oPC, "NeedsRest")) {
sa.ready = 1;
SetSpecialAbility(oPC, nIndex, sa);
}
}
if (GetHasSpellEffect(HGSPELL_EPIC_GIRDING_OF_THE_FAITHFUL, oPC)) {
oCreator = GetLocalObject(oPC, "EpicCreator_Gird");
if (!GetIsObjectValid(oCreator) || GetArea(oCreator) != oArea || !GetFactionEqual(oCreator, oPC)) {
if (GetLocalInt(oPC, "EpicCollapse_Gird") > 1) {
RemoveEffectsFromSpell(HGSPELL_EPIC_GIRDING_OF_THE_FAITHFUL, oPC);
DelayCommand(0.1, VoidRemoveAllItemPropertiesFromSpell(HGSPELL_EPIC_GIRDING_OF_THE_FAITHFUL, oPC));
FloatingTextStringOnCreature(
"* The Girding of the Faithful spell on you collapses without the support of its caster! *",
oPC, FALSE);
} else
AddLocalInt(oPC, "EpicCollapse_Gird", 1);
} else
DeleteLocalInt(oPC, "EpicCollapse_Gird");
}
if (GetHasSpellEffect(HGSPELL_EPIC_INSTRUMENTS_OF_FAITH, oPC)) {
oCreator = GetLocalObject(oPC, "EpicCreator_Instr");
if (!GetIsObjectValid(oCreator) || GetArea(oCreator) != oArea || !GetFactionEqual(oCreator, oPC)) {
if (GetLocalInt(oPC, "EpicCollapse_Instr") > 1) {
RemoveEffectsFromSpell(HGSPELL_EPIC_INSTRUMENTS_OF_FAITH, oPC);
DelayCommand(0.1, VoidRemoveAllItemPropertiesFromSpell(HGSPELL_EPIC_INSTRUMENTS_OF_FAITH, oPC));
FloatingTextStringOnCreature(
"* The Instruments of Faith spell on you collapses without the support of its caster! *",
oPC, FALSE);
} else
AddLocalInt(oPC, "EpicCollapse_Instr", 1);
} else
DeleteLocalInt(oPC, "EpicCollapse_Instr");
}
oPC = GetNextPC();
}
SetLocalInt(oMod, "LastEquipmentBuffCheck", (nUptime - 5) + Random(11));
}
}
By contrast, the old heartbeat we had, which looped all equipment on all players, as well as much more, often more than once, was so poorly conceived and executed that you could actually feel a pause every time it ran (in part due to other poor practices at the time - see my delagging tutorial in the lexicon if you're curious about other optimizations). Moral of the story: be smart, tread lightly, and you have NOTHING to worry about when using mod heartbeats.
Funky