runtime behaviour of scripts

Post » Sat Nov 17, 2012 4:55 am

I have red, papyrus is an onbject oriented scripting language.
So, assuming one has a script, that has an event which is triggered several times, what happens?

Is for each triggered event an instance of the script created? If yes, does each instance have its own set of variables even if they share the same name? Or do all instances of the script share the same variables? What if a variable is used to prohibt the event to run a funtion? Do i need to use GV's then instead of script-variables?
If you dont have several instances but one, how does the engine distinguish which event-input to use? Does it stack the inputs and then run them consecutively?

I have a few problems with Multi-Threading, thats why i ask. And yes, i know the wiki site :wink:. Still, i might have missed/not understood parts of it

Best regards,
Moorleiche2k
User avatar
Taylah Haines
 
Posts: 3439
Joined: Tue Feb 13, 2007 3:10 am

Post » Fri Nov 16, 2012 10:37 pm

Script in question:
Spoiler
Scriptname aaBloodmagicCastLeftScript extends Activemagiceffect;============================================================================================================================================;       Simulates Magicka-Depletion while casting and also determines amount of Spellcostss applied to health if bloodmagic-perk is taken;        and conditions met;============================================================================================================================================Spell Property aaBloodmagicMagickaReduction AutoGlobalVariable Property aaAbsoluteMagickaCostsLeft AutoGlobalVariable Property aaBMSpellChangeLeft AutoGlobalVariable Property aaDualCastMultiplier AutoGlobalVariable Property aaIsChannelingSpellLeft AutoGlobalVariable Property aaSpellswordVerifier AutoActor PlayerInt IsChannelingSpellFloat SpellcostsFloat DualCastMultiplierFloat SpellLength;============================================================================================================================================Bool DeLatchOfInputNeeded;       This variable blocks any consecutive calls of OnUpdate if the while loop is running. It additionally is intended to resets to "False";       just and only if Input.IsKeyPressed(256) is released (=delatched) at least a single time after casting (=OnSpellCast)Bool IsEmulatingDepletion;       Prohibits the reset of "DeLatchOfInputNeeded" and thus possible stacking Event-activations;============================================================================================================================================MagicEffect Property aaBloodmagicFortifyAlteration AutoMagicEffect Property aaBloodmagicFortifyConjuration AutoMagicEffect Property aaBloodmagicFortifyDestruction AutoMagicEffect Property aaBloodmagicFortifyIllusion AutoMagicEffect Property aaBloodmagicFortifyRestoration Auto;============================================================================================================================================Event OnInit();============================================================================================================================================           Player = Game.GetPlayer()        DeLatchOfInputNeeded = False        IsEmulatingDepletion = FalseEndEvent;============================================================================================================================================Event OnMagicEffectApply(ObjectReference akCaster, MagicEffect akEffect);============================================================================================================================================        If(akEffect == aaBloodmagicFortifyAlteration || akEffect == aaBloodmagicFortifyConjuration \                || akEffect == aaBloodmagicFortifyDestruction || akEffect == aaBloodmagicFortifyIllusion \                || akEffect == aaBloodmagicFortifyRestoration)                RegisterForUpdate(0.1)Debug.Notification("BM-Left Registered OnUpdate")               EndIfEndEvent;============================================================================================================================================Event OnSpellCast(Form akSpell);       DETAILS: This Event detects both, an successfull cast (=succesfull while-loop within OnUpdate) and also the release of;       left mouse button (This event is technically the event to detect an end of casting and thus OnUpdate = it unlocks OnUpdate);       If the spell was cast, OnUpdate can be registered again. A cast-interruption is NOT detected, as such unlocking variables must;       be done within OnUpdate after an interruption.;============================================================================================================================================        While (IsEmulatingDepletion == True)                Utility.Wait(0.1)        EndWhile        If ((akSpell as Spell) == Player.GetEquippedSpell(0) && IsEmulatingDepletion == False)                DeLatchOfInputNeeded = False;Debug.Notification("BM-Left OSC aktivated and delatching")        EndIf   EndEvent;============================================================================================================================================Event OnUpdate();============================================================================================================================================        If (Player.HasSpell(aaBloodmagicMagickaReduction) == False)                UnregisterForUpdate();Debug.Notification("BM-Left Unregistered OnUpdate")            ElseIf (Player.HasSpell(aaBloodmagicMagickaReduction) == True && DeLatchOfInputNeeded == False && Player.GetEquippedItemType(0) == 9 \                && Input.IsKeyPressed(256) == True);Debug.Notification("BM-Left emulation possible")                       Spell CastSpell = Player.GetEquippedSpell(0)                Int BMSpellChangeVariable = aaBMSpellChangeLeft.GetValueInt()                If (BMSpellChangeVariable == 1 || BMSpellChangeVariable == 2)                        Spellcosts = aaAbsoluteMagickaCostsLeft.GetValueInt() as Float                        IsChannelingSpell = aaIsChannelingSpellLeft.GetValueInt()                        DualCastMultiplier = aaDualCastMultiplier.GetValue()                        SpellLength = CastSpell.GetCastTime()                        If (BMSpellChangeVariable == 1)                                aaBMSpellChangeLeft.SetValueInt(3)                        ElseIf (BMSpellChangeVariable == 2)                                aaBMSpellChangeLeft.SetValueInt(0)                        EndIf                EndIf                If (IsChannelingSpell == 0)                        Float HealthAtCast = Player.GetAV("Health")                        Float MagickaAtCast = Player.GetAV("Magicka")                                           ;================================================================================================================================                        If (MagickaAtCast + HealthAtCast > Spellcosts)                        ;       Prevents death by Bloodmagic -> Interrupts Casting if it health-depletion would kill the player                        ;================================================================================================================================                                Float IncrementTimeStep = 0.1                                Float HealthUsed                                If (MagickaAtCast < SpellCosts)                                        HealthUsed = SpellCosts - MagickaAtCast                                Else                                        HealthUsed = 0                                EndIf                                Float IncrementSteps = Spelllength / IncrementTimeStep                                ;============================================================================================================================                                ;       Avoid hitting "0" Magicka since Game-Engine interrupts casting then (=leave 1 Magicka untouched)                                Float HealthPerIncrement = HealthUsed + 1 / IncrementSteps                                Float MagickaPerIncrement = MagickaAtCast - 1 / IncrementSteps                                ;============================================================================================================================                                Float IncrementCounter = 0                                While (IncrementCounter < IncrementSteps)                                        IsEmulatingDepletion = True                                        If (Input.IsKeyPressed(256) == True)                                                DeLatchOfInputNeeded = True                                                Player.DamageAV("Magicka", MagickaPerIncrement)                                                Player.DamageAV("Health", HealthPerIncrement)                                                IncrementCounter += 1                                                   Utility.Wait(IncrementTimeStep)                                        ;========================================================================================================================                                                       ElseIf (Input.IsKeyPressed(256) == False)                                        ;       Stopping condition for spellcasting. Since this script is delayed to casting-animation, its guaranteed that the                                        ;       spellcasting is canceled. Delatch happens due to "ElseIf"-condition, variables can be unlocked                                        ;       and OnUpdate be registered                                        ;========================================================================================================================                                                Player.RestoreAV("Magicka", MagickaPerIncrement * IncrementCounter)                                                Player.RestoreAV("Health", HealthPerIncrement * IncrementCounter)                                                DeLatchOfInputNeeded = False                                                RegisterForUpdate(0.1)                                                IncrementCounter = IncrementSteps                                        EndIf                                EndWhile                        Else                                Player.InterruptCast()                                Debug.Notification("You would not survive conjuring this spell")                        EndIf                        IsEmulatingDepletion = False                    EndIf        EndIf   EndEvent

