the Sim Settlements forums!

Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

Settlers randomly losing assignments

yaugie

Well-Known Member
Messages
3,648
Since the last major update (2.0.3), every time I travel back to a settlement I've built up, one settler at random will have 'lost' one of their assignments - either Home, Job, or Hobby. This is Great(tm) since it means something will now be short.

Has anyone else seen this? I have no idea what could've caused this to start happening, it was fine before. I have all auto-assign functionality turned off.

EDIT TO SAVE YOU READING THE ENTIRE THREAD:
At this point I am 99% sure it is nothing to do with the Disease system, and believe it to be an issue of Settlers temporarily losing even which settlement they belong to and losing an assignment in the process. Testing continues.
 
Last edited:
Well, per the stack trace, Bool Function AssignActor(Actor newActor = None) is being called at line 3291 line in WorkshopParentScript.AssignObjectToWorkshop(...). This eventually leads to a call to Bool Function TryToAssignActor(Actor newActor = None, Bool abManualAssignment = false, Int aiTargetSlot = 0). The section can be seen here:


It is being called because actorIndex == -1 as the actor owner (it's Franklin from SS2) is not being returned from WorkshopParent.GetWorkshopActors(...) for whatever reason, so it can't be found in the array WorkshopActors. There should be 10 actors at this workshop (Abernathy) and it is only returning 9.

So the question now becomes, why are not all actors being returned? Is there some other code running in parallel that is removing them temporarily? Is it script lag? Since WorkshopParent.GetWorkshopActors(...) calls ObjectReference.GetWorkshopResourceObjects(...), is it an engine level bug?
that would be the guess is that the base game coding on this is not written well enough for all the stuff ss2 is doing on top of it. but the leg work you just did should probably help somebody figure out a fix for it hopefully.
 
yaugie mentioned that they've noticed this issue since SS2 v2.0.3. That corresponds to WSFW v2.0.21, which has a "hack" for solving what seems to be the same issue added here. Maybe it's the exact same issue or maybe the fix is causing this now as a side effect?
 
yaugie mentioned that they've noticed this issue since SS2 v2.0.3. That corresponds to WSFW v2.0.21, which has a "hack" for solving what seems to be the same issue added here. Maybe it's the exact same issue or maybe the fix is causing this now as a side effect?
I made a small edit to WorkshopParentScript as a test to see if this might be the case and it doesn't seem to be. I also then used ObjectReference.GetActorsLinkedToMe(...) as a hack in said script to try again after it fails to find an actor with ObjectReference.GetWorkshopResourceObjects(...) and it seems that it solved the issue for me. I've no idea if it'll have any side effects though, but sometimes the "Todd method" is just what you need. :todd:
 
If ObjectReference.GetWorkshopResourceObjects fails to properly return all workshop actors in a given settlement, it could indicate a bigger issue. That function is frequently used in vanilla, WSFW and SS2 scripts to get workshop actors.
Code:
; WSFW - 2.0.21 - Discovered that actors can end up in weird state where they have a sort of broken link via WorkshopItemKeyword. When this occurs, GetLinkedRef(WorkshopItemKeyword) will still show the actor as connected to the workshop, but calling RecalculateResources can count them as not being part of the settlement and they end up reducing population count and unassigning from some objects. Linking them to something else temporarily and then back seems to resolve it.

ObjectReference[] WorkshopActors = WorkshopParent.GetWorkshopActors(Self)

int i = 0
while(i < WorkshopActors.Length)
    WorkshopActors[i].SetLinkedRef(PlayerRef, WorkshopItemKeyword)
    Utility.WaitMenuMode(0.01)
    WorkshopActors[i].SetLinkedRef(Self, WorkshopItemKeyword)
   
    i += 1
endWhile
; End WSFW 2.0.21 hack
I can't believe KG would write such an inefficient function... all those Wait calls. This would be much faster.
Code:
ObjectReference[] WorkshopActors = GetWorkshopResourceObjects(Population)    ; why call WSParent? WS rating - population AV defined in this script (Self is ObjectReference)
; Actor[] WorkshopActors = GetActorsLinkedToMe( apLinkKeyword = WorkshopItemKeyword, apExcludeKeyword = None )    ; Cyberslas, are you saying to use this call instead of GetWorkshopResourceObjects?

; break into 2 loops so we don't need the Wait call
int i = WorkshopActors.Length
while ( i > 0 )
    i -= 1
    WorkshopActors[i].SetLinkedRef(PlayerRef, WorkshopItemKeyword)    ; is PlayerRef even needed? none would work too
endWhile

; Utility.WaitMenuMode(0.01)    ; I doubt this call is even required...

i = WorkshopActors.Length
while ( i > 0 )
    i -= 1
    WorkshopActors[i].SetLinkedRef(Self, WorkshopItemKeyword)    ; re-link ref to workshop
endWhile
I think the reasoning here is if the link is broken, a "GetLinkedRef" type of all will fail to grab the ref. To me, if GetWorkshopResourceObjects is failing to return all actors, there may be something upstream failing to properly set the actor as a workshop resource object with the workshop population AV. I have read through the assignment code several times, but I might as well be reading ancient hieroglyphs... There are a lot of "moving parts" so to speak. I think a race condition can be ruled out as (it has been reported several times) the same actor(s) are repeatedly unassigned.
/WallOfText
 
Last edited:
If ObjectReference.GetWorkshopResourceObjects fails to properly return all workshop actors in a given settlement, it could indicate a bigger issue. That function is frequently used in vanilla, WSFW and SS2 scripts to get workshop actors.
Code:
; WSFW - 2.0.21 - Discovered that actors can end up in weird state where they have a sort of broken link via WorkshopItemKeyword. When this occurs, GetLinkedRef(WorkshopItemKeyword) will still show the actor as connected to the workshop, but calling RecalculateResources can count them as not being part of the settlement and they end up reducing population count and unassigning from some objects. Linking them to something else temporarily and then back seems to resolve it.

ObjectReference[] WorkshopActors = WorkshopParent.GetWorkshopActors(Self)

int i = 0
while(i < WorkshopActors.Length)
    WorkshopActors[i].SetLinkedRef(PlayerRef, WorkshopItemKeyword)
    Utility.WaitMenuMode(0.01)
    WorkshopActors[i].SetLinkedRef(Self, WorkshopItemKeyword)
  
    i += 1
endWhile
; End WSFW 2.0.21 hack
I can't believe KG would write such an inefficient function... all those Wait calls. This would be much faster.
Code:
ObjectReference[] WorkshopActors = GetWorkshopResourceObjects(Population)    ; why call WSParent? WS rating - population AV defined in this script (Self is ObjectReference)
; Actor[] WorkshopActors = GetActorsLinkedToMe( apLinkKeyword = WorkshopItemKeyword, apExcludeKeyword = None )    ; Cyberslas, are you saying to use this call instead of GetWorkshopResourceObjects?

; break into 2 loops so we don't need the Wait call
int i = WorkshopActors.Length
while ( i > 0 )
    i -= 1
    WorkshopActors[i].SetLinkedRef(PlayerRef, WorkshopItemKeyword)    ; is PlayerRef even needed? none would work too
endWhile

; Utility.WaitMenuMode(0.01)    ; I doubt this call is even required...

i = WorkshopActors.Length
while ( i > 0 )
    i -= 1
    WorkshopActors[i].SetLinkedRef(Self, WorkshopItemKeyword)    ; re-link ref to workshop
endWhile
I think the reasoning here is if the link is broken, a "GetLinkedRef" type of all will fail to grab the ref. To me, if GetWorkshopResourceObjects is failing to return all actors, there may be something upstream failing to properly set the actor as a workshop resource object with the workshop population AV. I have read through the assignment code several times, but I might as well be reading ancient hieroglyphs... There are a lot of "moving parts" so to speak. I think a race condition can be ruled out as (it has been reported several times) the same actor(s) are repeatedly unassigned.
/WallOfText
so now the question is will this ever make it to a patch, kingGath been super busy with hg2.0 like version 15 now. but I'd love to see this addressed as basically everybody playing now has this issue. also, once again I realize that coding is a foreign language to me as this all looks like gibberish.
 
Code:
; WSFW - 2.0.21 - Discovered that actors can end up in weird state where they have a sort of broken link via WorkshopItemKeyword. When this occurs, GetLinkedRef(WorkshopItemKeyword) will still show the actor as connected to the workshop, but calling RecalculateResources can count them as not being part of the settlement and they end up reducing population count and unassigning from some objects. Linking them to something else temporarily and then back seems to resolve it.

ObjectReference[] WorkshopActors = WorkshopParent.GetWorkshopActors(Self)

int i = 0
while(i < WorkshopActors.Length)
    WorkshopActors[i].SetLinkedRef(PlayerRef, WorkshopItemKeyword)
    Utility.WaitMenuMode(0.01)
    WorkshopActors[i].SetLinkedRef(Self, WorkshopItemKeyword)
 
    i += 1
endWhile
; End WSFW 2.0.21 hack
So, as far as I know, GetWorkshopResourceObjects only returns references linked to the workshop via SetLinkedRef. So if GetWorkshopResourceObjects is called during the brief moment a reference is assigned to the player instead of the workshop, it would fail to return that reference. I have not actually tested this, it's just an assumption. Since yaugie stated their problems started (or were noticed) around the time that code was added, it seemed to be a likely culprit. I "tested" (heavy emphasis on quotes) it by seeing if GetActorsLinkedToMe(WorkshopItemKeyword) returned the same as GetWorkshopActors at this line, which is where I noticed that GetWorkshopResourceObjects was returning a list missing settlers and subsequently unassigning them. It didn't, so I assumed something else goofy was going on and the above code was not interfering with GetWorkshopResourceObjects. This is not a definite conclusion, just my inner Todd flaring up. It is still possible the above code is actually interfering. It is also likely that the problem the above code attempted to solve is exactly the same as the issue seen with GetWorkshopResourceObjects.

I can't believe KG would write such an inefficient function... all those Wait calls. This would be much faster.
Code:
ObjectReference[] WorkshopActors = GetWorkshopResourceObjects(Population)    ; why call WSParent? WS rating - population AV defined in this script (Self is ObjectReference)
; Actor[] WorkshopActors = GetActorsLinkedToMe( apLinkKeyword = WorkshopItemKeyword, apExcludeKeyword = None )    ; Cyberslas, are you saying to use this call instead of GetWorkshopResourceObjects?

; break into 2 loops so we don't need the Wait call
int i = WorkshopActors.Length
while ( i > 0 )
    i -= 1
    WorkshopActors[i].SetLinkedRef(PlayerRef, WorkshopItemKeyword)    ; is PlayerRef even needed? none would work too
endWhile

; Utility.WaitMenuMode(0.01)    ; I doubt this call is even required...

i = WorkshopActors.Length
while ( i > 0 )
    i -= 1
    WorkshopActors[i].SetLinkedRef(Self, WorkshopItemKeyword)    ; re-link ref to workshop
endWhile
The code certainly does look very inefficient, and your version does look much better. Barring some arcane papyrus scripting reasons, I don't see why your version shouldn't be used.

Code:
; Actor[] WorkshopActors = GetActorsLinkedToMe( apLinkKeyword = WorkshopItemKeyword, apExcludeKeyword = None )    ; Cyberslas, are you saying to use this
No, I put a "failsafe" call like that in WorkshopParentScript like so:

Code:
ObjectReference[] WorkshopActors = GetWorkshopActors(workshopRef)
int actorIndex = WorkshopActors.Find(owner)
                   
if (actorIndex == -1)
    Actor[] LinkedWorkshopActors = workshopRef.GetActorsLinkedToMe(WorkshopItemKeyword)
    actorIndex = LinkedWorkshopActors.Find(owner)
endif
This is at the area I linked in WorkshopParentScript above. I'd call it a hack. It works (so far), but I've no idea if this could introduce any side effects, such as settlers who actually shouldn't be assigned staying assigned. I was just annoyed that my settlers kept getting unassigned lol.
 
I can't believe KG would write such an inefficient function... all those Wait calls.
A tiny Wait call like that certainly feels to me like it'd only have been put there for a damn good reason, but I don't know enough about the technicalities of this engine to even make a reasonable guess at why - making certain any 'flow-on' code has time to run, perhaps? I wouldn't think that would be necessary, but with this frankenstein of an engine...
I'm certainly no master level coder though, I have just picked up bits and pieces over the years
 
A tiny Wait call like that certainly feels to me like it'd only have been put there for a damn good reason, but I don't know enough about the technicalities of this engine to even make a reasonable guess at why - making certain any 'flow-on' code has time to run, perhaps? I wouldn't think that would be necessary, but with this frankenstein of an engine...
I'm certainly no master level coder though, I have just picked up bits and pieces over the years
The purpose of the wait in the original code is a "power of Todd Prevention kit." Its making sure the engine registers the first SetLinkedRef call before calling it again. If a settlement has 1-2 settlers, its no big deal. If the settlement has 20-30+ settlers, this has the potential to take a long time depending on how loaded the papyrus VM is. There is 1.2ms (value can be changed in the ini) available per slice for the VM to do work. Any work not completed is pushed to the next frame. There is FPS/60 slices per min. Usually, each native function call (calls to the game engine from the VM) takes 1 frame to return. (there are caveats to this and not well documented)

tl;dr by effectively breaking this into 2 loops, the 10ms wait call is no longer required as that amount of time should pass before the second loop starts.
 
So, as far as I know, GetWorkshopResourceObjects only returns references linked to the workshop via SetLinkedRef.
We'll never know without reverse engineering the game code. (way beyond me...)
Code:
; Get all of the resource-producing objects owned by this Workbench. If akAV is provided,
; only collect objects producing the corresponding resource. If aiOption is provided, use 0 for all nonzero resources, 1 for only damaged resources, 2 for only undamaged resources.
ObjectReference[] Function GetWorkshopResourceObjects(ActorValue akAV = None, int aiOption = 0) native
I assume its a combo of WorkshopRef.GetLinkedRefChildren(WorkshopItemKeyword) and ObjectReference.GetValue(FilterAV) > 0
This is not a definite conclusion, just my inner Todd flaring up.
I hear this!
Todds Mailbox.jpg
I'll do some digging and see if I can shake something loose.
I don't see why your version shouldn't be used.
We will see what the big man has to say. ;)
 
