/// This is used internally by the system to represent a graph update.
/// Generally you shouldn't need to care about it, unless you are implementing your own graph type.
/// </summary>
publicinterfaceIGraphUpdatePromise{
/// <summary>
/// Returns the progress of the update.
///
/// This should be a value between 0 and 1.
/// </summary>
floatProgress=>0.0f;
/// <summary>
/// Coroutine to prepare an update asynchronously.
///
/// If a JobHandle is returned, it will be awaited before the coroutine is ticked again, and before the <see cref="Apply"/> method is called.
///
/// After this coroutine has finished, the <see cref="Apply"/> method will be called.
///
/// Note: No changes must be made to the graph in this method. Those should only be done in the <see cref="Apply"/> method.
///
/// May return null if no async work is required.
/// </summary>
IEnumerator<JobHandle>Prepare()=>null;
/// <summary>
/// Applies the update in a single atomic update.
///
/// It is done as a single atomic update (from the main thread's perspective) to ensure
/// that even if one does an async scan or update, the graph will always be in a valid state.
/// This guarantees that things like GetNearest will still work during an async scan.
///
/// Warning: Must only be called after the <see cref="Prepare"/> method has finished.
/// </summary>
// TODO: Pass in a JobHandle and allow returning a JobHandle?
voidApply(IGraphUpdateContextcontext);
}
/// <summary>
/// Helper functions for graph updates.
///
/// A context is passed to graphs when they are updated, and to work items when they are executed.
/// The <see cref="IWorkItemContext"/> interface inherits from this interface.
/// </summary>
publicinterfaceIGraphUpdateContext{
/// <summary>
/// Mark a particular region of the world as having been changed.
///
/// This should be used whenever graphs are changed.
///
/// This is used to recalculate off-mesh links that touch these bounds, and it will also ensure <see cref="GraphModifier"/> events are callled.
///
/// The bounding box should cover the surface of all nodes that have been updated.
/// It is fine to use a larger bounding box than necessary (even an infinite one), though this may be slower, since more off-mesh links need to be recalculated.
/// You can even use an infinitely large bounding box if you don't want to bother calculating a more accurate one.
/// You can also call this multiple times to dirty multiple bounding boxes.
/// </summary>
voidDirtyBounds(Boundsbounds);
}
classGraphUpdateProcessor{
/// <summary>Holds graphs that can be updated</summary>
readonlyAstarPathastar;
/// <summary>Used for IsAnyGraphUpdateInProgress</summary>
boolanyGraphUpdateInProgress;
/// <summary>
/// Queue containing all waiting graph update queries. Add to this queue by using \link AddToQueue \endlink.
// If the job completed (maybe because a real job completed, or because the iterator returned a dummy JobHandle), then it must be doing some work on the main thread.
// In that case, we shouldn't sleep or yield while waiting.
anyMainThreadProgress=true;
it.Current.Complete();
}else{
if(firstNonFinished==-1)firstNonFinished=i;
continue;
}
}else{
it.Current.Complete();
}
MarkerCalculate.Begin();
try{
if(it.MoveNext()){
if(firstNonFinished==-1)firstNonFinished=i;
}elsepromises[i]=(promise,null);
}catch{
MarkerCalculate.End();
promises[i]=(null,null);
throw;
}
MarkerCalculate.End();
}
if(firstNonFinished==-1){
break;
}elseif(async){
if(timeSlice.expired){
returnfirstNonFinished;
}elseif(anyMainThreadProgress){
// Reset the idle time slice if we got something done on the main thread.
// This allows us to wait on more very short jobs.
idleTimeSlice=TimeSlice.MillisFromNow(0.1f);
}elseif(idleTimeSlice.expired){
returnfirstNonFinished;
}else{
// Allow waiting for a short amount of time to allow very short running
// jobs in graph updates to complete without having to wait until the next frame.
// While waiting we release our thread's time slice to make sure other threads get priority.