Author Topic: Persistent World Area of Effect Spells  (Read 585 times)

Legacy_Howlando_EFU

  • Newbie
  • *
  • Posts: 6
  • Karma: +0/-0
Persistent World Area of Effect Spells
« on: December 13, 2010, 07:05:54 pm »


               Hello -

On my persistent world, area of effect spells (such as Grease, Cloud of Bewilderment, etc.) simply only to fire off the first time a PC enters into the area of effect. Subsequent rounds of remaining in the cloud do not seem to work consistently.

Is this a common issue on other worlds, and/or is anyone aware of a good fix?

Thanks, 
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Persistent World Area of Effect Spells
« Reply #1 on: December 13, 2010, 07:58:06 pm »


               I have seen this happenning on the server I play atm too, the ILKAY's spell fix package at least fixed the most used like battletide, silence and magic circle against alignment, but not the rest

also it means there is something pretty wrong on your module, so the best would be to find the real cause and not workarounds for spells. I do not however can tell you cause without knowing anything of your server and module.

EDIT: more technically, the aoes have lowest priority as far as memory use, so if the module dont have any of it, they will does what you described
               
               

               


                     Modifié par ShaDoOoW, 13 décembre 2010 - 08:02 .
                     
                  


            

Legacy_TSMDude

  • Hero Member
  • *****
  • Posts: 1515
  • Karma: +0/-0
Persistent World Area of Effect Spells
« Reply #2 on: December 13, 2010, 08:27:22 pm »


               

Howlando_EFU wrote...
Is this a common issue on other worlds, and/or is anyone aware of a good fix?
 


This is actually a common thing on several pw worlds. There are numerous fixes on the vault but it has been so long I am unsure of which one we used. I will try and see which one we used but if I remember correctly it was not hard to fix once we read how....


I think the guys name was Don Wolf or something similiar though.
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Persistent World Area of Effect Spells
« Reply #3 on: December 13, 2010, 11:34:16 pm »


               The issue, as Shad says, is engine priority. PAoEs and spawned-in placeable heartbeats have incredibly low priority, and will fail to fire under even light server load. This does NOT necessarily mean that there's anything wrong with your module, however, as TSM notes - we have it on HG despite the best streamlining, even with only a couple players on. The solution is to replace the heartbeat with a psuedoheartbeat. You can find a bevvy of threads discussing pseudos in relation to this problem on the old forums.

Here's a sample I posted a few years back at someone's request:

Click Me


We've evolved the process somewhat since then on HG. All aoes now run through a central include. Here's the scripts for that acid fog spell now (code is mostly acaos'):

First, the spell itself:

#include "ac_aoeper_inc"


struct SpellInfo GetPersistentAoESpellInfo ()        { return GetSpellInfo(); }

int GetPersistentAoELimit (struct SpellInfo si)      {
    if (GetIsQuasiSpell(si, QUASIclass_HERALD_OF_STORMS))
        return -2;
    return -1;
}
int GetPersistentAoEEffect (struct SpellInfo si)     { return AOE_PER_FOGACID; }
string GetPersistentAoETag (struct SpellInfo si)     { return "VFX_PER_FOGACID"; }
string GetPersistentAoEName (struct SpellInfo si)    { return "Acid Fog"; }
int GetPersistentAoETargetMask (struct SpellInfo si) { return OBJECT_TYPE_CREATURE; }
int GetPersistentAoETargetType (struct SpellInfo si) { return TARGET_TYPE_DEFAULT; }


float GetPersistentAoEDuration (struct SpellInfo si) {
    return MetaDuration(si, si.clevel / 2, DURATION_IN_ROUNDS);
}

effect GetPersistentAoEImpactEffect (struct SpellInfo si) {
    effect eEff;
    return eEff;
}

effect GetPersistentAoEDurationEffect (struct SpellInfo si) {
    effect eDur;

    if (GetIsQuasiSpell(si, QUASIclass_HERALD_OF_STORMS))
        eDur = EffectDamageIncrease(1);
    return eDur;
}

effect GetPersistentAoEVisualEffect (struct SpellInfo si) {
    ApplyVisualAtLocation(VFX_FNF_GAS_EXPLOSION_ACID, si.loc);

    return EffectVisualEffect(VFX_FNF_GAS_EXPLOSION_ACID);
}


void ApplyAoEEffect (struct SpellInfo si, object oAoE, object oTarget, effect eEff, effect eDur, effect eVis) {
    if (!GetSpellResisted(si, oTarget, 0.0, 2)) {
        int nDamage, nDice = si.clevel, nEffType = GetEffectType(eDur);
        int nDamType = DAMAGE_TYPE_ACID, nSaveType = SAVING_THROW_TYPE_ACID;

        if (nDice < 1)
            nDice = 1;

        nDamage = MetaPower(si, nDice, 6);
        nDamage = GetReflexAdjustedDamage(nDamage, oTarget, si.dc, nSaveType, si.caster);

        if (nDamage > 0) {
            if (nEffType == EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE) {
                if (!GetHasSpecificEffect(eDur, oTarget))
                    ApplyEffectToObject(DURATION_TYPE_PERMANENT, eDur, oTarget);

                eEff = EffectDamage(nDamage / 2, nDamType);
                DelayCommand(0.1, ApplyEffectToObject(DURATION_TYPE_INSTANT, eEff, oTarget));

                eEff = EffectDamage(nDamage / 2, DAMAGE_TYPE_MAGICAL);
                DelayCommand(0.1, ApplyEffectToObject(DURATION_TYPE_INSTANT, eEff, oTarget));
            } else if (nEffType == EFFECT_TYPE_DAMAGE_INCREASE) {
                if (GetTrueDamageImmunity(oTarget, nDamType) < 100) {
                    int nDecrease = 25 - GetTotalDamageImmunityDecrease(oTarget, nDamType);

                    if (nDecrease > 5)
                        nDecrease = 5;

                    if (nDecrease > 0) {
                        eEff = EffectDamageImmunityDecrease(nDamType, nDecrease);
                        ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEff, oTarget);
                    }
                }

                eEff = EffectDamage(nDamage, nDamType);
                ApplyEffectToObject(DURATION_TYPE_INSTANT, eEff, oTarget);
            } else {
                eEff = EffectDamage(nDamage, nDamType);
                ApplyEffectToObject(DURATION_TYPE_INSTANT, eEff, oTarget);
            }

            ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);
        }
    }
}