The Todd hate mail is real. I will say though he has produced some of my favorite games so I don’t hate to hard just take it with a pinch of Todd.
 
Yeah, true. I have more hours in Beth games than (very likely) all other games I have ever played combined.

Is it strange that I get enjoyment from exchanging other 4 letter words with "Todd?" ;) (like a refreshing feeling...)
 
Personally, I think my solution - I mentioned ago will be the best option.
Make settlers purely cosmetic. Have plots function independently - including stores where the Cash Register or Terminal is used to activate the shop console.

That way doesn't matter if a settler is "assigned" or not.
Instead what SS2 should do is just keep track of total number of settlers and allow player to "Activate" plots based on that number. To move people around simply deactivate the plot to "Readd" to the pool.

Then SS2 just "Cosmetically" assigns the settlers and automatically applies the "best" in class based on settler stats to that plot.
 
Personally, I think my solution - I mentioned ago will be the best option.
Make settlers purely cosmetic. Have plots function independently - including stores where the Cash Register or Terminal is used to activate the shop console.

That way doesn't matter if a settler is "assigned" or not.
Instead what SS2 should do is just keep track of total number of settlers and allow player to "Activate" plots based on that number. To move people around simply deactivate the plot to "Readd" to the pool.

Then SS2 just "Cosmetically" assigns the settlers and automatically applies the "best" in class based on settler stats to that plot.
I had a similar line of thought but for the power system, basically none of the plots need power the power plot does nothing and it all works. Eliminate the problems form over function
 
