static void DumpPlayerGroupStats( TextWriter writer, PlayerInfo[] infos, string groupName ) { RankStats stat = new RankStats(); foreach( Rank rank2 in RankManager.Ranks ) { stat.PreviousRank.Add( rank2, 0 ); } infos = infos.Where( info => (info.TimeSinceLastLogin.TotalDays < 30 && !info.Banned) ).ToArray(); if( infos.Length == 0 ) { writer.WriteLine( "{0}: 0 players, 0 banned", groupName ); writer.WriteLine(); return; } for( int i = 0; i < infos.Length; i++ ) { stat.TimeSinceFirstLogin += infos[i].TimeSinceFirstLogin; stat.TimeSinceLastLogin += infos[i].TimeSinceLastLogin; stat.TotalTime += infos[i].TotalTime; stat.BlocksBuilt += infos[i].BlocksBuilt; stat.BlocksDeleted += infos[i].BlocksDeleted; stat.TimesVisited += infos[i].TimesVisited; stat.MessagesWritten += infos[i].LinesWritten; stat.TimesKicked += infos[i].TimesKicked; stat.TimesKickedOthers += infos[i].TimesKickedOthers; stat.TimesBannedOthers += infos[i].TimesBannedOthers; if( infos[i].Banned ) stat.Banned++; if( infos[i].PreviousRank != null ) stat.PreviousRank[infos[i].PreviousRank]++; } stat.BlockRatio = stat.BlocksBuilt / (double)Math.Max( stat.BlocksDeleted, 1 ); stat.BlocksChanged = stat.BlocksDeleted + stat.BlocksBuilt; stat.TimeSinceFirstLoginMedian = DateTime.UtcNow.Subtract( infos.OrderByDescending( info => info.FirstLoginDate ) .ElementAt( infos.Length / 2 ).FirstLoginDate ); stat.TimeSinceLastLoginMedian = DateTime.UtcNow.Subtract( infos.OrderByDescending( info => info.LastLoginDate ) .ElementAt( infos.Length / 2 ).LastLoginDate ); stat.TotalTimeMedian = infos.OrderByDescending( info => info.TotalTime ).ElementAt( infos.Length / 2 ).TotalTime; stat.BlocksBuiltMedian = infos.OrderByDescending( info => info.BlocksBuilt ).ElementAt( infos.Length / 2 ).BlocksBuilt; stat.BlocksDeletedMedian = infos.OrderByDescending( info => info.BlocksDeleted ).ElementAt( infos.Length / 2 ).BlocksDeleted; PlayerInfo medianBlocksChangedPlayerInfo = infos.OrderByDescending( info => (info.BlocksDeleted + info.BlocksBuilt) ).ElementAt( infos.Length / 2 ); stat.BlocksChangedMedian = medianBlocksChangedPlayerInfo.BlocksDeleted + medianBlocksChangedPlayerInfo.BlocksBuilt; PlayerInfo medianBlockRatioPlayerInfo = infos.OrderByDescending( info => (info.BlocksBuilt / (double)Math.Max( info.BlocksDeleted, 1 )) ) .ElementAt( infos.Length / 2 ); stat.BlockRatioMedian = medianBlockRatioPlayerInfo.BlocksBuilt / (double)Math.Max( medianBlockRatioPlayerInfo.BlocksDeleted, 1 ); stat.TimesVisitedMedian = infos.OrderByDescending( info => info.TimesVisited ).ElementAt( infos.Length / 2 ).TimesVisited; stat.MessagesWrittenMedian = infos.OrderByDescending( info => info.LinesWritten ).ElementAt( infos.Length / 2 ).LinesWritten; stat.TimesKickedMedian = infos.OrderByDescending( info => info.TimesKicked ).ElementAt( infos.Length / 2 ).TimesKicked; stat.TimesKickedOthersMedian = infos.OrderByDescending( info => info.TimesKickedOthers ).ElementAt( infos.Length / 2 ).TimesKickedOthers; stat.TimesBannedOthersMedian = infos.OrderByDescending( info => info.TimesBannedOthers ).ElementAt( infos.Length / 2 ).TimesBannedOthers; stat.TopTimeSinceFirstLogin = infos.OrderBy( info => info.FirstLoginDate ).ToArray(); stat.TopTimeSinceLastLogin = infos.OrderBy( info => info.LastLoginDate ).ToArray(); stat.TopTotalTime = infos.OrderByDescending( info => info.TotalTime ).ToArray(); stat.TopBlocksBuilt = infos.OrderByDescending( info => info.BlocksBuilt ).ToArray(); stat.TopBlocksDeleted = infos.OrderByDescending( info => info.BlocksDeleted ).ToArray(); stat.TopBlocksChanged = infos.OrderByDescending( info => (info.BlocksDeleted + info.BlocksBuilt) ).ToArray(); stat.TopBlockRatio = infos.OrderByDescending( info => (info.BlocksBuilt / (double)Math.Max( info.BlocksDeleted, 1 )) ).ToArray(); stat.TopTimesVisited = infos.OrderByDescending( info => info.TimesVisited ).ToArray(); stat.TopMessagesWritten = infos.OrderByDescending( info => info.LinesWritten ).ToArray(); stat.TopTimesKicked = infos.OrderByDescending( info => info.TimesKicked ).ToArray(); stat.TopTimesKickedOthers = infos.OrderByDescending( info => info.TimesKickedOthers ).ToArray(); stat.TopTimesBannedOthers = infos.OrderByDescending( info => info.TimesBannedOthers ).ToArray(); writer.WriteLine( "{0}: {1} players, {2} banned", groupName, infos.Length, stat.Banned ); writer.WriteLine( " TimeSinceFirstLogin: {0} mean, {1} median, {2} total", TimeSpan.FromTicks( stat.TimeSinceFirstLogin.Ticks / infos.Length ).ToCompactString(), stat.TimeSinceFirstLoginMedian.ToCompactString(), stat.TimeSinceFirstLogin.ToCompactString() ); if( infos.Count() > TopPlayersToList * 2 + 1 ) { foreach( PlayerInfo info in stat.TopTimeSinceFirstLogin.Take( TopPlayersToList ) ) { writer.WriteLine( " {0,20} {1}", info.TimeSinceFirstLogin.ToCompactString(), info.Name ); } writer.WriteLine( " ...." ); foreach( PlayerInfo info in stat.TopTimeSinceFirstLogin.Reverse().Take( TopPlayersToList ).Reverse() ) { writer.WriteLine( " {0,20} {1}", info.TimeSinceFirstLogin.ToCompactString(), info.Name ); } } else { foreach( PlayerInfo info in stat.TopTimeSinceFirstLogin ) { writer.WriteLine( " {0,20} {1}", info.TimeSinceFirstLogin.ToCompactString(), info.Name ); } } writer.WriteLine(); writer.WriteLine( " TimeSinceLastLogin: {0} mean, {1} median, {2} total", TimeSpan.FromTicks( stat.TimeSinceLastLogin.Ticks / infos.Length ).ToCompactString(), stat.TimeSinceLastLoginMedian.ToCompactString(), stat.TimeSinceLastLogin.ToCompactString() ); if( infos.Count() > TopPlayersToList * 2 + 1 ) { foreach( PlayerInfo info in stat.TopTimeSinceLastLogin.Take( TopPlayersToList ) ) { writer.WriteLine( " {0,20} {1}", info.TimeSinceLastLogin.ToCompactString(), info.Name ); } writer.WriteLine( " ...." ); foreach( PlayerInfo info in stat.TopTimeSinceLastLogin.Reverse().Take( TopPlayersToList ).Reverse() ) { writer.WriteLine( " {0,20} {1}", info.TimeSinceLastLogin.ToCompactString(), info.Name ); } } else { foreach( PlayerInfo info in stat.TopTimeSinceLastLogin ) { writer.WriteLine( " {0,20} {1}", info.TimeSinceLastLogin.ToCompactString(), info.Name ); } } writer.WriteLine(); writer.WriteLine( " TotalTime: {0} mean, {1} median, {2} total", TimeSpan.FromTicks( stat.TotalTime.Ticks / infos.Length ).ToCompactString(), stat.TotalTimeMedian.ToCompactString(), stat.TotalTime.ToCompactString() ); if( infos.Count() > TopPlayersToList * 2 + 1 ) { foreach( PlayerInfo info in stat.TopTotalTime.Take( TopPlayersToList ) ) { writer.WriteLine( " {0,20} {1}", info.TotalTime.ToCompactString(), info.Name ); } writer.WriteLine( " ...." ); foreach( PlayerInfo info in stat.TopTotalTime.Reverse().Take( TopPlayersToList ).Reverse() ) { writer.WriteLine( " {0,20} {1}", info.TotalTime.ToCompactString(), info.Name ); } } else { foreach( PlayerInfo info in stat.TopTotalTime ) { writer.WriteLine( " {0,20} {1}", info.TotalTime.ToCompactString(), info.Name ); } } writer.WriteLine(); writer.WriteLine( " BlocksBuilt: {0} mean, {1} median, {2} total", stat.BlocksBuilt / infos.Length, stat.BlocksBuiltMedian, stat.BlocksBuilt ); if( infos.Count() > TopPlayersToList * 2 + 1 ) { foreach( PlayerInfo info in stat.TopBlocksBuilt.Take( TopPlayersToList ) ) { writer.WriteLine( " {0,20} {1}", info.BlocksBuilt, info.Name ); } writer.WriteLine( " ...." ); foreach( PlayerInfo info in stat.TopBlocksBuilt.Reverse().Take( TopPlayersToList ).Reverse() ) { writer.WriteLine( " {0,20} {1}", info.BlocksBuilt, info.Name ); } } else { foreach( PlayerInfo info in stat.TopBlocksBuilt ) { writer.WriteLine( " {0,20} {1}", info.BlocksBuilt, info.Name ); } } writer.WriteLine(); writer.WriteLine( " BlocksDeleted: {0} mean, {1} median, {2} total", stat.BlocksDeleted / infos.Length, stat.BlocksDeletedMedian, stat.BlocksDeleted ); if( infos.Count() > TopPlayersToList * 2 + 1 ) { foreach( PlayerInfo info in stat.TopBlocksDeleted.Take( TopPlayersToList ) ) { writer.WriteLine( " {0,20} {1}", info.BlocksDeleted, info.Name ); } writer.WriteLine( " ...." ); foreach( PlayerInfo info in stat.TopBlocksDeleted.Reverse().Take( TopPlayersToList ).Reverse() ) { writer.WriteLine( " {0,20} {1}", info.BlocksDeleted, info.Name ); } } else { foreach( PlayerInfo info in stat.TopBlocksDeleted ) { writer.WriteLine( " {0,20} {1}", info.BlocksDeleted, info.Name ); } } writer.WriteLine(); writer.WriteLine( " BlocksChanged: {0} mean, {1} median, {2} total", stat.BlocksChanged / infos.Length, stat.BlocksChangedMedian, stat.BlocksChanged ); if( infos.Count() > TopPlayersToList * 2 + 1 ) { foreach( PlayerInfo info in stat.TopBlocksChanged.Take( TopPlayersToList ) ) { writer.WriteLine( " {0,20} {1}", (info.BlocksDeleted + info.BlocksBuilt), info.Name ); } writer.WriteLine( " ...." ); foreach( PlayerInfo info in stat.TopBlocksChanged.Reverse().Take( TopPlayersToList ).Reverse() ) { writer.WriteLine( " {0,20} {1}", (info.BlocksDeleted + info.BlocksBuilt), info.Name ); } } else { foreach( PlayerInfo info in stat.TopBlocksChanged ) { writer.WriteLine( " {0,20} {1}", (info.BlocksDeleted + info.BlocksBuilt), info.Name ); } } writer.WriteLine(); writer.WriteLine( " BlockRatio: {0:0.000} mean, {1:0.000} median", stat.BlockRatio, stat.BlockRatioMedian ); if( infos.Count() > TopPlayersToList * 2 + 1 ) { foreach( PlayerInfo info in stat.TopBlockRatio.Take( TopPlayersToList ) ) { writer.WriteLine( " {0,20:0.000} {1}", (info.BlocksBuilt / (double)Math.Max( info.BlocksDeleted, 1 )), info.Name ); } writer.WriteLine( " ...." ); foreach( PlayerInfo info in stat.TopBlockRatio.Reverse().Take( TopPlayersToList ).Reverse() ) { writer.WriteLine( " {0,20:0.000} {1}", (info.BlocksBuilt / (double)Math.Max( info.BlocksDeleted, 1 )), info.Name ); } } else { foreach( PlayerInfo info in stat.TopBlockRatio ) { writer.WriteLine( " {0,20:0.000} {1}", (info.BlocksBuilt / (double)Math.Max( info.BlocksDeleted, 1 )), info.Name ); } } writer.WriteLine(); writer.WriteLine( " TimesVisited: {0} mean, {1} median, {2} total", stat.TimesVisited / infos.Length, stat.TimesVisitedMedian, stat.TimesVisited ); if( infos.Count() > TopPlayersToList * 2 + 1 ) { foreach( PlayerInfo info in stat.TopTimesVisited.Take( TopPlayersToList ) ) { writer.WriteLine( " {0,20} {1}", info.TimesVisited, info.Name ); } writer.WriteLine( " ...." ); foreach( PlayerInfo info in stat.TopTimesVisited.Reverse().Take( TopPlayersToList ).Reverse() ) { writer.WriteLine( " {0,20} {1}", info.TimesVisited, info.Name ); } } else { foreach( PlayerInfo info in stat.TopTimesVisited ) { writer.WriteLine( " {0,20} {1}", info.TimesVisited, info.Name ); } } writer.WriteLine(); writer.WriteLine( " MessagesWritten: {0} mean, {1} median, {2} total", stat.MessagesWritten / infos.Length, stat.MessagesWrittenMedian, stat.MessagesWritten ); if( infos.Count() > TopPlayersToList * 2 + 1 ) { foreach( PlayerInfo info in stat.TopMessagesWritten.Take( TopPlayersToList ) ) { writer.WriteLine( " {0,20} {1}", info.LinesWritten, info.Name ); } writer.WriteLine( " ...." ); foreach( PlayerInfo info in stat.TopMessagesWritten.Reverse().Take( TopPlayersToList ).Reverse() ) { writer.WriteLine( " {0,20} {1}", info.LinesWritten, info.Name ); } } else { foreach( PlayerInfo info in stat.TopMessagesWritten ) { writer.WriteLine( " {0,20} {1}", info.LinesWritten, info.Name ); } } writer.WriteLine(); writer.WriteLine( " TimesKicked: {0:0.0} mean, {1} median, {2} total", stat.TimesKicked / (double)infos.Length, stat.TimesKickedMedian, stat.TimesKicked ); if( infos.Count() > TopPlayersToList * 2 + 1 ) { foreach( PlayerInfo info in stat.TopTimesKicked.Take( TopPlayersToList ) ) { writer.WriteLine( " {0,20} {1}", info.TimesKicked, info.Name ); } writer.WriteLine( " ...." ); foreach( PlayerInfo info in stat.TopTimesKicked.Reverse().Take( TopPlayersToList ).Reverse() ) { writer.WriteLine( " {0,20} {1}", info.TimesKicked, info.Name ); } } else { foreach( PlayerInfo info in stat.TopTimesKicked ) { writer.WriteLine( " {0,20} {1}", info.TimesKicked, info.Name ); } } writer.WriteLine(); writer.WriteLine( " TimesKickedOthers: {0:0.0} mean, {1} median, {2} total", stat.TimesKickedOthers / (double)infos.Length, stat.TimesKickedOthersMedian, stat.TimesKickedOthers ); if( infos.Count() > TopPlayersToList * 2 + 1 ) { foreach( PlayerInfo info in stat.TopTimesKickedOthers.Take( TopPlayersToList ) ) { writer.WriteLine( " {0,20} {1}", info.TimesKickedOthers, info.Name ); } writer.WriteLine( " ...." ); foreach( PlayerInfo info in stat.TopTimesKickedOthers.Reverse().Take( TopPlayersToList ).Reverse() ) { writer.WriteLine( " {0,20} {1}", info.TimesKickedOthers, info.Name ); } } else { foreach( PlayerInfo info in stat.TopTimesKickedOthers ) { writer.WriteLine( " {0,20} {1}", info.TimesKickedOthers, info.Name ); } } writer.WriteLine(); writer.WriteLine( " TimesBannedOthers: {0:0.0} mean, {1} median, {2} total", stat.TimesBannedOthers / (double)infos.Length, stat.TimesBannedOthersMedian, stat.TimesBannedOthers ); if( infos.Count() > TopPlayersToList * 2 + 1 ) { foreach( PlayerInfo info in stat.TopTimesBannedOthers.Take( TopPlayersToList ) ) { writer.WriteLine( " {0,20} {1}", info.TimesBannedOthers, info.Name ); } writer.WriteLine( " ...." ); foreach( PlayerInfo info in stat.TopTimesBannedOthers.Reverse().Take( TopPlayersToList ).Reverse() ) { writer.WriteLine( " {0,20} {1}", info.TimesBannedOthers, info.Name ); } } else { foreach( PlayerInfo info in stat.TopTimesBannedOthers ) { writer.WriteLine( " {0,20} {1}", info.TimesBannedOthers, info.Name ); } } writer.WriteLine(); }