What works:
Spoiler
Int iHotkey = [dxscancode here]Bool bIsHotkeyPressedFloat fPressedTimeFloat fUpdateInterval = 0.25Event OnInit()        RegisterForSingleUpdate(fUpdateInterval)EndEventEvent OnUpdate()        If bIsHotkeyPressed != IsKeyPressed(iHotkey)                bIsHotkeyPressed = !bIsHotkeyPressed                If bIsHotkeyPressed                        fPressedTime = Utility.GetCurrentRealTime()                        Debug.Trace("Key Pressed at: " + fPressedTime)                Else                        Debug.Trace("Hotkey Released " + (Utility.GetCurrentRealTime() - fPressedTime) + " seconds later")                EndIf        EndIf        RegisterForSingleUpdate(fUpdateInterval)EndEvent

As mentioned in the other thread, optimization is key to this working as you intend it to. With the whole thing converted to a ReferenceAlias script to be glued to the player, all should take form. You can use OnObjectEquipped, for instance, to check for the spell getting equipped in the left hand rather than polling for it every iteration OnUpdate and OnSpellCast will actually fire. Having all those globals swapped for local vars/properties/arguments will also reduce the 'expense' or time it takes to complete each iteration. If whittled down, a Channel() function with all the OnUpdate code would probably be the way to go as it should prove the most responsive.
User avatar
Ross Zombie
 