These suggestions are bordering on "make all settlements work like HQ Departments"... I dont even want to imagine how monumental a task thatd be.
 
Last edited:
I had a similar line of thought but for the power system, basically none of the plots need power the power plot does nothing and it all works. Eliminate the problems form over function
Turning on self-powered objects via MCM or the holotape accomplishes something similar to this.
These suggestions are bordering on "make all settlements work like HQ Departments"... I dont even want to imagine how monumental a task thatd be.
That sounds correct - we are basically talking about a complete rewrite of the entire workshop system in FO4. That said, there are tools in the next patch that help with the management of this stuff a lot.
 
My junky code had been necessary in the saves I had tested to fix the issue - the link to the player and the utility.wait seemed to be required to fix it.

I do believe you guys are correct though that this is the cause for the random unassignments, so it might just be we're stuck with the other bug I tried to fix as it's the lesser of evils. I'll try msalaba's fix for next patch - see where we land!
 
My junky code had been necessary in the saves I had tested to fix the issue - the link to the player and the utility.wait seemed to be required to fix it.

I do believe you guys are correct though that this is the cause for the random unassignments, so it might just be we're stuck with the other bug I tried to fix as it's the lesser of evils. I'll try msalaba's fix for next patch - see where we land!
Hope it works they seemed to figure a few things out that looked promising so good luck
 
Did the bug hit the HQ? Came back to the HQ and although there is more than enough power in the logistic department, the bar in the top is red. I open the management window and Lupe name is with the rest. After closing the window. I have a msg telling me some no name is now head of department. Was able to put Lupe back into her post. Same thing happened to Jake, but since they had no projects running, I didn't notice right away.
 
Last edited:
Did the bug hit the HQ? Came back to the HQ and although there is more than enough power in the logistic department, the bar in the top is red. I open the management window and Lupe name is with the rest. After closing the window. I have a msg telling me some no name is now head of department. Was able to put Lupe back into her post. Same thing happened to Jake, but since they had no projects running, I didn't notice right away.
I believe it can affect HQ - it is technically a settlement underneath everything, after all. I haven't seen it happen since "the big HQ2.0 Patch" myself, although the total Population count there seems to jump up and down quite regularly now...
 
Top