Jump to content

Devious Devices Framework Development/Beta


Recommended Posts

1 hour ago, Skullered said:

I'm tempted to just force Argonian and Khajiit texture sets with BRRF, but that doesn't seem like a very elegant solution, since this affects any race, not just beast races.

You could just make additional armoraddons for Argonian and Khajiit texture sets and add those to the armor. This is how the game assigns textures to naked bodies. Look up "NakedHands" in the armoraddons tab of the Creation kit.

Link to comment
15 minutes ago, Code Serpent said:

You could just make additional armoraddons for Argonian and Khajiit texture sets and add those to the armor. This is how the game assigns textures to naked bodies. Look up "NakedHands" in the armoraddons tab of the Creation kit.

That's even better because the hands on beasts have different meshes but required other set of skills.

Link to comment
On 11/7/2020 at 7:36 PM, Ryu Gabriev said:

There's pretty heavy clipping though, when I do. In contrast with the exact same bodytype (Wench Body, btw), regular Armbinder had almost no clipping at all. I should also mention that the Ropebinder, which in practice is the same thing as an elbowbinder (changes the same areas, same pose, etc.) it DOES have a bodyslide conversion and looks fine.

I had that too in my game (using CBBE version). Armbinder was looking good on all NPCs that I tried. Elbowbinder was working well on some, but had bad clipping with others. Often the upper arm muscles were clipping out of the elbowbinder. I especially noticed it with female redguards. Not sure if it is caused by racial body morphs or by weight.

Link to comment
2 hours ago, Code Serpent said:

You could just make additional armoraddons for Argonian and Khajiit texture sets and add those to the armor. This is how the game assigns textures to naked bodies. Look up "NakedHands" in the armoraddons tab of the Creation kit.

That's what I did in the file I uploaded, but ideally the game should be replacing those textures automatically, similar to how some vanilla bracers do (Falmer Gauntlets do this, and they don't have a unique armoraddon per race).

Link to comment

I've got a partial bodyslide for the SE version, it's just the chest part not the sleeve. I just found out today how to pose in outfit studio. I think i'll play with this after I finish the basic SE conversion. Too bad the pose doesn't seem to save from the short test i did.

 

This was just playing with the clavicle, upper arm, and forearm bones

Spoiler

Starting test pose:
NPC L ForeArm
    ROT X -.31
    ROT Y -.47
    ROT Z 0.0
NPC L UpperArm
    ROT X .42
    ROT Y .43
    ROT Z -.77
NPC L UpperarmTwist1
    ROT X .58
    ROT Y -.08
    ROT Z .11
NPC L Clavicle
    ROT X -.36
    ROT Y 0.0
    ROT Z -.73
    OFF X -1.1
    OFF Y 4.24
    OFF Z 0.0

 

image.thumb.png.ed8ccdef4f3ba2eb17a39d790622e769.png

 

And if this is not news to anyone, sorry. I'm just so happy to have found a new toy :D

 

9 hours ago, Skullered said:

-Fixed beast races having human hands with the "Strict Rope Armbinder"

 

Thanks Skullered! Worked great for argonian/khajiit, not so much for the custom race, which uses the khajiit proxy keyword.

I'll have to think about that since I like the DagiRaht mod, but that's not a DD issue.

So confirming it fixed the issue as far as DD is concerned, and thank you again.

Link to comment
39 minutes ago, Code Serpent said:

A small suggestion to add to the pile:

 

Could you add a prompt to give the player the choice on whether or not to equip items onto npcs when trading? Sometimes I just want my companions to carry my metric ton of bondage gear without trying it on.

If you don't mind using other mods then devious lore already have that function. Just select "friend, would you help me with something" and ask her/him to carry DD gears for you.

Link to comment
17 hours ago, Kimy said:

About the Elbowbinder: I am not qualified to create a Bodyslide version if that item, but if a volunteer would like to make one, I'd merge it! :)

