public MULTIbalancer()
{
/* Private members */
fIsEnabled = false;
fFinalizerActive = false;
fAborted = false;
fPluginState = PluginState.Disabled;
fGameState = GameState.Unknown;
fServerInfo = null;
fRefreshCommand = false;
fServerUptime = 0;
fServerCrashed = false;
fDebugScramblerBefore = new List<PlayerModel>[2]{new List<PlayerModel>(), new List<PlayerModel>()};
fDebugScramblerAfter = new List<PlayerModel>[2]{new List<PlayerModel>(), new List<PlayerModel>()};
fDebugScramblerStartRound = new List<PlayerModel>[2]{new List<PlayerModel>(), new List<PlayerModel>()};
fBalancedRound = 0;
fUnstackedRound = 0;
fUnswitchedRound = 0;
fExcludedRound = 0;
fExemptRound = 0;
fFailedRound = 0;
fTotalRound = 0;
fBalanceIsActive = false;
fRoundsEnabled = 0;
fGrandTotalQuits = 0;
fGrandRageQuits = 0;
fTotalQuits = 0;
fRageQuits = 0;
fPlayerCount = 0;
fBF4CommanderCount = 0;
fBF4SpectatorCount = 0;
fMoveThread = null;
fFetchThread = null;
fListPlayersThread = null;
fScramblerThread = null;
fTimerThread = null;
fModeToSimple = new Dictionary<String,String>();
fEasyTypeDict = new Dictionary<int, Type>();
fEasyTypeDict.Add(0, typeof(int));
fEasyTypeDict.Add(1, typeof(Int16));
fEasyTypeDict.Add(2, typeof(Int32));
fEasyTypeDict.Add(3, typeof(Int64));
fEasyTypeDict.Add(4, typeof(float));
fEasyTypeDict.Add(5, typeof(long));
fEasyTypeDict.Add(6, typeof(String));
fEasyTypeDict.Add(7, typeof(string));
fEasyTypeDict.Add(8, typeof(double));
fBoolDict = new Dictionary<int, Type>();
fBoolDict.Add(0, typeof(Boolean));
fBoolDict.Add(1, typeof(bool));
fListStrDict = new Dictionary<int, Type>();
fListStrDict.Add(0, typeof(String[]));
fPerMode = new Dictionary<String,PerModeSettings>();
fAllPlayers = new List<String>();
fKnownPlayers = new Dictionary<String, PlayerModel>();
fTeam1 = new List<PlayerModel>();
fTeam2 = new List<PlayerModel>();
fTeam3 = new List<PlayerModel>();
fTeam4 = new List<PlayerModel>();
fUnassigned = new List<String>();
fRoundStartTimestamp = DateTime.MinValue;
fRoundOverTimestamp = DateTime.MinValue;
fListPlayersTimestamp = DateTime.MinValue;
fFullUnstackSwapTimestamp = DateTime.MinValue;
fLastValidationTimestamp = DateTime.MinValue;
fListPlayersQ = new Queue<DelayedRequest>();
fPendingTeamChange = new Dictionary<String,int>();
fMoving = new Dictionary<String, MoveInfo>();
fMoveQ = new Queue<MoveInfo>();
fReassigned = new List<String>();
fReservedSlots = new List<String>();
fTickets = new int[5]{0,0,0,0,0};
fFriendlyMaps = new Dictionary<String,String>();
fFriendlyModes = new Dictionary<String,String>();
fMaxTickets = -1;
fRushMaxTickets = -1;
fLastBalancedTimestamp = DateTime.MinValue;
fEnabledTimestamp = DateTime.MinValue;
fFinalStatus = null;
fIsFullRound = false;
fUnstackState = UnstackState.Off;
fLastMsg = null;
fRushStage = 0;
fRushPrevAttackerTickets = 0;
fRushAttackerStageLoss = 0;
fRushAttackerStageSamples = 0;
fMoveStash = new List<MoveInfo>();
fLastVersionCheckTimestamp = DateTime.MinValue;
fTimeOutOfJoint = 0;
fUnstackGroupCount = 0;
fPriorityFetchQ = new PriorityQueue(this);
fIsCacheEnabled = false;
fScramblerLock = new DelayedRequest();
fWinner = 0;
fUpdateThreadLock = new DelayedRequest();
fLastServerInfoTimestamp = DateTime.Now;
fStageInProgress = false;
fHost = String.Empty;
fPort = String.Empty;
fRushMap3Stages = new List<String>(new String[11]{"MP_007", "XP4_Quake", "XP5_002", "MP_012", "XP4_Rubble", "MP_Damage", "XP0_Caspian", "XP0_Firestorm", "XP1_001" /* BF4 */, "XP1_003" /* BF4 */, "XP2_003"});
fRushMap5Stages = new List<String>(new String[6]{"MP_013", "XP3_Valley", "MP_017", "XP5_001", "MP_Prison", "MP_Siege"});
fGroupAssignments = new int[5]{0,0,0,0,0};
fDispersalGroups = new List<String>[5]{null, new List<String>(), new List<String>(), new List<String>(), new List<String>()};
fNeedPlayerListUpdate = false;
fFriends = new Dictionary<int, List<String>>();
fAllFriends = new List<String>();
fWhileScrambling = false;
fExtrasLock = new DelayedRequest();
fExtraNames = new List<String>();
fGotLogin = false;
fDebugScramblerSuspects = new Dictionary<String,String>();
fTimerRequestList = new List<DelayedRequest>();
fAverageTicketLoss = new Queue<double>[3]{null, new Queue<double>(), new Queue<double>()};
fTicketLossHistogram = new Histogram();
fFactionByTeam = new int[5]{-1,-1,-1,-1,-1};
fRevealSettings = false;
fShowRiskySettings = false;
fLastFastMoveTimestamp = DateTime.MinValue;
fRoundTimeLimit = 1.0;
fScrambleByCommand = false;
fDisableUnswitcherByRemote = false;
fLastAutoChatTimestamp = DateTime.MinValue;
/* Settings */
/* ===== SECTION 0 - Presets ===== */
SettingsVersion = 1;
Preset = PresetItems.Standard;
EnableUnstacking = false;
EnableAdminKillForFastBalance = false;
SelectFastBalanceBy = ForceMove.Newest;
EnableSettingsWizard = false;
WhichMode = "Conquest Large";
MetroIsInMapRotation = false;
MaximumPlayersForMode = 64;
LowestMaximumTicketsForMode = 300;
HighestMaximumTicketsForMode = 400;
PreferredStyleOfBalancing = PresetItems.Standard;
ApplySettingsChanges = false;
/* ===== SECTION 1 - Settings ===== */
DebugLevel = 2;
MaximumServerSize = 64;
EnableBattlelogRequests = true;
MaximumRequestRate = 10; // in 20 seconds
WaitTimeout = 30; // seconds
WhichBattlelogStats = BattlelogStats.ClanTagOnly;
MaxTeamSwitchesByStrongPlayers = 1;
MaxTeamSwitchesByWeakPlayers = 2;
UnlimitedTeamSwitchingDuringFirstMinutesOfRound = 5.0;
Enable2SlotReserve = false;
EnablerecruitCommand = false;
EnableWhitelistingOfReservedSlotsList = true;
Whitelist = new String[] {DEFAULT_LIST_ITEM};
fSettingWhitelist = new List<String>(Whitelist);
DisperseEvenlyList = new String[] {DEFAULT_LIST_ITEM};
fSettingDisperseEvenlyList = new List<String>(DisperseEvenlyList);
FriendsList = new String[] {DEFAULT_LIST_ITEM};
fSettingFriendsList = new List<String>();
SecondsUntilAdaptiveSpeedBecomesFast = 3*60; // 3 minutes default
EnableInGameCommands = true;
ReassignNewPlayers = true;
EnableTicketLossRateLogging = false;
/* ===== SECTION 2 - Exclusions ===== */
OnWhitelist = true;
OnFriendsList = false;
ApplyFriendsListToTeam = false;
TopScorers = true;
SameClanTagsInSquad = true;
SameClanTagsInTeam = false;
SameClanTagsForRankDispersal = false;
LenientRankDispersal = false;
MinutesAfterJoining = 5;
MinutesAfterBeingMoved = 90; // 1.5 hours
JoinedEarlyPhase = true;
JoinedMidPhase = true;
JoinedLatePhase = false;
/* ===== SECTION 3 - Round Phase & Population Settings ===== */
EarlyPhaseTicketPercentageToUnstack = new double[3] { 0,120,120};
MidPhaseTicketPercentageToUnstack = new double[3] { 0,120,120};
LatePhaseTicketPercentageToUnstack = new double[3] { 0, 0, 0};
SpellingOfSpeedNamesReminder = Speed.Click_Here_For_Speed_Names;
EarlyPhaseBalanceSpeed = new Speed[3] { Speed.Fast, Speed.Adaptive, Speed.Adaptive};
MidPhaseBalanceSpeed = new Speed[3] { Speed.Fast, Speed.Adaptive, Speed.Adaptive};
LatePhaseBalanceSpeed = new Speed[3] { Speed.Stop, Speed.Stop, Speed.Stop};
/* ===== SECTION 4 - Scrambler ===== */
OnlyByCommand = false;
OnlyOnNewMaps = true; // false means scramble every round
OnlyOnFinalTicketPercentage = 120; // 0 means scramble regardless of final score
ScrambleBy = DefineStrong.RoundScore;
KeepSquadsTogether = true;
KeepClanTagsInSameTeam = true;
KeepFriendsInSameTeam = false;
DivideBy = DivideByChoices.None;
ClanTagToDivideBy = String.Empty;
DelaySeconds = 50;
/* ===== SECTION 5 - Messages ===== */
QuietMode = false; // false: chat is global, true: chat is private. Yells are always private
YellDurationSeconds = 10;
BadBecauseMovedByBalancer = "autobalance moved you to the %toTeam% team";
BadBecauseWinningTeam = "switching to the winning team is not allowed";
BadBecauseBiggestTeam = "switching to the biggest team is not allowed";
BadBecauseRank = "this server splits high rank players between teams";
BadBecauseDispersalList = "you're on the list of players to split between teams";
BadBecauseClan = "players with same clan tags are split up"; // DCE
ChatMovedForBalance = "*** MOVED %name% for balance ...";
YellMovedForBalance = "Moved %name% for balance ...";
ChatMovedToUnstack = "*** MOVED %name% to unstack teams ...";
YellMovedToUnstack = "Moved %name% to unstack teams ...";
ChatDetectedBadTeamSwitch = "%name%, you can't switch to team %fromTeam%: %reason%, sending you back ...";
YellDetectedBadTeamSwitch = "You can't switch to the %fromTeam% team: %reason%, sending you back!";
ChatDetectedGoodTeamSwitch = "%name%, thanks for helping out the %toTeam% team!";
YellDetectedGoodTeamSwitch = "Thanks for helping out the %toTeam% team!";
ChatAfterUnswitching = "%name%, please stay on the %toTeam% team for the rest of this round";
YellAfterUnswitching = "Please stay on the %toTeam% team for the rest of this round";
TeamsWillBeScrambled = "*** Teams will be SCRAMBLED next round!";
ChatAutobalancing = "Preparing to autobalance ... (%technicalDetails%)";
YellAutobalancing = String.Empty; // no yell by default
/* ===== SECTION 6 - Unswitcher ===== */
EnableImmediateUnswitch = true;
ForbidSwitchingAfterAutobalance = UnswitchChoice.Always;
ForbidSwitchingToWinningTeam = UnswitchChoice.Always;
ForbidSwitchingToBiggestTeam = UnswitchChoice.Always;
ForbidSwitchingAfterDispersal = UnswitchChoice.Always;
/* ===== SECTION 7 - TBD ===== */
/* ===== SECTION 8 - Per-Mode Settings ===== */
/* ===== SECTION 9 - Debug Settings ===== */
ShowInLog = INVALID_NAME_TAG_GUID;
ShowCommandInLog = String.Empty;
LogChat = true;
EnableLoggingOnlyMode = false;
EnableExternalLogging = false;
ExternalLogSuffix = "_mb.log";
EnableRiskyFeatures = false;
}