Best Practices (Papyrus)

Post » Sun Nov 18, 2012 9:35 pm

Yeah, something like http://oblivion.nexusmods.com/mods/13092 for Skyrim mightn't be a terrible idea. Even if it's just replacing these "convenience" functions it would be some improvement. I don't know how much of a noticeable different it would make though.

I wish Papyrus has an "inline function" feature, where functions like these could be specified as inline in order to tell the compiler to put the function body directly in the code in order to get around this. Unfortunately, in order to modify the compiler I'd essentially need to recreate it from scratch first, but for something like this a preprocessor, which takes in the script and spits its own output into the compiler, might do the job.

It's worth keeping in mind that, although extra function calls that aren't absolutely necessary do add extra overhead, they should still be used where they can significantly cut down on code duplication.

It's hard to have a guideline about whether "multiple small scripts" is better or worse than "fewer longer scripts" - it's basically going to depend on what you're trying to do. When deciding whether to use one main "controller" script or several shorter scripts on different objects, the primary concern should always be how they will work with threading.

As far as I'm aware, a single object can only have a single active thread at any one time. So, if you were to set up your mod such that one "master" object (probably a quest) did everything, and lots of other objects were calling functions on it all the time, you might find that it's busy enough to slow down your script and you could split it into multiple scripts in order to speed things up.

However, if you split everything into multiple scripts then you might end up slowing things down overall because multiple objects need to talk to one another when you could instead be keeping everything on the same object. Like I said, it's going to depend on the mod.

Cipscis
User avatar
Sarah Knight
 
Posts: 3416
Joined: Mon Jun 19, 2006 5:02 am

Post » Sun Nov 18, 2012 7:26 pm

I was messing around with http://www.creationkit.com/GetDistance and I think there's a diff between "Game.GetPlayer()" and "PlayerRef" when in an exterior worldspace. With "Game.GetPlayer()", it's always the exact distance no matter which cell the object and player are in. But with "PlayerRef", it seems to return "0" for any object not in the same exterior cell as the player. I'm not sure of any others, but that might be something for the tutorials mentioned that may use "PlayerRef".
Distance in exterior cells ... which maybe aren't exterior ... is a MINEFIELD!

The problem is when you want to get the distance between a normal exterior (wilderness) cell and a cell flagged as a HOLDLocation (or a child of that hold).

Although it appears that the cells inside the walls of Whiterun are right next to the exterior cell outside of the gates, they are not. The distance returned by measuring in between those is nonsense (and sometimes 0 is returned when you try it).

In the end, I modified my distance script to check first whether the two "places" were in the same cell, or in the same parent ... if they were neither of those, I had to assume the calculation might produce nonsense.




That said, this report is interesting ... Is it possible that the PROPERTY version is not continually updating? So, the location of the property version is old until (somehow) recalculated. Where-as the GetPlayer version always calculates position on-the-fly?

(worth a bit more testing, I think?)
User avatar
Khamaji Taylor
 
Posts: 3437
Joined: Sun Jul 29, 2007 6:15 am

Post » Mon Nov 19, 2012 4:48 am

Just to be sure, variables do not flag an object as Persistent, correct? Just properties?
User avatar
katie TWAVA
 
Posts: 3452
Joined: Tue Jul 04, 2006 3:32 am

Post » Sun Nov 18, 2012 6:16 pm

