GameFrameWork learning notes

About resource loading, This article makes a summary (the boss who wrote this article also wrote some articles about GF, which is a summary of various modules of GF. It is also well written. It is recommended to have a look). I won't repeat it. Although the logic of large blocks is very clear, there are still a lot of details in the code. If you can understand the logic for the first time, the purpose has been achieved. Please study the details later.

1, DownloadManager

Next, this part is closely related to resources, that is, downloading. In fact, this part is indeed used in resource update. Look at the script directory in the folder. Leaving aside various event scripts, there are only two parts left: idownloadagenthhelper interface and DownloadManager for developers. DownloadManager also includes DownloadAgent, DownloadCounter and DownloadTask.
Idownloadagenthhelper is a simple interface that only declares several overloads and events of the download method. Next, let's look at the contents of DownloadManager.
The variables and methods declared in the IDownloadManager interface are quite intuitive.

public interface IDownloadManager
    {
        /// <summary>
        ///Gets or sets whether the download is paused.
        /// </summary>
        bool Paused { get; set; }

        /// <summary>
        ///Get the total number of download agents.
        /// </summary>
        int TotalAgentCount { get; }

        /// <summary>
        ///Gets the number of available download agents.
        /// </summary>
        int FreeAgentCount { get; }

        /// <summary>
        ///Get the number of download agents in work.
        /// </summary>
        int WorkingAgentCount { get; }

        /// <summary>
        ///Gets the number of tasks waiting to be downloaded.
        /// </summary>
        int WaitingTaskCount { get; }

        /// <summary>
        ///Gets or sets the critical size to write the buffer to disk.
        /// </summary>
        int FlushSize { get; set; }

        /// <summary>
        ///Gets or sets the download timeout in seconds.
        /// </summary>
        float Timeout { get; set; }

        /// <summary>
        ///Get the current download speed.
        /// </summary>
        float CurrentSpeed { get; }

        /// <summary>
        ///Download start event.
        /// </summary>
        event EventHandler<DownloadStartEventArgs> DownloadStart;

        /// <summary>
        ///Download update events.
        /// </summary>
        event EventHandler<DownloadUpdateEventArgs> DownloadUpdate;

        /// <summary>
        ///Download success event.
        /// </summary>
        event EventHandler<DownloadSuccessEventArgs> DownloadSuccess;

        /// <summary>
        ///Download failure event.
        /// </summary>
        event EventHandler<DownloadFailureEventArgs> DownloadFailure;

        /// <summary>
        ///Add download agent assistant.
        /// </summary>
        ///< param name = "downloadagenthhelper" > download agent helper to be added</ param>
        void AddDownloadAgentHelper(IDownloadAgentHelper downloadAgentHelper);

        /// <summary>
        ///Add download task.
        /// </summary>
        ///< param name = "downloadpath" > storage path after downloading</ param>
        ///< param name = "downloaduri" > original download address</ param>
        ///< returns > the serial number of the newly added download task</ returns>
        int AddDownload(string downloadPath, string downloadUri);

        /// <summary>
        ///Add download task.
        /// </summary>
        ///< param name = "downloadpath" > storage path after downloading</ param>
        ///< param name = "downloaduri" > original download address</ param>
        ///< param name = "priority" > the priority of the download task</ param>
        ///< returns > the serial number of the newly added download task</ returns>
        int AddDownload(string downloadPath, string downloadUri, int priority);

        /// <summary>
        ///Add download task.
        /// </summary>
        ///< param name = "downloadpath" > storage path after downloading</ param>
        ///< param name = "downloaduri" > original download address</ param>
        ///< param name = "UserData" > user defined data</ param>
        ///< returns > the serial number of the newly added download task</ returns>
        int AddDownload(string downloadPath, string downloadUri, object userData);

        /// <summary>
        ///Add download task.
        /// </summary>
        ///< param name = "downloadpath" > storage path after downloading</ param>
        ///< param name = "downloaduri" > original download address</ param>
        ///< param name = "priority" > the priority of the download task</ param>
        ///< param name = "UserData" > user defined data</ param>
        ///< returns > the serial number of the newly added download task</ returns>
        int AddDownload(string downloadPath, string downloadUri, int priority, object userData);

        /// <summary>
        ///Remove the download task.
        /// </summary>
        ///< param name = "serialid" > the serial number of the download task to be removed</ param>
        ///< returns > whether to remove the download task successfully</ returns>
        bool RemoveDownload(int serialId);

        /// <summary>
        ///Remove all download tasks.
        /// </summary>
        void RemoveAllDownloads();

        /// <summary>
        ///Get information about all download tasks.
        /// </summary>
        ///< returns > information of all download tasks</ returns>
        TaskInfo[] GetAllDownloadInfos();
    }

