Fixed memory leak, trivia hints are after 6 sec, etc

This commit is contained in:
Master Kwoth
2016-01-30 05:24:32 +01:00
parent 1316c83bd4
commit 00f09b66bd
11 changed files with 178 additions and 209 deletions

View File

@@ -5,27 +5,31 @@ using System.Threading.Tasks;
using System.Security.Cryptography;
using Discord.Commands;
using Discord;
using Discord.Legacy;
using NadekoBot.Modules;
using System.IO;
using System.Drawing;
namespace NadekoBot.Extensions
{
namespace NadekoBot.Extensions {
public static class Extensions
{
public static string Scramble(this string word) {
var letters = word.ToArray();
for (int i = 0; i < letters.Length; i++)
{
if (i % 3 == 0)
{
int count = 0;
for (int i = 0; i < letters.Length; i++) {
if (letters[i] == ' ')
continue;
count++;
if (count <= letters.Length / 5)
continue;
if (count % 3 == 0)
continue;
}
if (letters[i] != ' ')
letters[i] = '_';
}
return "`"+string.Join(" ", letters)+"`";
}

View File

@@ -15,13 +15,15 @@ namespace NadekoBot.Classes.Music {
public StreamRequest CurrentSong;
public bool IsPaused { get; internal set; } = false;
public bool Stopped { get; private set; }
public IAudioClient VoiceClient;
private readonly object _voiceLock = new object();
public MusicControls() {
Task.Run(async () => {
while (true) {
while (!Stopped) {
try {
lock (_voiceLock) {
if (CurrentSong == null) {
@@ -36,7 +38,7 @@ namespace NadekoBot.Classes.Music {
} catch (Exception e) {
Console.WriteLine("Bug in music task run. " + e);
}
await Task.Delay(200);
await Task.Delay(500);
}
});
}
@@ -52,7 +54,11 @@ namespace NadekoBot.Classes.Music {
if (SongQueue.Count != 0) {
CurrentSong = SongQueue[0];
SongQueue.RemoveAt(0);
} else return;
} else {
VoiceClient?.Disconnect();
VoiceClient = null;
return;
}
}
try {
@@ -60,32 +66,35 @@ namespace NadekoBot.Classes.Music {
} catch (Exception ex) {
Console.WriteLine($"Starting failed: {ex}");
CurrentSong?.Stop();
CurrentSong = null;
}
}
internal void Stop() {
lock (_voiceLock) {
Stopped = true;
foreach (var kvp in SongQueue) {
if(kvp != null)
kvp.Cancel();
}
SongQueue?.Clear();
LoadNextSong();
SongQueue.Clear();
CurrentSong?.Stop();
CurrentSong = null;
VoiceClient?.Disconnect();
VoiceClient = null;
}
}
internal StreamRequest CreateStreamRequest(CommandEventArgs e, string query, Channel voiceChannel) {
internal async Task<StreamRequest> CreateStreamRequest(CommandEventArgs e, string query, Channel voiceChannel) {
if (VoiceChannel == null)
throw new ArgumentNullException("Please join a voicechannel.");
StreamRequest sr = null;
if (VoiceClient == null) {
VoiceChannel = voiceChannel;
VoiceClient = await NadekoBot.client.Audio().Join(VoiceChannel);
}
sr = new StreamRequest(e, query, this);
lock (_voiceLock) {
if (VoiceClient == null) {
VoiceClient = NadekoBot.client.Audio().Join(VoiceChannel).Result;
}
sr = new StreamRequest(e, query, this);
SongQueue.Add(sr);
}
return sr;

View File

@@ -6,13 +6,13 @@ using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.Audio;
using YoutubeExtractor;
using NadekoBot.Modules;
using System.IO;
using System.Diagnostics;
using NadekoBot.Extensions;
using System.Threading;
using Timer = System.Timers.Timer;
using YoutubeExtractor;
namespace NadekoBot.Classes.Music {
public enum StreamState {
@@ -94,12 +94,12 @@ namespace NadekoBot.Classes.Music {
musicStreamer?.Stop();
}
internal Task Start() =>
Task.Run(async () => {
Console.WriteLine("Start called.");
internal async Task Start() {
Console.WriteLine("Start called.");
int attemptsLeft = 4;
//wait for up to 4 seconds to resolve a link
int attemptsLeft = 4;
//wait for up to 4 seconds to resolve a link
try {
while (State == StreamState.Resolving) {
await Task.Delay(1000);
Console.WriteLine("Resolving...");
@@ -107,13 +107,15 @@ namespace NadekoBot.Classes.Music {
throw new TimeoutException("Resolving timed out.");
}
}
try {
await musicStreamer.StartPlayback();
} catch (Exception ex) {
Console.WriteLine("Error in start playback." + ex.Message);
privateState = StreamState.Completed;
}
});
await musicStreamer.StartPlayback();
} catch (TimeoutException) {
Console.WriteLine("Resolving timed out.");
privateState = StreamState.Completed;
} catch (Exception ex) {
Console.WriteLine("Error in start playback." + ex.Message);
privateState = StreamState.Completed;
}
}
}
public class MusicStreamer {
@@ -127,7 +129,7 @@ namespace NadekoBot.Classes.Music {
StreamRequest parent;
private readonly object _bufferLock = new object();
private CancellationTokenSource bufferCancelSource;
private bool prebufferingComplete = false;
public MusicStreamer(StreamRequest parent, string directUrl, Channel channel) {
this.parent = parent;
@@ -136,7 +138,6 @@ namespace NadekoBot.Classes.Music {
this.Url = directUrl;
Console.WriteLine("Created new streamer");
State = StreamState.Queued;
bufferCancelSource = new CancellationTokenSource();
}
public string Stats() =>
@@ -162,12 +163,9 @@ namespace NadekoBot.Classes.Music {
int attempt = 0;
while (true) {
//wait for the read pos to catch up with write pos
while (buffer.writePos - buffer.readPos > 5.MB() && State != StreamState.Completed) {
if (!bufferCancelSource.IsCancellationRequested) {
Console.WriteLine("Canceling buffer token");
Task.Run(() => bufferCancelSource.Cancel());
}
prebufferingComplete = true;
await Task.Delay(500);
}
@@ -228,11 +226,18 @@ namespace NadekoBot.Classes.Music {
if (parent.OnBuffering != null)
parent.OnBuffering();
BufferSong();
try {
await Task.Delay(5000, bufferCancelSource.Token);
} catch (Exception) {
Console.WriteLine("Buffered enough in less than 5 seconds!");
// prebuffering wait stuff start
int bufferAttempts = 0;
int waitPerAttempt = 500;
while (!prebufferingComplete && bufferAttempts++ < 10) {
await Task.Delay(waitPerAttempt);
}
if (prebufferingComplete) {
Console.WriteLine($"Prebuffering finished in {bufferAttempts*500}");
}
// prebuffering wait stuff end
//Task.Run(async () => { while (true) { Console.WriteLine($"Title: {parent.Title} State:{State}"); await Task.Delay(200); } });
if (parent.OnStarted != null)
parent.OnStarted();
@@ -265,8 +270,6 @@ namespace NadekoBot.Classes.Music {
} else
attempt = 0;
if (State == StreamState.Completed) {
Console.WriteLine("Canceled");
break;

View File

@@ -12,10 +12,11 @@ using System.Timers;
using NadekoBot.Extensions;
using System.Collections;
namespace NadekoBot
{
public class Trivia : DiscordCommand
{
//github.com/micmorris contributed quite a bit to making trivia better!
namespace NadekoBot {
public class Trivia : DiscordCommand {
public static float HINT_TIME_SECONDS = 6;
public static Dictionary<ulong, TriviaGame> runningTrivias;
public Trivia() : base() {
@@ -28,47 +29,37 @@ namespace NadekoBot
var tg = new TriviaGame(e, NadekoBot.client);
runningTrivias.Add(e.Server.Id, tg);
return tg;
}
public TriviaQuestion GetCurrentQuestion(ulong serverId) => runningTrivias[serverId].currentQuestion;
public override Func<CommandEventArgs, Task> DoFunc() => async e =>
{
public override Func<CommandEventArgs, Task> DoFunc() => async e => {
TriviaGame tg;
if ((tg = StartNewGame(e)) != null)
{
await e.Send("**Trivia game started!**\nFirst player to get to 10 points wins! You have 30 seconds per question.\nUse command [tq] if game was started by accident.\nTyping [idfk] 15 seconds after the question has started will give you a hint.");
}
else
if ((tg = StartNewGame(e)) != null) {
await e.Send("**Trivia game started!**\nFirst player to get to 10 points wins! You have 30 seconds per question.\nUse command `tq` if game was started by accident.**");
} else
await e.Send("Trivia game is already running on this server. The question is:\n**" + GetCurrentQuestion(e.Server.Id).Question + "**");
};
private Func<CommandEventArgs, Task> LbFunc() => async e =>
{
if (runningTrivias.ContainsKey(e.Server.Id))
{
private Func<CommandEventArgs, Task> LbFunc() => async e => {
if (runningTrivias.ContainsKey(e.Server.Id)) {
var lb = runningTrivias[e.User.Server.Id].GetLeaderboard();
await e.Send(lb);
}
else
} else
await e.Send("Trivia game is not running on this server.");
};
private Func<CommandEventArgs, Task> RepeatFunc() => async e =>
{
if (runningTrivias.ContainsKey(e.Server.Id))
{
private Func<CommandEventArgs, Task> RepeatFunc() => async e => {
if (runningTrivias.ContainsKey(e.Server.Id)) {
var lb = runningTrivias[e.User.Server.Id].GetLeaderboard();
await e.Send(lb);
}
else
} else
await e.Send("Trivia game is not running on this server.");
};
public override void Init(CommandGroupBuilder cgb)
{
public override void Init(CommandGroupBuilder cgb) {
cgb.CreateCommand("t")
.Description("Starts a game of trivia.")
.Alias("-t")
@@ -87,18 +78,14 @@ namespace NadekoBot
.Do(QuitFunc());
}
private Func<CommandEventArgs, Task> QuitFunc() => async e =>
{
if (runningTrivias.ContainsKey(e.Server.Id) && runningTrivias[e.Server.Id].ChannelId == e.Channel.Id)
{
await e.Send("Trivia will stop after this question. Run [**@NadekoBot clr**] to remove this bot's messages from the channel.");
private Func<CommandEventArgs, Task> QuitFunc() => async e => {
if (runningTrivias.ContainsKey(e.Server.Id) && runningTrivias[e.Server.Id].ChannelId == e.Channel.Id) {
await e.Send("Trivia will stop after this question.");
runningTrivias[e.Server.Id].StopGame();
}
else await e.Send("No trivias are running on this channel.");
} else await e.Send("No trivias are running on this channel.");
};
internal static void FinishGame(TriviaGame triviaGame)
{
internal static void FinishGame(TriviaGame triviaGame) {
runningTrivias.Remove(runningTrivias.Where(kvp => kvp.Value == triviaGame).First().Key);
}
}
@@ -127,11 +114,13 @@ namespace NadekoBot
private Stopwatch stopwatch;
private bool isQuit = false;
private System.Threading.CancellationTokenSource hintCancelSource;
public TriviaGame(CommandEventArgs starter, DiscordClient client) {
this.users = new Dictionary<ulong, int>();
this.client = client;
this._serverId = starter.Server.Id;
this._channellId= starter.Channel.Id;
this._channellId = starter.Channel.Id;
oldQuestions = new List<string>();
client.MessageReceived += PotentialGuess;
@@ -149,12 +138,12 @@ namespace NadekoBot
stopwatch = new Stopwatch();
timeout.Elapsed += (s, e) => { TimeUp(); };
hintCancelSource = new System.Threading.CancellationTokenSource();
TriviaQuestionsPool.Instance.Reload();
LoadNextRound();
}
private async void PotentialGuess(object sender, MessageEventArgs e)
{
private async void PotentialGuess(object sender, MessageEventArgs e) {
if (e.Server == null || e.Channel == null) return;
if (e.Server.Id != _serverId || !active)
return;
@@ -163,26 +152,24 @@ namespace NadekoBot
return;
if (e.Message.Text.ToLower().Equals("idfk")) {
GetHint(e);
GetHint(e.Channel);
return;
}
if (IsAnswerCorrect(e.Message.Text.ToLower(), currentQuestion.Answer.ToLower()))
{
if (IsAnswerCorrect(e.Message.Text.ToLower(), currentQuestion.Answer.ToLower())) {
active = false; //start pause between rounds
timeout.Enabled = false;
stopwatch.Stop();
if (!users.ContainsKey(e.User.Id))
users.Add(e.User.Id, 1);
else
{
else {
users[e.User.Id]++;
}
await e.Send( e.User.Mention + " Guessed it!\n The answer was: **" + currentQuestion.Answer + "**");
await e.Send(e.User.Mention + " Guessed it!\n The answer was: **" + currentQuestion.Answer + "**");
if (users[e.User.Id] >= 10) {
await e.Send( " We have a winner! It's " + e.User.Mention+"\n"+GetLeaderboard()+"\n To start a new game type '@NadekoBot t'");
await e.Send(" We have a winner! It's " + e.User.Mention + "\n" + GetLeaderboard() + "\n To start a new game type '@NadekoBot t'");
FinishGame();
return;
}
@@ -192,16 +179,13 @@ namespace NadekoBot
}
}
private bool IsAnswerCorrect(string guess, string answer)
{
if(guess.Equals(answer))
{
private bool IsAnswerCorrect(string guess, string answer) {
if (guess.Equals(answer)) {
return true;
}
guess = CleanString(guess);
answer = CleanString(answer);
if (guess.Equals(answer))
{
if (guess.Equals(answer)) {
return true;
}
@@ -209,12 +193,9 @@ namespace NadekoBot
return Judge(guess.Length, answer.Length, levDistance);
}
private bool Judge(int guessLength, int answerLength, int levDistance)
{
foreach(Tuple<int, int> level in strictness)
{
if(guessLength <= level.Item1 || answerLength <= level.Item1)
{
private bool Judge(int guessLength, int answerLength, int levDistance) {
foreach (Tuple<int, int> level in strictness) {
if (guessLength <= level.Item1 || answerLength <= level.Item1) {
if (levDistance <= level.Item2)
return true;
else
@@ -224,8 +205,7 @@ namespace NadekoBot
return false;
}
private string CleanString(string str)
{
private string CleanString(string str) {
str = " " + str + " ";
str = Regex.Replace(str, "\\s+", " ");
str = Regex.Replace(str, "[^\\w\\d\\s]", "");
@@ -239,30 +219,24 @@ namespace NadekoBot
return str;
}
public async void GetHint(MessageEventArgs e) {
if (timeout != null && !isQuit && stopwatch.ElapsedMilliseconds > 10000)
await e.Send( currentQuestion.Answer.Scramble());
else {
await e.Send( $"You have to wait {10-stopwatch.ElapsedMilliseconds/1000} more seconds in order to get a hint.");
}
public async void GetHint(Channel ch) {
await ch.Send(currentQuestion.Answer.Scramble());
}
public void StopGame() {
isQuit = true;
}
private void LoadNextRound()
{
private void LoadNextRound() {
Channel ch = client.GetChannel(_channellId);
if(currentQuestion!=null)
if (currentQuestion != null)
oldQuestions.Add(currentQuestion.Question);
currentQuestion = TriviaQuestionsPool.Instance.GetRandomQuestion(oldQuestions);
if (currentQuestion == null || isQuit)
{
if (currentQuestion == null || isQuit) {
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
ch.Send("Trivia bot stopping. :\\\n" + GetLeaderboard());
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
@@ -279,12 +253,16 @@ namespace NadekoBot
timeout.Enabled = true;//starting countdown of the next question
stopwatch.Reset();
stopwatch.Start();
try {
await Task.Delay((int)Trivia.HINT_TIME_SECONDS * 1000);
GetHint(ch);
} catch (Exception) { }
};
return;
}
private async void TimeUp() {
await client.GetChannel(_channellId)?.Send("**Time's up.**\nCorrect answer was: **" + currentQuestion.Answer+"**\n\n*[tq quits trivia][tl shows leaderboard]["+NadekoBot.botMention+" clr clears my messages]*");
await client.GetChannel(_channellId)?.Send("**Time's up.**\nCorrect answer was: **" + currentQuestion.Answer + "**\n\n*[tq quits trivia][tl shows leaderboard][" + NadekoBot.botMention + " clr clears my messages]*");
LoadNextRound();
}
@@ -302,29 +280,26 @@ namespace NadekoBot
public string GetLeaderboard() {
if (users.Count == 0)
return "";
string str = "**Leaderboard:**\n-----------\n";
if(users.Count>1)
if (users.Count > 1)
users.OrderBy(kvp => kvp.Value);
foreach (var KeyValuePair in users)
{
str += "**" + client.GetServer(_serverId).GetUser(KeyValuePair.Key).Name + "** has " +KeyValuePair.Value + (KeyValuePair.Value == 1 ? "point." : "points.") + Environment.NewLine;
foreach (var KeyValuePair in users) {
str += "**" + client.GetServer(_serverId).GetUser(KeyValuePair.Key).Name + "** has " + KeyValuePair.Value + (KeyValuePair.Value == 1 ? "point." : "points.") + Environment.NewLine;
}
return str;
}
}
public class TriviaQuestion
{
public class TriviaQuestion {
public string Category;
public string Question;
public string Answer;
public TriviaQuestion(string q, string a)
{
public TriviaQuestion(string q, string a) {
this.Question = q;
this.Answer = a;
}
@@ -338,8 +313,7 @@ namespace NadekoBot
public class TriviaQuestionsPool {
private static TriviaQuestionsPool instance = null;
public static TriviaQuestionsPool Instance
{
public static TriviaQuestionsPool Instance {
get {
if (instance == null)
instance = new TriviaQuestionsPool();
@@ -352,21 +326,18 @@ namespace NadekoBot
private Random _r;
public TriviaQuestionsPool()
{
public TriviaQuestionsPool() {
Reload();
}
public TriviaQuestion GetRandomQuestion(List<string> exclude) {
if (pool.Count == 0)
return null;
TriviaQuestion tq = pool[_r.Next(0, pool.Count)];
if (exclude.Count > 0 && exclude.Count < pool.Count)
{
while (exclude.Contains(tq.Question))
{
if (exclude.Count > 0 && exclude.Count < pool.Count) {
while (exclude.Contains(tq.Question)) {
tq = pool[_r.Next(0, pool.Count)];
}
}