using RBG_Server; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace RBG_Server_WPF { /// /// Interaction logic for ConnectionPage.xaml /// public partial class ConnectionPage : Page { public ConnectionPage() { InitializeComponent(); } // ************************************************************ // ****************** Visibility Toggles ********************** // ************************************************************ #region visibility toggles private void HostButton_Click(object sender, RoutedEventArgs e) { // Draw host window ServerGrid.Visibility = Visibility.Visible; ServerSelectPanel.Visibility = Visibility.Collapsed; } private void ConnectButton_Click(object sender, RoutedEventArgs e) { // Draw client window ClientGrid.Visibility = Visibility.Visible; ServerSelectPanel.Visibility = Visibility.Collapsed; } private void BackButton_Click(object sender, RoutedEventArgs e) { // Hide host view, show connect view ServerGrid.Visibility = Visibility.Collapsed; ServerSelectPanel.Visibility = Visibility.Visible; } private void ClientBackButton_Click(object sender, RoutedEventArgs e) { ClientGrid.Visibility = Visibility.Collapsed; ServerSelectPanel.Visibility = Visibility.Visible; } #endregion // ************************************************************ // *************** Client Connection Logic ******************* // ************************************************************ #region client connection logic TcpClient client; /// /// Connect to server buttonm runs an asynchronous task that attempts to connect to a server, /// and load the thumbnails from the server /// private void ClientServerConnectButton_Click(object sender, RoutedEventArgs e) { // Run an async connection task // This task establishes a connection, then Task clientConnect = new(async () => { client = new(); try { await client.ConnectAsync(ServerAddressConnectBox.Text, int.Parse(ServerPortConnectBox.Text)); if (client.Connected) { // Invoke the display to update Dispatcher.Invoke(() => { StatusTextBlock.Text = "Connected!"; StatusTextBlock.Foreground = new SolidColorBrush(Color.FromRgb(255, 255, 255)); }); // Run image downloading for the sprite // This task is contained in the CommunicationHandler class Progress progress = new Progress(); Task t = App.Context.GameCommunicationHandler.InitDataLoader(client.GetStream(), progress); progress.ProgressChanged += Progress_ProgressChanged; t.Start(); // t is asynchronous; we don't need to monitor/await progress as it fires its progress through the assigned handler // We should, however, ensure that we don't make requests on client until progress is completed (which can be monitored via // the Progress object) } else { Dispatcher.Invoke(() => { StatusTextBlock.Text = "Connection Failed"; StatusTextBlock.Foreground = new SolidColorBrush(Color.FromRgb(255, 0, 0)); }); } } catch (Exception ex) { Dispatcher.Invoke(() => { StatusTextBlock.Text = ex.Message[..(128 < ex.Message.Length ? 128 : ex.Message.Length)]; StatusTextBlock.Foreground = new SolidColorBrush(Color.FromRgb(255, 0, 0)); }); client?.Dispose(); } }); } /// /// Progress callback, that responds to changes in the state of the task /// /// /// /// private void Progress_ProgressChanged(object sender, CommunicationHandler.ProgressData e) { // Load iamges as they arrive if (e.CurrentActivity == CommunicationHandler.ProgressData.Activity.ImageDownloaded) { // Show the user the total number of images downloaded Dispatcher.Invoke(() => StatusTextBlock.Text = $"Downloaded {e.Progress} of {App.Context.GameCommunicationHandler.ImageList.Count} images."); // Attempt to retrieve the image data if(App.Context.GameCommunicationHandler.ImageCollection.TryGetValue(e.Message, out CachedByteArray cachedByteArray)) { BitmapImage image = new(); image.BeginInit(); image.StreamSource = new MemoryStream(cachedByteArray, false); image.EndInit(); if (e.Message == App.Context.GameCommunicationHandler.BoardName) { // Set the board image to `image` _ = Dispatcher.Invoke(() => BoardPreviewImage.Source = image); } else { // Create a new image object Image spriteImage = new() { Source = image }; // Image is a sprite, load it LoadedSpriteGrid loadedSprite = null; foreach (LoadedSpriteGrid item in ClientLoadedSprites.Children) { // If the image already exists, replace the current image with the new one if (item.Equals(e.Message)) { loadedSprite = item; // Set the image to the new loaded image Dispatcher.Invoke(() => loadedSprite.FrameImage = spriteImage); break; } } if (loadedSprite == null) { loadedSprite = new LoadedSpriteGrid(spriteImage, e.Message); // Listen to any selection event loadedSprite.MouseDown += SpriteImage_Tapped; loadedSprite.TouchDown += SpriteImage_Tapped; loadedSprite.StylusDown += SpriteImage_Tapped; // Add the loaded image into the view Dispatcher.Invoke(() => ClientLoadedSprites.Children.Add(loadedSprite)); } } } } } LoadedSpriteGrid currentlySelected = null; private void SpriteImage_Tapped(object sender, InputEventArgs e) { currentlySelected.frame.BorderThickness = new Thickness(0, 0, 0, 0); currentlySelected = sender as LoadedSpriteGrid; currentlySelected.frame.BorderThickness = new Thickness(3, 3, 3, 3); // Finally, enable the button CheckEnableVis(); } /// /// Every check required to enable the 'Join Server' button /// private void CheckEnableVis() { if (currentlySelected != null && PlayerNameBox.Text != "" && client.Connected) ClientStartGameButton.IsEnabled = true; else ClientStartGameButton.IsEnabled = false; } /// /// Layout that comprises of a Border containing an Image /// internal class LoadedSpriteGrid : Grid { string imageName; public Border frame; public Image FrameImage { get; set; } public LoadedSpriteGrid(Image spriteImage, string imageName) { FrameImage = spriteImage; frame = new(); frame.Child = FrameImage; frame.BorderThickness = new Thickness(0, 0, 0, 0); frame.BorderBrush = new SolidColorBrush(Color.FromArgb(255, 0, 100, 200)); Children.Add(frame); this.imageName = imageName; } /// /// Gets if the two grids are referencing the same image (regardless of the loaded mip level) /// /// /// public new bool Equals(object obj) { if (obj == null || GetType() != obj.GetType()) { return false; } string otherName = ((LoadedSpriteGrid)obj).imageName.Replace(App.MIP_LOW, "").Replace(App.MIP_MEDIUM, "").Replace(App.MIP_HIGH, "").Replace(App.MIP_RAW, ""); string tName = imageName.Replace(App.MIP_LOW, "").Replace(App.MIP_MEDIUM, "").Replace(App.MIP_HIGH, "").Replace(App.MIP_RAW, ""); return tName.Equals(otherName); } } private void PlayerNameBox_LostFocus(object sender, RoutedEventArgs e) { CheckEnableVis(); } #endregion // ************************************************************ // *************** Server Connection Logic ******************* // ************************************************************ #region server connection logic #endregion } }