using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Myra; using Myra.Graphics2D.Brushes; using Myra.Graphics2D.TextureAtlases; using Myra.Graphics2D.UI; using Myra.Graphics2D.UI.File; using Newtonsoft.Json; using Sledgemapper.Shared.Entities; using Sledgemapper.UI; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; namespace Sledgemapper { public class Sledgemapper : Game { private readonly CommunicationManager _communicationManager; private readonly State _state; private readonly GraphicsDeviceManager _graphics; private SpriteBatch _spriteBatch; private readonly Desktop _desktop; private KeyboardState oldState; private MouseState oldMouseState; private Vector3 _viewportCenter = new(0, 0, 0); private Dictionary _fonts; private readonly Session _sessionData; private AuthenticateResponse _authResponse; private MainWidget _mainWidget; public Sledgemapper() { _graphics = new GraphicsDeviceManager(this); _graphics.GraphicsProfile = GraphicsProfile.Reach; _graphics.PreferMultiSampling = false; Content.RootDirectory = "Content"; _desktop = new Desktop(); MyraEnvironment.Game = this; _sessionData = new Session(); IsFixedTimeStep = false; _communicationManager = new CommunicationManager(_sessionData); _communicationManager.Connection.Reconnected += OnHubReconnected; _communicationManager.Connection.Reconnecting += OnHubReconnecting; _communicationManager.Connection.Closed += OnHubDisconnected; _state = new State(); } private async Task OnHubDisconnected(Exception arg) { _mainWidget.lblConnectionStatus.Text = "Disconnected"; } private async Task OnHubReconnecting(Exception arg) { _mainWidget.lblConnectionStatus.Text = "Reconnecting"; } private async Task OnHubReconnected(string arg) { _mainWidget.lblConnectionStatus.Text = "Connected"; } protected override void Initialize() { IsMouseVisible = true; Window.AllowUserResizing = true; base.Initialize(); } private void AddItemToToolGrid(Grid grid, EventHandler eventAction, string folder) { var tilesFolderContent = Content.LoadContentFolder(folder); var indexX = 0; var indexY = 0; foreach (var item in tilesFolderContent) { var tileButton = new ImageButton { Image = new TextureRegion(item.Value), GridColumn = indexY, GridRow = indexX, Id = item.Key, Width = 40, Height = 40 }; tileButton.Click += eventAction; grid.Widgets.Add(tileButton); indexY++; if (indexY == 4) { indexY = 0; indexX++; } } } protected override void LoadContent() { _spriteBatch = new SpriteBatch(GraphicsDevice); MyraEnvironment.Game = this; _mainWidget = new MainWidget(); _mainWidget.MenuConnectSync.Selected += OnMenuConnectSyncSelected; _mainWidget.MenuFileLoad.Selected += OnMenuFileLoadSelected; _mainWidget.MenuFileSave.Selected += OnMenuFileSaveSelected; _mainWidget.MenuConnectLogin.Selected += OnMenuConnectLoginSelected; _mainWidget.MenuConnectNew.Selected += OnMenuConnectNewSelected; _mainWidget.MenuConnectJoin.Selected += OnMenuConnectJoinSelected; _mainWidget.MenuConnectUpload.Selected += OnMenuConnectUploadSelected; _mainWidget.MenuConnectNew.Enabled = false; _mainWidget.MenuConnectJoin.Enabled = false; _mainWidget.MenuConnectSync.Enabled = false; _mainWidget.MenuConnectUpload.Enabled = false; AddItemToToolGrid(_mainWidget.GridTiles, OnTileButtonClicked, "tiles"); AddItemToToolGrid(_mainWidget.GridWalls, OnWallButtonClicked, "walls"); AddItemToToolGrid(_mainWidget.GridOverlays, OnOverlayButtonClicked, "overlays"); _fonts = Content.LoadContentFolder("fonts"); _desktop.Root = _mainWidget; } protected override void Update(GameTime gameTime) { KeyboardState newState = Keyboard.GetState(); if (IsActive && GraphicsDevice.Viewport.Bounds.Contains(Mouse.GetState().Position) && !_desktop.IsMouseOverGUI && !_desktop.HasModalWidget) { var mouseState = Mouse.GetState(); var screenPosition = new Point(mouseState.Position.X - (int)_viewportCenter.X, mouseState.Position.Y - (int)_viewportCenter.Y); _state._hoveredTile.X = screenPosition.X / _state._tileSize; _state._hoveredTile.Y = screenPosition.Y / _state._tileSize; if (screenPosition.X < 0) { _state._hoveredTile.X--; } if (screenPosition.Y < 0) { _state._hoveredTile.Y--; } if (_state._insertMode == InsertMode.Wall) { _state.SelectClosestWall(screenPosition); } if (_state._insertMode == InsertMode.Overlay) { _state.SelectOverlay(screenPosition); } if (newState.IsKeyDown(Keys.LeftControl) && newState.IsKeyDown(Keys.C)) { CenterOnSelectedTile(); } if (mouseState.LeftButton == ButtonState.Pressed && mouseState.LeftButton != oldMouseState.LeftButton) { _state._selectedTile.X = _state._hoveredTile.X; _state._selectedTile.Y = _state._hoveredTile.Y; _communicationManager.Connection?.SendAsync("UpdatePosition", _sessionData.SessionName, _sessionData.SessionId, _state._selectedTile); } if (newState.IsKeyDown(Keys.LeftControl) && mouseState.LeftButton == ButtonState.Pressed && ((mouseState.LeftButton != oldMouseState.LeftButton) || (_state._selectedTile.X != _state._hoveredTile.X && _state._selectedTile.Y != _state._hoveredTile.Y))) { switch (_state._insertMode) { case InsertMode.Tile: _state._selectedTile.X = _state._hoveredTile.X; _state._selectedTile.Y = _state._hoveredTile.Y; _communicationManager.Connection?.SendAsync("UpdatePosition", _sessionData.SessionName, _sessionData.SessionId, _state._selectedTile); _sessionData.NewTile(_state._selectedTile, _state._currentTileId); break; case InsertMode.Wall: _sessionData.NewWall(_state._selectedWall, _state._currentWallId); break; case InsertMode.Overlay: _sessionData.NewOverlay(_state._selectedOverlay, _state._currentOverlayId); break; } } if (!newState.IsKeyDown(Keys.LeftControl) && mouseState.LeftButton == ButtonState.Pressed && mouseState.LeftButton == oldMouseState.LeftButton) { _viewportCenter = new Vector3(_viewportCenter.X + mouseState.Position.X - oldMouseState.Position.X, _viewportCenter.Y + mouseState.Position.Y - oldMouseState.Position.Y, 0); Console.WriteLine($"{_state._selectedTile.X}:{_state._selectedTile.Y} / {_viewportCenter.X}:{_viewportCenter.Y}"); } if (newState.IsKeyDown(Keys.LeftControl) && mouseState.ScrollWheelValue != oldMouseState.ScrollWheelValue) { if (mouseState.ScrollWheelValue > oldMouseState.ScrollWheelValue) { _state._tileSize = Math.Min(120, _state._tileSize + 10); } else if (mouseState.ScrollWheelValue < oldMouseState.ScrollWheelValue) { _state._tileSize = Math.Max(10, _state._tileSize - 10); } } oldMouseState = mouseState; } if (newState.IsKeyDown(Keys.Delete)) { switch (_state._insertMode) { case InsertMode.Tile: _state._selectedTile.X = _state._hoveredTile.X; _state._selectedTile.Y = _state._hoveredTile.Y; _sessionData.DeleteTile(_state._selectedTile); break; case InsertMode.Wall: _sessionData.DeleteWall(_state._selectedWall); break; case InsertMode.Overlay: _sessionData.DeleteOverlay(_state._selectedOverlay); break; } } foreach (var key in newState.GetPressedKeys()) { switch (key) { case Keys.Left: if (oldState.IsKeyUp(Keys.Left) && newState.IsKeyDown(Keys.Left)) { _state._selectedTile.X--; } break; case Keys.Right: if (oldState.IsKeyUp(Keys.Right) && newState.IsKeyDown(Keys.Right)) { _state._selectedTile.X++; } break; case Keys.Up: if (oldState.IsKeyUp(Keys.Up) && newState.IsKeyDown(Keys.Up)) { _state._selectedTile.Y--; } break; case Keys.Down: if (oldState.IsKeyUp(Keys.Down) && newState.IsKeyDown(Keys.Down)) { _state._selectedTile.Y++; } break; } _communicationManager.Connection?.SendAsync("UpdatePosition", _sessionData.SessionName, _state._selectedTile); } oldState = newState; base.Update(gameTime); } private void CenterOnSelectedTile() { _viewportCenter.X=_state._selectedTile.X*_state._tileSize; _viewportCenter.Y=_state._selectedTile.Y*_state._tileSize; } protected override void Draw(GameTime gameTime) { if (_spriteBatch is null) { return; } GraphicsDevice.Clear(Color.LightGray); var visibleTilesX = GraphicsDevice.Viewport.Width / _state._tileSize + 1; var visibleTilesY = GraphicsDevice.Viewport.Height / _state._tileSize + 1; _spriteBatch.Begin(transformMatrix: Matrix.CreateTranslation(_viewportCenter)); DrawGrid(visibleTilesX, visibleTilesY); DrawTiles(); DrawWalls(); DrawOverlays(); if (string.IsNullOrWhiteSpace(_sessionData.SessionName)) { _spriteBatch.DrawRectangle(new Rectangle((_state._selectedTile.X * _state._tileSize) - 2, (_state._selectedTile.Y * _state._tileSize) - 2, _state._tileSize + 3, _state._tileSize + 3), Color.Red, 2); } DrawPlayers(); var startWall = new Vector2(_state._selectedWall.X * _state._tileSize, _state._selectedWall.Y * _state._tileSize); if (_state._insertMode == InsertMode.Wall) { _spriteBatch.DrawLine(startWall, _state._tileSize, MathHelper.ToRadians(90 * _state._selectedWall.Rotation), Color.Red, 2); } var overlay = new Vector2(_state._selectedOverlay.X * _state._tileSize, _state._selectedOverlay.Y * _state._tileSize); if (_state._insertMode == InsertMode.Overlay && _state._selectedOverlay.Intersection) { _spriteBatch.DrawCircle(overlay, _state._tileSize / 3f, 100, Color.Red, 2); } _spriteBatch.End(); try { _desktop?.Render(); } catch { System.Console.WriteLine("Desktop render error"); } base.Draw(gameTime); } private void DrawGrid(int visibleTilesX, int visibleTilesY) { for (var i = -1; i < visibleTilesX + 2; i++) { var posX1 = i * _state._tileSize - _viewportCenter.X; var posY1 = -_viewportCenter.Y; posX1 -= posX1 % _state._tileSize; posY1 -= posY1 % _state._tileSize; var posX2 = i * _state._tileSize - _viewportCenter.X; var posY2 = GraphicsDevice.Viewport.Height - _viewportCenter.Y; posX2 -= posX2 % _state._tileSize; posY2 -= posY2 % _state._tileSize; _spriteBatch.DrawLine( posX1, posY1, posX2, posY2, Color.GhostWhite); } for (var i = -1; i < visibleTilesY + 2; i++) { var posX1 = -_viewportCenter.X; var posY1 = i * _state._tileSize - _viewportCenter.Y; posX1 -= posX1 % _state._tileSize; posY1 -= posY1 % _state._tileSize; var posX2 = GraphicsDevice.Viewport.Width - _viewportCenter.X; var posY2 = i * _state._tileSize - _viewportCenter.Y; posX2 -= posX2 % _state._tileSize; posY2 -= posY2 % _state._tileSize; _spriteBatch.DrawLine(posX1, posY1, posX2, posY2, Color.GhostWhite); } } private void DrawOverlays() { foreach (var tile in _sessionData.Overlays.Values) { var content = Content.Load($"overlays/{tile.ID}"); if (tile.Intersection) { var posX = tile.X * _state._tileSize; var posY = tile.Y * _state._tileSize; _spriteBatch.Draw(content, new Vector2(posX, posY), null, new Color(24, 118, 157), MathHelper.ToRadians(90 * tile.Rotation), new Vector2(content.Width / 2, content.Height / 2), ((float)_state._tileSize - 10) / content.Width, SpriteEffects.None, 0); } else { var posX = tile.X * _state._tileSize + _state._tileSize / 2f; var posY = tile.Y * _state._tileSize + _state._tileSize / 2f; _spriteBatch.Draw(content, new Vector2(posX, posY), null, new Color(24, 118, 157), MathHelper.ToRadians(90 * tile.Rotation), new Vector2(content.Width / 2, content.Height / 2), ((float)_state._tileSize - 10) / content.Width, SpriteEffects.None, 0); } } } private void DrawPlayers() { foreach (var player in _sessionData.Players.Copy()) { var hexs = player.Color.TrimStart('#').Split(2).ToArray(); var color = new Color(int.Parse(hexs[0], System.Globalization.NumberStyles.HexNumber), int.Parse(hexs[1], System.Globalization.NumberStyles.HexNumber), int.Parse(hexs[2], System.Globalization.NumberStyles.HexNumber)); _spriteBatch.DrawRectangle(new Rectangle(player.Position.X * _state._tileSize, player.Position.Y * _state._tileSize, _state._tileSize - 1, _state._tileSize - 1), color, 2); var ffont = _fonts.FirstOrDefault(m => int.Parse(m.Key.Replace("font", "")) > _state._tileSize).Value ?? _fonts.Last().Value; var fscale = _state._tileSize / ((float)ffont.LineSpacing * 2); _spriteBatch.DrawString(ffont, player.Initials, new Vector2(player.Position.X * _state._tileSize + 2, player.Position.Y * _state._tileSize + _state._tileSize - 2 - ffont.LineSpacing * fscale), color, 0, Vector2.Zero, fscale, SpriteEffects.None, 0); } } private void DrawTiles() { foreach (var tile in _sessionData.Map.Values) { var content = Content.Load($"tiles/{tile.ID}"); var posX = tile.X * _state._tileSize + _state._tileSize / 2f; var posY = tile.Y * _state._tileSize + _state._tileSize / 2f; _spriteBatch.Draw(content, new Vector2(posX, posY), null, Color.White, MathHelper.ToRadians(90 * tile.Rotation), new Vector2(content.Width / 2, content.Height / 2), ((float)_state._tileSize - 1) / content.Width, SpriteEffects.None, 0); } } private void DrawWalls() { foreach (var wall in _sessionData.Walls.Values) { var content = Content.Load($"walls/{wall.ID}"); var scale = _state._tileSize / (float)content.Height; var offset = scale * content.Width / 2f; var posX = wall.X * _state._tileSize; var posY = wall.Y * _state._tileSize; if (wall.Rotation == 1) { posX -= (int)offset; } else if (wall.Rotation == 0) { posY += (int)offset; } _spriteBatch.Draw(content, new Vector2(posX, posY), null, Color.White, MathHelper.ToRadians(90 * (wall.Rotation - 1)), new Vector2(0, 0), scale, SpriteEffects.None, 0); } } private async void OnButtonJoinSessionClicked(object sender, EventArgs e) { Container container = ((TextButton)sender).Parent; while (!(container is Window)) { container = container.Parent; } var localWindow = (Window)container; var localContent = localWindow.Content as SessionWindow; var isValid = ValidateTextbox(localContent.TxtSession); if (!isValid) { return; } if (_communicationManager.Connection.State != HubConnectionState.Connected) { await _communicationManager.Connection.StartAsync(); } var successful = false; try { var result = await _communicationManager.Connection?.InvokeAsync("JoinSession", localContent.TxtSession.Text); if (result != null) { _sessionData.Map = result.Map; _sessionData.Walls = result.Walls; _sessionData.Overlays = result.Overlays; _sessionData.MapEntityAdded -= OnMapEntityAdded; _sessionData.MapEntityDeleted -= OnMapEntityDeleted; _sessionData.MapEntityAdded += OnMapEntityAdded; _sessionData.MapEntityDeleted += OnMapEntityDeleted; _sessionData.SessionId = result.SessionId; _sessionData.SessionName = result.SessionName; } successful = result != null; } catch (Exception ex) { Console.WriteLine(ex.Message); } if (successful) { _sessionData.SessionName = localContent.TxtSession.Text; _mainWidget.lblSessionName.Text = _sessionData.SessionName; localWindow.Close(); } } private async void OnButtonNewSessionClicked(object sender, EventArgs e) { Container container = ((TextButton)sender).Parent; while (!(container is Window)) { container = container.Parent; } var localWindow = (Window)container; var localContent = localWindow.Content as SessionWindow; var isValid = ValidateTextbox(localContent.TxtSession); if (!isValid) { return; } if (_communicationManager.Connection.State != HubConnectionState.Connected) { _mainWidget.lblConnectionStatus.Text = "Connecting"; await _communicationManager.Connection.StartAsync(); UpdateConnectionState(_communicationManager.Connection); } var successful = false; try { var result = await _communicationManager.Api.NewSession(localContent.TxtSession.Text); if (result) { _sessionData.SessionName = localContent.TxtSession.Text; _sessionData.MapEntityAdded -= OnMapEntityAdded; _sessionData.MapEntityDeleted -= OnMapEntityDeleted; _sessionData.MapEntityAdded += OnMapEntityAdded; _sessionData.MapEntityDeleted += OnMapEntityDeleted; } successful = result; var result2 = await _communicationManager.Connection?.InvokeAsync("JoinSession", localContent.TxtSession.Text); _sessionData.SessionId = result2.SessionId; _sessionData.SessionName = localContent.TxtSession.Text; } catch (Exception ex) { Console.WriteLine(ex.Message); } if (successful) { _sessionData.SessionName = localContent.TxtSession.Text; _communicationManager.SessionData.Map = _sessionData.Map; _communicationManager.SessionData.Overlays = _sessionData.Overlays; _communicationManager.SessionData.Walls = _sessionData.Walls; _mainWidget.lblSessionName.Text = _sessionData.SessionName; localWindow.Close(); } } private void UpdateConnectionState(HubConnection connection) { switch (connection.State) { case HubConnectionState.Connected: _mainWidget.lblConnectionStatus.Text = "Connected"; break; case HubConnectionState.Connecting: _mainWidget.lblConnectionStatus.Text = "Connecting"; break; case HubConnectionState.Disconnected: _mainWidget.lblConnectionStatus.Text = "Disconnected"; break; case HubConnectionState.Reconnecting: _mainWidget.lblConnectionStatus.Text = "Reconnecting"; break; } } private async void OnButtonLoginClick(object sender, EventArgs e) { var button = ((TextButton)sender); Container container = button.Parent; while (!(container is Window)) { container = container.Parent; } var localWindow = (Window)container; var localContent = localWindow.Content as LoginRegisterWindow; if (!button.Enabled) { return; } var isValid = true; isValid &= ValidateTextbox(localContent.TxtEmail); isValid &= ValidateTextbox(localContent.TxtPassword); if (!isValid) { localContent.LblLoginError.Text = "Username or password is not valid"; localContent.LblLoginError.Visible = true; return; } var successful = false; try { button.Text = "Wait..."; localContent.LblLoginError.Text = ""; localContent.LblLoginError.Visible = false; _authResponse = await _communicationManager.Login(new AuthenticateModel { Username = localContent.TxtEmail.Text, Password = localContent.TxtPassword.Text }); successful = _authResponse != null; } catch (Refit.ApiException refitException) { localContent.LblLoginError.Text = refitException.Content; localContent.LblLoginError.Visible = true; } catch (Exception ex) { localContent.LblLoginError.Text = "Can't connect to the server"; localContent.LblLoginError.Visible = true; } finally { button.Enabled = true; button.Text = "Login"; } if (successful) { _mainWidget.MenuConnectNew.Enabled = true; _mainWidget.MenuConnectJoin.Enabled = true; _mainWidget.MenuConnectSync.Enabled = true; _mainWidget.MenuConnectUpload.Enabled = true; _mainWidget.lblUsername.Text = $"{_authResponse.Username} - {_authResponse.Initials}"; localWindow.Close(); } } private async void OnButtonRegisterClick(object sender, EventArgs e) { var button = ((TextButton)sender); Container container = button.Parent; while (!(container is Window)) { container = container.Parent; } var localWindow = (Window)container; var localContent = localWindow.Content as LoginRegisterWindow; if (!button.Enabled) { return; } var isValid = true; isValid &= ValidateTextbox(localContent.TxtEmail); isValid &= ValidateTextbox(localContent.TxtPassword); isValid &= ValidateTextbox(localContent.TxtFirstname); isValid &= ValidateTextbox(localContent.TxtLastname); isValid &= ValidateTextbox(localContent.TxtInitials); if (!isValid) { localContent.LblLoginError.Text = "Please complete all the fields"; localContent.LblLoginError.Visible = true; return; } var successful = false; try { button.Text = "Wait..."; localContent.LblLoginError.Text = ""; localContent.LblLoginError.Visible = false; var result = await _communicationManager.Register(new RegisterModel { Username = localContent.TxtEmail.Text, Password = localContent.TxtPassword.Text, FirstName = localContent.TxtFirstname.Text, LastName = localContent.TxtLastname.Text, Initials = localContent.TxtInitials.Text }); if (result.IsSuccessStatusCode) { _authResponse = await _communicationManager.Login(new AuthenticateModel { Username = localContent.TxtEmail.Text, Password = localContent.TxtPassword.Text }); successful = true; } else { localContent.LblLoginError.Text = result.ReasonPhrase; localContent.LblLoginError.Visible = true; } } catch (Refit.ApiException refitException) { localContent.LblLoginError.Text = refitException.Content; localContent.LblLoginError.Visible = true; } catch (Exception ex) { localContent.LblLoginError.Text = "Can't connect to the server"; localContent.LblLoginError.Visible = true; } finally { button.Enabled = true; button.Text = "Register"; } if (successful) { _mainWidget.MenuConnectNew.Enabled = true; _mainWidget.MenuConnectJoin.Enabled = true; _mainWidget.MenuConnectSync.Enabled = true; _mainWidget.MenuConnectUpload.Enabled = true; _mainWidget.lblUsername.Text = $"{_authResponse.Username} - {_authResponse.Initials}"; localWindow.Close(); } } private void OnOverlayButtonClicked(object sender, EventArgs e) { _state._currentOverlayId = ((ImageButton)sender).Id; _mainWidget.ClearSelection(); ((ImageButton)sender).Border = new SolidBrush(Color.Red); ((ImageButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); _state._insertMode = InsertMode.Overlay; } private void OnTileButtonClicked(object sender, EventArgs e) { _state._currentTileId = ((ImageButton)sender).Id; _mainWidget.ClearSelection(); ((ImageButton)sender).Border = new SolidBrush(Color.Red); ((ImageButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); _state._insertMode = InsertMode.Tile; } private void OnWallButtonClicked(object sender, EventArgs e) { _state._currentWallId = ((ImageButton)sender).Id; _mainWidget.ClearSelection(); ((ImageButton)sender).Border = new SolidBrush(Color.Red); ((ImageButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); _state._insertMode = InsertMode.Wall; } private void OnMenuConnectJoinSelected(object sender, EventArgs e) { if (!((MenuItem)sender).Enabled) { return; } Window window = new() { Title = "Join mapping session" }; var content = new SessionWindow(); content.BtnLogin.Text = "Join"; content.BtnLogin.Click += OnButtonJoinSessionClicked; window.Content = content; window.ShowModal(_desktop); } private void OnMenuConnectLoginSelected(object sender, EventArgs e) { Window window = new() { Title = "Login" }; var content = new LoginRegisterWindow(); content.RdoLogin.IsPressed = true; content.RdoLogin.Click += (s, e) => { content.TxtFirstname.Visible = false; content.TxtLastname.Visible = false; content.TxtInitials.Visible = false; content.LblFirstname.Visible = false; content.LblLastname.Visible = false; content.LblInitials.Visible = false; content.BtnLogin.Visible = true; content.BtnRegister.Visible = false; window.Title = "Login"; }; content.RdoRegister.Click += (s, e) => { content.TxtFirstname.Visible = true; content.TxtLastname.Visible = true; content.TxtInitials.Visible = true; content.LblFirstname.Visible = true; content.LblLastname.Visible = true; content.LblInitials.Visible = true; content.BtnLogin.Visible = false; content.BtnRegister.Visible = true; window.Title = "Register"; }; content.BtnRegister.Click += OnButtonRegisterClick; content.BtnLogin.Click += OnButtonLoginClick; window.Content = content; window.ShowModal(_desktop); } private async void OnMenuConnectSyncSelected(object sender, EventArgs e) { if (!((MenuItem)sender).Enabled) { return; } var serverMap = await _communicationManager.Api.Session(_sessionData.SessionName); _sessionData.Overlays = serverMap.Overlays; _sessionData.Map = serverMap.Map; _sessionData.Walls = serverMap.Walls; } private async void OnMenuConnectUploadSelected(object sender, EventArgs e) { if (!((MenuItem)sender).Enabled) { return; } await _communicationManager.Api.SaveSnapshot(_sessionData, _sessionData.SessionName); } private void OnMenuConnectNewSelected(object sender, EventArgs e) { if (!((MenuItem)sender).Enabled) { return; } Window window = new() { Title = "New mapping session" }; var content = new SessionWindow(); content.BtnLogin.Text = "Join"; content.BtnLogin.Click += OnButtonNewSessionClicked; window.Content = content; window.ShowModal(_desktop); } private void OnMenuFileSaveSelected(object sender, EventArgs e) { FileDialog dialog = new(FileDialogMode.SaveFile) { Filter = "*.map" }; dialog.Closed += (s, a) => { if (!dialog.Result) { return; } using StreamWriter file = File.CreateText(dialog.FilePath); JsonSerializer serializer = new(); serializer.Serialize(file, _sessionData); }; dialog.ShowModal(_desktop); } private void OnMenuFileLoadSelected(object sender, EventArgs e) { var dialog = new FileDialog(FileDialogMode.OpenFile) { Filter = "*.map" }; dialog.Closed += (s, a) => { if (!dialog.Result) { return; } using StreamReader file = File.OpenText(dialog.FilePath); JsonSerializer serializer = new(); var loadData = (Session)serializer.Deserialize(file, typeof(Session)); _sessionData.Map = loadData.Map; _sessionData.Overlays = loadData.Overlays; _sessionData.Walls = loadData.Walls; }; dialog.ShowModal(_desktop); } private void OnMapEntityAdded(object sender, MapEntityAddedEventArgs e) { _communicationManager.Enqueue(e.MapEntity, TileAction.Add); } private void OnMapEntityDeleted(object sender, MapEntityDeletedEventArgs e) { _communicationManager.Enqueue(e.MapEntity, TileAction.Delete); } private bool ValidateTextbox(TextBox textBox) { var valid = !string.IsNullOrWhiteSpace(textBox.Text); if (!valid) { textBox.Background = new SolidBrush(Color.Red); } else { textBox.Background = new SolidBrush(new Color(51, 51, 51)); ; } return valid; } } }