Then, the include
ac_aoeper_inc (which, unlike most includes, actually has the void main in it - don't let that throw you)
:

#include "hg_inc"
#include "ac_spell_inc"

int GetIsHeraldAoE(object oAoE) {
    string sTag = GetTag(oAoE);
    if (sTag == "VFX_PER_FOGFREEZE"     ||
        sTag == "VFX_AOE_CONSECRATE_20" ||
        sTag == "VFX_PER_FOGACID"       ||
        sTag == "VFX_PER_FOGKILL"       ||
        sTag == "VFX_PER_FOGMIND"       ||
        sTag == "VFX_PER_FOGSTINK")
        return TRUE;

    return FALSE;
}

struct SpellInfo GetPersistentAoESpellInfo ();

int GetPersistentAoELimit (struct SpellInfo si);
int GetPersistentAoEEffect (struct SpellInfo si);
string GetPersistentAoETag (struct SpellInfo si);
string GetPersistentAoEName (struct SpellInfo si);
int GetPersistentAoETargetMask (struct SpellInfo si);
int GetPersistentAoETargetType (struct SpellInfo si);

float GetPersistentAoEDuration (struct SpellInfo si);

effect GetPersistentAoEImpactEffect (struct SpellInfo si);
effect GetPersistentAoEDurationEffect (struct SpellInfo si);
effect GetPersistentAoEVisualEffect (struct SpellInfo si);

void ApplyAoEEffect (struct SpellInfo si, object oAoE, object oTarget, effect eEff, effect eDur, effect eVis);


float GetPersistentAoERemaining (object oAoE) {
    int nRemaining = GetLocalInt(oAoE, "AoEExpires") - GetLocalInt(GetModule(), "uptime");

    return IntToFloat(nRemaining < 1 ? 1 : nRemaining);
}


void DoAoEHeartbeat (struct SpellInfo si, object oAoE, effect eEff, effect eDur, effect eVis) {
    int bDestroy = FALSE, bAllPCs = FALSE;

    if (!GetIsObjectValid(oAoE))
        return;


    /* Storm of Vengeance doesn't destroy properly sometimes */
    if (GetPersistentAoEEffect(si) == AOE_PER_STORM) {
        if (si.id == SPELL_STORM_OF_VENGEANCE && GetLocalInt(GetArea(oAoE), "Area_Underwater"))
            bAllPCs = TRUE;

        if (GetLocalInt(GetModule(), "uptime") > GetLocalInt(oAoE, "AoEExpires"))
            bDestroy = TRUE;
    }

    if (bDestroy                     ||
        !GetIsObjectValid(si.caster) ||
        GetIsDead(si.caster)         ||
        GetArea(si.caster) != si.area) {

        SetPlotFlag(oAoE, FALSE);
        DestroyObject(oAoE);
        return;
    }

    DelayCommand(6.0, DoAoEHeartbeat(si, oAoE, eEff, eDur, eVis));
    AddLocalInt(oAoE, "AoERounds", 1);


    si.target = oAoE;
    int nMask = GetPersistentAoETargetMask(si);
    int nType = GetPersistentAoETargetType(si);

    for (si.target = GetFirstInPersistentObject(oAoE, nMask);
         GetIsObjectValid(si.target);
         si.target = GetNextInPersistentObject(oAoE, nMask)) {

        if (GetIsSpellTarget(si, si.target, nType) || (bAllPCs && GetIsPC(si.target))) {
            SignalEvent(si.target, EventSpellCastAt(si.caster, si.id));

            AssignCommand(si.caster, DelayCommand(GetRandomDelay(0.5, 4.5), ApplyAoEEffect(si, oAoE, si.target, eEff, eDur, eVis)));
        }
    }
}

void StartAoEHeartbeat (struct SpellInfo si, string sTag, effect eEff, effect eDur, effect eVis, int nUntil) {
    int nCount = 1;
    object oLoc, oAoE;

    do {
        oLoc = GetNearestObjectToLocation(OBJECT_TYPE_ALL, si.loc, nCount++);
    } while (GetTag(oLoc) == sTag);

    nCount = 1;
    oAoE = GetNearestObjectByTag(sTag, oLoc);

    while (GetIsObjectValid(oAoE)) {
        if (GetAreaOfEffectCreator(oAoE) == si.caster && !GetLocalInt(oAoE, "AoEHeartbeat")) {

            SetLocalInt(oAoE, "AoEHeartbeat", 1);
            SetLocalInt(oAoE, "AoEExpires", nUntil);
            SetLocalInt(oAoE, "AoESP", si.sp);

            DelayCommand(0.0, DoAoEHeartbeat(si, oAoE, eEff, eDur, eVis));
            return;
        }

        oAoE = GetNearestObjectByTag(sTag, oLoc, ++nCount);
    }
}

void main () {
    struct SpellInfo si = GetPersistentAoESpellInfo();
    if (si.id < 0)
        return;

    float fDur  = GetPersistentAoEDuration(si);
    string sTag = GetPersistentAoETag(si);
    effect eAoE = EffectAreaOfEffect(GetPersistentAoEEffect(si),
        (si.id == SPELL_GREASE ? "nw_s0_greasea" : "****"), "****", "****");


    int nLimit = GetPersistentAoELimit(si);

    if (nLimit > 0) {
        int nFound = 0, nCount = 1;
        object oOther, oCreator, oArea = GetArea(si.caster);

        while (GetIsObjectValid(oOther = GetNearestObjectByTag(sTag, si.caster, nCount++))) {
            oCreator = GetAreaOfEffectCreator(oOther);
            if (oCreator == oArea                                       ||
                GetLocalInt(oOther, "NoAoEDispel"))
                continue;

            if (GetIsPC(si.caster) && GetIsPC(oCreator)) {
                if (GetIsHeraldAoE(oOther)) {
                    if (!GetIsQuasiclass(QUASIclass_HERALD_OF_STORMS, oCreator))
                        nFound++;
                } else
                    nFound++;
            } else if (GetFactionEqual(si.caster, oCreator)) {
                nFound++;
            }

            if (nFound >= nLimit) {
                SetPlotFlag(oOther, FALSE);
                DestroyObject(oOther);

                if (nFound == nLimit)
                    FloatingTextStringOnCreature("To prevent lag, only " +
                        IntToString(nLimit) + " " + GetPersistentAoEName(si) +
                        " spell" + (nLimit == 1 ? "" : "s") + " may exist in an area at the same time.", si.caster);
            }
        }
    } else if (nLimit == -1) {
        object oAoE, oArea = GetArea(si.caster);

        for (oAoE = GetFirstObjectInArea(oArea); GetIsObjectValid(oAoE); oAoE = GetNextObjectInArea(oArea)) {
            if (GetObjectType(oAoE) != OBJECT_TYPE_AREA_OF_EFFECT ||
                GetAreaOfEffectCreator(oAoE) != si.caster)
                continue;

            if (FindSubString(" VFX_PER_FOGACID VFX_PER_FOGFIRE VFX_PER_FOGFREEZE VFX_PER_STORM VFX_PER_RAINFIRE VFX_PER_RAINFREEZE VFX_AOE_CONSECRATE_20 ",
                              " " + GetTag(oAoE) + " ") >= 0) {
                SetPlotFlag(oAoE, FALSE);
                DestroyObject(oAoE);

                FloatingTextStringOnCreature("Only one area of effect damage field may exist per caster in an area at the same time.", si.caster);
            }
        }
    } else if (nLimit == -2) {
        object oAoE, oArea = GetArea(si.caster);
        int nFound;
        for (oAoE = GetFirstObjectInArea(oArea); GetIsObjectValid(oAoE); oAoE = GetNextObjectInArea(oArea)) {
            if (GetObjectType(oAoE) != OBJECT_TYPE_AREA_OF_EFFECT ||
                !GetIsQuasiclass(QUASIclass_HERALD_OF_STORMS, GetAreaOfEffectCreator(oAoE)))
                continue;

            if (FindSubString(" VFX_PER_FOGACID VFX_PER_FOGSTINK VFX_PER_FOGKILL VFX_AOE_CONSECRATE_20 VFX_PER_FOGFREEZE VFX_PER_FOGMIND ",
                              " " + GetTag(oAoE) + " ") >= 0) {
                nFound++;
                if (nFound >= 2) {
                    SetPlotFlag(oAoE, FALSE);
                    DestroyObject(oAoE);

                    if (nFound == 2)
                        FloatingTextStringOnCreature("Only two herald of storms clouds may exist in an area at the same time.", si.caster);
                }
            }
        }
    }


    effect eEff = GetPersistentAoEImpactEffect(si);
    effect eDur = GetPersistentAoEDurationEffect(si);
    effect eVis = GetPersistentAoEVisualEffect(si);


    AssignCommand(si.area, DelayCommand(2.0, StartAoEHeartbeat(si, sTag, eEff, eDur, eVis,
        GetLocalInt(GetModule(), "uptime") + FloatToInt(fDur))));

    ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eAoE, si.loc, fDur);
}


