Esempio n. 1
0
        public void Initialize()
        {
            if (Initialized == true)
            {
                return;
            }

            //Use invariant culture
            Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture;

            //Set up the logger
            string logPath = Path.Combine(LoggingConstants.LogFolderPath, LoggingConstants.LOG_FILE_NAME);

            if (Utilities.FileHelpers.ValidatePathForFile(logPath) == false)
            {
                Console.WriteLine("Logger path cannot be validated. This is a problem! Double check the path is correct.");
            }

            //Set up the logger
            //Cap the size at 10 MB
            TRBotLogger.SetupLogger(logPath, Serilog.Events.LogEventLevel.Verbose,
                                    Serilog.RollingInterval.Day, 1024L * 1024L * 10L, TimeSpan.FromSeconds(60d));

            //Initialize database
            string databasePath = Path.Combine(DataConstants.DataFolderPath, DataConstants.DATABASE_FILE_NAME);

            TRBotLogger.Logger.Information($"Validating database at: {databasePath}");
            if (Utilities.FileHelpers.ValidatePathForFile(databasePath) == false)
            {
                TRBotLogger.Logger.Error($"Cannot create database path at {databasePath}. Check if you have permission to write to this directory. Aborting.");
                return;
            }

            TRBotLogger.Logger.Information("Database path validated! Initializing database and importing migrations.");

            DatabaseManager.SetDatabasePath(databasePath);
            DatabaseManager.InitAndMigrateContext();

            TRBotLogger.Logger.Information("Checking to initialize default values for missing database entries.");

            //Check for and initialize default values if the database was newly created or needs updating
            int addedDefaultEntries = DataHelper.InitDefaultData();

            if (addedDefaultEntries > 0)
            {
                TRBotLogger.Logger.Information($"Added {addedDefaultEntries} additional entries to the database.");
            }

            //Set the logger's log level
            long logLevel = DataHelper.GetSettingInt(SettingsConstants.LOG_LEVEL, (long)Serilog.Events.LogEventLevel.Information);

            TRBotLogger.SetLogLevel((Serilog.Events.LogEventLevel)logLevel);

            TRBotLogger.Logger.Information("Initializing client service");

            //Initialize client service
            InitClientService();

            //If the client service doesn't exist, we can't continue
            if (ClientService == null)
            {
                TRBotLogger.Logger.Error("Client service failed to initialize; please check your settings. Aborting.");
                return;
            }

            //Set client service and message cooldown
            MsgHandler.SetClientService(ClientService);

            MessageThrottlingOptions msgThrottleOption = (MessageThrottlingOptions)DataHelper.GetSettingInt(SettingsConstants.MESSAGE_THROTTLE_TYPE, 0L);
            long msgCooldown      = DataHelper.GetSettingInt(SettingsConstants.MESSAGE_COOLDOWN, 30000L);
            long msgThrottleCount = DataHelper.GetSettingInt(SettingsConstants.MESSAGE_THROTTLE_COUNT, 20L);

            MsgHandler.SetMessageThrottling(msgThrottleOption, new MessageThrottleData(msgCooldown, msgThrottleCount));

            //Subscribe to events
            UnsubscribeEvents();
            SubscribeEvents();

            DataContainer.SetMessageHandler(MsgHandler);
            DataContainer.SetDataReloader(DataReloader);

            TRBotLogger.Logger.Information("Setting up virtual controller manager.");

            VirtualControllerTypes lastVControllerType = (VirtualControllerTypes)DataHelper.GetSettingInt(SettingsConstants.LAST_VCONTROLLER_TYPE, 0L);

            VirtualControllerTypes curVControllerType = VControllerHelper.ValidateVirtualControllerType(lastVControllerType, TRBotOSPlatform.CurrentOS);

            //Show a message saying the previous value wasn't supported and save the changes
            if (VControllerHelper.IsVControllerSupported(lastVControllerType, TRBotOSPlatform.CurrentOS) == false)
            {
                MsgHandler.QueueMessage($"Current virtual controller {lastVControllerType} is not supported by the {TRBotOSPlatform.CurrentOS} platform. Switched it to the default of {curVControllerType} for this platform.");

                using (BotDBContext context = DatabaseManager.OpenContext())
                {
                    Settings lastVControllerSetting = DataHelper.GetSettingNoOpen(SettingsConstants.LAST_VCONTROLLER_TYPE, context);
                    lastVControllerSetting.ValueInt = (long)curVControllerType;
                    context.SaveChanges();
                }
            }

            DataContainer.SetCurVControllerType(curVControllerType);

            IVirtualControllerManager controllerMngr = VControllerHelper.GetVControllerMngrForType(curVControllerType);

            DataContainer.SetControllerManager(controllerMngr);

            int controllerCount = 0;

            //Clamp the controller count to the min and max allowed by the virtual controller manager
            using (BotDBContext context = DatabaseManager.OpenContext())
            {
                Settings joystickCountSetting = DataHelper.GetSettingNoOpen(SettingsConstants.JOYSTICK_COUNT, context);

                int minCount = DataContainer.ControllerMngr.MinControllers;
                int maxCount = DataContainer.ControllerMngr.MaxControllers;

                //Validate controller count
                if (joystickCountSetting.ValueInt < minCount)
                {
                    MsgHandler.QueueMessage($"Controller count of {joystickCountSetting.ValueInt} in database is invalid. Clamping to the min of {minCount}.");
                    joystickCountSetting.ValueInt = minCount;
                    context.SaveChanges();
                }
                else if (joystickCountSetting.ValueInt > maxCount)
                {
                    MsgHandler.QueueMessage($"Controller count of {joystickCountSetting.ValueInt} in database is invalid. Clamping to the max of {maxCount}.");
                    joystickCountSetting.ValueInt = maxCount;
                    context.SaveChanges();
                }

                controllerCount = (int)joystickCountSetting.ValueInt;
            }

            DataContainer.ControllerMngr.Initialize();
            int acquiredCount = DataContainer.ControllerMngr.InitControllers(controllerCount);

            TRBotLogger.Logger.Information($"Setting up virtual controller {curVControllerType} and acquired {acquiredCount} controllers!");

            CmdHandler = new CommandHandler();
            CmdHandler.Initialize(DataContainer, RoutineHandler);

            DataReloader.SoftDataReloadedEvent -= OnSoftReload;
            DataReloader.SoftDataReloadedEvent += OnSoftReload;

            DataReloader.HardDataReloadedEvent -= OnHardReload;
            DataReloader.HardDataReloadedEvent += OnHardReload;

            //Initialize routines
            InitRoutines();

            //Cache our parser
            InputParser = new Parser();

            Initialized = true;
        }
        public override void ExecuteCommand(EvtChatCommandArgs args)
        {
            List <string> arguments = args.Command.ArgumentsAsList;

            long lastVControllerType         = DataHelper.GetSettingInt(SettingsConstants.LAST_VCONTROLLER_TYPE, 0L);
            VirtualControllerTypes curVCType = (VirtualControllerTypes)lastVControllerType;

            //See the virtual controller
            if (arguments.Count == 0)
            {
                QueueMessage($"The current virtual controller is {(VirtualControllerTypes)lastVControllerType}. To set the virtual controller, add one as an argument: {CachedVCTypesStr}");
                return;
            }

            //Invalid number of arguments
            if (arguments.Count > 1)
            {
                QueueMessage(UsageMessage);
                return;
            }

            using (BotDBContext context = DatabaseManager.OpenContext())
            {
                //Check if the user has the ability to set the type
                User user = DataHelper.GetUserNoOpen(args.Command.ChatMessage.Username, context);

                if (user != null && user.HasEnabledAbility(PermissionConstants.SET_VCONTROLLER_TYPE_ABILITY) == false)
                {
                    QueueMessage("You don't have the ability to set the virtual controller type!");
                    return;
                }
            }

            string vControllerStr = arguments[0];

            //Parse
            if (EnumUtility.TryParseEnumValue(vControllerStr, out VirtualControllerTypes parsedVCType) == false)
            {
                QueueMessage($"Please enter a valid virtual controller: {CachedVCTypesStr}");
                return;
            }

            //Same type
            if (parsedVCType == curVCType && DataContainer.ControllerMngr.Initialized == true)
            {
                QueueMessage($"The current virtual controller is already {curVCType}!");
                return;
            }

            //Make sure this virtual controller is supported on this platform
            if (VControllerHelper.IsVControllerSupported(parsedVCType, TRBotOSPlatform.CurrentOS) == false)
            {
                QueueMessage($"{parsedVCType} virtual controllers are not supported on {TRBotOSPlatform.CurrentOS} platforms.");
                return;
            }

            using (BotDBContext context = DatabaseManager.OpenContext())
            {
                //Set the value and save
                Settings lastVControllerSetting = DataHelper.GetSettingNoOpen(SettingsConstants.LAST_VCONTROLLER_TYPE, context);
                lastVControllerSetting.ValueInt = (long)parsedVCType;

                context.SaveChanges();
            }

            //Stop and halt all inputs
            InputHandler.StopAndHaltAllInputs();

            try
            {
                //Assign the new controller manager
                IVirtualControllerManager controllerMngr = VControllerHelper.GetVControllerMngrForType(parsedVCType);

                if (controllerMngr == null)
                {
                    QueueMessage($"Virtual controller manager of new type {parsedVCType} failed to initialize. This indicates an invalid {SettingsConstants.LAST_VCONTROLLER_TYPE} setting in the database or an unimplemented platform.");
                    return;
                }

                //Dispose the controller manager
                DataContainer.ControllerMngr.Dispose();

                DataContainer.SetCurVControllerType(parsedVCType);

                DataContainer.SetControllerManager(controllerMngr);

                DataContainer.ControllerMngr.Initialize();

                //Ensure we clamp the controller count to the correct value for this virtual controller manager
                int minControllerCount = DataContainer.ControllerMngr.MinControllers;
                int maxControllerCount = DataContainer.ControllerMngr.MaxControllers;

                int joystickCount    = (int)DataHelper.GetSettingInt(SettingsConstants.JOYSTICK_COUNT, 0L);
                int newJoystickCount = joystickCount;

                if (joystickCount < minControllerCount)
                {
                    QueueMessage($"Controller count of {joystickCount} is invalid. Clamping to the min of {minControllerCount}.");
                    newJoystickCount = minControllerCount;
                }
                else if (joystickCount > maxControllerCount)
                {
                    QueueMessage($"Controller count of {joystickCount} is invalid. Clamping to the max of {maxControllerCount}.");
                    newJoystickCount = maxControllerCount;
                }

                if (joystickCount != newJoystickCount)
                {
                    using (BotDBContext context = DatabaseManager.OpenContext())
                    {
                        //Adjust the joystick count setting
                        Settings joystickCountSetting = DataHelper.GetSettingNoOpen(SettingsConstants.JOYSTICK_COUNT, context);
                        joystickCountSetting.ValueInt = newJoystickCount;

                        context.SaveChanges();
                    }
                }

                int acquiredCount = DataContainer.ControllerMngr.InitControllers(newJoystickCount);

                QueueMessage($"Set virtual controller to {parsedVCType} with {acquiredCount} controller(s) and reset all running inputs!");
            }
            catch (Exception e)
            {
                DataContainer.MessageHandler.QueueMessage($"Error changing virtual controller type: {e.Message}");
                return;
            }
            finally
            {
                //Resume inputs
                InputHandler.ResumeRunningInputs();
            }
        }