I may know someone interested in doing this. He wants to know where to find the original assets that would need to be modified.

Link to comment
8 hours ago, hungvipbcsok said:

If you don't mind using other mods then devious lore already have that function. Just select "friend, would you help me with something" and ask her/him to carry DD gears for you.

I know, I made that mod. I just think it should be included in the framework as well.

Link to comment

@Kimy Surprised I didn't notice before but the Ebonite versions of the Hobble Straitjackets didn't have "Environment_Mapping" set in the Shader Flags. I know what happened, when I made these new straitjacket variants I cheated and created the leather versions first then simply changed texture and shader settings to create the ebonite versions. Better then creating the new variants twice in Outfit Studio.

 

So I made sure "Environment_Mapping" was set in the Shader Flags for the Ebonite versions, it effects both the CBBE and UUNP versions. It's nothing major just visual, should look better though. These are just the BodySlide meshes found in the ShapeData folder, just let them overwrite the files in whatever final build/release candidate project you have for DD5.

 

EboniteHobbleStraitjacketStuff.7z

 

Another small coat of wax.

Link to comment
2 hours ago, Code Serpent said:

I know, I made that mod. I just think it should be included in the framework as well.

I will look into it. At this point I don't want to introduce new features requiring more intensive testing. So it might or might not have to wait until 5.1.

Link to comment
49 minutes ago, Kimy said:

I will look into it. At this point I don't want to introduce new features requiring more intensive testing. So it might or might not have to wait until 5.1.

Alright. Just so I'm clear, I'm thinking of a simple "Yes/No" message box prompt when devices are given to npcs through a trading menu. I'm not suggesting you add in the dialogue system I made in Devious Lore.

Link to comment

@Kimy

Problem I'm trying to solve:

  • Various Devices, randomly, are not actually being replaced when they are replaced by script
  • Testing with DD5 Beta 12
  • It's most likely a race condition

Specific Case:

  • When a certain arousal level is reached, the Virgin Nipple Bell will be replaced with a Virgin Horny Nipple Bell. Vice-versa when arousal goes back down
  • The code has its own mutex so as not to call DD functions in it's own race conditions. The code snippet...
	;switch from normal to horny Nipple Bells
	libs.removeDevice(Playerref, BellsInventory, BellsRender, libs.zad_DeviousPiercingsNipple, True, False, True)	
	libs.equipdevice(Playerref, BellsHInventory, BellsHRender, libs.zad_DeviousPiercingsNipple, False, True)
	;switch from horny to normal Nipple Bells
	libs.removeDevice(Playerref, BellsHInventory, BellsHRender, libs.zad_DeviousPiercingsNipple, True, False, True)	
	libs.equipdevice(Playerref, BellsInventory, BellsRender, libs.zad_DeviousPiercingsNipple, False, True)

Past Scenario with same Case:

  • In the past, when I was testing in a DD4 beta, I had the same change in behavior. The problem was a race condition, and solved by using "skipMutex"

 

I know that the depreciated functions... EquipDevice & removeDevice are now passing to lockDevice & UnlockDevice, but not passing Mutex. Also the new functions are "Bool" and can be checked for success.

 

I've written a test function. The Test runs 2 times using depreciated functions, then another 2 times, using the new functions. It applies a delay, tries 15 times, then lowers the delay, repeating, until breaking point or success without a delay. It also retries once, on a fail, if using the new functions.

 

Here is the code for the test...

Spoiler
Spoiler

 


