Jump to content


  • Content count

  • Joined

  • Last visited

About saebel

  • Rank
    Cave Bear

Profile Information

  • Gender
    Not Telling
  1. There is a lot of code to sort through. But essentially it comes down to the following: There is code in OBSE that allows you to erase elements. But it requires knowing the array ID number. UInt32 ArrayVarMap::EraseElements(ArrayID id, const ArrayKey& lo, const ArrayKey& hi) { ArrayVar* var = Get(id); if (!var || lo.KeyType() != hi.KeyType() || lo.KeyType() != var->KeyType()) return -1; // find first elem to erase std::map<ArrayKey, ArrayElement>::iterator iter = var->m_elements.begin(); while (iter != var->m_elements.end() && iter->first < lo) ++iter; UInt32 numErased = 0; // erase. if element is an arrayID, clean up that array while (iter != var->m_elements.end() && iter->first <= hi) { iter->second.Unset(); iter = var->m_elements.erase(iter); numErased++; } // if array is packed we must shift elements down if (var->IsPacked()) var->Pack(); return numErased; } There is code that allows you to get the array ID number, but it requires getting the array as a passed argument via a script. // From Comands_Array.cpp static bool Cmd_ar_Erase_Execute(COMMAND_ARGS) { // returns num elems erased or -1 on error UInt32 numErased = -1; ExpressionEvaluator eval(PASS_COMMAND_ARGS); if (eval.ExtractArgs() && eval.Arg(0)) { if (eval.Arg(0)->CanConvertTo(kTokenType_Array)) { ArrayID arrID = eval.Arg(0)->GetArray(); if (eval.Arg(1)) { // are we erasing a range or a single element? const Slice* slice = eval.Arg(1)->GetSlice(); if (slice) { ArrayKey lo, hi; slice->GetArrayBounds(lo, hi); numErased = g_ArrayMap.EraseElements(arrID, lo, hi); } else { if (eval.Arg(1)->CanConvertTo(kTokenType_String)) { ArrayKey toErase(eval.Arg(1)->GetString()); numErased = g_ArrayMap.EraseElements(arrID, toErase, toErase); } else if (eval.Arg(1)->CanConvertTo(kTokenType_Number)) { ArrayKey toErase(eval.Arg(1)->GetNumber()); numErased = g_ArrayMap.EraseElements(arrID, toErase, toErase); } } } else { // second arg omitted - erase all elements of array numErased = g_ArrayMap.EraseAllElements(arrID); } } } if (numErased == -1) { *result = -1.0; } else { *result = numErased; } return true; } The "GetArray()" function that passes the Array ID is buried in the Script Token section of OBSE. For example: // From ScriptTokens.cpp #if OBLIVION ArrayID ScriptToken::GetArray() const { if (type == kTokenType_Array) return value.arrID; else if (type == kTokenType_ArrayVar) return value.var->data; else return 0; } // snip ArrayID ArrayElementToken::GetArray() const { ArrayID out = 0; g_ArrayMap.GetElementArray(GetOwningArrayID(), key, &out); return out; } Within the OBSE plugin API framework, I haven't found any internal function for getting the array ID outside of the scripted method of passing an argument. However, I *can* get the array ID via a script, using the following code: scn sdrGetArrayID array_var aArray string_var sArrayID short iArrayID Begin Function {aArray} ; calculates and saves the Array ID Let sArrayID := sv_Construct "%g" aArray Let iArrayID := ToNumber sArrayID sv_Destruct sArrayID SetFunctionValue iArrayID End What my mod does is: - On game load, either retrieve or create the master arrays that store all the data. - Using my scripted function, it then determines the array ID of the master IDs and stores them in custom game setting variables - Those game setting variables can then be accessed by the sdr.dll to grab the master arrays and process them. *However*, when the subarrays are created and stored inside each parent array, all of that is handled from within the sdr.dll functions. And because there are no script arguments involved in the lower tiers, I can't figure out a way to extract the arrID. I have tried code like the following: ArrayID GetArrayID(OBSEArray* arr) { ArrayID out = 0; g_ArrayMap.GetElementArray(GetOwningArrayID(), arr, &out); return out; } But none of my experiments have worked.
  2. I already have solved my problem using scripting and OBSE array functions. But I would prefer to move all of that into the .dll plugin to improve performance when it comes to loading/save times. With a small load order and a short adventure time, performance hit is negligible. I'm concerned about heavily modded games with many hours of play time. The arrays for those will be really large, and processing very time consuming (I imagine).
  3. In order to do what you suggested, I would have to do the following: Copy the OBSE array into a C++ array. Use native C++ to find and remove the key/value pair. Copy the native C++ array into a new OBSE array. Replace the old OBSE array with the new one. So here's the deal with how they set up the OBSE Array API. They provided an example of how to do step 3. But they did not provide an example of how to do step 1. Considering how many tiers and subarrays we are talking about, I can't believe that coming up with a set of functions to constantly transpose each of the OBSE arrays into C++ and back again would be of any performance benefit. I suppose it may be possible to keep all the arrays in C++ locally during the game session, and only do the conversion upon game load/save. But the OBSE API has everything else already covered in terms of storing integers, floats, and strings. I can already drill down to an OBSE key/value pair using their API. I just want to be able to remove it. I guess one other possible method would be to take the existing OBSE array that needs the key/value removed, and generate a duplicate of the array by rolling through and adding the key/values, then skip the key/value I want to remove. But again, that just seems like extra-processing intensive.
  4. Hoping someone with knowledge sees this. So I'm working on revamping my SDR.dll via OBSE's plugin system. I'm trying to improve the overall processing time, get rid of memory leaks, etc., to the best of my ability. Before I get into my problem, I should explain the structure. There are two "Master" arrays used. The first one is the Master COV Map, which uses nested maps to store custom object values. It's structure is as follows: map ["Parent Mod" (string), "Child Refs" (map["BaseRefID" (UInt32), "COV Groups" (map["GroupID" (UInt32), "COV Data Set" (map["Data Key" (string), "Data Value" (num, string, or ref)])])])] The second one is the ModGroup ID Array, which is a simple array to assign an ID (the key) to a Mod Name / Group Name pairing. The Group ID is then used for setting COV values (above) when functions are called to set/get/mod the COVs. It will look like this: [0] "SDR.esp|Actor Values" [1] "SDR.esp|Weapon Values" [2] "SomeOtherMod.esp|Name of the Group" I have already completed the core programming needed to set/get/mod values and to add/create maps on the fly as needed. However, I am concerned about bloat. I have created some clean up processes that removes data from invalid references and mods that are no longer in the active load order. However, they are all done through scripts through the SDR.esm. I would much rather port those features into the SDR.dll to boost performance. I need to be able to loop through all the Master data and remove any Group ID maps, reference maps, and mod maps that are no longer valid or active - removing entries as I go. The Group ID array does not need entries removed, because I don't want the key numbers to shift (that can impact data stored in the active save game). So if a group gets deleted, the entry will be set to "". In the SDR.dll, I'm using the OBSE Array Interface system (PluginAPI.h), which has all the functions I need - except one: there is no method to *remove* a key/value pair!!! If the arrays/maps were just a standard C++ implementation, it would be no problem. But the OBSE Array Interface has a very whacky construct. I've tried tapping into ArrayVar.h/.cpp files to see if I can bypass the OBSE Array Interface and use some of the functions deeper under the hood, but I haven't had any luck. C++ is not my strong suit, and I've come to a dead stop on how to handle this. Suggestions of any kind would be extremely welcome.
  5. [RELz] Oblivion XP

    SON OF A BISCUIT! I missed that one too. (sigh), New version is up. 8.233/8.234Released February 26, 2015 SDR.esm changesThe following issues were caused because I was a moron and didn't notice that I spelled "Oblivion XP" as "Oblivon XP" when trying to detect the mod. Corrected issue where SDR's Assasination skill up system was not passing on rewards to Oblivion XP if loaded. Corrected issue where SDR's alt sneak skill up system was not disabled when Oblivion XP was loaded.
  6. [RELz] Oblivion XP

    For anyone using SDR with Oblivion XP, you should update to version 8.233. I accidentally misspelled Oblivion XP (go figure), so SDR wasn't detecting that it was loaded and was not disabling the alt sneak skill up system. Sorry for the goof. I need to get more sleep. You can download SDR here: TES Nexus TES Alliance
  7. Poison Kill Script Discussion

    That assumes that it ain't broke. Maybe there's a tiny leak you have noticed yet.
  8. Poison Kill Script Discussion

    My script shouldn't have that problem since I always use PlayerRef. I'm also passing the PlayerRef to OB XPs sdrPoisonKill listener as one of the array items. Even though Player and PlayerRef are technically interchangeable, the cs wiki recommends to always use PlayerRef since it refers to the reference while Player refers to the base object. Here's the article: http://cs.elderscrolls.com/index.php?title=Player Note it details how the Player and PlayerRef have fixed form IDs, even though the PlayerRef can't be found in the Oblivion.esm. Implementing the change isn't too difficult. I opened all scripts and did a search and replace for the following: "player" with PlayerRef using Whole Word Only, case insensitive "Player." with "PlayerRef.", case insensitive "PlayerRefRef" with "PlayerRef", case insensitive I then did a compile all scripts to see if there were errors generated in the console window and cleaned up whatever was left.
  9. Poison Kill Script Discussion

    P.S. I am officially confirming that you are correct in how you set up the event listener. I agree that we're good to go. So I'm going to do a final check on everything, package it, and release it. Also, because of the changes to the sdr.dll, the version gamesetting change, as well as some fixes to the RGO patch, I'm going to release it as and recommend users do a clean install. This will be the first official non-beta release.
  10. Poison Kill Script Discussion

    I'm curious about all the 000000s followed by the version number. Is it being reset or do those indicate that you reloaded a saved game?
  11. Poison Kill Script Discussion

    Well it certainly sounds like it's on the right track. I'd like to see how the code is set up. I might do some experiments on my side. Technically any mod could receive the "dispatch" and do something with it. I have an extra "test" mod for proof of concept trials, collecting frame rates, stuff like that. I'll set that up with a listener and see what happens.
  12. Poison Kill Script Discussion

  13. Poison Kill Script Discussion

    Too late.
  14. Poison Kill Script Discussion

    Yes-ish. The game setting will be zero until the initialization process populates it with the version number. But as long as it is zero during the initialization process, none of the rest of my scripts will run, so you should be fine. I removed the iVcheck variable entirely and replaced it with the new iSDRsVersion game setting. So if you test for the variable and it exists, then the user has an old version of SDR. Switching over to a game setting is actually much better for my mod, OB XP aside, for similar reasons. Whenever folks upgraded from one version to the next, there would be a brief delay during the initialization phase when the scripts were running for a few seconds when they shouldn't have because it was looking at a quest variable. Using the gamesetting instead should stop that from happening. So solving our SDR-OBXP issue ends up helping SDR in general. When it comes to point where you decide whether or not you need to apply the poison fix script, I think all you need to check is: GetGS iSDRsApplyPoisonFix == 1 GetGS iSDRsVersion >= 8213 (I bumped it up one since it'll be different than the one you have) If both those are true, then you won't need to apply OB XPs poison script fix. It's important that you run it at the time the poison fix would be applied because the user could change the gamesettings via console commands in the middle of the game. Here's the new version: https://www.dropbox.com/s/ipwav63xvphp63w/SDR_Hotfix.zip?dl=0 Give it a whirl and let me know how it goes.
  15. Poison Kill Script Discussion

    The event sender isn't a script function. It's an event handler like any other event handler, but instead of calling one my functions, it just sends a message out into the ether for whatever is listening for it. In this case the listener that you set up with the code I provided. I think the best idea is to create the SDR version GameSetting. It will default to 0 on load. As long is it is at 0, my poison fix script won't run. When my initialization is done, it will set the game setting with the version number. On your end, you just check that game setting every time you need to apply the poison fix script. As long as the version is 0, you can safely apply it because my poison fix won't be applied. You can leave the listener set up as is, because as long as my poison script doesn't run, it also won't send out any messages. The moment my initializer finishes and sets the version number, your script will see it and apply it. The only real issue is going to be folks who are still using an older version of SDR. You could use the version checker to capture that (eventually) and pop up a warning and perhaps disable your poison fix script for the duration. Would something like that work for you?