Esempio n. 3
0
 public void SetControllerManager(IVirtualControllerManager controllerMngr)
 {
     ControllerMngr = controllerMngr;
 }
Esempio n. 4
0
        private static void ExecuteInput(object obj)
        {
            /*************************************************************
            * PERFORMANCE CRITICAL CODE                                  *
            * Even the smallest change must be thoroughly tested         *
            *************************************************************/

            //Increment running threads
            Interlocked.Increment(ref RunningInputThreads);

            //Get the input list - this should have been validated beforehand
            InputWrapper inputWrapper = (InputWrapper)obj;

            Parser.Input[][] inputArray = inputWrapper.InputArray;

            Stopwatch sw = new Stopwatch();

            List <int> indices = new List <int>(16);
            IVirtualControllerManager vcMngr = InputGlobals.ControllerMngr;

            int controllerCount = vcMngr.ControllerCount;

            int[] nonWaits = new int[controllerCount];

            //This is used to track which controller ports were used across all inputs
            //This helps prevent updating controllers that weren't used at the end
            int[] usedControllerPorts = new int[controllerCount];

            ConsoleBase curConsole = InputGlobals.CurrentConsole;

            //Don't check for overflow to improve performance
            unchecked
            {
                for (int i = 0; i < inputArray.Length; i++)
                {
                    ref Parser.Input[] inputs = ref inputArray[i];

                    indices.Clear();

                    //Press all buttons unless it's a release input
                    for (int j = 0; j < inputs.Length; j++)
                    {
                        indices.Add(j);

                        //Get a reference to avoid copying the struct
                        ref Parser.Input input = ref inputs[j];

                        //Don't do anything for a wait input
                        if (curConsole.IsWait(input) == true)
                        {
                            continue;
                        }

                        int port = input.controllerPort;

                        //Get the controller we're using
                        IVirtualController controller = vcMngr.GetController(port);

                        //These are set to 1 instead of incremented to prevent any chance of overflow
                        nonWaits[port]            = 1;
                        usedControllerPorts[port] = 1;

                        if (input.release == true)
                        {
                            controller.ReleaseInput(input);
                        }
                        else
                        {
                            controller.PressInput(input);
                        }
                    }

                    //Update the controllers if there are non-wait inputs
                    for (int waitIdx = 0; waitIdx < nonWaits.Length; waitIdx++)
                    {
                        if (nonWaits[waitIdx] > 0)
                        {
                            IVirtualController controller = vcMngr.GetController(waitIdx);
                            controller.UpdateController();
                            nonWaits[waitIdx] = 0;
                        }
                    }

                    sw.Start();

                    while (indices.Count > 0)
                    {
                        //End the input prematurely
                        if (StopRunningInputs == true)
                        {
                            goto End;
                        }

                        //Release buttons when we should
                        for (int j = indices.Count - 1; j >= 0; j--)
                        {
                            ref Parser.Input input = ref inputs[indices[j]];

                            if (sw.ElapsedMilliseconds < input.duration)
                            {
                                continue;
                            }

                            //Release if the input isn't a hold and isn't a wait input
                            if (input.hold == false && curConsole.IsWait(input) == false)
                            {
                                int port = input.controllerPort;

                                //Get the controller we're using
                                IVirtualController controller = vcMngr.GetController(port);

                                controller.ReleaseInput(input);

                                //Track that we have a non-wait or hold input so we can update the controller with all input releases at once
                                nonWaits[port] = 1;

                                usedControllerPorts[port] = 1;
                            }

                            indices.RemoveAt(j);
                        }

                        //Update the controllers if there are non-wait inputs
                        for (int waitIdx = 0; waitIdx < nonWaits.Length; waitIdx++)
                        {
                            if (nonWaits[waitIdx] > 0)
                            {
                                IVirtualController controller = vcMngr.GetController(waitIdx);
                                controller.UpdateController();

                                nonWaits[waitIdx] = 0;
                            }
                        }
                    }
 public InputWrapper(ParsedInput[][] inputArray, GameConsole console, IVirtualControllerManager vcMngr)
 {
     InputArray = inputArray;
     Console    = console;
     VCManager  = vcMngr;
 }
        private static void ExecuteInput(object obj)
        {
            /*************************************************************
            * PERFORMANCE CRITICAL CODE                                  *
            * Even the smallest change must be thoroughly tested         *
            *************************************************************/

            //Increment running threads
            Interlocked.Increment(ref RunningInputThreads);

            //Get the input list - this should have been validated beforehand
            InputWrapper inputWrapper = (InputWrapper)obj;

            ParsedInput[][] inputArray = inputWrapper.InputArray;

            Stopwatch sw = new Stopwatch();

            List <int> indices = new List <int>(16);
            IVirtualControllerManager vcMngr = inputWrapper.VCManager;

            int controllerCount = vcMngr.ControllerCount;

            //Use Span with stack memory to avoid allocations and improve speed
            Span <int> nonWaits = stackalloc int[controllerCount];

            nonWaits.Clear();

            //This is used to track which controller ports were used across all inputs
            //This helps prevent updating controllers that weren't used at the end
            Span <int> usedControllerPorts = stackalloc int[controllerCount];

            usedControllerPorts.Clear();

            GameConsole curConsole = inputWrapper.Console;

            //Don't check for overflow to improve performance
            unchecked
            {
                for (int i = 0; i < inputArray.Length; i++)
                {
                    ref ParsedInput[] inputs = ref inputArray[i];

                    indices.Clear();

                    //Press all buttons unless it's a release input
                    for (int j = 0; j < inputs.Length; j++)
                    {
                        indices.Add(j);

                        //Get a reference to avoid copying the struct
                        ref ParsedInput input = ref inputs[j];

                        //Don't do anything for a blank input
                        if (curConsole.IsBlankInput(input) == true)
                        {
                            continue;
                        }

                        int port = input.controllerPort;

                        //Get the controller we're using
                        IVirtualController controller = vcMngr.GetController(port);

                        //These are set to 1 instead of incremented to prevent any chance of overflow
                        nonWaits[port]            = 1;
                        usedControllerPorts[port] = 1;

                        if (input.release == true)
                        {
                            InputHelper.ReleaseInput(input, curConsole, controller);
                        }
                        else
                        {
                            InputHelper.PressInput(input, curConsole, controller);
                        }
                    }

                    //Update the controllers if there are non-wait inputs
                    for (int waitIdx = 0; waitIdx < nonWaits.Length; waitIdx++)
                    {
                        //Store by ref and change directly to avoid calling the indexer twice
                        ref int nonWaitVal = ref nonWaits[waitIdx];

                        if (nonWaitVal > 0)
                        {
                            IVirtualController controller = vcMngr.GetController(waitIdx);
                            controller.UpdateController();
                            nonWaitVal = 0;
                        }
                    }
        /// <summary>
        /// Carries out a set of inputs.
        /// </summary>
        /// <param name="inputList">A list of lists of inputs to execute.</param>
        public static void CarryOutInput(List <List <ParsedInput> > inputList, GameConsole currentConsole, IVirtualControllerManager vcManager)
        {
            /*Kimimaru: We're using a thread pool for efficiency
             * Though very unlikely, there's a chance the input won't execute right away if it has to wait for a thread to be available
             * However, there are often plenty of available threads, so this shouldn't be an issue since we use only one thread per input string
             * Uncomment the following lines to see how many threads are supported in the pool on your machine */
            //ThreadPool.GetMinThreads(out int workermin, out int completionmin);
            //ThreadPool.GetMaxThreads(out int workerthreads, out int completionPortThreads);
            //TRBotLogger.Logger.Information($"Min workers: {workermin} Max workers: {workerthreads} Min async IO threads: {completionmin} Max async IO threads: {completionPortThreads}");

            //Kimimaru: Copy the input list over to an array, which is more performant
            //and lets us bypass redundant copying and bounds checks in certain instances
            //This matters once we've begun processing inputs since we're
            //trying to reduce the delay between pressing and releasing inputs as much as we can
            ParsedInput[][] inputArray = new ParsedInput[inputList.Count][];
            for (int i = 0; i < inputArray.Length; i++)
            {
                inputArray[i] = inputList[i].ToArray();
            }

            InputWrapper inputWrapper = new InputWrapper(inputArray, currentConsole, vcManager);

            ThreadPool.QueueUserWorkItem(new WaitCallback(ExecuteInput), inputWrapper);
        }