;.......... Test Equip/Remove Device Race Condition
Function VirginTestRaceCond()
	Bool DepFunction = True
	Int Test = 1
	While Test <= 4
		Int Try = 1
		Float Delay = 0.5
		Int DelayCount = 1
		Bool Alternate = False
		Bool Failed = False
		While ! Failed
			If Alternate
				;switch from normal to horny Nipple Bells
				If DepFunction
					libs.removeDevice(Playerref, BellsInventory, BellsRender, libs.zad_DeviousPiercingsNipple, True, False, True)
					;Utility.Wait(0.1) ;this is here to see if it has impact. In tests its not been needed
					libs.equipdevice(Playerref, BellsHInventory, BellsHRender, libs.zad_DeviousPiercingsNipple, False, True)
				Else
					If libs.UnlockDevice(Playerref, BellsInventory, destroyDevice = True)
						;worked
					Else
						libs.UnlockDevice(Playerref, BellsInventory, destroyDevice = True) ;one retry
						Debug.Trace("[Virgin] UnLockDevice Retry")
					EndIf
					;Utility.Wait(0.1)
					If libs.LockDevice(Playerref, BellsHInventory)
						;worked
					Else
						libs.LockDevice(Playerref, BellsHInventory) ;one retry
						Debug.Trace("[Virgin] LockDevice Retry")
					EndIf
				EndIf
				Alternate = False
			Else	
				;switch from horny to normal Nipple Bells
				If DepFunction
					libs.removeDevice(Playerref, BellsHInventory, BellsHRender, libs.zad_DeviousPiercingsNipple, True, False, True)
					;Utility.Wait(0.1)
					libs.equipdevice(Playerref, BellsInventory, BellsRender, libs.zad_DeviousPiercingsNipple, False, True)
				Else
					If libs.UnlockDevice(Playerref, BellsHInventory, destroyDevice = True)
						;worked
					Else
						libs.UnlockDevice(Playerref, BellsHInventory, destroyDevice = True) ;one retry
						Debug.Trace("[Virgin] UnLockDevice Retry")
					EndIf
					;Utility.Wait(0.1)
					If libs.LockDevice(Playerref, BellsInventory)
						;worked
					Else
						libs.LockDevice(Playerref, BellsInventory)  ;one retry
						Debug.Trace("[Virgin] LockDevice Retry")
					EndIf
				EndIf
				Alternate = True
			EndIf
			Utility.Wait(Delay)
			If libs.isWearingDeviceType(PlayerRef, libs.zad_DeviousPiercingsNipple)
				;no fail
			Else
				Failed = True
			EndIf
			If DelayCount > 15
				DelayCount = 1
				Delay = Delay - 0.1
				If Delay < 0
					Debug.MessageBox("SUCCESS! No Race Condition Occurred after 15 tries even at 0 delay. (if DD5, one rety is in play)")
					Debug.Trace("[Virgin] SUCCESS! No Race Condition Occurred after 15 tries even at 0 delay. (if DD5, one rety is in play)")
					Return
				EndIf
			EndIF
			Try += 1
			DelayCount += 1
		EndWhile
		Debug.MessageBox("TESTING Depreciated Function: " + DepFunction + " / Total Tries to failure: " + Try + " / Delay reached: " + Delay)
		Debug.Trace("[Virgin] TESTING Depreciated Function: " + DepFunction + " / Total Tries to failure: " + Try + " / Delay reached: " + Delay)
		Test += 1
		If Test == 3
			DepFunction = False
		EndIf
	EndWhile
EndFunction

 

  • The test function was activated after waiting a good 20 seconds after game load. If I don't wait, the test is capable of hanging DD
  • I've confirmed that block_generic keywords are present on each involved inventory device
  • I made sure an arousal increase/decrease could not trigger and interfere with the test, by turning it off
  • Results in the attached Log31. txt

Summary of results from log:

