-
Notifications
You must be signed in to change notification settings - Fork 75
GameLoop and Interupts
This page could use some formating love. Origionaly, the ManagerSubpulses resided in the StarSystem class, this has now been moved to the EntityManager. This makes it a little easier to get a given entities subpulse (by myEntity.Manager.ManagerSubpulses)
This is a fairly complex system since each game 'tick' can be a variable length of time, this is a feature that was inherited from aurora which worked fairly well there. this allows the game to progress fairly quickly from the players point of view during long boring economy/industry building and slow ship movement phases of the game, while subpulse interupts allow events to happen when they need to.
MasterTimePulse.cs: https://github.com/Pulsar4xDevs/Pulsar4x/blob/Master/Pulsar4X/Pulsar4X.ECSLib/GameLoop/MasterTimePulse.cs
This is the primary loop / pulse.
ManagerSubPulse.cs: https://github.com/Pulsar4xDevs/Pulsar4x/blob/Master/Pulsar4X/Pulsar4X.ECSLib/GameLoop/ManagerSubPulse.cs
This handles the pulses on each entity manager or star system.
ProcessorManager.cs: https://github.com/Pulsar4xDevs/Pulsar4x/blob/Master/Pulsar4X/Pulsar4X.ECSLib/GameLoop/ProcessorManager.cs
This class is created when Game is created, it then automaticaly creates and stores concrete classes for any class that derives from IHotloopProcessor, IInstanceProcessor, and, (in future) IRecalcProcessor
the purpose of this class is to allow code to call a processor using a datablob as a reference, or more commonly call a processor to process all datablobs in a manager that has datablobs that the processor relates to.
This file also currently has the Interfaces for the IHotloopProcessor, IInstanceProcessor and IRecalcProcessor themselfs.
This is responsible for running the pulses at a given rate if running continuously
The length of the ticks can be changed using (TimeSpan) Ticklength Property
The Frequency of the ticks can be changed using (TimeSpan) TickFrequency Property
there is also TimeMultiplier which multiplies the TickFrequency, this function is possibly redundant.
The Master pulse checks for any InterSystem interrupts, runs any processors relevant to that interrupt (probably a system jump), then runs (in parallel) each System's ManagerSubpulses. (getting them from the TimeLoop._game.Systems[].EntityManager.ManagerSubpulse)
Once all the ManagerSubpulses have completed it updates the (DateTime) GameGlobalDateTime Property which fires the GameGlobalDateChangedEvent.
This event can be listened to for updating the UI.
After this has been completed, if the timespan is less than the Ticklength, it'll repeat the process. otherwise it'll wait for the TickFrequency.
Are very similar and work nearly exactly the same as the above:
- Checks for SystemInterupts,
- Runs the movement processor on any entity with a PropulsionDB,
- Processors relevant to the interrupt (if any),
- Updates the SystemLocalDateTime, which fires the SystemDateChangedEvent (which can be subscribed to for ui updates)
- Repeats the process till the SystemLocalDateTime is at the target DateTime given to it by the TimeLoop.
classes which inherit from this interface get added to the Processor manager automatically.
These get fired on a given game tick frequency. this is set in the inherited classes RunFrequency
They typcialy process all the enitites in a starsystem which have the related Datablob (set in the inherited class's GetParameterType
like the IHotloopProcessor classes which inherit from this get added to the processor manager automatically. unlike the hotloop processors, these can get fired at any time, typically on a given event, they typicaly process a single given entity, rather than all the entities in a starsystem.
TimeLoop and ManagerSubpulses both have code so that the master(UI) thread can subscribe to the GameGlobalDateTime and SystemLocalDateTime events, and these events will be marshalled to the master(UI) thread by the TimeLoop or System Subpulse code, you shouldn't need to worry about it in the UI. note that this will only happen if the Game.SynchContext != null, if it is null, the event will not be marshalled and will run on the thread that fired it. The InterSystemJumpProcessor will create an InterSystemInterupt on the Timeloop, causing systems to be in synch for these events.
The TimeLoop processes each Starsystem's EntityManager's subpulse in parallel if this is enabled in game settings. there are few instances when starsystems interact and need to be in synch timewise with each other, the primary reason when this would be a problem is when ships jump from one system to another. This is handled by an InterSystemInterupt in the TimeLoop, which causes all systems to be in timestep at that point in time.
interupts in this context are a delegate linked to a DateTime, there are currently three types of Interupt:
InterSystemInterupt which can be added via the TimeLoop.AddSystemInteractionInterupt. currently there are only two delegates that use this, InterSystemJumpProcessor.JumpOut and InterSystemJumpProcessor.JumpIn see the InterSystemJumpProcessor.SetJump for an example.
SystemInterupts on the ManagerSubpulses and added via SystemSubpulses.AddSystemInterupt this interrupt relies on the processor to find the relevant entities, typically by using starSystem.SystemManager.GetAllEntitiesWithDataBlob<> See the EconProcessor and OrbitProcessors for examples. note that because these run repeatedly, the processors re-add themselves to the ManagerSubpulses as an interrupt when they're run.
EntityInterupts again on the ManagerSubpulses and is added via ManagerSubpulses.AddEntityInterupt these are entity specific and will run a given process on a given entity on the given date.
Interrupts are added using the string name of the processor class name, ie: GetType().Name note that you are restricted in the info you can pass into the processor. if you need to pass something else, first consider adding a datablob to the relevant entity and getting the data that way, note that on construction, Hotloop processors get passed 'Game' some HotloopProcessors will use this to store static data. if that is not going to work then discuss it with the team. ie if it's something that will regularly be used across multiple processors and doesn't make sense in a datablob, we might look at adding something simular to the IInstanceProcessors as well.
Documentation
-
Contribution
-
Design
-
Processes
-
Datatypes
-
Guides and Resources
-
Modding & Json files