Jump to content
Sclerocephalus

FO4: Unlimited Settlements/Settlers - A Caveat for Mod Users

Recommended Posts

This stops a reset from getting queued up if one recently completed, but what about if one just started? Until it finishes, you could still wind up queuing up multiple resets, and the longer it takes the first reset to finish, the more resets you could queue up, each taking just as long...

 

True.

 

Therefore, UFO4P 1.03beta is using the following version:

Event Actor.OnLocationChange(Actor akSender, Location akOldLoc, Location akNewLoc)
	;;debug.trace(self + " OnLocationChange: akNewLoc=" + akNewLoc)

	;EDITS - GENERAL NOTES:
	;----------------------
	;While monitoring this script's activities in debug mode, it was found that this event fires when the player activates the workbench (this is handled as a
	;location exit event below) and again when he leaves the menu/workshop mode (handled as a location enter event below). As a result, the reset procedure could
	;be repeated over and over again. This is not a problem at game start when settlements are small and the reset takes less than a minute. Though later on, when
	;the settlements grow, the function draws much performance and may take several minutes to complete. Sine other functions are locked out from editing work-
	;shop-related data while it's rtunning, this could lead to assignment of settlers to newly built objects taking an exceedingly long time or not working at
	;all. To prevent unnecessary resets from being carried out, this event has been modified as follows:
	;
	;(1) When the player apparently leaves from a settlement location, this location is stored in the new tracking variable UFO4P_PreviousWorkshopLocation
	;(2) When the reset function starts running, the game time is stored in the new variable UFO4P_GameTimeOfLastResetStarted
	;
	;When the player apparently enters a settlement location, the event now checks whether this is the same as the one he just left (by comparing akNewLoc
	;to UFO4P_PreviousWorkshopLocation). If this is true AND the last reset started running less than a game day before, it will not be run again. Note that
	;short leaves from the location (with 'valid' change location events), e.g. to check the environment after the turrets started shooting at something, will
	;also not result in repeated resets carried out. Travels to other locations and back only do when the workshop location unloads in the meantime or when they
	;take more than one game day.

	If UFO4P_PreviousWorkshopLocation
		;wsTrace (" OnLocationChange: akOldLoc = " + akOldLoc)
		;wsTrace (" OnLocationChange: akNewLoc = " + akNewLoc)
		;wsTrace (" OnLocationChange: UFO4P_PreviousWorkshopLocation = " + UFO4P_PreviousWorkshopLocation + "; IsLoaded = " + UFO4P_PreviousWorkshopLocation.IsLoaded())
		If UFO4P_PreviousWorkshopLocation.IsLoaded() == False
			UFO4P_PreviousWorkshopLocation = None
			;wsTrace (" OnLocationChange: UFO4P_PreviousWorkshopLocation reset to none.")
		EndIf
	EndIf

	if akNewLoc && akNewLoc.HasKeyword(LocTypeWorkshopSettlement)
		;wsTrace(" OnLocationChange: entered workshop settlement location " + akNewLoc)
		; when player enters a workshop location, recalc the workbench ratings
		; get the workbench
		WorkshopScript workshopRef = GetWorkshopFromLocation(akNewLoc)
		if !workshopRef
			wsTrace(" ERROR - OnLocationChange: failed to find workshop matching " + akNewLoc + " which has the LocTypeWorkshopSettlement keyword", 2)
			return
		else
			;EDIT: Check whether the player really entered a new location.
			;If NOT, skip the reset if the last reset of this location ran less than a game day ago.
			If UFO4P_PreviousWorkshopLocation && UFO4P_PreviousWorkshopLocation.IsSameLocation (akNewLoc)
				;wsTrace(" OnLocationChange: New workshop location is the same as the last workshop location left by the player.")
				Float UFO4P_GameTimeSinceLastReset = Utility.GetCurrentGameTime() - UFO4P_GameTimeOfLastResetStarted
				;wsTrace(" OnLocationChange: Game time since last reset = " + UFO4P_GameTimeSinceLastReset)
				If UFO4P_GameTimeSinceLastReset < 1
					;wsTrace(" OnLocationChange: Skipping reset.")
					Return
				EndIf
			EndIf

			;EDIT: Remember the current game time as the time when the last reset started running:
			UFO4P_GameTimeOfLastResetStarted = Utility.GetCurrentGameTime()

			ResetWorkshop(workshopRef)
			; send change location script event
			; OBSOLETE - moved to always use MinRadiantStart
;			WorkshopEventChangeLocation.SendStoryEvent(akNewLoc, workshopRef)

		EndIf
	EndIf


	if akOldLoc && akOldLoc.HasKeyword(LocTypeWorkshopSettlement)		
		;wsTrace(" OnLocationChange: exited workshop location " + akOldLoc)
		; when player leaves a workshop location, recalc the workbench ratings
		; get the workbench
		WorkshopScript workshopRef = GetWorkshopFromLocation(akOldLoc)
		if !workshopRef
			wsTrace(" ERROR - OnLocationChange: failed to find workshop matching " + akOldLoc + " which has the LocTypeWorkshopSettlement keyword", 2)
			return
		else
			;EDIT: Remember this location as the last workshop visited by the player
			UFO4P_PreviousWorkshopLocation = akOldLoc
			; reset days since last visit for this workshop
			workshopRef.DaysSinceLastVisit = 0
		endif
	endif

EndEvent

There are a few other modifications as well. The 'comments' section has all the details.

Share this post


Link to post
Share on other sites