[11/10/2020 - 01:43:13PM] [Virgin] TESTING Depreciated Function: TRUE / Total Tries to failure: 45 / Delay reached: 0.300000
[11/10/2020 - 01:43:43PM] [Virgin] TESTING Depreciated Function: TRUE / Total Tries to failure: 33 / Delay reached: 0.300000
[11/10/2020 - 01:43:43PM] ============================================================
[11/10/2020 - 01:43:43PM] [Zad] (((ERROR))): LookupDeviceType received invalid keyword None
[11/10/2020 - 01:43:43PM] ============================================================
[11/10/2020 - 01:44:08PM] [Virgin] TESTING Depreciated Function: False / Total Tries to failure: 25 / Delay reached: 0.400000
[11/10/2020 - 01:44:34PM] [Virgin] TESTING Depreciated Function: False / Total Tries to failure: 27 / Delay reached: 0.400000
 

I'm looking for advice on how I can solve this problem. I don't see an obvious path.

 

Log31 - Equip Unequip race condition.log

Link to comment

I guess the safest way to swap devices using the same DD slot would be checking for UnlockDevice() returning true AND the old -rendered- device actually removed from the slot with !IsEquipped(). The actual removal is done by OnUnequip(), which can indeed complete a tad later than UnlockDevice().

 

I might look into a function to safely swap devices, but right now I'd suggest trying the above.

Link to comment
1 hour ago, Kimy said:

I might look into a function to safely swap devices, but right now I'd suggest trying the above.

That could be good. Most device manipulation in SLaV is Swapping. I would think its very common in other mods too. Maybe you should also "detect" swapping, so people's code as is would take that new safe path. Like, if same slot experiences a remove then equip, in less than X seconds, treat it as a swap.

1 hour ago, Kimy said:

I guess the safest way to swap devices using the same DD slot would be checking for UnlockDevice() returning true AND the old -rendered- device actually removed from the slot with !IsEquipped(). The actual removal is done by OnUnequip(), which can indeed complete a tad later than UnlockDevice().

I would think you want to fix this for mods that are not being updated. A new Swap function alone wont help for backward compatibility. This is not a rare edge case. Its guaranteed to happen 1 out of 10, or likely worse as it did not take me much gameplay to notice it. I did not at first, find this with targeted testing. So you can expect it to be relatively widespread.

 

I added IsEquipped to the test function. Log produced attached. Code follows. Comments in code showing lines changed.

Spoiler
Spoiler

 

 


