Jump to content

NVSE plugin -- What do we need?


prideslayer

Recommended Posts

I think that the modding community would benefit greatly from a "wait for condition" command.

 

This isn't too much work' date=' it's basically impossible. [/quote']

 

Here is the implementation I was thinking about:

 

First, you count how many times this command is present in the script

 

Second, you count blocks of code. The first line of code is a block. Any conditional bit of code is a block. Each one of these waitfor commands marks two blocks -- itself, and the code after it. So now you have block labels, 0, 1, 2, 3, 4, ...

 

Third you create a variable to store a block number.

 

Fourth you wrap the entire script in a "while" statement which tests if the block number is still valid.

 

Then, you transform the code to be something like (expressed in some bastardized C flavoured GECK pseudocode):

 

while (Block is valid) {
   switch (Block) {
       case 0:
           ...
           set Block to 1;
       case 1:
           set Block to 2;
       case 2:
           ...
           Set Block to 0;
           return;
   }
}

 

Note that this also means that if statements need to be transformed to set their block number. The purpose of the "while" statement was so you could re-implement conditionals using "set Block to ConditionalTarget; break;" If you wind up not needing to re-implement if statements, you can get rid of that also.

 

Or maybe you do not even need to identify those blocks -- the real issue there is making sure that internal state is consistent and if there is none other than next byte to execute then this becomes a non-issue -- you eliminate the while statement then and you do not bother with block numbers for blocks that appear in conditionals.

 

Finally, for each "waitfor" statement, do the test and return thing.

 

That said, simpler to implement and more generically useful would be

 

Begin conditional

 

This is just syntactic sugar for doing your own stuff in begin game mode, but so was the wait command.

 

And, yes, since this is syntactic sugar and not fundamental capability, it's not something to sweat about. If it's not easy to implement, I certainly would not want you wasting time here.

 

That said, I think that this kind of thing makes code easier to read, and I think that that would be a good thing for mod authors. If you disagree that would be another reason to not waste time here.

 

Anyways... enough of the sugary stuff... another thing i think would be useful would be a %{g:male|female} (or %{g:male|female|other} -- but use male for other in the typical case) syntax in string expansions. This might also be outside your comfort level though. This would allow general use of gender specific slang and phrases instead of the rather limited set of gender specific terminology we currently have.

Link to comment

I think that the modding community would benefit greatly from a "wait for condition" command.

 

This isn't too much work' date=' it's basically impossible. [/quote']

 

Here is the implementation I was thinking about:

 

First, you count how many times this command is present in the script

 

Impossible I said! I cannot see (nor parse) the script sourcecode, and even if I could, there's just no way I'd get that involved in it. Read, parse, and modify every script using that command on the fly? You're mad as a hatter. ;)

 

That said, simpler to implement and more generically useful would be

 

Begin conditional

 

This is just syntactic sugar for doing your own stuff in begin game mode, but so was the wait command.

 

Again, and I'm sorry to disappoint, but *parsing* the scripts is not something I'm even going to attempt. The last thing I want to do is build a lexical analyzer into this thing.

 

Function calls, that's what I'm doing. Advanced function calls, as advanced as you want as long as they're useful, but that's it.

 

I am already thinking of ways I can do things like provide new pseudo-types like stacks and arrays, via some group of functions like int:createArray(), addItemToArray(int:arrayid, form:item), getItemFromArray(int:arrayid, int:index), getArrayCount(int:arrayid), etc.

 

Anyways... enough of the sugary stuff... another thing i think would be useful would be a %{g:male|female} (or %{g:male|female|other} -- but use male for other in the typical case) syntax in string expansions. This might also be outside your comfort level though. This would allow general use of gender specific slang and phrases instead of the rather limited set of gender specific terminology we currently have.

 

I think you can do that already..

 

%p(o,s,p) is the pronoun prefix, %po is 'him/her/it'.

 

So you should be able to do %{po:him} and so on.

Link to comment

Oh before anyone asks or points it out, it's more than an analyzer and parser for the scripting language that I'd need -- I need a compiler too.

 

The scripts are stored in plaintext in the esp/esm, but that is not the code that is run. Their bytecode is stored there too (compiled format), and *that* is what is run. I'd have to look at the source, do all that parsing magic to see if my services are needed, and then (somehow) recompile the script then and there -- and obviously flag it somehow so that I know I've already done it for that script and to not do it again... and that is still a massive oversimplification of what I'd actually have to do.

 

 

 

 

Link to comment

Oh before anyone asks or points it out' date=' it's more than an analyzer and parser for the scripting language that I'd need -- I need a compiler too.

 

The scripts are stored in plaintext in the esp/esm, but that is not the code that is run. Their bytecode is stored there too (compiled format), and *that* is what is run. I'd have to look at the source, do all that parsing magic to see if my services are needed, and then (somehow) recompile the script then and there -- and obviously flag it somehow so that I know I've already done it for that script and to not do it again... and that is still a massive oversimplification of what I'd actually have to do.

 

 

 

 

[/quote']

 

And because it's all bytecode, THAT is why every reference you include in a script has to be resolved at compile time. It's also why buildref was an NVSE addition since it was required to surrogate the resolved reference properly.

Link to comment

Sadly, you are probably right about me being "mad as a hatter" -- I do suffer from mercury poiosoning. That said... here's a bytecode implementation (assuming you can access the bytecode "program counter"), since it sounds like the parser is inaccessibly complex.

 

I know it's quite unlikely you will implement this, but I just feel like I have to defend the intellectual validity of my position. Or something. Maybe I am just too chatty...

 

Anyways, the bytecode implementation would involve three functions:

 

ResumeCurrentWait()

WaitForCondition()

StopWaiting()

 

ResumeCurrentWait() does nothing, normally.

 

WaitForCondition() saves the current bytecode location. Future instances of ResumeCurrentWait will set the program counter to the next instruction after this WaitForCondition()

 

StopWaiting() clears the saved program counter value and thus sets ResumeCurrentWait() back to "do nothing" mode.

 

The programmer has to manually return if the condition is not satisfied.

 

There are probably better names for these things.

 

Anyways... on to the good stuff -- run time resource modding!

 

texture references are stored in the mesh (.nif) file, in the BSShaderTextureSet block (each NiNode -- which is a "top level mesh unit" in the mesh document tree -- has one BSShaderTextureSet). So when you replace a mesh you will automatically get replaced textures unless you go in and replace them again. And since a new mesh can have a completely different organization from the old one this is an issue that would have to be left to the programmer specifying the mesh.

 

In other words, I *think* that the texture remapping consequence would be represented in the data structure whose pointer to it you would be replacing when you update the mesh reference. (I cannot think how to use better english here, I hope I am making sense... and I also hope I am not too far wrong.) Note that this potentially raises another issue, which is garbage collection. Hopefully memory management issues will not be show stoppers...

 

But the whole thing of navigating the object structures and finding the right methods to fire off, to invalidate old references and get new ones to load seems daunting. Possibly you will need to create a special plugin full of unused references which you can "bring live" to force loading of new mesh files?

Link to comment

I don't think you understand the complexity of what you're asking, regarding the script stuff.

Future instances of ResumeCurrentWait will set the program counter to the next instruction after this WaitForCondition()

 

This just won't work without heavily modifying how the engine handles scripts. There is no hook for script starting, so I can't simply intercept the engines call when it runs a script and tell it to abort. So what would happen is the script would run "up to" the WaitForCondition() call repeatedly.

 

What you've basically asked me to do is to make an alias for "Return", that takes an expression as it's argument, a sort of ReturnIf(...), which is functionally no different from "If ... return". Implementing it any other way requires me to runtime patch the game engine (exactly as NVSE itself does), to either:

 

- intercept calls to start script, to prevent it from running the script again until the condition is met. Then patch the VM to allow it to start at an arbitrary point in the script rather than at the beginning.

 

- Completely reimplement the interpreter/script VM, thread it, and make "waitforcondition" basically a process suspend that the main thread will wake up.

 

Given the complexity, I'm forced to ask why you don't just write your scripts another way? You can do functionally the same thing now, by just making clever use of a script stage variable.

 

I'm not really interested in doing something so complex, for no real benefit that I can see.

 

As for the model/texture stuff, what are you trying to accomplish? You listed a lot of thoughts about the NIF format and structure (which I can load and walk quite easily thanks to niflib, I expect), but no indication of what you're trying to accomplish. Texture swapping? Model+texture swapping?

Link to comment

sen4mi

 

Do you want Prideslayer to write a JIT comp/decomp for bytecode? If so, you're asking for a helluva lot. If you need a ton of control over NIFs, you'd have better luck (and success) asking the NifSE team to port to NV imo.

Link to comment

I don't think you understand the complexity of what you're asking' date=' regarding the script stuff.[/quote']

 

I cannot disagree with you there.

 

Future instances of ResumeCurrentWait will set the program counter to the next instruction after this WaitForCondition()

 

This just won't work without heavily modifying how the engine handles scripts. There is no hook for script starting' date=' so I can't simply intercept the engines call when it runs a script and tell it to abort. So what would happen is the script would run "up to" the WaitForCondition() call repeatedly.[/quote']

 

I am not sure what you said here, but I think you have told me that you cannot access nor change the program counter -- the pointer that loads the next byte code. So from this I gather that that's probably private data. Or buried in some other stack frame...

 

As for the model/texture stuff' date=' what are you trying to accomplish? You listed a lot of thoughts about the NIF format and structure (which I can load and walk quite easily thanks to niflib, I expect), but no indication of what you're trying to accomplish. Texture swapping? Model+texture swapping?

[/quote']

 

Both of those are viable goals.

 

For now, I am thinking of a way of implementing pregnancy, that does not require bodysuits. But this could be used for other things.

 

 

Link to comment

Future instances of ResumeCurrentWait will set the program counter to the next instruction after this WaitForCondition()

 

This just won't work without heavily modifying how the engine handles scripts. There is no hook for script starting' date=' so I can't simply intercept the engines call when it runs a script and tell it to abort. So what would happen is the script would run "up to" the WaitForCondition() call repeatedly.[/quote']

 

I am not sure what you said here

 

Shortversion is that the feature you're asking for is:

1) something you can already accomplish easily by restructuring your script.

2) Entirely too complicated to attempt, given #1.

 

The kind of data and control you're assuming exists, doesn't in this context. I would have to invent it. Months of work, at least.

 

As for the model/texture stuff' date=' what are you trying to accomplish? You listed a lot of thoughts about the NIF format and structure (which I can load and walk quite easily thanks to niflib, I expect), but no indication of what you're trying to accomplish. Texture swapping? Model+texture swapping?

[/quote']

 

Both of those are viable goals.

 

For now, I am thinking of a way of implementing pregnancy, that does not require bodysuits. But this could be used for other things.

 

I'll see what I can do on both of those. I also am going to see if I have access to the skeleton beyond just seeing its name. It may be possible for me to access and set scale bones myself, giving us pregnancy, breast sizing, etc. -- basically dynamic body scaling somewhat like skyrim has, as long as the player is using a supported skeleton + mesh.

 

This is part of my thought process on integrating niflib. "Worst case" I can copy the physical skeleton file and adjust the default scale of the involved bones -- then it's a matter of being able to assign this new skeleton file to the actor, which I'm not 100% sure on.

Link to comment

I *believe* I should be able to directly set the scale though; this is what the engine itself does with setscale I think.

I think their implementaion of SetScale is not skeleton based. I believe it's a pure opengl/directx resize. I could be wrong though. That isn't to say you can't directly manipulate scale with access to the nif in memory.

Link to comment

You might be correct, I haven't tried to figure out what they're doing at all, just figured that it was a skeleton thing. In any case, I do have a handle to the skeleton, that's where the name comes from in the example -- in my code, it's "pActor->model.GetPath()"

 

I haven't yet tried to change anything on it, but there IS a "setPath" method I can try, for "plan C". Plan C does look like it's becoming plan B though, that model object has next to nothing else on it..

 

class TESModel : public BaseFormComponent
{
public:
TESModel();
~TESModel();

enum
{
	kFacegenFlag_Head =			0x01,
	kFacegenFlag_Torso =		0x02,
	kFacegenFlag_RightHand =	0x04,
	kFacegenFlag_LeftHand =		0x08,
};

virtual void *	Destroy(bool noDealloc);	// 04
virtual char *	GetPath(void);
virtual void	SetPath(char * path);		// 06

String	nifPath;		// 04
UInt32	unk0C;			// 0C
void	* unk10;		// 10
UInt8	facegenFlags;	// 14
UInt8	pad15[3];		// 15

void SetPath(const char* newPath)	{ nifPath.Set(newPath); }
};

 

I will investigate the two "unk's", one of them might be a pointer to somewhere/something useful.

Link to comment

Well the second one' date=' unk10, is a void pointer which generally indicates a pointer to an unknown type that will be cast to a type on use. unk0C might be the data size of what it's pointing to maybe?

[/quote']

 

Could be but typically there aren't any sizes in the structures. I think it's just void because the nvse guys don't know what kind of thing it points to..

Link to comment

More functions in the works.. not all of them I have planned are done, just want to let y'all know what to expect and for you to let me know if you have changes or additions you think should be made.

 

Persistent storage functions

NVSE provides hooks to plugins to notify us that the game is loaded, started, or saved. I can use this to store data of my own that "goes with" a given savegame.

 

-- (ref).NX_SetEVxxxx(name, value)

- Basically our own unlimited set of actorvalues, named whatever YOU want to call them, and storing any datatype. 'xxxx' indicates data type. Can be stored on any reference, not just actors. Examples:

; for FormIDs.  Base values and static Refs ok.  Dynamic refs (starting with 0xff) would be meaningless.
someRef.NX_SetEVFo("my value name", someRef) ; EVFo = ExtendedValueForm
someRef.NX_SetEVFo("my value name", someBase)

; integers
someRef.NX_SetEVI("my value name", 42) ; EVI = .. integer

; floats
someRef.NX_SetEVFl("my value name", 3.2) ; EVFl = .. float

; strings
someRef.NX_SetEVS("my value name", "some string value") ; EVS = ... string

 

-- someRef.NX_CompEVxxxx(name, value, ci)

- Returns 1 if the values match, 0 if they don't. "ci" is an optional flag (1 or 0) for the String function only, indicating that the compare should be case insensitive.

 

-- someRef.NX_GetEVxxxx

- Same as the Set functions, except for strings, you will have to use the compare function instead.

 

 

 

Extended data types

I am planning initial support for arrays, which will lead eventually to queues (FIFOs) and stacks (FILOs). Allocation will be automatic. Cleanup will be automatic when the array is empty. Arrays will be saved 'globally' and not attached to specific refs like the "Extended Value" things above. My initial usage interface right now will look something like this:

 

NX_SetArrayValxxxx(name, index, value)

NX_GetArrayValxxxx(name, index)

NX_GetArrayCount(name)

 

name is the name of the array, index is.. well the index.

 

A rosetta stone may explain better, so given a scripting language like PHP, the commands would be along the lines of this:

 

$stuff['foo'] = 'bar'

is

NX_SetArrayValS("stuff", "foo", "bar");

 

$bar = $stuff['foo']

is

set bar to GetArrayValI("stuff", "foo");

 

and so on.

 

This is just a rough sketch in my head now.

 

Trying to see if there is a way for this, and the EV functions, to be overloaded and allow any datatype to be set or retrieved, but I'm not sure I can actually do that within the confines of NVSE and the scripting language.

Link to comment

Might not be out today as intended' date=' but it's early yet. Having some trouble with ExtractArgs and ExtractArgsEx only giving me back the first argument to any of my calls. Not feeling any hulk rage.. yet. ;)

[/quote']

 

I'll assume you don't mean they're just returning a boolean and are actually checking the data structs/classes you gave as return params ;)

Link to comment

Might not be out today as intended' date=' but it's early yet. Having some trouble with ExtractArgs and ExtractArgsEx only giving me back the first argument to any of my calls. Not feeling any hulk rage.. yet. ;)

[/quote']

 

I'll assume you don't mean they're just returning a boolean and are actually checking the data structs/classes you gave as return params ;)

 

Yes of course.. :P

 

All the params I give to the function, except the first one, come back NULL. I copied the function pretty much verbatim from ListReplaceNthForm since that takes the same arguments.. a formlist, a form, and an optional int.

 

At this point, no idea what's going on.

 

 

 

 

Link to comment

Oh, it is returning false each time at least.. which is nice, but doesn't really help me get to the bottom of the problem. ;)

 

 BGSListForm* pFormList  = NULL; // the formlist
 TESForm*     pItem      = NULL; // item we are looking for
 TESForm*     pListItem  = NULL; // current item from list
 bool         eares      = false;

 eares = ExtractArgs(EXTRACT_ARGS, &pFormList, &pItem, &n);
_MESSAGE(" ARGS %x %x %i", pFormList, pItem, n);
_MESSAGE("  EAS %i", eares);
 return true;

 

results in this type of result, consistently..

ARGS 110b5f78 0 0
 EAS 0

Link to comment

Might not be out today as intended' date=' but it's early yet. Having some trouble with ExtractArgs and ExtractArgsEx only giving me back the first argument to any of my calls. Not feeling any hulk rage.. yet. ;)

[/quote']

 

I'll assume you don't mean they're just returning a boolean and are actually checking the data structs/classes you gave as return params ;)

 

Yes of course.. :P

 

All the params I give to the function, except the first one, come back NULL. I copied the function pretty much verbatim from ListReplaceNthForm since that takes the same arguments.. a formlist, a form, and an optional int.

 

At this point, no idea what's going on.

 

 

 

 

Are Args a stack? Meaning do you have to re-iteratively call ExtractArgs until it returns false?

 

Link to comment

Archived

This topic is now archived and is closed to further replies.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. For more information, see our Privacy Policy & Terms of Use