Posts: 3328
Joined: Wed Jul 11, 2007 5:40 pm

Post » Sat Nov 17, 2012 6:14 am

Channel()
Agreed, if possible. Regarding what is necessary to achieve this, basecosts, input-detection (via event), absolute spell costs calculation and more i worry it wont be implemented soon. it something different if its just a hud-control-function to which you yourself deliver this informations.

With the whole thing converted to a ReferenceAlias script to be glued to the player, all should take form.
I dont get the advantages since i simply havent understood the difference of an reference-alias script.

OnEquippedObject
Well, the problem is that this event is used by another script which fills the GV's. In my case this led to the situation, that the script recieves the GV value one OnEquippedObject - event later -> Multi-threading. Thats why i put it inside the OnUpdate cycle to ensure a safe processing hierarchy with the tradeoff of a higher PC-Load and a small delay (Registerforupdate).
In other words, if an ref-alias script is able to do the following within just a single OnEquippedObject-event i am safe to you use your suggested optimization and a few more:

Within the same event:
OnEquippedObject in script1 -> SetValue (most of the GVs defined on the script-top)
OnEquippedObject in script2 -> GetValue (same GV's)


Well, this problem will be solved at the time Silverlock publishes its events at last.
Thanks a lot man

Ps.: It also would be awesome if you could answere the to my first questions on top :wink:
User avatar
Charlotte Lloyd-Jones
 
Posts: 3345
Joined: Fri Jun 30, 2006 4:53 pm

Post » Sat Nov 17, 2012 11:00 am

assuming one has a script, that has an event which is triggered several times, what happens?
Each will be processed in the order they're received. OnUpdate will stagger if it's not given enough time, thus RegisterForSingleUpdate, in cases like this, is better as it won't iterate again until the last instance has completed.

Is for each triggered event an instance of the script created? If yes, does each instance have its own set of variables even if they share the same name? Or do all instances of the script share the same variables? What if a variable is used to prohibt the event to run a funtion? Do i need to use GV's then instead of script-variables?
For each instance an event occurs, its arguments will be set accordingly. OnItemAdded's akBaseItem, for instance, will be set to each base item added. Different instances of a script attached won't cause them to clash, that is each form the script is attached to will retain its own property/variable values, thus you wouldn't need to reach out to the globals if you operate from a ReferenceAlias script glued to Bendu and can tighten up the input detection by carving out the fat.
User avatar
Mark Hepworth
 
Posts: 3490
Joined: Wed Jul 11, 2007 1:51 pm

Post » Sat Nov 17, 2012 8:58 am

My Goal ist to "simulate" an event iskeypressed:
As such i need to poll with this function and when fitting, simulate that event with all its consequences:
- prohibit different Instances of the script, as such blocking all instance which might change values till the first instance has finished.
- Blocking consecutive runs of OnUpdate within an instance.

Ok, out of your answere I extract:

- RegisterForSingleUpdate to prohibit an additional update WIHTIN a single instance of the script.
- Preventing the stacking execution by several events of different Instances of the script by setting and reading GV's instead of local ones (e.g. IsEmulatingDepletion needs to be exposed as a GV and not a script related one, same for "DelatchofInputNeeded")

Actually that way i see a way to achieve this goal. If there is a logical failure pls tell me in advance, since i have to rewrite ~600 lines of code ;)
User avatar
N Only WhiTe girl
 
Posts: 3353
Joined: Mon Oct 30, 2006 2:30 pm

Post » Fri Nov 16, 2012 11:29 pm

I'd go without the GlobalVariables, is what I'm saying. Move the whole thing to a ReferenceAlias script and pin it on the player. You'll have a lot less stuff to check for each update/while loop iteration as much can be determined via OnObjectEquipped/Unequipped, etc. Since the player is like the Highlander and there's only one, there will assuredly be only one instance of the While loop/OnUpdate event running at any given time provided you don't prompt the OnUpdate polling or Channel function before it's finished.
User avatar
Conor Byrne
 
Posts: 3411
Joined: Wed Jul 11, 2007 3:37 pm


Return to V - Skyrim