The main reason for the change in systems was to easily create more without a lot of duplicative code.

Funky
               
               

               


                     Modifié par FunkySwerve, 13 décembre 2010 - 11:37 .
                     
                  


            

Legacy_TSMDude

  • Hero Member
  • *****
  • Posts: 1515
  • Karma: +0/-0
Persistent World Area of Effect Spells
« Reply #4 on: December 14, 2010, 04:09:57 am »


               Quick linked for further looking.



Thanks Funky for sharing that.
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Persistent World Area of Effect Spells
« Reply #5 on: December 14, 2010, 07:33:37 am »


               

FunkySwerve wrote...

This does NOT necessarily mean that there's anything wrong with your module, however, as TSM notes - we have it on HG despite the best streamlining, even with only a couple players on.

Weeel, maybe. I haven't seen this issue on many servers and many of them was even poorly scripted, whats the difference?

For what I know. On the server this issue is, its from the start even after restart, so it does not appear as "heavy load" problem. And are mostly linux hosted. Anyone have this issue on windows? NWNX or not?

From what I know about the servers where I seen this, I never found anything what could cause it.

But maybe we can find it out. I post there few servers specs one have this issue second one does not.

with this issue

Arkhalia, PW Action server
linux hosting (debian)
CEP1 and 2GB of custom haks
NWNX odbc using MySQL probably
restart twice per week
like 100 spawned NPCs when load whose do not despawn and wander in towns
0-5 players average
monsters and loot have despawn 5 minutes (this is done with delay command running on every monster)
muling allowed, players have PC storages, they very often relogs
no persistent chests
overloaded stores after some time
around 500 locations