We only perform pre-release beta testing on PC since there's access to debug functions that can't be used on consoles. I'm afraid you're going to have to wait unless you've got the game on PS4. After PS4 is out of beta itself though we won't be doing that at all.

Share this post


Link to post
Share on other sites

A simplified explanation of this issue which I recently posted on the Nexus. I'm posting this here again so it won't get lost. Also, people are still asking for a fix over and over again, so I have a place now to direct them to. Understanding why some things can't be fixed (i.e. why this will remain an issue with large settlements after uncapping all vanilla limits) requires a basic understanding of the porblem.

 

 

Trying to explain it in a less technical manner:

This is the reference to the fix in the changelog: of UFO4P 1.0.3:

"WorkshopParentScript: Event Actor.OnLocationChange was spawning too many reset events when it shouldn't have, causing severe degradation in performance for the entire settlement reset system. (Bug #20576)"

The "workshop reset" is a sequence of lengthy tasks that start running on WorkshopParentScript every time the player arrives at a settlement location. While it is running, all other tasks get locked out.

Think of the whole workshop system as a very large number of largely independent scripts that do all handle the same data (they may be doing very different things with those data though). When two or more scripts could access the same data at the same time, you would inevitably make a big mess: simply speaking, one script would modify data right underneath the arse of another script. This is what we call a threading issue and those issues have to be avoided at all cost. Now, this is what a script lock is for: when certain tasks are running, access of all other scripts to the respective data is blocked. That's the basic idea.

It's worth noting here that not only the workshop reset will lock the script. Pretty much every activity on WorkshopParentScript locks the script, but the vast majority of those tasks will complete within less than a second and we don't have to worry about them. The workshop reset is differrent because it takes a pretty long time to complete. With maximum settlement sizes in a vanilla game, it may easily take 2-3 minutes,

During this time, all other tasks have to wait. Since WorkshopParentScript acts as a hub (the individual scripts don't communicate directly with each other if they need to, but call WorkshopParentScript instead which may then call other scripts; it may not be obvious, but this makes sense), this includes the majority of workshop-related tasks (note that the list is not exhaustive):
- assigning and unassigning of settlers: when you do, the script that runs on the actor calls WorkshopParentScript to register the respective object as owned by the actor and to update the stats (counts of employed and unemployed settlers and production or defense ratings, whichever applies)
- building a new resource object (any workshop object with a script, e.g. turrets, crops, shops, etc.): the script that runs on the object calls WorkshopParentScript to register the object as owned by the workshop (objects that are not assigned to any workshop cannot be assigned to actors) and to update the stats
- creation of new settlers (i.e. recruitment): this is done by a function on WorkshopScript (i.e. the script that runs on the workbench)
- starting workshop attacks : the chances are evaluated by WorkshopScript which then calls WorkshopParentScript to actually trigger an attack
- regular happiness updates: done by WorkshopScript, which then calls WorkshopPArentScript at some point

If you wait (REALLY wait, i.e. do ABSOLUTELY NOTHING) for a short period of time, you may circumvent the problem, because the reset will have finished running before you enter workshop mode and start building things or assigning actors (this led to people thinking that there may be a script lag -but there isn't: all scripts are active at that time and very busy). HOWEVER, this does not work reliably (see below).

You may also wonder why the workshop reset can be responsible for the glitches at all if it takes only 2-3 minutes to complete but all workshop functions may be locked occasionally for a much longer time. This is because there is a major flaw in the vanilla script, (and this is also why waiting does not always work).

As mentioned earlier, the reset starts running when the player arrives at a settlement location. But how does the script know this ?

This is done by the infamous "Actor.OnLocationChange event": simply speaking, the game sends a notification to the script whenever the player has changed locations. Unfortunately though, when the player enters menu mode, this counts as a location change and a notification is sent to WorkshopParentScript.

Imagine that you arrive at a settlement location. This is a "regular" location change and a reset starts running. Then you walk to the workbench and activate it (you don't have to enter workshop mode; just looking at the inventory is sufficient; also note that it doesn't have to be THE workbench, activating an armor or weapons workbench has the same effect) and WorkshopParentScript thinks you have left the settlement. When you leave menu mode, it thinks that you have arrived at the settlement and starts a new reset. Most likely, the first reset will not have finished running at this point, but you have another one already waiting in a queue now. Then you enter workshop mode to assign some actors, onlyto see that it doesn't work. You leave workshop mode (another reset added to the queue ... ), but then you have the idea to try it again (maybe it works this time ?) .... well, you probably got the point.

Now, you have a real problem because you have blocked any activity for 10-15 minutes real time. And it gets worse because of the way a script lock works: this is not a queue where the first call getting blocked will be the first call resumed when the lock is released. Instead, whichever call is the first to find the lock released (they check this in a loop) will run and block all others. For example, if you have planted a crop and then assigned an actor to work on it, the actor assignment task may run first - and both will fail because you cannot assign an actor to a crop that has not previously been assigned to a workshop.

It is this bug that has been fixed: we simply filter out any invalid location changes and keep the number of resets running while you' stay at a settlement as low as possible: there is now only one reset per game day - and this reliably eliminates all problems in normal gameplay.

There's one exception though: with unlimited numbers of settlers and objects, the time for a reset to run may be significantly longer than 2-3 minutes. There's nothing we can do here, because the game is unable to handle an overly large number of objects in a sufficiently short period of time. Waiting is the only thing you can do here.

Share this post


Link to post
Share on other sites

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

Support us on Patreon!

×
×
  • Create New...