2021-08-29 15:04:28 +12:00
using Microsoft.Win32 ;
using System ;
2021-08-29 21:12:37 +12:00
using System.Collections.Concurrent ;
2021-08-29 15:04:28 +12:00
using System.Collections.Generic ;
using System.IO ;
2021-08-29 21:12:37 +12:00
using System.Threading ;
using System.Threading.Tasks ;
2021-08-29 15:04:28 +12:00
using System.Windows ;
using System.Windows.Controls ;
using System.Windows.Controls.Primitives ;
using System.Windows.Media ;
using System.Windows.Media.Effects ;
using System.Windows.Media.Imaging ;
using System.Windows.Shapes ;
namespace RBG_Server.WPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
GameServer gameServer ;
MemoryStream gameBoard ;
public MainWindow ( )
{
InitializeComponent ( ) ;
}
/// <summary>
/// Browse for file button
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BrowseButton_Click ( object sender , RoutedEventArgs e )
{
OpenFileDialog ofd = new ( ) ;
2021-08-29 21:12:37 +12:00
ofd . Filter = "Image Files|*.png;*.jpg" ;
2021-08-29 15:04:28 +12:00
if ( ofd . ShowDialog ( ) = = true )
{
ImageSourceTextBox . Text = ofd . FileName ;
UpdateImage ( ) ;
}
}
/// <summary>
/// Update the image when the text box looses focus
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ImageSourceTextBox_LostFocus ( object sender , RoutedEventArgs e )
{
UpdateImage ( ) ;
}
private void UpdateImage ( )
{
try
{
using FileStream fs = File . OpenRead ( ImageSourceTextBox . Text ) ;
gameBoard = new ( ) ;
fs . CopyTo ( gameBoard ) ; // Load into memory, so the server can also utilise the stream
BitmapImage gameBoardImage = new ( ) ;
gameBoardImage . BeginInit ( ) ;
gameBoardImage . StreamSource = gameBoard ;
//gameBoardImage.CacheOption = BitmapCacheOption.OnLoad; // Must preload the image into memory, before it is unloaded
gameBoardImage . EndInit ( ) ;
PreviewImage . Source = gameBoardImage ;
RedrawGrid ( ) ;
}
catch ( Exception e )
{
Console . WriteLine ( e ) ;
}
}
/// <summary>
/// Creates and starts the game server
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void StartServerButton_Click ( object sender , RoutedEventArgs e )
{
2021-08-29 21:12:37 +12:00
gameServer = new ( ( byte ) StartingRow . Value , ( byte ) StartingColumn . Value , ( byte ) NumberOfRows . Value , ( byte ) NumberOfColumns . Value , ( byte ) ZoomBoxStartRow . Value , ( byte ) ZoomBoxStartColumn . Value , ( byte ) ZoomBoxSpan . Value , ( byte ) ZoomBoxSpan . Value , gameBoard ) ;
2021-08-29 15:04:28 +12:00
}
private void NumberOfRows_ValueChanged ( object sender , RoutedPropertyChangedEventArgs < double > e )
{
if ( IsInitialized )
{
StartingRow . Maximum = NumberOfRows . Value ;
ZoomBoxStartRow . Maximum = NumberOfRows . Value ;
ZoomBoxSpan . Maximum = Math . Min ( NumberOfRows . Value - ZoomBoxStartRow . Value , NumberOfColumns . Value - ZoomBoxStartColumn . Value ) ;
RedrawGrid ( ) ;
}
}
private void NumberOfColumns_ValueChanged ( object sender , RoutedPropertyChangedEventArgs < double > e )
{
if ( IsInitialized )
{
StartingColumn . Maximum = NumberOfColumns . Value ;
ZoomBoxStartColumn . Maximum = NumberOfColumns . Value ;
ZoomBoxSpan . Maximum = Math . Min ( NumberOfRows . Value - ZoomBoxStartRow . Value , NumberOfColumns . Value - ZoomBoxStartColumn . Value ) ;
RedrawGrid ( ) ;
}
}
private void RedrawGrid ( )
{
SolidColorBrush borderBrush = new ( Color . FromRgb ( 255 , 0 , 0 ) ) ;
PreviewImageOverlay . RowDefinitions . Clear ( ) ;
PreviewImageOverlay . ColumnDefinitions . Clear ( ) ;
PreviewImageOverlay . Children . Clear ( ) ;
for ( int i = 0 ; i < NumberOfColumns . Value ; i + + )
{
PreviewImageOverlay . ColumnDefinitions . Add ( new ColumnDefinition ( ) ) ;
}
for ( int i = 0 ; i < NumberOfRows . Value ; i + + )
{
PreviewImageOverlay . RowDefinitions . Add ( new RowDefinition ( ) ) ;
}
// Draw the overlay
Rectangle overlay = new ( ) ;
overlay . Fill = new SolidColorBrush ( Color . FromArgb ( 128 , 0 , 128 , 255 ) ) ;
_ = PreviewImageOverlay . Children . Add ( overlay ) ;
Grid . SetRow ( overlay , ( int ) ZoomBoxStartRow . Value ) ;
Grid . SetColumn ( overlay , ( int ) ZoomBoxStartColumn . Value ) ;
Grid . SetRowSpan ( overlay , ( int ) ZoomBoxSpan . Value ) ;
Grid . SetColumnSpan ( overlay , ( int ) ZoomBoxSpan . Value ) ;
// Draw the starting position
overlay = new ( ) ;
overlay . Fill = new SolidColorBrush ( Color . FromArgb ( 64 , 0 , 255 , 128 ) ) ;
_ = PreviewImageOverlay . Children . Add ( overlay ) ;
Grid . SetRow ( overlay , ( int ) StartingRow . Value ) ;
Grid . SetColumn ( overlay , ( int ) StartingColumn . Value ) ;
// Draw grids onto the preview image
for ( int v = 0 ; v < PreviewImageOverlay . RowDefinitions . Count ; v + + )
{
for ( int u = 0 ; u < PreviewImageOverlay . ColumnDefinitions . Count ; u + + )
{
Border border = new ( ) ;
border . BorderBrush = borderBrush ;
border . BorderThickness = new Thickness ( 2 ) ;
_ = PreviewImageOverlay . Children . Add ( border ) ;
Grid . SetRow ( border , v ) ;
Grid . SetColumn ( border , u ) ;
}
}
}
/// <summary>
/// Generates elements to go inside the game grid from the provided list of players
/// The grid should be appropriately sized before using this
/// </summary>
private void GenerateGameBoard ( List < Player > players )
{
SolidColorBrush buttonBrush = new ( Color . FromArgb ( 1 , 255 , 255 , 255 ) ) ;
Grid [ ] cells = new Grid [ PreviewImageOverlay . RowDefinitions . Count * PreviewImageOverlay . ColumnDefinitions . Count ] ;
List < Player > unaddedPlayers = players ;
for ( int i = 0 ; i < cells . Length ; i + + )
{
cells [ i ] = new Grid ( ) ;
// Each cell has several components:
// Stackpanel, which has each sprite in the cell
UniformGrid cellStack = new ( ) ;
cellStack . Columns = 4 ;
//cellStack.ItemWidth = cellStack.Width / 4;
Queue < Player > removedPlayers = new ( ) ;
foreach ( Player item in unaddedPlayers )
{
if ( ( item . Row * PreviewImageOverlay . ColumnDefinitions . Count ) + item . Column = = i )
{
removedPlayers . Enqueue ( item ) ;
}
}
while ( removedPlayers . Count > 0 )
{
Player removed = removedPlayers . Dequeue ( ) ;
_ = unaddedPlayers . Remove ( removed ) ;
_ = cellStack . Children . Add ( removed ) ;
}
_ = cells [ i ] . Children . Add ( cellStack ) ;
// Button, whose content is empty
Button cellButton = new ( )
{
Content = ""
} ;
cellButton . Background = buttonBrush ;
cellButton . Tag = new Tuple < int , int > ( i / PreviewImageOverlay . ColumnDefinitions . Count , i % PreviewImageOverlay . ColumnDefinitions . Count ) ; // Tag is a Tuple<row, column>
cellButton . Click + = CellButton_Click ;
_ = cells [ i ] . Children . Add ( cellButton ) ;
_ = PreviewImageOverlay . Children . Add ( cells [ i ] ) ;
Grid . SetColumn ( cells [ i ] , i % PreviewImageOverlay . ColumnDefinitions . Count ) ;
Grid . SetRow ( cells [ i ] , i / PreviewImageOverlay . ColumnDefinitions . Count ) ;
}
}
/// <summary>
/// Represents a cell on the board
/// </summary>
class BoardCell
{
// brush the button should have
static SolidColorBrush buttonBrush = new ( Color . FromArgb ( 1 , 255 , 255 , 255 ) ) ;
// Each cell has a uniform grid that
UniformGrid cellStack = new ( )
{
Columns = 4 ,
HorizontalAlignment = HorizontalAlignment . Stretch ,
VerticalAlignment = VerticalAlignment . Stretch
} ;
Button cellButton = new ( )
{
Content = ""
} ;
Queue < Player > removedPlayers = new ( ) ;
Grid parentGrid ;
public BoardCell ( Grid parentGrid )
{
this . parentGrid = parentGrid ;
}
}
2021-08-29 21:12:37 +12:00
private void GenerateNewGameBoard ( List < Player > players )
2021-08-29 15:04:28 +12:00
{
SolidColorBrush buttonBrush = new ( Color . FromArgb ( 1 , 255 , 255 , 255 ) ) ;
Grid [ ] cells = new Grid [ PreviewImageOverlay . RowDefinitions . Count * PreviewImageOverlay . ColumnDefinitions . Count ] ;
List < Player > unaddedPlayers = players ;
for ( int i = 0 ; i < cells . Length ; i + + )
{
}
}
private void CellButton_Click ( object sender , RoutedEventArgs e )
{
Button cellButton = sender as Button ;
Console . WriteLine ( "Button at {0}, {1} was clicked" , ( ( Tuple < int , int > ) cellButton . Tag ) . Item2 , ( ( Tuple < int , int > ) cellButton . Tag ) . Item1 ) ;
}
private void Sliders_ValueChanged ( object sender , RoutedPropertyChangedEventArgs < double > e )
{
if ( IsInitialized )
{
RedrawGrid ( ) ;
}
}
private void GridVisibilityToggleButton_Click ( object sender , RoutedEventArgs e )
{
PreviewImageOverlay . Visibility = PreviewImageOverlay . Visibility = = Visibility . Visible ? Visibility . Collapsed : Visibility . Visible ;
}
private static readonly string [ ] sprites = new string [ ]
{
"01.png" ,
"02.png" ,
"03.png" ,
"04.png" ,
} ;
private void DrawRandomizedPlayers_Click ( object sender , RoutedEventArgs e )
{
List < Player > randPlayers = new ( ) ;
Random rand = new ( ) ;
int count = rand . Next ( 1 , 10 ) ;
for ( int i = 0 ; i < count ; i + + )
{
// Create a new player at a random location
Player p = new ( "Player " + i , rand . Next ( 0 , PreviewImageOverlay . RowDefinitions . Count ) , rand . Next ( PreviewImageOverlay . ColumnDefinitions . Count ) ) ;
// Load the sprite
BitmapSource pSprite = new BitmapImage ( new Uri ( "pack://application:,,,/Sprites/" + sprites [ rand . Next ( 0 , sprites . Length ) ] ) ) ;
// Recolour:
if ( rand . Next ( 1 , 4 ) = = 1 )
{
pSprite = RBG . Helpers . ImageProcessing . UpdatePixelColours ( pSprite , 0.0 , 0.0 , 1.0 ) ;
}
p . Source = pSprite ;
randPlayers . Add ( p ) ;
}
GenerateGameBoard ( randPlayers ) ;
}
2021-08-29 21:12:37 +12:00
ConcurrentDictionary < string , MemoryStream > loadedSprites = new ( ) ;
Task loadingFiles ;
private void GameSpritesBrowser_Click ( object sender , RoutedEventArgs e )
{
OpenFileDialog ofd = new ( ) ;
ofd . Filter = "Image Files|*.png;*.jpg" ;
ofd . Multiselect = true ;
if ( ofd . ShowDialog ( ) = = true )
{
string [ ] resultFiles = ofd . FileNames ;
loadedSprites . Clear ( ) ;
// Run the following work in a separate thread
loadingFiles = Task . Run ( ( ) = >
{
// Continue in parallel
_ = Parallel . ForEach ( resultFiles , ( file ) = >
{
MemoryStream ms = new ( ) ;
using FileStream fs = File . OpenRead ( file ) ;
if ( new FileInfo ( file ) . Length > 512e6 ) // greater than 512 kB; resize the sprite to something more reasonable
{
BitmapImage gameBoardImage = new ( ) ;
gameBoardImage . BeginInit ( ) ;
gameBoardImage . StreamSource = fs ;
gameBoardImage . CacheOption = BitmapCacheOption . Default ;
gameBoardImage . EndInit ( ) ;
double vRate = 128 / ( double ) gameBoardImage . PixelHeight ; // Resize so the largest dimension is 128 px
double hRate = 128 / ( double ) gameBoardImage . PixelWidth ;
TransformedBitmap modified = new ( gameBoardImage , new ScaleTransform ( gameBoardImage . PixelWidth * Math . Min ( vRate , hRate ) , gameBoardImage . PixelHeight * Math . Min ( vRate , hRate ) ) ) ;
PngBitmapEncoder encoder = new ( ) ;
encoder . Frames . Add ( BitmapFrame . Create ( modified ) ) ;
encoder . Save ( ms ) ;
}
else
{
fs . CopyTo ( ms ) ;
}
_ = loadedSprites . TryAdd ( file , ms ) ;
} ) ;
// Invoke the dispatcher to add all of the sprites to the loaded sprites list
_ = Application . Current . Dispatcher . BeginInvoke ( System . Windows . Threading . DispatcherPriority . Render , new Action ( ( ) = >
{
LoadedSprites . Children . Clear ( ) ;
foreach ( KeyValuePair < string , MemoryStream > item in loadedSprites )
{
BitmapImage image = new ( ) ;
image . BeginInit ( ) ;
image . StreamSource = item . Value ;
image . EndInit ( ) ;
Image spriteImage = new ( )
{
Source = image
} ;
_ = LoadedSprites . Children . Add ( spriteImage ) ;
}
} ) ) ;
} ) ;
}
}
2021-08-29 15:04:28 +12:00
}
}