Skip to content

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);
}