Integration
Note
Before proceeding, it is recommended to first read the components document to get an understanding of how the Dependency Container system works.
The article goes over the steps to utilize the IContainer
by providing examples from IdleKit's implementation to show how custom Entities
or Services
can be integrated.
1. Setup
First, we need to prepare the classes for the IContainer
by implementing the IInjectable
.
IEntity
All entities
should implement the IInjectable
interface which allows them to be bound in the IContainer
and their dependencies injected when they are first loaded by the IEntityLoaderService
. For more information on IEntity
, please check out the Entity document.
public class Stage : IStage, IInjectable
{
...
public virtual void Inject(IResolver resolver)
{
_actionService = resolver.Resolve<IActionService>();
...
}
}
IService
All services
should implement the IInjectable
interface which allows them to be bound in the IContainer
and their dependencies injected. They are bound to the IContainer
as a singleton in the IInstaller
during the startup process and injected into objects that need them.
IActionResolver
and IEntityResolver
These are specialized and cached versions of IResolver
that only allow resolving of certain types of objects such as the IEntity
and IAction
to limit the access of the IResolver
. Please check out the following two implementations of ITypedResolver
: IEntityResolver
and IActionResolver
. Below is an example of an IService
utilizing the IEntityResolver
to fetch the IEntity
in the IContainer
.
public class RewardService : IRewardService, IInjectable
{
protected IEntityResolver _entityResolver;
public virtual void Inject(IResolver resolver)
{
_entityResolver = resolver.Resolve<IEntityResolver>();
...
}
...
public virtual TReward GetReward<TReward>(string id) where TReward : IReward
{
return _entityResolver.Resolve<TReward>(id);
}
}
Note
In the default IdleKit implementation, services
would persist throughout the lifetime of the app.
IAction
Integration
All actions
implement the IInjectable
interface that allows them to be bound in the IContainer
and their dependencies injected. They are bound to the IContainer
as a singleton in the IInstaller
during the startup process and injected into the objects that need them. You can retrieve an instance of any IAction
from the IActionService
. The actions
persist throughout the lifetime of the app. Below is an example of an IStateAction
implementing IInjectable
and having its dependencies injected.
public class BuyGeneratorStateAction : IStateAction : IInjectable
{
protected ICurrencyService _currencyService;
protected ITimerService _timerService;
...
public virtual void Inject(IResolver resolver)
{
_currencyService = resolver.Resolve<ICurrencyService>();
_timerService = resolver.Resolve<ITimerService>();
}
}
2. Binding
After the classes are prepared, they needed to be bound to the IContainer
in the IInstaller
.
The following services
are bound to the IContainer
in IdleKit.
public virtual void BindServices(IContainer container)
{
//Helper
container.Bind<IUserSavedData>().ToInstance(new UserSavedData(USER_SAVED_DATA)).AsSingleton().Conclude();
container.Bind<SerializedDataCache>().ToInstance(new SerializedDataCache(SERIALIZED_DATA_CACHE))
.AsSingleton().Conclude();
container.Bind<IModifierCache>().To<ModifierCache>().AsSingleton().Conclude();
container.Bind<IEntityResolver>().To<EntityResolver>().AsSingleton().Conclude();
container.Bind<IProjectionCalculator>().To<ProjectionCalculator>().AsSingleton().Conclude();
// ModifierFormulaParser is transient because it caches information related to the ModifierFormula that creates it
container.Bind<IModifierFormulaParser>().To<ModifierFormulaParser>().AsTransient().Conclude();
//Generator States
container.Bind<IAutoCollectState>().To<AutoCollectState>().AsTransient().Conclude();
container.Bind<IProduceState>().To<ProduceState>().AsTransient().Conclude();
container.Bind<IWaitToBuyState>().To<WaitToBuyState>().AsTransient().Conclude();
container.Bind<IWaitToCollectState>().To<WaitToCollectState>().AsTransient().Conclude();
//IService
container.Bind<ActivityTrackingService>().AsSingleton().Conclude();
container.Bind<ICurrencyService>().To<CurrencyService>().AsSingleton().Conclude();
container.Bind<IEntityLoaderService>().To<EntityLoaderService>().AsSingleton().Conclude();
container.Bind<IEventService>().To<EventService>().AsSingleton().Conclude();
container.Bind<IModifierService>().To<ModifierService>().AsSingleton().Conclude();
container.Bind<IProjectionService>().To<ProjectionService>().AsSingleton().Conclude();
container.Bind<IRandomService>().To<RandomService>().AsSingleton().Conclude();
container.Bind<IRewardService>().To<RewardService>().AsSingleton().Conclude();
container.Bind<IStoreService>().To<StoreService>().AsSingleton().Conclude();
container.Bind<ITagService>().To<TagService>().AsSingleton().Conclude();
container.Bind<ITimerService>().To<TimerService>().AsSingleton().Conclude();
container.Bind<ITrackService>().To<TrackService>().AsSingleton().Conclude();
container.Bind<ITradeService>().To<TradeService>().AsSingleton().Conclude();
container.Bind<ILocalizationService>().To<LocalizationService>().AsSingleton().Conclude();
container.Bind<SerializationService>().AsSingleton().Conclude();
container.Bind<IPromoService>().To<PromoService>().AsSingleton().Conclude();
container.Bind<GachaOddsService>().AsSingleton().Conclude();
// Analytics Tracking
container.Bind<UserAcquisitionAnalyticsTrackingService>().AsSingleton().Conclude();
container.Bind<CurrencyChangeAnalyticsTrackingService>().AsSingleton().Conclude();
container.Bind<ContentProgressionAnalyticsTrackingService>().AsSingleton().Conclude();
}
The following actions
are bound to the IContainer
in IdleKit.
public virtual void BindActions(IContainer container)
{
// StateActions
container.Bind<AdvanceStageStateAction>().AsSingleton().Conclude();
container.Bind<AscensionCompleteStateAction>().AsSingleton().Conclude();
container.Bind<AutomateGeneratorStateAction>().AsSingleton().Conclude();
container.Bind<BuyFromStoreCollectionStateAction>().AsSingleton().Conclude();
container.Bind<BuyCollectorStateAction>().AsSingleton().Conclude();
container.Bind<BuyGeneratorStateAction>().AsSingleton().Conclude();
container.Bind<BuyStoreRewardStateAction>().AsSingleton().Conclude();
container.Bind<ClaimGoalStateAction>().AsSingleton().Conclude();
container.Bind<ClaimMilestoneTrackRewardsStateAction>().AsSingleton().Conclude();
container.Bind<ClaimStoreTimedRewardStateAction>().AsSingleton().Conclude();
container.Bind<ClaimTimedTrackRewardsStateAction>().AsSingleton().Conclude();
container.Bind<ClaimEventRewardsStateAction>().AsSingleton().Conclude();
container.Bind<ClaimEventRankRewardsStateAction>().AsSingleton().Conclude();
container.Bind<ClearContentIdStateAction>().AsSingleton().Conclude();
container.Bind<ClearEventsSavedDataStateAction>().AsSingleton().Conclude();
container.Bind<CollectFromCollectorStateAction>().AsSingleton().Conclude();
container.Bind<CollectFromGeneratorStateAction>().AsSingleton().Conclude();
container.Bind<CompleteMilestoneTrackStateAction>().AsSingleton().Conclude();
container.Bind<ProgressTrackStateAction>().AsSingleton().Conclude();
container.Bind<CreateGoalStateAction>().AsSingleton().Conclude();
container.Bind<EventEndedStateAction>().AsSingleton().Conclude();
container.Bind<EventStartedStateAction>().AsSingleton().Conclude();
container.Bind<GeneratorUnitTargetHitStateAction>().AsSingleton().Conclude();
container.Bind<IncrementGeneratorUnitStateAction>().AsSingleton().Conclude();
container.Bind<LogLastActiveTimeStateAction>().AsSingleton().Conclude();
container.Bind<MakeExchangeStateAction>().AsSingleton().Conclude();
container.Bind<NewContentStateAction>().AsSingleton().Conclude();
container.Bind<NewStageStateAction>().AsSingleton().Conclude();
container.Bind<NewUserStateAction>().AsSingleton().Conclude();
container.Bind<ProgressGoalStateAction>().AsSingleton().Conclude();
container.Bind<ProgressMilestoneStateAction>().AsSingleton().Conclude();
container.Bind<ResetEntitySavedDataStateAction>().AsSingleton().Conclude();
container.Bind<SetAvailableCurrenciesStateAction>().AsSingleton().Conclude();
container.Bind<SetContentStateAction>().AsSingleton().Conclude();
container.Bind<SetEventEndTimeStateAction>().AsSingleton().Conclude();
container.Bind<SetGoalProgressStateAction>().AsSingleton().Conclude();
container.Bind<SetMilestoneProgressStateAction>().AsSingleton().Conclude();
container.Bind<SetMilestoneCompletionInfoStateAction>().AsSingleton().Conclude();
container.Bind<StoreTimedRewardInitializeStateAction>().AsSingleton().Conclude();
container.Bind<StoreTimedRewardUnlockedStateAction>().AsSingleton().Conclude();
container.Bind<ToggleBoostStateAction>().AsSingleton().Conclude();
container.Bind<ToggleMilestoneStateAction>().AsSingleton().Conclude();
container.Bind<ToggleTimedActivatableStateAction>().AsSingleton().Conclude();
container.Bind<ToggleTimedBoostStateAction>().AsSingleton().Conclude();
container.Bind<ToggleTrackStateAction>().AsSingleton().Conclude();
container.Bind<ToggleAvailableStateAction>().AsSingleton().Conclude();
container.Bind<UpgradeCurrencyStateAction>().AsSingleton().Conclude();
container.Bind<TogglePromoReadyStateAction>().AsSingleton().Conclude();
container.Bind<ToggleTimeLeftInEventTriggeredStateAction>().AsSingleton().Conclude();
container.Bind<ClaimNumGoalsTriggerProgressStateAction>().AsSingleton().Conclude();
container.Bind<MilestonesCompletedTriggerProgressStateAction>().AsSingleton().Conclude();
container.Bind<EventPhaseStartedStateAction>().AsSingleton().Conclude();
// Actions
container.Bind<StageAdvancedAction>().AsSingleton().Conclude();
container.Bind<AscensionAvailableAction>().AsSingleton().Conclude();
container.Bind<AscensionStartAction>().AsSingleton().Conclude();
container.Bind<ClearContentAction>().AsSingleton().Conclude();
container.Bind<CollectorInitializedAction>().AsSingleton().Conclude();
container.Bind<CollectorPayoutChangedAction>().AsSingleton().Conclude();
container.Bind<CollectorSpeedChangedAction>().AsSingleton().Conclude();
container.Bind<CollectorStateChangedAction>().AsSingleton().Conclude();
container.Bind<ContentInitializedAction>().AsSingleton().Conclude();
container.Bind<CurrencyAvailableAction>().AsSingleton().Conclude();
container.Bind<CurrencyChangedAction>().AsSingleton().Conclude();
container.Bind<CurrencyObtainedAction>().AsSingleton().Conclude();
container.Bind<EntityAddedAction>().AsSingleton().Conclude();
container.Bind<EntityInitializedAction>().AsSingleton().Conclude();
container.Bind<EntityRemovedAction>().AsSingleton().Conclude();
container.Bind<GeneratorInitializedAction>().AsSingleton().Conclude();
container.Bind<GeneratorPayoutChangedAction>().AsSingleton().Conclude();
container.Bind<GeneratorSpeedChangedAction>().AsSingleton().Conclude();
container.Bind<GeneratorStateChangedAction>().AsSingleton().Conclude();
container.Bind<GeneratorModifierIndexChangedAction>().AsSingleton().Conclude();
container.Bind<GoalActivatedAction>().AsSingleton().Conclude();
container.Bind<IdleKitInitializedAction>().AsSingleton().Conclude();
container.Bind<LocalizationLanguageChangedAction>().AsSingleton().Conclude();
container.Bind<MilestoneClaimableAction>().AsSingleton().Conclude();
container.Bind<ModifierRegisteredAction>().AsSingleton().Conclude();
container.Bind<ModifierUnregisteredAction>().AsSingleton().Conclude();
container.Bind<ModifierIndexChangedAction>().AsSingleton().Conclude();
container.Bind<ModifierToggledAction>().AsSingleton().Conclude();
container.Bind<OfflineProgressStartAction>().AsSingleton().Conclude();
container.Bind<OfflineProgressEndAction>().AsSingleton().Conclude();
container.Bind<PostSerializationAction>().AsSingleton().Conclude();
container.Bind<PreClearContentAction>().AsSingleton().Conclude();
container.Bind<PreSerializationAction>().AsSingleton().Conclude();
container.Bind<RewardGrantedAction>().AsSingleton().Conclude();
container.Bind<StageInitializedAction>().AsSingleton().Conclude();
container.Bind<StoreCollectionRefreshedStateAction>().AsSingleton().Conclude();
container.Bind<TimedActivatableEndedAction>().AsSingleton().Conclude();
container.Bind<StoreTimedRewardAvailableAction>().AsSingleton().Conclude();
container.Bind<StoreTimedRewardStartAction>().AsSingleton().Conclude();
container.Bind<TrackCompletedAction>().AsSingleton().Conclude();
container.Bind<TradeAcceptedAction>().AsSingleton().Conclude();
container.Bind<TradeDeclinedAction>().AsSingleton().Conclude();
container.Bind<TriggerCompletedAction>().AsSingleton().Conclude();
container.Bind<SuccessfulSignInAction>().AsSingleton().Conclude();
container.Bind<FailedSignInAction>().AsSingleton().Conclude();
container.Bind<RequestSignInAction>().AsSingleton().Conclude();
container.Bind<SuccessfulRegistrationAction>().AsSingleton().Conclude();
container.Bind<FailedRegistrationAction>().AsSingleton().Conclude();
}
3. Startup
Lastly, in the startup script (Startup.cs
), the IContext
is bound, which creates IContainer
and calls the IInstaller.Install
method. The services
and actions
from the previous step would be bound. After the binding is complete, the app would be ready for gameplay.
Startup.cs
public virtual void StartGame()
{
Context.Bind();
ResetLoader();
SetInitializationLoadPhases();
SetLoadContentLoadPhases();
Loader.Start();
}
Context.cs
public virtual void Bind()
{
Installer.Install(Container);
}
IKInstaller.cs
public override void Install(IContainer container)
{
BindLogger(container);
BindServices(container);
BindActions(container);
BindLoadPhases(container);
}