private void OnNextJobGeneratedMessage(Message message)
    {
        using (DarkRiftReader reader = message.GetReader())
        {
            Main.Log($"[CLIENT] < JOB_NEXT_JOB");

            while (reader.Position < reader.Length)
            {
                Job generatedJob = reader.ReadSerializable <Job>();
                jobs.FirstOrDefault(j => j.ChainId == generatedJob.ChainId && j.IsCurrentJob).IsCurrentJob = false;
                Job referencedJob = jobs.FirstOrDefault(j => j.Id == generatedJob.Id);
                if (referencedJob != null)
                {
                    referencedJob.GameId       = generatedJob.GameId;
                    referencedJob.IsCurrentJob = true;
                    if (referencedJob.Definition)
                    {
                        referencedJob.Definition.JobGenerated += OnNextJobInChainGenerated;
                        referencedJob.Definition.ForceJobId(referencedJob.GameId);
                        referencedJob.Definition.TryToGenerateJob();
                    }
                }
            }
        }
    }
    private IEnumerator ExpireJobAfterTime(Job job)
    {
        // Wait 2 minutes so the user can still accept the job
        yield return(new WaitForSeconds(2 * 60));

        job.CanTakeJob = false;
    }
    private IEnumerator InitializeNewChainsAndJobs(Chain[] chains, Job[] cjobs)
    {
        IsChangedByNetwork = true;
        foreach (Chain chain in chains)
        {
            Job[] chainJobs = cjobs.Where(j => j.ChainId == chain.Id).ToArray();
            Main.Log($"Job chain with ID {chain.Id} loading, amount of jobs in chain: {chainJobs.Length}");
            JobChainSaveData jobSaveData = JsonConvert.DeserializeObject <JobChainSaveData>(chain.Data, JobSaveManager.serializeSettings);
            yield return(new WaitUntil(() =>
            {
                foreach (string guid in jobSaveData.trainCarGuids)
                {
                    if (!SingletonBehaviour <NetworkTrainManager> .Instance.localCars.Any(t => t.CarGUID == guid))
                    {
                        return false;
                    }
                }
                return true;
            }));

            GameObject chainGO = SingletonBehaviour <JobSaveManager> .Instance.LoadJobChain(jobSaveData);

            if (chainGO)
            {
                Main.Log("Job chain succesfully loaded");
                StaticJobDefinition[] chainJobDefinitions = chainGO.GetComponents <StaticJobDefinition>();

                foreach (StaticJobDefinition definition in chainJobDefinitions)
                {
                    Main.Log("Register job definition");
                    Job job = chainJobs.FirstOrDefault(j => j.Type == (definition.job != null ? definition.job.jobType : GetJobTypeFromDefinition(definition)));
                    job.Definition = definition;
                    Main.Log("Add to jobs list");
                    jobs.Add(job);

                    if (job.IsCurrentJob)
                    {
                        Main.Log("Job is current registering job taken event");
                        if (!job.IsTaken)
                        {
                            job.Definition.job.JobTaken += OnJobTaken;
                        }
                        else
                        {
                            job.CanTakeJob = false;
                        }
                    }
                    Main.Log("Job fully loaded");
                }
                Main.Log("Chain successfully loaded");
            }
        }
        IsChangedByNetwork = false;
    }
 internal void ExpireJobsUnTakenInStation(StationController station)
 {
     foreach (DV.Logic.Job.Job job in station.logicStation.availableJobs.ToList())
     {
         Job sjob = jobs.FirstOrDefault(j => j.GameId == job.ID);
         if (sjob != null && !sjob.IsTaken)
         {
             job.ExpireJob();
         }
     }
 }
    private void SendNextJobGenerated(Job job)
    {
        Main.Log($"[CLIENT] > JOB_NEXT_JOB");

        using (DarkRiftWriter writer = DarkRiftWriter.Create())
        {
            writer.Write(job);

            using (Message message = Message.Create((ushort)NetworkTags.JOB_NEXT_JOB, writer))
                SingletonBehaviour <UnityClient> .Instance.SendMessage(message, SendMode.Reliable);
        }
    }
    private void OnJobSyncMessage(Message message)
    {
        using (DarkRiftReader reader = message.GetReader())
        {
            Main.Log($"[CLIENT] < JOB_SYNC");

            while (reader.Position < reader.Length)
            {
                IsChangedByNetwork = true;
                Chain[] chains = reader.ReadSerializables <Chain>();
                Job[]   cjobs  = reader.ReadSerializables <Job>();
                foreach (Chain chain in chains)
                {
                    Job[] chainJobs = cjobs.Where(j => j.ChainId == chain.Id).ToArray();
                    Main.Log($"Job chain with ID {chain.Id} loading, amount of jobs in chain: {chainJobs.Length}");
                    JobChainSaveData jobSaveData = JsonConvert.DeserializeObject <JobChainSaveData>(chain.Data, JobSaveManager.serializeSettings);
                    GameObject       chainGO     = SingletonBehaviour <JobSaveManager> .Instance.LoadJobChain(jobSaveData);

                    if (chainGO)
                    {
                        Main.Log("Job chain succesfully loaded");
                        StaticJobDefinition[] chainJobDefinitions = chainGO.GetComponents <StaticJobDefinition>();
                        foreach (StaticJobDefinition definition in chainJobDefinitions)
                        {
                            Main.Log("Register job definition");
                            Job job = chainJobs.FirstOrDefault(j => j.Type == (definition.job != null ? definition.job.jobType : GetJobTypeFromDefinition(definition)));
                            job.Definition = definition;
                            Main.Log("Add to jobs list");
                            jobs.Add(job);

                            if (job.IsCurrentJob)
                            {
                                Main.Log("Job is current registering job taken event");
                                if (!job.IsTaken)
                                {
                                    job.Definition.job.JobExpired += OnJobExpired;
                                    job.Definition.job.JobTaken   += OnJobTaken;
                                }
                                else
                                {
                                    job.CanTakeJob = false;
                                }
                            }
                            Main.Log("Job fully loaded");
                        }
                        Main.Log("Chain successfully loaded");
                    }
                }
                IsChangedByNetwork = false;
            }
        }
        IsSynced = true;
    }
    private void OnJobTaken(DV.Logic.Job.Job job, bool takenViaGame)
    {
        job.JobCompleted += OnJobCompleted;
        Job sJob = jobs.FirstOrDefault(j => j.GameId == job.ID);

        if (sJob != null && sJob.CanTakeJob)
        {
            SendJobTaken(sJob.Id);
            sJob.IsTakenByLocalPlayer = true;
            sJob.IsTaken = true;
        }
    }
    private void OnJobCompleted(DV.Logic.Job.Job job)
    {
        Job sJob = jobs.FirstOrDefault(j => j.GameId == job.ID);

        if (sJob != null && !sJob.IsCompleted && sJob.IsTakenByLocalPlayer)
        {
            SendJobCompleted(sJob.Id);
            sJob.IsCompleted = true;
            if (NetworkManager.IsHost())
            {
                UpdateChainSaveData(sJob.ChainId);
            }
        }
    }
    private void OnNextJobInChainGenerated(StaticJobDefinition jobDef, DV.Logic.Job.Job job)
    {
        if (NetworkManager.IsHost())
        {
            Job data    = jobs.FirstOrDefault(j => j.Definition == jobDef);
            Job prevJob = jobs.FirstOrDefault(j => j.ChainId == data.ChainId && j.IsCurrentJob);
            prevJob.IsCurrentJob = false;
            data.GameId          = job.ID;
            data.IsCurrentJob    = true;
            SendNextJobGenerated(data);
        }

        Main.Log("Register job taken event");
        job.JobTaken += OnJobTaken;
        Main.Log("Job fully loaded");
    }
    private void OnOtherPlayerTakenJobMessage(Message message)
    {
        using (DarkRiftReader reader = message.GetReader())
        {
            Main.Log($"[CLIENT] < JOB_TAKEN");

            while (reader.Position < reader.Length)
            {
                IsChangedByNetwork = true;
                string id  = reader.ReadString();
                Job    job = jobs.FirstOrDefault(j => j.Id == id);
                if (!job.IsTaken)
                {
                    job.IsTaken = true;
                    SingletonBehaviour <CoroutineManager> .Instance.Run(ExpireJobAfterTime(job));
                }
                IsChangedByNetwork = false;
            }
        }
    }
    private void OnJobCompletedMesssage(Message message)
    {
        using (DarkRiftReader reader = message.GetReader())
        {
            Main.Log($"[CLIENT] < JOB_COMPLETED");

            while (reader.Position < reader.Length)
            {
                IsChangedByNetwork = true;
                string id  = reader.ReadString();
                Job    job = jobs.FirstOrDefault(j => j.Id == id);
                if (job != null && !job.IsCompleted && !job.IsTakenByLocalPlayer)
                {
                    job.IsCompleted = true;
                    job.Definition.job.CompleteJob();
                    if (NetworkManager.IsHost())
                    {
                        UpdateChainSaveData(job.ChainId);
                    }
                }
                IsChangedByNetwork = false;
            }
        }
    }
    private Tuple <List <Chain>, List <Job> > GenerateChainsAndJobs(JobChainController[] chainControllers)
    {
        List <Chain> newChains = new List <Chain>();
        List <Job>   newJobs   = new List <Job>();

        foreach (JobChainController controller in chainControllers)
        {
            string chainId = Guid.NewGuid().ToString();
            while (jobChains.Any(c => c.Id == chainId))
            {
                chainId = Guid.NewGuid().ToString();
            }

            Chain chain = new Chain()
            {
                Id          = chainId,
                Data        = JsonConvert.SerializeObject(controller.GetJobChainSaveData(), JobSaveManager.serializeSettings),
                IsCompleted = false,
                Controller  = controller
            };

            jobChains.Add(chain);
            newChains.Add(chain);

            controller.JobChainCompleted += OnJobChainCompleted;
            controller.JobOfChainExpired += OnJobInChainExpired;
            StaticJobDefinition[] definitions = controller.jobChainGO.GetComponents <StaticJobDefinition>();
            foreach (StaticJobDefinition definition in definitions)
            {
                string jobId = Guid.NewGuid().ToString();
                while (jobs.Any(c => c.Id == jobId))
                {
                    jobId = Guid.NewGuid().ToString();
                }

                Job job = new Job()
                {
                    Id                   = jobId,
                    ChainId              = chainId,
                    GameId               = definition.job != null ? definition.job.ID : "",
                    IsCurrentJob         = definition.job != null,
                    Type                 = definition.job != null ? definition.job.jobType : GetJobTypeFromDefinition(definition),
                    Definition           = definition,
                    CanTakeJob           = definition.job != null ? definition.job.State != JobState.Completed && definition.job.State != JobState.InProgress : true,
                    IsCompleted          = definition.job != null ? definition.job.State == JobState.Completed : false,
                    IsTaken              = definition.job != null ? definition.job.State == JobState.InProgress : false,
                    IsTakenByLocalPlayer = definition.job != null ? definition.job.State == JobState.InProgress : false,
                };

                if (definition.job != null)
                {
                    definition.job.JobTaken += OnJobTaken;
                }
                else
                {
                    definition.JobGenerated += OnNextJobInChainGenerated;
                }

                jobs.Add(job);
                newJobs.Add(job);
            }
        }
        return(Tuple.Create(newChains, newJobs));
    }
    internal bool IsAllowedToTakeJob(string id)
    {
        Job job = jobs.FirstOrDefault(j => j.GameId == id);

        return(job != null && job.CanTakeJob);
    }