without

Demona, Light RP server
windows hosting
1GB custom haks (was done pre-CEP)
no NWNX, uses bio DB with custom scripted workaround to improve efficiency (which does HUGE lag at module load btw)
restart every 3-5 hours, often crashes LOL
like 50 npcs wandering in towns
5-15 players average
monsters never despawn
transfer items between characters disallowed
persistent chest using conversation, able to store like 50 items, using resref only
merchants do not buy much but there is player store which heavy rely on DB
around 300 locations

Lands of Larinia, Action quest based server
windows hosting
no haks
NWNX with all recent plugins
restart once per 5 hours
like 300 wandering NPCS in towns
0-5 players average
monsters despawn (delay command on all of them)
mullling allowed so far, but noone does this '<img'>
no persistent chest yet
merchants buy everything but its restarted every 5 hours, merchants are never overloaded
around 500 locations


So far I see no other difference than hosting...

BTW do not bring the server on list if you do not know certainly, like I know that Bastions of War do not have this problem, but they might use pseudo heartbeats workaround.
               
               

               


                     Modifié par ShaDoOoW, 14 décembre 2010 - 07:35 .
                     
                  


            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Persistent World Area of Effect Spells
« Reply #6 on: December 14, 2010, 07:37:40 am »


               It's not linux-only - we had it when we were win-hosted as well. Though it's possible it's worse on linux, I doubt it. Your answer is likely staring you in the face, though - the RP server is going to have a lot less load than an action server, and the action server you show without the issue has far fewer players on it.