;.......... Test Equip/Remove Device Race Condition
Function VirginTestRaceCond()
	Bool DepFunction = True
	Int Test = 1
	While Test <= 4
		Int Try = 1
		Float Delay = 0.5
		Int DelayCount = 1
		Bool Alternate = False
		Bool Failed = False
		While ! Failed
			If Alternate
				;switch from normal to horny Nipple Bells
				If DepFunction
					libs.removeDevice(Playerref, BellsInventory, BellsRender, libs.zad_DeviousPiercingsNipple, True, False, True)
					;Utility.Wait(0.1) ;this is here to see if it has impact. In tests its not been needed
					libs.equipdevice(Playerref, BellsHInventory, BellsHRender, libs.zad_DeviousPiercingsNipple, False, True)
				Else
					If libs.UnlockDevice(Playerref, BellsInventory, destroyDevice = True) &&  ! PlayerRef.IsEquipped(BellsRender) ; <-- Changed
						;worked
					Else
						Utility.Wait(0.1) ; <-- Changed
						libs.UnlockDevice(Playerref, BellsInventory, destroyDevice = True) ;one retry
						Debug.Trace("[Virgin] UnLockDevice Retry")
					EndIf
					;Utility.Wait(0.1)
					If libs.LockDevice(Playerref, BellsHInventory) &&  ! PlayerRef.IsEquipped(BellsHRender) ; <-- Changed
						;worked
					Else
						Utility.Wait(0.1) ; <-- Changed
						libs.LockDevice(Playerref, BellsHInventory) ;one retry
						Debug.Trace("[Virgin] LockDevice Retry")
					EndIf
				EndIf
				Alternate = False
			Else	
				;switch from horny to normal Nipple Bells
				If DepFunction
					libs.removeDevice(Playerref, BellsHInventory, BellsHRender, libs.zad_DeviousPiercingsNipple, True, False, True)
					;Utility.Wait(0.1)
					libs.equipdevice(Playerref, BellsInventory, BellsRender, libs.zad_DeviousPiercingsNipple, False, True)
				Else
					If libs.UnlockDevice(Playerref, BellsHInventory, destroyDevice = True) &&  ! PlayerRef.IsEquipped(BellsHRender) ; <-- Changed
						;worked
					Else
						Utility.Wait(0.1) ; <-- Changed
						libs.UnlockDevice(Playerref, BellsHInventory, destroyDevice = True) ;one retry
						Debug.Trace("[Virgin] UnLockDevice Retry")
					EndIf
					;Utility.Wait(0.1)
					If libs.LockDevice(Playerref, BellsInventory)  &&  ! PlayerRef.IsEquipped(BellsRender) ; <-- Changed
						;worked
					Else
						Utility.Wait(0.1) ; <-- Changed
						libs.LockDevice(Playerref, BellsInventory)  ;one retry
						Debug.Trace("[Virgin] LockDevice Retry")
					EndIf
				EndIf
				Alternate = True
			EndIf
			Utility.Wait(Delay)
			If libs.isWearingDeviceType(PlayerRef, libs.zad_DeviousPiercingsNipple)
				;no fail
			Else
				Failed = True
			EndIf
			If DelayCount > 15
				DelayCount = 1
				Delay = Delay - 0.1
				If Delay < 0
					Debug.MessageBox("SUCCESS! No Race Condition Occurred after 15 tries even at 0 delay. (if DD5, one rety is in play)")
					Debug.Trace("[Virgin] SUCCESS! No Race Condition Occurred after 15 tries even at 0 delay. (if DD5, one rety is in play)")
					Return
				EndIf
			EndIF
			Try += 1
			DelayCount += 1
		EndWhile
		Debug.MessageBox("TESTING Depreciated Function: " + DepFunction + " / Total Tries to failure: " + Try + " / Delay reached: " + Delay)
		Debug.Trace("[Virgin] TESTING Depreciated Function: " + DepFunction + " / Total Tries to failure: " + Try + " / Delay reached: " + Delay)
		Test += 1
		If Test == 3
			DepFunction = False
		EndIf
	EndWhile
EndFunction

 

 

 

I don't know what it's doing, but somehow, when it reaches test 3 (that is when it starts using the new functions instead of the depreciated), it repeatedly gives a message box, and reports the -- [Zad] (((ERROR))): LookupDeviceType received invalid keyword None -- error in the log. I did not let it finish as its a message box for every try (or retry?).

 

Don't know what I can do more at this point, or what my path forward is.

 

20201110170101_1.jpg

Log32 - race condition, added IsEquipped.log

Link to comment
18 hours ago, VirginMarie said:

added IsEquipped to the test function. Log produced attached. Code follows.

It's not nice, but short term, you could try waiting a bit longer to catch cases with heavy script load:
 

Bool isEquipped = true
If (libs.UnlockDevice(Playerref, BellsInventory, destroyDevice = True))
    isEquipped = PlayerRef.IsEquipped(BellsRender)
    Int waitCount = 0
    While (isEquipped && waitCount < 20) ; or whatever number you are comfortable with instead of 20
        Utility.Wait(0.1)
        isEquipped = PlayerRef.IsEquipped(BellsRender)
        waitCount = waitCount + 1
    EndWhile
EndIf
If (isEquipped)
    ; failed to unequip (either UnlockDevice returned false, or Player.IsEquipped was true after trying 20 times)
Else
    ; succeeded
EndIf

I have used similar ugly code in my FO4 mod when waiting for events doing something.

 

[Edit]

It's also very much not nice from an API POV that UnlockDevice returns true but calling LockDevice directly afterwards will then fail. This would catch me off-guard as a developer using the API. @Kimy would it be possible to detect the problem (old rendered device still equipped) in LockDevice and take corrective action there? Or is that not possible because the two cases (device not equipped but old rendered devices still equipped, device actually equipped) will look the same?

