Aller au contenu

Photo

Time advancement


  • Veuillez vous connecter pour répondre
21 réponses à ce sujet

#1
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
For some reason on our module, time does not advance. It is stuck at month 6, day 1, hour 13. Does anyone know what factors could cause this?

Also when I load the mod locally time does move forward, but for some reason on the server it doesn't.

#2
Squatting Monk

Squatting Monk
  • Members
  • 445 messages
This can happen if your server load gets high. The server diverts processing time from less-important tasks (like updating the clock). If you have NWNX, the nwnx_fixes plugin takes care of it. If you don't, you can add the following to your module's OnHeartbeat script:

SetTime(GetTimeHour(), GetTimeMinute(), GetTimeSecond(), GetTimeMillisecond());


#3
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
Thanks Monk, I may give that a try. Does that plugin still require NWNX_Core_2.7_Beta_4 (nwnx-module.dll)?

#4
Squatting Monk

Squatting Monk
  • Members
  • 445 messages
IIRC, yes. I use the 2.8 core, though, so I can't say from experience.

#5
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
Hmm well I am trying nwnx_fixes right now, and no effect. Is there a specific ini setting I need to enable?

And on a side note is there a core 2.8 for Windows? I can't seem to find it.

#6
virusman

virusman
  • Members
  • 282 messages
As I said in another thread, unfortunately, the Windows version of NWNX Fixes is outdated and doesn't contain the time advancement fix.

#7
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
Thanks for the info virusman. If the fix does get ported to windows, I would happily use it.

#8
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages

Squatting Monk wrote...

This can happen if your server load gets high. The server diverts processing time from less-important tasks (like updating the clock). If you have NWNX, the nwnx_fixes plugin takes care of it. If you don't, you can add the following to your module's OnHeartbeat script:

SetTime(GetTimeHour(), GetTimeMinute(), GetTimeSecond(), GetTimeMillisecond());


Everything there is a NWN function,  It will work without nwnx.

#9
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
We don't use a heart beat script, but if I made one as simple as this:
void main()
{
SetTime(GetTimeHour(), GetTimeMinute(), GetTimeSecond(), GetTimeMillisecond());
}

Would that work?

#10
Squatting Monk

Squatting Monk
  • Members
  • 445 messages
Ayup.

#11
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
I was told heartbeat scripts on the mod can cause lag, but I will give it a shot and see what happens. Thanks everyone.

#12
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
Well in case anyone is interested, we are using this now as our module event heartbeat script, and so far have not experienced any lag because of it.

#13
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
I stand corrected. Although it's true we have not gotten any lag due to this, but this HB scripting solution seems to have resulted in:

spawns not rendering, party members not rendering, maps not getting explored, rest timer acting wonky, and restart times being extended way beyond the scheduled interval

I do not recommend this solution to anyone who has near the big or complex of a module as we have... It might work ok for a less complex mod, but I can not vouch for that.

#14
Squatting Monk

Squatting Monk
  • Members
  • 445 messages
This is how large, complex modules handled the issue before NWNX fixed it. I doubt these results are simply from updating the clock OnHeartbeat.

#15
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
My apologies, you are correct, this doesn't seem to have caused the problem after all. We recently just changed to a new server, and apparently are having technical difficulties.

#16
Shadooow

Shadooow
  • Members
  • 4 470 messages

Lazarus Magni wrote...

I was told heartbeat scripts on the mod can cause lag, but I will give it a shot and see what happens. Thanks everyone.

This is common superstition. If you knew how many scripts, and how many instructions takes a single hostile creature, you would not seen a threat in module heartbeat.

Of course a script running each six seconds takes some resources. But if you are going to replace the heartbeat with a pseudoheartbeat that will also run each six seconds, you might end up worse, especially if the pseudo heartbeat transfere parameters into another recurse.

A module heartbeat is no way inefficient. If there are no players in module, you can use timestop effect to stop whole module which stops not only module's heatbeat but everything else.

Alternatively, a placeables also run hearbeats and placeables might be created and destroyed. Thus a placeable can be used to run module-wise heartbeat and builder will have controll to stop and run this heartbeat at any time.

Still its important to determine where a pseudo-HB makes a job of the heartbeat better and where not. Mostly, the pseudo-HB is better choice, but for time counting and custom events running a module's heartbeat is a best choice.

#17
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
To me this is a case for a pseudo-HB. The game clock does not show minutes on it anyway. so just run a pseudo-HB every game hour.

void UpdateClock(float fDelay)
{
SetTime(GetTimeHour(), GetTimeMinute(), GetTimeSecond(), GetTimeMillisecond());
DelayCommand( fDelay, UpdateClock(fDelay);
}

void main ()
{
...

UpdateClock( HoursToSeconds(1) );
...
}


}

#18
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
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. :P

#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

#19
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages

FunkySwerve wrote...

 see my delagging tutorial in the lexicon if you're curious about other optimizations


Got a link for this? I would certainly be interested in checking this out. But again, neither this heartbeat, nor our mod in general is what is causing our current problem.

#20
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
I understand that the heartbeat isn't the issue - I'm just reinforcing the point, as well as advocating for the use of a heartbeat rather than a pseudo here. You can find the 1.69 lexicon on the vault:
http://nwvault.ign.c....Detail&id=1340

You can find the tutorial under the Contents tab, in Lyceum/Tutorial/Toolset/Lag Busting - How to Make Your Server Run More Smoothly

Funky

#21
Rolo Kipp

Rolo Kipp
  • Members
  • 2 791 messages
<fading in...>

Lag Busting

<...and out on the hb>

#22
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
Thanks guys.