diff --git a/Client/App.xaml b/Client/App.xaml new file mode 100644 index 0000000..408569d --- /dev/null +++ b/Client/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/Client/App.xaml.cs b/Client/App.xaml.cs new file mode 100644 index 0000000..9f2324b --- /dev/null +++ b/Client/App.xaml.cs @@ -0,0 +1,30 @@ +using Client.Views; +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace Client +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + + protected override void OnStartup(StartupEventArgs e) + { + base.OnStartup(e); + LoginScreen startWindow = new LoginScreen(); + //ViewModel VM = new ViewModel(); + //startWindow.DataContext = VM; + startWindow.Show(); + } + + } + + +} diff --git a/Client/AssemblyInfo.cs b/Client/AssemblyInfo.cs new file mode 100644 index 0000000..8b5504e --- /dev/null +++ b/Client/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/Client/Client.cs b/Client/Client.cs new file mode 100644 index 0000000..5346d92 --- /dev/null +++ b/Client/Client.cs @@ -0,0 +1,148 @@ +using SharedClientServer; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net.Sockets; +using System.Text; +using static SharedClientServer.JSONConvert; + +namespace Client +{ + public delegate void OnLobbyCreated(int id); + class Client : ObservableObject + { + private TcpClient tcpClient; + private NetworkStream stream; + private byte[] buffer = new byte[1024]; + private byte[] totalBuffer = new byte[1024]; + private int totalBufferReceived = 0; + public int Port = 5555; + public bool Connected = false; + private string username; + public Callback OnSuccessfullConnect; + public Callback OnLobbiesListReceived; + public Callback OnLobbyJoinSuccess; + public Callback OnLobbiesReceivedAndWaitingForHost; + public OnLobbyCreated OnLobbyCreated; + public Lobby[] Lobbies { get; set; } + + public Client(string username) + { + this.username = username; + this.tcpClient = new TcpClient(); + Debug.WriteLine("Starting connect to server"); + tcpClient.BeginConnect("localhost", Port, new AsyncCallback(OnConnect), null); + } + + private void OnConnect(IAsyncResult ar) + { + Debug.Write("finished connecting to server"); + this.tcpClient.EndConnect(ar); + this.stream = tcpClient.GetStream(); + OnSuccessfullConnect?.Invoke(); + SendMessage(JSONConvert.ConstructUsernameMessage(username)); + this.stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(OnReadComplete),null); + } + + private void OnReadComplete(IAsyncResult ar) + { + int amountReceived = stream.EndRead(ar); + + if (totalBufferReceived + amountReceived > 1024) + { + throw new OutOfMemoryException("buffer too small"); + } + + Array.Copy(buffer, 0, totalBuffer, totalBufferReceived, amountReceived); + totalBufferReceived += amountReceived; + + int expectedMessageLength = BitConverter.ToInt32(totalBuffer, 0); + + while (totalBufferReceived >= expectedMessageLength) + { + // we have received the complete packet + byte[] message = new byte[expectedMessageLength]; + // put the message received into the message array + Array.Copy(totalBuffer, 0, message, 0, expectedMessageLength); + + handleData(message); + + totalBufferReceived -= expectedMessageLength; + expectedMessageLength = BitConverter.ToInt32(totalBuffer, 0); + } + + stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(OnReadComplete), null); + } + + private void handleData(byte[] message) + { + byte id = message[4]; + + byte[] payload = new byte[message.Length - 5]; + Array.Copy(message, 5, payload, 0, message.Length - 5); + + switch (id) + { + case JSONConvert.LOGIN: + // json log in username data + break; + case JSONConvert.MESSAGE: + // json message data + (string, string) combo = JSONConvert.GetUsernameAndMessage(payload); + string textUsername = combo.Item1; + string textMsg = combo.Item2; + + //TODO display username and message in chat window + Debug.WriteLine("[CLIENT] INCOMING MESSAGE!"); + Debug.WriteLine("[CLIENT] User name: {0}\t User message: {1}", textUsername, textMsg); + break; + + case JSONConvert.LOBBY: + // lobby data + LobbyIdentifier lobbyIdentifier = JSONConvert.GetLobbyIdentifier(payload); + switch (lobbyIdentifier) + { + case LobbyIdentifier.LIST: + Debug.WriteLine("got lobbies list"); + Lobbies = JSONConvert.GetLobbiesFromMessage(payload); + OnLobbiesListReceived?.Invoke(); + OnLobbiesReceivedAndWaitingForHost?.Invoke(); + break; + case LobbyIdentifier.HOST: + // we receive this when the server has made us a host of a new lobby + // TODO get lobby id + Debug.WriteLine("[CLIENT] got lobby object"); + int lobbyCreatedID = JSONConvert.GetLobbyID(payload); + OnLobbyCreated?.Invoke(lobbyCreatedID); + break; + case LobbyIdentifier.JOIN_SUCCESS: + OnLobbyJoinSuccess?.Invoke(); + break; + } + //TODO fill lobby with the data received + break; + + case JSONConvert.CANVAS: + // canvas data + break; + + default: + Debug.WriteLine("[CLIENT] Received weird identifier: " + id); + break; + } + + } + + 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) + { + Debug.WriteLine("[CLIENT] finished writing"); + stream.EndWrite(ar); + } + } +} diff --git a/Client/Client.csproj b/Client/Client.csproj new file mode 100644 index 0000000..d5b88aa --- /dev/null +++ b/Client/Client.csproj @@ -0,0 +1,22 @@ + + + + WinExe + netcoreapp3.1 + true + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/ClientData.cs b/Client/ClientData.cs new file mode 100644 index 0000000..8a217a0 --- /dev/null +++ b/Client/ClientData.cs @@ -0,0 +1,72 @@ +using SharedClientServer; +using System; +using System.Collections.Generic; +using System.Data; +using System.Text; + +namespace Client +{ + class ClientData + { + private static ClientData _instance; + private static readonly object padlock = new object(); + + public static ClientData Instance + { + get + { + lock (padlock) + { + if (_instance == null) + { + _instance = new ClientData(); + } + return _instance; + } + } + } + + + private User _user; + private Client _client; + private Lobby _lobby; + private string _message; + + private ClientData() + { + + } + + + public User User + { + get { return _user; } + set { _user = value; } + } + + public Client Client + { + get { return _client; } + set { _client = value; } + } + + public Lobby Lobby + { + get { return _lobby; } + set { _lobby = value; } + } + + public String Message + { + get + { + return _message; + } + set + { + _message = value; + } + } + + } +} diff --git a/Client/FodyWeavers.xml b/Client/FodyWeavers.xml new file mode 100644 index 0000000..d5abfed --- /dev/null +++ b/Client/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Client/FodyWeavers.xsd b/Client/FodyWeavers.xsd new file mode 100644 index 0000000..69dbe48 --- /dev/null +++ b/Client/FodyWeavers.xsd @@ -0,0 +1,74 @@ + + + + + + + + + + + Used to control if the On_PropertyName_Changed feature is enabled. + + + + + Used to control if the Dependent properties feature is enabled. + + + + + Used to control if the IsChanged property feature is enabled. + + + + + Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form. + + + + + Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project. + + + + + Used to control if equality checks should use the Equals method resolved from the base class. + + + + + Used to control if equality checks should use the static Equals method resolved from the base class. + + + + + Used to turn off build warnings from this weaver. + + + + + Used to turn off build warnings about mismatched On_PropertyName_Changed methods. + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/Client/Models/Model.cs b/Client/Models/Model.cs new file mode 100644 index 0000000..9e50229 --- /dev/null +++ b/Client/Models/Model.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; + +namespace Client +{ + class Model : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + private int _numbers; + private bool _status; + private bool _canStartGame; + + //Test code + public int Numbers + { + get { return _numbers; } + + set + { + _numbers = value; + } + } + + + public bool Status + { + get + { + return _status; + } + + set + { + _status = value; + } + } + + public bool CanStartGame + { + get { return _canStartGame; } + set { _canStartGame = value; } + } + + + public Model() + { + _status = false; + _numbers = 0; + _canStartGame = true; + } + + } +} diff --git a/Client/ViewModels/ViewModel.cs b/Client/ViewModels/ViewModel.cs new file mode 100644 index 0000000..bbb9843 --- /dev/null +++ b/Client/ViewModels/ViewModel.cs @@ -0,0 +1,172 @@ +using GalaSoft.MvvmLight.Command; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; +using System.Windows.Input; +using SharedClientServer; +using System.Diagnostics; +using System.Windows; +using System.Collections.ObjectModel; +using Client.Views; +using System.Linq; +using System.Windows.Data; + +namespace Client +{ + class ViewModel : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + public ICommand OnHostButtonClick { get; set; } + public ICommand JoinSelectedLobby { get; set; } + + public Lobby SelectedLobby { get; set; } + + private Client client; + + private bool wantToBeHost = false; + private int wantToBeHostId = 0; + + public ViewModel() + { + _model = new Model(); + _lobbies = new ObservableCollection(); + client = ClientData.Instance.Client; + client.OnLobbiesListReceived = updateLobbies; + + + OnHostButtonClick = new RelayCommand(hostGame); + + JoinSelectedLobby = new RelayCommand(joinLobby, true); + } + + private void hostGame() + { + Debug.WriteLine("attempting to host game for " + ClientData.Instance.User.Username); + client.SendMessage(JSONConvert.ConstructLobbyHostMessage()); + client.OnLobbyCreated = becomeHostForLobby; + } + + private void becomeHostForLobby(int id) + { + + Debug.WriteLine($"got host succes with data {id} "); + wantToBeHost = true; + wantToBeHostId = id; + client.OnLobbiesReceivedAndWaitingForHost = hostLobbiesReceived; + + } + + private void hostLobbiesReceived() + { + if (wantToBeHost) + foreach (Lobby l in Lobbies) + { + if (l.ID == wantToBeHostId) + { + Debug.WriteLine("found lobby that we want to be host of: " + l.ID + ", joining.."); + SelectedLobby = l; + startGameInLobby(); + wantToBeHost = false; + client.OnLobbiesReceivedAndWaitingForHost = null; + break; + } + } + } + + private void joinLobby() + { + // lobby die je wilt joinen verwijderen + // nieuwe binnengekregen lobby toevoegen + client.OnLobbyJoinSuccess = OnLobbyJoinSuccess; + client.SendMessage(JSONConvert.ConstructLobbyJoinMessage(SelectedLobby.ID)); + } + + private void OnLobbyJoinSuccess() + { + startGameInLobby(); + } + + + + private void updateLobbies() + { + Debug.WriteLine("updating lobbies..."); + Lobby[] lobbiesArr = client.Lobbies; + Application.Current.Dispatcher.Invoke(delegate + { + + //for (int i = 0; i < lobbiesArr.Length; i++) + //{ + // Lobby lobby = lobbiesArr[i]; + // Debug.WriteLine(lobby.PlayersIn); + // if (i < _lobbies.Count && _lobbies[i].ID == lobby.ID) + // { + // _lobbies[i].Set(lobby); + // } else + // { + // _lobbies.Add(lobbiesArr[i]); + // } + //} + + _lobbies.Clear(); + + foreach (Lobby l in lobbiesArr) + { + _lobbies.Add(l); + } + + }); + } + + private void startGameInLobby() + { + if (SelectedLobby != null) + { + ClientData.Instance.Lobby = SelectedLobby; + startGameWindow(); + } + } + + private void startGameWindow() + { + _model.CanStartGame = false; + Application.Current.Dispatcher.Invoke(delegate + { + GameWindow window = new GameWindow(); + window.Show(); + }); + } + + private void ClickCheck() + { + if(!(_model.Status)) + _model.Status = true; + + _model.Numbers = _model.Numbers + 5; + } + + + private Model _model; + public Model Model + { + get + { + return _model; + } + + set + { + _model = value; + } + } + + private ObservableCollection _lobbies; + public ObservableCollection Lobbies + { + get { return _lobbies; } + set { _lobbies = value; } + } + } +} diff --git a/Client/ViewModels/ViewModelGame.cs b/Client/ViewModels/ViewModelGame.cs new file mode 100644 index 0000000..65fd353 --- /dev/null +++ b/Client/ViewModels/ViewModelGame.cs @@ -0,0 +1,126 @@ + +using Client.Views; +using GalaSoft.MvvmLight.Command; +using SharedClientServer; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Windows; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Shapes; + +namespace Client.ViewModels +{ + class ViewModelGame : INotifyPropertyChanged + { + private ClientData data = ClientData.Instance; + + public event PropertyChangedEventHandler PropertyChanged; + + private Point currentPoint = new Point(); + private Color color; + + public ObservableCollection Messages { get; } = new ObservableCollection(); + + private dynamic _payload; + + private string _username; + + private string _message; + public string Message + { + get + { + return _message; + } + set + { + _message = value; + } + } + public ICommand OnKeyDown { get; set; } + + 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.GetMessageToSend(0x04, coordinates)); + } + } + + 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; + } + + + public ViewModelGame() + { + if (_payload == null) + { + _message = ""; + + } + else + { + //_message = data.Message; + //_username = data.User.Username; + //Messages.Add($"{data.User.Username}: {Message}"); + } + OnKeyDown = new RelayCommand(ChatBox_KeyDown); + } + + 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)); + } + + + } +} + diff --git a/Client/Views/GameWindow.xaml b/Client/Views/GameWindow.xaml new file mode 100644 index 0000000..6614197 --- /dev/null +++ b/Client/Views/GameWindow.xaml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +