public UserInteractionHandler(
      PluginTrace pluginTrace, PluginInfo pluginInfo, Configuration config, 
      WorldMetadata worldMetadata, PluginCooperationHandler pluginCooperationHandler, Action reloadConfigurationCallback
    ): base(pluginTrace) {
      Contract.Requires<ArgumentNullException>(pluginTrace != null);
      Contract.Requires<ArgumentNullException>(config != null);
      Contract.Requires<ArgumentNullException>(worldMetadata != null);
      Contract.Requires<ArgumentNullException>(pluginCooperationHandler != null);
      Contract.Requires<ArgumentNullException>(reloadConfigurationCallback != null);

      this.PluginInfo = pluginInfo;
      this.Config = config;
      this.WorldMetadata = worldMetadata;
      this.PluginCooperationHandler = pluginCooperationHandler;
      this.ReloadConfigurationCallback = reloadConfigurationCallback;

      this.RegisterCommand(new[] { "advancedcircuits", "ac" }, this.RootCommand_Exec);
    }
    public CircuitHandler(PluginTrace pluginTrace,Configuration config, WorldMetadata worldMetadata, PluginCooperationHandler pluginCooperationHandler) {
      Contract.Requires<ArgumentNullException>(pluginTrace != null);
      Contract.Requires<ArgumentNullException>(config != null);
      Contract.Requires<ArgumentNullException>(worldMetadata != null);
      Contract.Requires<ArgumentNullException>(pluginCooperationHandler != null);

      this.PluginTrace = pluginTrace;
      this.Config = config;
      this.WorldMetadata = worldMetadata;
      this.PluginCooperationHandler = pluginCooperationHandler;
      this.isDayTime = Main.dayTime;
      this.isDaylight = (Main.dayTime && Main.time >= 7200 && Main.time <= 46800);

      // Timers are always inactive when a map is loaded, so switch them into their active state.
      foreach (DPoint activeTimerLocation in this.WorldMetadata.ActiveTimers.Keys) {
        ObjectMeasureData timerMeasureData = TerrariaUtils.Tiles.MeasureObject(activeTimerLocation);
        
        if(!TerrariaUtils.Tiles.ObjectHasActiveState(timerMeasureData))
          TerrariaUtils.Tiles.SetObjectState(timerMeasureData, true);
      }
    }
    public static Configuration Read(string filePath) {
      XmlReaderSettings configReaderSettings = new XmlReaderSettings {
        ValidationType = ValidationType.Schema,
        ValidationFlags = XmlSchemaValidationFlags.ProcessIdentityConstraints | XmlSchemaValidationFlags.ReportValidationWarnings
      };
      
      string configSchemaPath = Path.Combine(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath) + ".xsd");
      configReaderSettings.Schemas.Add(null, configSchemaPath);

      XmlDocument document = new XmlDocument();
      using (XmlReader configReader = XmlReader.Create(filePath, configReaderSettings))
        document.Load(configReader);

      // Before validating using the schema, first check if the configuration file's version matches with the supported version.
      XmlElement rootElement = document.DocumentElement;
      string fileVersionRaw;
      if (rootElement.HasAttribute("Version"))
        fileVersionRaw = rootElement.GetAttribute("Version");
      else
        fileVersionRaw = "1.0";
      
      if (fileVersionRaw != Configuration.CurrentVersion) {
        throw new FormatException(string.Format(
          "The configuration file is either outdated or too new. Expected version was: {0}. File version is: {1}", 
          Configuration.CurrentVersion, fileVersionRaw
        ));
      }

      Configuration resultingConfig = new Configuration(false);
      resultingConfig.OverrideVanillaCircuits = BoolEx.ParseEx(rootElement["OverrideVanillaCircuits"].InnerXml);
      resultingConfig.AdvancedCircuitsEnabled = BoolEx.ParseEx(rootElement["AdvancedCircuitsEnabled"].InnerText);
      resultingConfig.MaxTrapsPerCircuit  = int.Parse(rootElement["MaxTrapsPerCircuit"].InnerText);
      resultingConfig.MaxStatuesPerCircuit    = int.Parse(rootElement["MaxStatuesPerCircuit"].InnerText);
      resultingConfig.MaxPumpsPerCircuit      = int.Parse(rootElement["MaxPumpsPerCircuit"].InnerText);
      resultingConfig.MaxCircuitLength        = int.Parse(rootElement["MaxCircuitLength"].InnerText);
      if (string.IsNullOrWhiteSpace(rootElement["MaxTimerActivityTime"].InnerText)) {
        resultingConfig.MaxTimerActivityTime = TimeSpan.Zero;
      } else {
        TimeSpan maxTimerActivityTime;
        if (TimeSpanEx.TryParseShort(rootElement["MaxTimerActivityTime"].InnerText, out maxTimerActivityTime))
          resultingConfig.MaxTimerActivityTime = maxTimerActivityTime;
      }
      resultingConfig.SignConfig              = SignConfig.FromXmlElement(rootElement["SignConfig"]);
      resultingConfig.BlockActivatorConfig    = BlockActivatorConfig.FromXmlElement(rootElement["BlockActivatorConfig"]);

      XmlElement pumpConfigsNode = rootElement["PumpConfigs"];
      foreach (XmlNode pumpConfigNode in pumpConfigsNode.ChildNodes) {
        XmlElement pumpConfigElement = (pumpConfigNode as XmlElement);
        if (pumpConfigElement == null)
          continue;

        PaintColor paintColor = (PaintColor)Enum.Parse(typeof(PaintColor), pumpConfigElement.Attributes["Paint"].Value);
        resultingConfig.PumpConfigs.Add(paintColor, PumpConfig.FromXmlElement(pumpConfigElement));
      }

      XmlElement trapConfigsNode = rootElement["TrapConfigs"];
      foreach (XmlNode trapConfigNode in trapConfigsNode.ChildNodes) {
        XmlElement trapConfigElement = (trapConfigNode as XmlElement);
        if (trapConfigElement == null)
          continue;
        
        TrapStyle trapStyle = (TrapStyle)Enum.Parse(typeof(TrapStyle), trapConfigElement.Attributes["TrapType"].Value);
        PaintColor paintColor = (PaintColor)Enum.Parse(typeof(PaintColor), trapConfigElement.Attributes["Paint"].Value);
        resultingConfig.TrapConfigs.Add(new TrapConfigKey(trapStyle, paintColor), TrapConfig.FromXmlElement(trapConfigElement));
      }

      /*XmlElement explosivesConfigsNode = rootElement["ExplosivesConfigs"];
      foreach (XmlNode explosivesConfigNode in explosivesConfigsNode.ChildNodes) {
        XmlElement explosivesConfigElement = (explosivesConfigNode as XmlElement);
        if (explosivesConfigElement == null)
          continue;

        ComponentConfigProfile componentConfigProfile = (ComponentConfigProfile)Enum.Parse(typeof(ComponentConfigProfile), explosivesConfigElement.Attributes["Profile"].Value);
        resultingConfig.explosivesConfigs.Add(componentConfigProfile, ExplosivesConfig.FromXmlElement(explosivesConfigElement));
      }*/

      XmlElement wirelessTransmitterConfigsNode = rootElement["WirelessTransmitterConfigs"];
      foreach (XmlNode wirelessTransmitterConfigNode in wirelessTransmitterConfigsNode.ChildNodes) {
        XmlElement wirelessTransmitterConfigElement = (wirelessTransmitterConfigNode as XmlElement);
        if (wirelessTransmitterConfigElement == null)
          continue;

        PaintColor paintColor = (PaintColor)Enum.Parse(typeof(PaintColor), wirelessTransmitterConfigElement.Attributes["Paint"].Value);
        resultingConfig.WirelessTransmitterConfigs.Add(paintColor, WirelessTransmitterConfig.FromXmlElement(wirelessTransmitterConfigElement));
      }

      XmlElement statueConfigsNode = rootElement["StatueConfigs"];
      foreach (XmlNode statueConfigNode in statueConfigsNode.ChildNodes) {
        XmlElement statueConfigElement = (statueConfigNode as XmlElement);
        if (statueConfigElement == null)
          continue;

        StatueStyle statueStyle = (StatueStyle)Enum.Parse(typeof(StatueStyle), statueConfigElement.Attributes["StatueType"].Value);
        resultingConfig.StatueConfigs.Add(statueStyle, StatueConfig.FromXmlElement(statueConfigElement));
      }

      return resultingConfig;
    }