But there is still a question: what is the relationship between downloadagenthhelper and DownloadManager? This question is answered in the DownloadManager. Firstly, the DownloadAgent is defined in the DownloadManager, and the DownloadAgent holds the Hepler to execute the Download task through the Download method in the Helper. Then, the DownloadManager can add an Agent through the adddownloadagenthhelper() method and add the Agent to the task pool.

Therefore, the author's logic is that there is no direct download operation in the DownloadManager, but just add the download operation as a task to the TaskPool, add the DownloadAgent in the TaskPool, and poll the two through the update method to drive the download.

As for DownloadCounter, I don't understand the meaning of its existence at all. Is it just to calculate the download speed?

2, TaskPool

TaskPool is composed of agent and task. TaskPool defines ITaskAgent type stack and GameFrameworkLinkedList, which are used to save agents in idle state and working state respectively; A GameFrameworkLinkedList is defined to store pending tasks. When adding agent and task, they will be added to the idle agent and waiting task list respectively, and then processed through update.

/// <summary>
    ///Task pool.
    /// </summary>
    ///< typeparam name = "t" > task type</ typeparam>
    internal sealed class TaskPool<T> where T : TaskBase
    {
        private readonly Stack<ITaskAgent<T>> m_FreeAgents;
        private readonly GameFrameworkLinkedList<ITaskAgent<T>> m_WorkingAgents;
        private readonly GameFrameworkLinkedList<T> m_WaitingTasks;
        private bool m_Paused;

        /// <summary>
        ///Initialize a new instance of the task pool.
        /// </summary>
        public TaskPool()
        {
            m_FreeAgents = new Stack<ITaskAgent<T>>();
            m_WorkingAgents = new GameFrameworkLinkedList<ITaskAgent<T>>();
            m_WaitingTasks = new GameFrameworkLinkedList<T>();
            m_Paused = false;
        }

        /// <summary>
        ///Gets or sets whether the task pool is paused.
        /// </summary>
        public bool Paused
        {
            get { return m_Paused; }
            set { m_Paused = value; }
        }

        /// <summary>
        ///Get the total number of task agents.
        /// </summary>
        public int TotalAgentCount
        {
            get { return FreeAgentCount + WorkingAgentCount; }
        }

        /// <summary>
        ///Gets the number of available task agents.
        /// </summary>
        public int FreeAgentCount
        {
            get { return m_FreeAgents.Count; }
        }

        /// <summary>
        ///Gets the number of task agents in work.
        /// </summary>
        public int WorkingAgentCount
        {
            get { return m_WorkingAgents.Count; }
        }

        /// <summary>
        ///Get the number of waiting tasks.
        /// </summary>
        public int WaitingTaskCount
        {
            get { return m_WaitingTasks.Count; }
        }

        /// <summary>
        ///Task pool polling.
        /// </summary>
        ///< param name = "elapseseconds" > logical elapsed time, in seconds</ param>
        ///< param name = "realelapseseconds" > Real elapsed time, in seconds</ param>
        public void Update(float elapseSeconds, float realElapseSeconds)
        {
            if (m_Paused)
            {
                return;
            }

            ProcessRunningTasks(elapseSeconds, realElapseSeconds);
            ProcessWaitingTasks(elapseSeconds, realElapseSeconds);
        }

        /// <summary>
        ///Close and clean up the task pool.
        /// </summary>
        public void Shutdown()
        {
            RemoveAllTasks();

            while (FreeAgentCount > 0)
            {
                m_FreeAgents.Pop().Shutdown();
            }
        }

        /// <summary>
        ///Add task agent.
        /// </summary>
        ///< param name = "agent" > task agent to be added</ param>
        public void AddAgent(ITaskAgent<T> agent)
        {
            if (agent == null)
            {
                throw new GameFrameworkException("Task agent is invalid.");
            }

            agent.Initialize();
            m_FreeAgents.Push(agent);
        }

        /// <summary>
        ///Add tasks.
        /// </summary>
        ///< param name = "task" > tasks to be added</ param>
        public void AddTask(T task)
        {
            LinkedListNode<T> current = m_WaitingTasks.Last;
            while (current != null)
            {
                if (task.Priority <= current.Value.Priority)
                {
                    break;
                }

                current = current.Previous;
            }

            if (current != null)
            {
                m_WaitingTasks.AddAfter(current, task);
            }
            else
            {
                m_WaitingTasks.AddFirst(task);
            }
        }

        /// <summary>
        ///Remove the task.
        /// </summary>
        ///< param name = "serialid" > the serial number of the task to be removed</ param>
        ///< returns > whether to remove the task successfully</ returns>
        public bool RemoveTask(int serialId)
        {
            foreach (T task in m_WaitingTasks)
            {
                if (task.SerialId == serialId)
                {
                    m_WaitingTasks.Remove(task);
                    ReferencePool.Release(task);
                    return true;
                }
            }

            foreach (ITaskAgent<T> workingAgent in m_WorkingAgents)
            {
                if (workingAgent.Task.SerialId == serialId)
                {
                    T task = workingAgent.Task;
                    workingAgent.Reset();
                    m_FreeAgents.Push(workingAgent);
                    m_WorkingAgents.Remove(workingAgent);
                    ReferencePool.Release(task);
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        ///Remove all tasks.
        /// </summary>
        public void RemoveAllTasks()
        {
            foreach (T task in m_WaitingTasks)
            {
                ReferencePool.Release(task);
            }

            m_WaitingTasks.Clear();

            foreach (ITaskAgent<T> workingAgent in m_WorkingAgents)
            {
                T task = workingAgent.Task;
                workingAgent.Reset();
                m_FreeAgents.Push(workingAgent);
                ReferencePool.Release(task);
            }

            m_WorkingAgents.Clear();
        }

        public TaskInfo[] GetAllTaskInfos()
        {
            List<TaskInfo> results = new List<TaskInfo>();
            foreach (ITaskAgent<T> workingAgent in m_WorkingAgents)
            {
                T workingTask = workingAgent.Task;
                results.Add(new TaskInfo(workingTask.SerialId, workingTask.Priority,
                    workingTask.Done ? TaskStatus.Done : TaskStatus.Doing, workingTask.Description));
            }

            foreach (T waitingTask in m_WaitingTasks)
            {
                results.Add(new TaskInfo(waitingTask.SerialId, waitingTask.Priority, TaskStatus.Todo,
                    waitingTask.Description));
            }

            return results.ToArray();
        }

        private void ProcessRunningTasks(float elapseSeconds, float realElapseSeconds)
        {
            LinkedListNode<ITaskAgent<T>> current = m_WorkingAgents.First;
            while (current != null)
            {
                T task = current.Value.Task;
                if (!task.Done)
                {
                    current.Value.Update(elapseSeconds, realElapseSeconds);
                    current = current.Next;
                    continue;
                }

                LinkedListNode<ITaskAgent<T>> next = current.Next;
                current.Value.Reset();
                m_FreeAgents.Push(current.Value);
                m_WorkingAgents.Remove(current);
                ReferencePool.Release(task);
                current = next;
            }
        }

        private void ProcessWaitingTasks(float elapseSeconds, float realElapseSeconds)
        {
            LinkedListNode<T> current = m_WaitingTasks.First;
            while (current != null && FreeAgentCount > 0)
            {
                ITaskAgent<T> agent = m_FreeAgents.Pop();
                LinkedListNode<ITaskAgent<T>> agentNode = m_WorkingAgents.AddLast(agent);
                T task = current.Value;
                LinkedListNode<T> next = current.Next;
                StartTaskStatus status = agent.Start(task);
                if (status == StartTaskStatus.Done || status == StartTaskStatus.HasToWait ||
                    status == StartTaskStatus.UnknownError)
                {
                    agent.Reset();
                    m_FreeAgents.Push(agent);
                    m_WorkingAgents.Remove(agentNode);
                }

                if (status == StartTaskStatus.Done || status == StartTaskStatus.CanResume ||
                    status == StartTaskStatus.UnknownError)
                {
                    m_WaitingTasks.Remove(current);
                }

                if (status == StartTaskStatus.Done || status == StartTaskStatus.UnknownError)
                {
                    ReferencePool.Release(task);
                }

                current = next;
            }
        }
    }

In the framework, it is found that only DownloadAgent, LoadResourceAgent and WebRequestAgent inherit the ITaskAgent interface. It can be concluded that the three time-consuming parts of downloading, loading and network request in the project generally adopt asynchronous operation.

Posted by ElkySS on Sat, 16 Apr 2022 14:03:19 +0930