Link to comment

I will look into it. The problem is that waiting for completion in LockDevice() before returning will needlessly add delay for the 95% of all device operations that are NOT swaps and therefore have no reason to wait until OnEquipped() is finished. It will require a bit of thought.

 

Btw. your above code sample is pretty much how I planned the safe-swap function anyway. :)

Link to comment
42 minutes ago, Kharos said:

It's also very much not nice from an API POV that UnlockDevice returns true but calling LockDevice directly afterwards will then fail. This would catch me off-guard as a developer using the API.

Yes I know I can do that, but I've already tried a single retry (updated version of the test above), which has DD spitting out that error message box, so I'm going to wait to see if Kimy has a suggestion or solution.

Quote

@Kimy would it be possible to detect the problem (old rendered device still equipped) in LockDevice and take corrective action there? Or is that not possible because the two cases (device not equipped but old rendered devices still equipped, device actually equipped) will look the same?

The problem is broader I believe. The root problem is a race condition and could cause other symptoms beyond swap. Maybe its why DD has that long standing issue of broken devices. Solution I suspect is Mutex.  "old rendered device still equipped" is the symptom of the problem, and likely so is the ages old broken device problem.

 

Swap is more than 50% of cases in my mod, hard to believe it could be only 5% in something like DCL.

Link to comment

It's way less than 5% in DCL. The only feature that does it lots is the prison, and since it's a well known fact that chaining unlock/lock calls for the same device slot is problematic, I designed my features to do it safely. Usually I perform the unequip in one dialogue topic and the equip in the next, which more or less guarantees the old device to be removed before the new one gets put on. The few cases I cannot handle this way, I have always used generous Utility.Wait() delays for. It worked quite ok for the past years, really. *shrug*

 

The normal usecase for DD is locking on a device for the duration of a quest or for the player to escape it on their own, which covers almost everything of what DCL does. Your mod is just full of edge-cases, haha! ?

Link to comment
51 minutes ago, Kimy said:

I will look into it. The problem is that waiting for completion in LockDevice() before returning will needlessly add delay for the 95% of all device operations that are NOT swaps and therefore have no reason to wait until OnEquipped() is finished. It will require a bit of thought.

 

Btw. your above code sample is pretty much how I planned the safe-swap function anyway. :)

Yeah, I fully agree about waiting in UnlockDevice()/LockDevice(), it will make code that (un)locks multiple devices really slow which is definitively not something that we want. That's why I asked if it is possible to instead recognize the 1% case in the LockDevice that follows the UnlockDevice and do some additional corrective action (e.g. unequip the rendered device that conflicts with the new one that we want to equip).

 

[Edit] Fixed confusing use of unlock/lock, sorry!

Link to comment
34 minutes ago, Kimy said:

The normal usecase for DD is locking on a device for the duration of a quest or for the player to escape it on their own, which covers almost everything of what DCL does. Your mod is just full of edge-cases, haha! ?

K well, I can tell you that with DD4 (all DD4 versions), swapping was 100% stable in SLaV. It's because I used the mutex everywhere. And if I ran only my mod for DD content, the ages old broken device syndrome was non existent. 

 

Edge case? I think you underestimate how much these "edge cases" in SLAV, are actually normal, well beyond just SLaV. Testing is all about edge cases, its what makes a quality software product. If you don't test for and fix your edge cases, it's called alpha software.

 

I'm doing a full playthrough to test new non-DD related things, but with DD5 still installed, and DCL is there to create more "edge cases" (from SLaV's perspective, yes, what DCL can impact are MY edge cases). And I can tell you I've seen a certain behavior which we no longer talk about, that you've deemed just an unimportant "edge case", recreate from a common DCL scene, 2 out of 5 times now. 

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • 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