Just to be sure, variables do not flag an object as Persistent, correct? Just properties?
Please get a reply from Justin or Cipscis ... persistance breaks my head :(
User avatar
Elizabeth Davis
 
Posts: 3406
Joined: Sat Aug 18, 2007 10:30 am

Post » Mon Nov 19, 2012 4:12 am

Just to be sure, variables do not flag an object as Persistent, correct? Just properties?
That would seem the case. A property won't necessarily make the cell child persistent if it isn't filled/autofilled. Most actors are *already persistent, so no worries there.

*I can see 'em with TES5Edit
User avatar
Sharra Llenos
 
Posts: 3399
Joined: Wed Jan 17, 2007 1:09 pm

Post » Mon Nov 19, 2012 12:01 am

Just to be sure, variables do not flag an object as Persistent, correct? Just properties?
From the wiki's documentation of http://www.creationkit.com/Persistence_(Papyrus)#Variables:
When any variable in a currently loaded script points at a reference, that reference is temporarily persistent. It will stay persistent until no variables are pointing at it any more, at which point it will unload. (This assumes no other game system is keeping the object alive) This means that you should try not to have variables holding on to objects any longer then you need them. You can clear variables by assigning "None" to them.

When it comes to Game.GetPlayer vs. a property, I don't think the property is failing to update. Remember, object variables (like the one an Actor auto property would set up) are pointers to objects. The value of the property essentially stays the same because it keeps pointing to the same place, but the object at which it's pointing may change.

Cipscis
User avatar
Andrea P
 
Posts: 3400
Joined: Mon Feb 12, 2007 7:45 am

Post » Mon Nov 19, 2012 10:40 am

From the wiki's documentation of http://www.creationkit.com/Persistence_(Papyrus)#Variables:


When it comes to Game.GetPlayer vs. a property, I don't think the property is failing to update. Remember, object variables (like the one an Actor auto property would set up) are pointers to objects. The value of the property essentially stays the same because it keeps pointing to the same place, but the object at which it's pointing may change.

Cipscis

Very helpful, gives me a good idea what I'm going to be trying here.
User avatar
Matthew Aaron Evans
 
Posts: 3361
Joined: Wed Jul 25, 2007 2:59 am

Post » Sun Nov 18, 2012 8:35 pm

!!Disregard!! GetDistance works the same on both.

Hmm, that seems unlikely to me, as the same object should be returned via either method and if I remember correctly I've compared them in a script to confirm this. Could I ask you to set up a test to compare the two and confirm your findings?

I can set up another separate plugin or what not if needed, but I've tested this quite a bit. I thought I was losing my mind for a while because it seems like it should work just fine.

The testing I did was in a new worldspace, but I don't see why it would be any different with other worlds. The script I was tinkering on basically disables objects when the player moves a certain distance away.

Here's a test script:
Spoiler
Scriptname DistTestScript extends ObjectReferenceActor Property PlayerRef Autobool bUpdate = FalseEvent OnUpdate()	ObjectReference rPlayerRef = None	;rPlayerRef = PlayerRef	rPlayerRef = Game.GetPlayer()		; display dist to screen	Debug.Notification("Dist: " + GetDistance(rPlayerRef) + " " + Self)	If (bUpdate)		RegisterForSingleUpdate(3)	EndIfEndEventEvent OnCellAttach()	bUpdate = True	RegisterForSingleUpdate(3)EndEventEvent OnCellDetach()	bUpdate = False	UnregisterForUpdate()EndEvent
With "rPlayerRef = Game.GetPlayer()", it will always return the correct distance. If I uncomment "rPlayerRef = PlayerRef" and comment out "rPlayerRef = Game.GetPlayer()", it will return "0" for any of them not in the same cell. I know "PlayerRef" ifs filled because it gets the correct distance for the cell I'm in (plus I quadruple checked it).

Just attach that script to 2 objects in different exterior cells that are in the same worldspace (the cells can be right next to each other). IE: One object on this side of the line and one on the other side. I can see it and nearly reach out and touch it, but it returns "0" distance.

Distance in exterior cells ... which maybe aren't exterior ... is a MINEFIELD!

The problem is when you want to get the distance between a normal exterior (wilderness) cell and a cell flagged as a HOLDLocation (or a child of that hold).

Although it appears that the cells inside the walls of Whiterun are right next to the exterior cell outside of the gates, they are not. The distance returned by measuring in between those is nonsense (and sometimes 0 is returned when you try it).
Good to know :smile:. But just to be clear though, what I'm referring to is a non-hold, simple worldspace. The equivalent of checking the distance between two objects that are both in Whiterun, but in adjacent exterior cells.
User avatar
Janeth Valenzuela Castelo
 
Posts: 3411
Joined: Wed Jun 21, 2006 3:03 am

Post » Sun Nov 18, 2012 9:14 pm

I just run some tests in Whiterun and both are working the same like they're supposed to. Now I'm not sure what to think. I'm going to do some more and try to see why it return 0 in the world I was initially in.

Edit:
Forget what I said, I'm a moron. I had "2" separate base objects running that script (I totally forgot about the other one without a mesh) and I must have only filled the player ref on one of them. The Debug notification cuts off where it shows "self", so I assumed it was all the same objects. My assumptions are digging me holes I can barely see out of. :blush:

So ya, they seem to work the same with GetDistance, so disregard my previous posts on this. Going to go find a wall I can bang my head against.
User avatar
Laura Samson
 
Posts: 3337
Joined: Wed Aug 29, 2007 6:36 pm

Post » Sun Nov 18, 2012 8:28 pm

I just run some tests in Whiterun and both are working the same like they're supposed to. Now I'm not sure what to think. I'm going to do some more and try to see why it return 0 in the world I was initially in.

Edit:
Forget what I said, I'm a moron. I had "2" separate base objects running that script (I totally forgot about the other one without a mesh) and I must have only filled the player ref on one of them. The Debug notification cuts off where it shows "self", so I assumed it was all the same objects. My assumptions are digging me holes I can barely see out of. :blush:

So ya, they seem to work the same with GetDistance, so disregard my previous posts on this. Going to go find a wall I can bang my head against.
Well done for some rigorous testing, mate. :smile:

Good to see someone chase something until they end up with a definitive answer.

(Cipscis will be happy ... I'm relieved nothing strange - well, nothing else strange - is going on!)
User avatar
N Only WhiTe girl
 
Posts: 3353
Joined: Mon Oct 30, 2006 2:30 pm

Post » Sun Nov 18, 2012 9:03 pm

Persistence question. I created a cell with an activator in it. When activated by a magic effect it runs code to set various properties and arrays. When finished the arrays and properties data is transferred to new properties/arrays in a "lighter" script attached to an alias for later use. My question is after these values are transferred will the cell and activator still be persistent? It is apparent that all values are still stored in the activator when it is reactivated.

BTW this is a great discussion. I think it should be pinned.
User avatar
Anna Kyselova
 
Posts: 3431
Joined: Sun Apr 01, 2007 9:42 am

Post » Mon Nov 19, 2012 9:51 am

Persistence question. I created a cell with an activator in it. When activated by a magic effect it runs code to set various properties and arrays. When finished the arrays and properties data is transferred to new properties/arrays in a "lighter" script attached to an alias for later use. My question is after these values are transferred will the cell and activator still be persistent? It is apparent that all values are still stored in the activator when it is reactivated.

BTW this is a great discussion. I think it should be pinned.
If a property is pointing at the activator reference (probably in your ActiveMagicEffect script), it will be flagged as persistent in the plugin and thus will forever remain persistent. ReferenceAliases, on the other hand, cease to persist when either their owning quest stops/ceases to exist or are http://www.creationkit.com/Clear_-_ReferenceAlias unless they're either filling another ReferenceAlias or are otherwise flagged as persistent.

*seconds pinning this*
User avatar
Felix Walde
 
Posts: 3333
Joined: Sat Jun 02, 2007 4:50 pm

Post » Sun Nov 18, 2012 6:48 pm

Hmm since both my magic effect and reading script (attached to referencealias) points to the writing script (attached to the activator object in cell) they will always be loaded. I don't think this is avoidable. I wonder how much of a hit this creates since the cell also contains a lot other miscobjects. And more importantly is this hit transferred to my papyrus resources for the scripts or just the game itself.

Thanks for the reply
User avatar
Fiori Pra
 
Posts: 3446
Joined: Thu Mar 15, 2007 12:30 pm

Post » Sun Nov 18, 2012 6:27 pm

http://www.creationkit.com/Persistence_(Papyrus) only makes sense in the context of http://www.creationkit.com/ObjectReference_Script. For other types of object, such as http://www.creationkit.com/Quest_Script, http://www.creationkit.com/Cell_Script, http://www.creationkit.com/ReferenceAlias_Script etc., the concept of persistence isn't relevant. It basically just doesn't make sense in that context - such objects can't be persistent, as how they are loaded and unloaded doesn't work in the same way.

As JustinOther mentioned, if you've pointed a property at an ObjectReference by assigning its value in the Creation Kit, then that ObjectReference will remain persistent forever. If this is not the case, then it is possible for the ObjectReference to no longer be persistent.

So long as a variable or property is pointing at an ObjectReference, it will remain persistent. If that value is only assigned via script (as opposed to being assigned in the Creation Kit) then you can clear it by setting it to None. If all the variables and properties that were pointing at an ObjectReference are set to None in this way, then it will only remain persistent if something else is keeping it persistent, like if it's still registered for a particular event.

I notice that the wiki's page on persistence doesn't mention aliases. I wonder if any ObjectReference that has a ReferenceAlias pointing at it might also be persistent, but I haven't tested this and all the ReferenceAliases I've ever worked with have been used only to apply scripts that usually keep them persistent anyway. Is anyone in a position to confirm or deny this?

As for pinning, I find the most worthy discussions tend to remain at the top of the forum by virtue of their being being interesting. Generally I find it's more useful for announcements and PSA-like threads that are important to read but might not generate much discussion. That said, however, whether or not any thread is pinned isn't up to me.

Cipscis
User avatar
Kathryn Medows
 
Posts: 3547
Joined: Sun Nov 19, 2006 12:10 pm

Post » Mon Nov 19, 2012 1:54 am

Thanks for the excellent info. As of now everything is pointed to via CK. This was convenient but I believe it can and should be avoided after reading you explanation. I will definitely implement this but I am still curious. Are these persistent items directly effecting my papyrus resources or are they effecting my over all system resources. Not a big deal just curios.

Thanks again.
User avatar
FABIAN RUIZ
 
Posts: 3495
Joined: Mon Oct 15, 2007 11:13 am

Post » Mon Nov 19, 2012 10:06 am

If I understand things correctly, here's how it should work: When a script calls an external or latent function
One thing that bothers me with the formulation from the wiki.... Native functions are external functions. I guess what they means is the following...

The following calls suspend the execution and pushes it back to the waiting queue:
* non-native functions from another script than the caller (including properties' getters and setters)
* native functions flagged as latent

The following calls does NOT suspend the execution:
* non-native functions from the same script than the caller.(including properties' getters and setters)
* native functions NOT flagged as latent

Could anyone confirm it please? Also, does someone know whether the latent functions list on the wiki is exhaustive and reliable?
User avatar
Joanne
 
Posts: 3357
Joined: Fri Oct 27, 2006 1:25 pm

Post » Mon Nov 19, 2012 6:06 am

Native functions are external functions.
No, or at least not always. Native functions, just like non-native functions, can be external functions but don't have to be. Here's an example of a native function (http://www.creationkit.com/GetBaseObject_-_ObjectReference) that is not being called as an external function:
Spoiler
ScriptName Test extends ObjectReferenceForm kBaseObjectEvent OnInit()	kBaseForm = GetBaseObject()EndEvent
In this case, GetBaseObject is not an external function because it is being called implicitly on the "Self" object. The function itself is declared in the http://www.creationkit.com/ObjectReference_Script. Because this Test script extends ObjectReference, it inherits that function.

However, GetBaseObject is one of the many native functions that is forced to synchronise with the framerate, so although it's neither external nor latent it will slow down scripts.

The following calls suspend the execution and pushes it back to the waiting queue:
* non-native functions from another script than the caller (including properties' getters and setters)
* native functions flagged as latent

The following calls does NOT suspend the execution:
* non-native functions from the same script than the caller.(including properties' getters and setters)
* native functions NOT flagged as latent

Could anyone confirm it please?
As I understand it, all external functions (which means all functions called on anything other than the "Self" object, including native, non-native, global, and getter/setter functions) and all latent functions will cause the current thread to leave the "Self" object and remove the lock on it.

Only internal non-latent functions will not cause the thread to leave the object (which is synonymous with the lock being removed), although most native functions (like the example above) will slow down the script significantly because they must by synced with the framerate. There's a list of exceptions to this rule on the wiki - http://www.creationkit.com/Category:Non-delayed_Native_Function

Also, does someone know whether the latent functions list on the wiki is exhaustive and reliable?
New functions have generally been added to the wiki by Bethesda developers, so I would expect the list to be exhaustive and reliable. Before you edited it, this was stated on the http://www.creationkit.com/Category:Latent_Functions category page, and that page had only been edited by Jlundin, when it was created. Jeff Lundin, a.k.a. SmkViper, is the Bethesda developer who wrote and implemented Papyrus.

To be fair, that page hadn't been updated since the various patches which have added new functions to Papyrus were released, but to my knowledge all these new functions have been flagged as latent where appropriate.

Do you have reason to believe that the list on the wiki is not exhaustive, or should that edit perhaps be reverted?

Cipscis

EDIT:

Sorry to nitpick, but I just noticed most of your edits on the wiki have been flagged as "minor". I just wanted to point out that the "minor edit" flag on the wiki should only be ticked for edits that don't significantly change the content of the page. Pretty much the only things that should use this flags are fixing typos and formatting. Any content changes are not minor.

Cipscis
User avatar
Valerie Marie
 
Posts: 3451
Joined: Wed Aug 15, 2007 10:29 am

Post » Mon Nov 19, 2012 6:53 am

@Cipscis
I agree on your corrections and post-scriptum, and I thank you for the precisions. Now, earlier today, I investigated the topic further on http://www.gamesas.com/topic/1373784-papyrus-threading-alternative-techniques/page__view__findpost__p__21628353 and had undone most of my edits on the wiki before that. While most of my original understandings were wrong, partly because of the ambiguity in the "threading notes" article and and partly because of a misinterpretation of the game's logs, the deepen tests I conducted since then tend to show that what is stated on the wiki does not entirely match the empirical results: latent functions do not exhibit the stated behavior.
User avatar
Nicholas C
 
Posts: 3489
Joined: Tue Aug 07, 2007 8:20 am

Previous

Return to V - Skyrim