diff --git a/Client/Client.cs b/Client/Client.cs index 4a409de..77fc98b 100644 --- a/Client/Client.cs +++ b/Client/Client.cs @@ -5,16 +5,22 @@ using System.Diagnostics; using System.IO; using System.Net.Sockets; using System.Text; -using System.Windows.Media; +using System.Windows.Media; using System.Windows; + using static SharedClientServer.JSONConvert; namespace Client { public delegate void LobbyJoinCallback(bool isHost); - public delegate void CanvasDataReceived(double[] coordinates, Color color); + + public delegate void RandomWord(string word); + public delegate void HandleIncomingMsg(string username, string msg); + internal delegate void HandleIncomingPlayer(Lobby lobby); + public delegate void CanvasDataReceived(double[][] coordinates, Color color); public delegate void CanvasReset(); public delegate void LobbyCallback(int id); + class Client : ObservableObject { @@ -33,8 +39,12 @@ namespace Client public LobbyJoinCallback OnLobbyJoinSuccess; public Callback OnLobbiesReceivedAndWaitingForHost; public Callback OnServerDisconnect; + public Callback OnLobbyUpdate; public LobbyCallback OnLobbyCreated; public LobbyCallback OnLobbyLeave; + public RandomWord RandomWord; + public HandleIncomingMsg IncomingMsg; + public HandleIncomingPlayer IncomingPlayer; private ClientData data = ClientData.Instance; public CanvasDataReceived CanvasDataReceived; public CanvasReset CReset; @@ -56,6 +66,7 @@ namespace Client this.tcpClient.EndConnect(ar); this.stream = tcpClient.GetStream(); OnSuccessfullConnect?.Invoke(); + OnLobbyUpdate = updateGameLobby; SendMessage(JSONConvert.ConstructUsernameMessage(username)); this.stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(OnReadComplete),null); @@ -68,17 +79,20 @@ namespace Client private void OnReadComplete(IAsyncResult ar) { + if (ar == null || (!ar.IsCompleted) || (!this.stream.CanRead) || !this.tcpClient.Client.Connected) return; + try { int amountReceived = stream.EndRead(ar); - if (totalBufferReceived + amountReceived > 1024) + if (totalBufferReceived + amountReceived > 2048) { throw new OutOfMemoryException("buffer too small"); } + Array.Copy(buffer, 0, totalBuffer, totalBufferReceived, amountReceived); totalBufferReceived += amountReceived; @@ -90,7 +104,6 @@ namespace Client byte[] message = new byte[expectedMessageLength]; // put the message received into the message array Array.Copy(totalBuffer, 0, message, 0, expectedMessageLength); - handleData(message); totalBufferReceived -= expectedMessageLength; @@ -104,7 +117,6 @@ namespace Client Debug.WriteLine("[CLIENT] server not responding! got error: " + e.Message); OnServerDisconnect?.Invoke(); } - } private void handleData(byte[] message) @@ -128,7 +140,7 @@ namespace Client if(textUsername != data.User.Username) { - ViewModels.ViewModelGame.HandleIncomingMsg(textUsername, textMsg); + IncomingMsg?.Invoke(textUsername, textMsg); } //TODO display username and message in chat window @@ -146,6 +158,7 @@ namespace Client Lobbies = JSONConvert.GetLobbiesFromMessage(payload); OnLobbiesListReceived?.Invoke(); OnLobbiesReceivedAndWaitingForHost?.Invoke(); + OnLobbyUpdate?.Invoke(); break; case LobbyIdentifier.HOST: // we receive this when the server has made us a host of a new lobby @@ -155,7 +168,6 @@ namespace Client OnLobbyCreated?.Invoke(lobbyCreatedID); break; case LobbyIdentifier.JOIN_SUCCESS: - OnLobbyJoinSuccess?.Invoke(JSONConvert.GetLobbyJoinIsHost(payload)); break; case LobbyIdentifier.LEAVE: @@ -169,25 +181,30 @@ namespace Client case JSONConvert.CANVAS: // canvas data //clientData.CanvasData = JSONConvert.getCoordinates(payload); - CanvasInfo type = JSONConvert.GetCanvasMessageType(payload); - switch (type) - { - case CanvasInfo.RESET: - CReset?.Invoke(); - break; - - case CanvasInfo.DRAWING: - CanvasDataReceived?.Invoke(JSONConvert.getCoordinates(payload), JSONConvert.getCanvasDrawingColor(payload)); - break; + int type = JSONConvert.GetCanvasMessageType(payload); + switch (type) + { + case JSONConvert.CANVAS_RESET: + CReset?.Invoke(); + break; + + case JSONConvert.CANVAS_WRITING: + CanvasDataReceived?.Invoke(JSONConvert.getCoordinates(payload), JSONConvert.getCanvasDrawingColor(payload)); + // we hebben gedrawed, dus stuur dat we weer kunnen drawen + + break; } - break; + break; + case JSONConvert.RANDOMWORD: //Flag byte for receiving the random word. int lobbyId = JSONConvert.GetLobbyID(payload); + string randomWord = JSONConvert.GetRandomWord(payload); - if(data.Lobby?.ID == lobbyId) - ViewModels.ViewModelGame.HandleRandomWord(JSONConvert.GetRandomWord(payload)); + if (data.Lobby?.ID == lobbyId) + RandomWord?.Invoke(randomWord); + break; default: Debug.WriteLine("[CLIENT] Received weird identifier: " + id); @@ -197,11 +214,20 @@ namespace Client } + private void updateGameLobby() + { + foreach (var item in Lobbies) + { + Debug.WriteLine("[CLIENT] lobby data: {0}", item.Users.Count); + if (item.ID == data.Lobby?.ID) + IncomingPlayer?.Invoke(item); + } + } + public void SendMessage(byte[] message) { Debug.WriteLine("[CLIENT] sending message " + Encoding.ASCII.GetString(message)); stream.BeginWrite(message, 0, message.Length, new AsyncCallback(OnWriteComplete), null); - } private void OnWriteComplete(IAsyncResult ar) diff --git a/Client/ViewModels/ViewModel.cs b/Client/ViewModels/ViewModel.cs index 23b7b53..b4e67b4 100644 --- a/Client/ViewModels/ViewModel.cs +++ b/Client/ViewModels/ViewModel.cs @@ -11,10 +11,10 @@ using System.Collections.ObjectModel; using Client.Views; using System.Linq; using System.Windows.Data; -using System.Data; -using System.Windows.Controls.Primitives; -using System.Windows.Controls; - +using System.Data; +using System.Windows.Controls.Primitives; +using System.Windows.Controls; + namespace Client { class ViewModel : INotifyPropertyChanged @@ -92,16 +92,14 @@ namespace Client private void joinLobby() { - // lobby die je wilt joinen verwijderen - // nieuwe binnengekregen lobby toevoegen - if (SelectedLobby != null) - { - if (SelectedLobby.PlayersIn == SelectedLobby.MaxPlayers || !SelectedLobby.LobbyJoinable) - { - return; - } - client.OnLobbyJoinSuccess = OnLobbyJoinSuccess; - client.SendMessage(JSONConvert.ConstructLobbyJoinMessage(SelectedLobby.ID)); + if (SelectedLobby != null) + { + if (SelectedLobby.PlayersIn == SelectedLobby.MaxPlayers || !SelectedLobby.LobbyJoinable) + { + return; + } + client.OnLobbyJoinSuccess = OnLobbyJoinSuccess; + client.SendMessage(JSONConvert.ConstructLobbyJoinMessage(SelectedLobby.ID)); } } diff --git a/Client/ViewModels/ViewModelGame.cs b/Client/ViewModels/ViewModelGame.cs index 6ddcf48..212584e 100644 --- a/Client/ViewModels/ViewModelGame.cs +++ b/Client/ViewModels/ViewModelGame.cs @@ -1,203 +1,263 @@ - -using Client.Views; -using GalaSoft.MvvmLight.Command; -using SharedClientServer; -using System; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Diagnostics; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Shapes; - -namespace Client.ViewModels -{ - class ViewModelGame : INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged; - private ClientData data = ClientData.Instance; - private GameWindow window; - private Point currentPoint = new Point(); - private Color color; - - public static ObservableCollection Messages { get; } = new ObservableCollection(); - - private dynamic _payload; - - public static string Word - { - get; - set; - } - - public string _username; - - public string _message; - public string Message - { - get - { - return _message; - } - set - { - _message = value; - } - } - - public bool IsHost - { - get { return data.User.Host; } - } - - public ViewModelGame(GameWindow window) - { - this.window = window; - if (_payload == null) - { - _message = ""; - - } - else - { - //_message = data.Message; - //_username = data.User.Username; - //Messages.Add($"{data.User.Username}: {Message}"); - } - OnKeyDown = new RelayCommand(ChatBox_KeyDown); - ButtonStartGame = new RelayCommand(BeginGame); - ButtonResetCanvas = new RelayCommand(CanvasResetLocal); - data.Client.CanvasDataReceived = UpdateCanvasWithNewData; - data.Client.CReset = CanvasResetData; - } - - public ICommand OnKeyDown { get; set; } - public ICommand ButtonStartGame { get; set; } - public ICommand ButtonResetCanvas { get; set; } - - public void BeginGame() - { - data.Client.SendMessage(JSONConvert.ConstructGameStartData(data.Lobby.ID)); - } - - - private void CanvasResetLocal() - { - this.window.CanvasForPaint.Children.Clear(); - data.Client.SendMessage(JSONConvert.GetMessageToSend(JSONConvert.CANVAS, JSONConvert.CanvasInfo.RESET)); - } - - - public void Canvas_MouseDown(MouseButtonEventArgs e, GameWindow window) - { - if (e.ButtonState == MouseButtonState.Pressed) - { - currentPoint = e.GetPosition(window.CanvasForPaint); - } - } - - public void Canvas_MouseMove(MouseEventArgs e, GameWindow window) - { - if (e.LeftButton == MouseButtonState.Pressed) - { - double[] coordinates = new double[4]; - Line line = new Line(); - - line.Stroke = new SolidColorBrush(color); - //line.Stroke = SystemColors.WindowFrameBrush; - line.X1 = currentPoint.X; - line.Y1 = currentPoint.Y; - line.X2 = e.GetPosition(window.CanvasForPaint).X; - line.Y2 = e.GetPosition(window.CanvasForPaint).Y; - coordinates[0] = line.X1; - coordinates[1] = line.Y1; - coordinates[2] = line.X2; - coordinates[3] = line.Y2; - currentPoint = e.GetPosition(window.CanvasForPaint); - - window.CanvasForPaint.Children.Add(line); - data.Client.SendMessage(JSONConvert.ConstructCanvasDataSend(JSONConvert.CanvasInfo.DRAWING,coordinates, color)); - } - } - - public void Color_Picker(RoutedPropertyChangedEventArgs e, GameWindow window) - { - Color colorSelected = new Color(); - colorSelected.A = 255; - colorSelected.R = window.ClrPcker_Background.SelectedColor.Value.R; - colorSelected.G = window.ClrPcker_Background.SelectedColor.Value.G; - colorSelected.B = window.ClrPcker_Background.SelectedColor.Value.B; - color = colorSelected; - } - - private void UpdateCanvasWithNewData(double[] coordinates, Color color) - { - Application.Current.Dispatcher.Invoke(delegate - { - Line line = new Line(); - line.Stroke = new SolidColorBrush(color); - line.X1 = coordinates[0]; - line.Y1 = coordinates[1]; - line.X2 = coordinates[2]; - line.Y2 = coordinates[3]; - this.window.CanvasForPaint.Children.Add(line); - }); - } - - private void CanvasResetData() - { - this.window.CanvasForPaint.Children.Clear(); - } - - private void ChatBox_KeyDown() - { - //if enter then clear textbox and send message. - if (Message != string.Empty) AddMessage(Message); - Message = string.Empty; - } - - internal void AddMessage(string message) - { - Messages.Add($"{data.User.Username}: {message}"); - - _payload = new - { - username = data.User.Username, - message = message - }; - - //Broadcast the message after adding it to the list! - data.Client.SendMessage(JSONConvert.GetMessageToSend(JSONConvert.MESSAGE, _payload)); - } - - /* - * MISC make this a callback - * Handles the incoming chat message from another client. - */ - public static void HandleIncomingMsg(string username, string message) - { - Application.Current.Dispatcher.Invoke(delegate - { - Messages.Add($"{username}: {message}"); - }); - } - public void LeaveGame(object sender, CancelEventArgs e) - { - Debug.WriteLine("Leaving..."); - data.Client.SendMessage(JSONConvert.ConstructLobbyLeaveMessage(data.Lobby.ID)); - } - - /* - * MISC make this a callback - * Handles the random word that has been received from the server. - */ - public static void HandleRandomWord(string randomWord) - { - Debug.WriteLine("[CLIENT] Reached the handle random word method!"); - Word = "NegerPik"; - } - } -} - +using Client.Views; +using GalaSoft.MvvmLight.Command; +using SharedClientServer; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics; +using System.Timers; +using System.Windows; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Shapes; + +namespace Client.ViewModels +{ + class ViewModelGame : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + private ClientData data = ClientData.Instance; + private GameWindow window; + private Point currentPoint = new Point(); + public Color color; + public double[][] buffer; + public int pos = 0; + public int maxLines = 50; + public Queue linesQueue; + private Timer queueTimer; + + public static ObservableCollection Messages { get; } = new ObservableCollection(); + public ObservableCollection Players { get; } = new ObservableCollection(); + + private dynamic _payload; + + public string _username; + + public string _message; + public string Message + { + get + { + return _message; + } + set + { + _message = value; + } + } + + private string _randomWord; + + public string RandomWord + { + get { return _randomWord; } + set { _randomWord = value; } + } + + public static string Word + { + get; + set; + } + + public bool IsHost + { + get { return data.User.Host; } + } + + public ViewModelGame(GameWindow window) + { + this.window = window; + if (_payload == null) + { + _message = ""; + + } + else + { + //_message = data.Message; + //_username = data.User.Username; + //Messages.Add($"{data.User.Username}: {Message}"); + } + + buffer = new double[maxLines][]; + linesQueue = new Queue(); + OnKeyDown = new RelayCommand(ChatBox_KeyDown); + ButtonStartGame = new RelayCommand(BeginGame); + ButtonResetCanvas = new RelayCommand(CanvasResetLocal); + data.Client.CanvasDataReceived = UpdateCanvasWithNewData; + data.Client.CReset = CanvasResetData; + data.Client.RandomWord = HandleRandomWord; + data.Client.IncomingMsg = HandleIncomingMsg; + data.Client.IncomingPlayer = HandleIncomingPlayer; + } + + public ICommand OnKeyDown { get; set; } + public ICommand ButtonStartGame { get; set; } + public ICommand ButtonResetCanvas { get; set; } + + public void BeginGame() + { + + queueTimer = new Timer(50); + queueTimer.Start(); + queueTimer.Elapsed += sendArrayFromQueue; + data.Client.SendMessage(JSONConvert.ConstructGameStartData(data.Lobby.ID)); + } + + + private void CanvasResetLocal() + { + this.window.CanvasForPaint.Children.Clear(); + data.Client.SendMessage(JSONConvert.GetMessageToSend(JSONConvert.CANVAS, JSONConvert.CANVAS_RESET)); + } + + + public void Canvas_MouseDown(MouseButtonEventArgs e, GameWindow window) + { + if (e.ButtonState == MouseButtonState.Pressed) + { + currentPoint = e.GetPosition(window.CanvasForPaint); + } + } + + public void Canvas_MouseMove(MouseEventArgs e, GameWindow window) + { + if (e.LeftButton == MouseButtonState.Pressed) + { + double[] coordinates = new double[4]; + Line line = new Line(); + + line.Stroke = new SolidColorBrush(color); + //line.Stroke = SystemColors.WindowFrameBrush; + line.X1 = currentPoint.X; + line.Y1 = currentPoint.Y; + line.X2 = e.GetPosition(window.CanvasForPaint).X; + line.Y2 = e.GetPosition(window.CanvasForPaint).Y; + coordinates[0] = line.X1; + coordinates[1] = line.Y1; + coordinates[2] = line.X2; + coordinates[3] = line.Y2; + currentPoint = e.GetPosition(window.CanvasForPaint); + buffer[pos] = coordinates; + pos++; + + window.CanvasForPaint.Children.Add(line); + if (pos == maxLines) + { + double[][] temp = new double[maxLines][]; + for (int i = 0; i < maxLines; i++) + { + temp[i] = buffer[i]; + } + linesQueue.Enqueue(temp); + Array.Clear(buffer, 0, buffer.Length); + pos = 0; + } + + } + } + + public void Canvas_MouseUp(object sender, MouseButtonEventArgs e) + { + sendArrayFromQueue(sender, null); + } + + private void sendArrayFromQueue(object sender, ElapsedEventArgs e) + { + + if (linesQueue.Count != 0) + { + double[][] temp = linesQueue.Dequeue(); + data.Client.SendMessage(JSONConvert.ConstructDrawingCanvasData(temp,color)); + } + } + + public void Color_Picker(RoutedPropertyChangedEventArgs e, GameWindow window) + { + Color colorSelected = new Color(); + colorSelected.A = 255; + colorSelected.R = window.ClrPcker_Background.SelectedColor.Value.R; + colorSelected.G = window.ClrPcker_Background.SelectedColor.Value.G; + colorSelected.B = window.ClrPcker_Background.SelectedColor.Value.B; + color = colorSelected; + } + + private void UpdateCanvasWithNewData(double[][] buffer, Color color) + { + Application.Current.Dispatcher.Invoke(delegate + { + foreach (double[] arr in buffer) + { + Line line = new Line(); + line.Stroke = new SolidColorBrush(color); + line.X1 = arr[0]; + line.Y1 = arr[1]; + line.X2 = arr[2]; + line.Y2 = arr[3]; + this.window.CanvasForPaint.Children.Add(line); + } + }); + } + + private void CanvasResetData() + { + this.window.CanvasForPaint.Children.Clear(); + } + + private void ChatBox_KeyDown() + { + //if enter then clear textbox and send message. + if (Message != string.Empty) AddMessage(Message); + Message = string.Empty; + } + + internal void AddMessage(string message) + { + Messages.Add($"{data.User.Username}: {message}"); + + _payload = new + { + username = data.User.Username, + message = message + }; + + //Broadcast the message after adding it to the list! + data.Client.SendMessage(JSONConvert.GetMessageToSend(JSONConvert.MESSAGE, _payload)); + } + + public static void HandleIncomingMsg(string username, string message) + { + Application.Current.Dispatcher.Invoke(delegate + { + Messages.Add($"{username}: {message}"); + }); + } + public void LeaveGame(object sender, System.ComponentModel.CancelEventArgs e) + { + Debug.WriteLine("Leaving..."); + data.Client.SendMessage(JSONConvert.ConstructLobbyLeaveMessage(data.Lobby.ID)); + } + + public static void HandleRandomWord(string randomWord) + { + Debug.WriteLine("[CLIENT] Reached the handle random word method!"); + Application.Current.Dispatcher.Invoke(delegate + { + Word = randomWord; + }); + } + public void HandleIncomingPlayer(Lobby lobby) + { + Application.Current.Dispatcher.Invoke(delegate + { + Players.Clear(); + foreach (var item in lobby.Users) + { + Players.Add(item.Username); + } + }); + } + } +} diff --git a/Client/Views/GameWindow.xaml b/Client/Views/GameWindow.xaml index 607b4a1..dde8efc 100644 --- a/Client/Views/GameWindow.xaml +++ b/Client/Views/GameWindow.xaml @@ -22,16 +22,9 @@ - - - - - - - - + @@ -44,6 +37,9 @@