Funky
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Persistent World Area of Effect Spells
« Reply #7 on: December 14, 2010, 08:17:43 am »


               Players shouldn't be the cause, because even right after server restart on the first server when I log in as first player the issue is there. Freshly restarted with NWNX, explain this. And there arent so many players on HG as well because they are scattered between multiple servers.
               
               

               
            

Legacy_TSMDude

  • Hero Member
  • *****
  • Posts: 1515
  • Karma: +0/-0
Persistent World Area of Effect Spells
« Reply #8 on: December 14, 2010, 01:33:06 pm »


               I know FRC had this problem for awhile and though an RP server they do have a ton of folks.



City of Arabel had this for a bit as well...maybe it is somewhat linked to players though as the servers I have seen it on most are servers with a healthy player pop.
               
               

               
            

Legacy_Howlando_EFU

  • Newbie
  • *
  • Posts: 6
  • Karma: +0/-0
Persistent World Area of Effect Spells
« Reply #9 on: December 14, 2010, 02:47:04 pm »


               Thanks for the replies. I don't think there's anything with our server, although I'm not really a scripter myself, it's a quite well built module. We have a somewhat large playerbase, typically 25-50 players on at any one time.
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Persistent World Area of Effect Spells
« Reply #10 on: December 15, 2010, 08:58:01 am »


               

ShaDoOoW wrote...

Players shouldn't be the cause, because even right after server restart on the first server when I log in as first player the issue is there. Freshly restarted with NWNX, explain this. And there arent so many players on HG as well because they are scattered between multiple servers.

Players cause load. Load causes this. Even a single player creates load in all sorts of ways - remember that the server is streaming everything to him, from sending placeable info for every one he perceives, to the more commonly considered script load. Put another way, you don't need players to see this problem, just too much load - and even a little is too much.

Funky
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Persistent World Area of Effect Spells
« Reply #11 on: December 15, 2010, 12:01:14 pm »


               

FunkySwerve wrote...
Players cause load. Load causes this. Even a single player creates load in all sorts of ways - remember that the server is streaming everything to him, from sending placeable info for every one he perceives, to the more commonly considered script load. Put another way, you don't need players to see this problem, just too much load - and even a little is too much.

Funky

This doesnt seem to match the server I described, really.
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Persistent World Area of Effect Spells
« Reply #12 on: December 15, 2010, 05:30:21 pm »


               There's no way to tell how much load they have, otherwise. We had this problem when I inherited HG even when I was the only player on, in testing, before I began delagging and streamlining. Now it tends not to crop up until player 3 or so (you can test this by spawning in a placeable with a heartbeat, even if you're running a pseudo fix).

Frankly, the server that doesn't really fit with what I've observed of this issue is the 5-15 player server (with regular crashes and no NPC despawn). Could they be running a psuedo fix without you knowing it?

Funky
               
               

               


                     Modifié par FunkySwerve, 15 décembre 2010 - 05:32 .
                     
                  


            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Persistent World Area of Effect Spells
« Reply #13 on: December 15, 2010, 11:37:47 pm »


               

FunkySwerve wrote...

Frankly, the server that doesn't really fit with what I've observed of this issue is the 5-15 player server (with regular crashes and no NPC despawn). Could they be running a psuedo fix without you knowing it?

Funky

No they can't. I know this server very well, its very old it was created before expansions and even CEP. Thats why they dont have it until now yet. After years it became changed by various new wbs but their scripting skills was often not great, but anyway I played there and that issue was never there. But problem with crashes they become to have recently, I believe its the toll for heavy using of bioware db. Also they in past (yet 2 years back) have 50+ players and it was verry laggy, but aoes worked great all the time. I still checking their forum even though I dont playe there anymore because the last few WB groups totally screwed it out.':sick:'

Oh and btw max level is 30 because they never finished new areas since expansions came out.
               
               

               


                     Modifié par ShaDoOoW, 15 décembre 2010 - 11:40 .
                     
                  


            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Persistent World Area of Effect Spells
« Reply #14 on: December 16, 2010, 03:18:18 am »


               What server is this? I'd like to take a look. From what you describe, I just can't believe they're working (as opposed to simply getting tripped onenter, which always works).

Those crashes sound like the result of exploded bioware databases. See here for the extent of my knowledge on that:

Click Here

Funky
               
               

               


                     Modifié par FunkySwerve, 16 décembre 2010 - 03:19 .