From 0a3eb44248e560ad0e29390e7b7803aa44be842d Mon Sep 17 00:00:00 2001 From: Tim Sarbin Date: Thu, 29 Nov 2018 23:26:51 -0500 Subject: [PATCH] Started work on session manager. Fixed mouse rendering logic. --- OpenDiablo2.Common/Enums/eSessionType.cs | 21 +++++ .../Interfaces/Drawing/IRenderWindow.cs | 3 +- OpenDiablo2.Common/Interfaces/IGameState.cs | 2 +- .../Interfaces/ISessionManager.cs | 14 +++ .../Interfaces/ISessionServer.cs | 12 +++ OpenDiablo2.Common/OpenDiablo2.Common.csproj | 8 +- OpenDiablo2.Core/AutofacModule.cs | 1 + OpenDiablo2.Core/GameEngine.cs | 17 +--- OpenDiablo2.Core/GameState/GameState.cs | 50 ++-------- OpenDiablo2.Core/OpenDiablo2.Core.csproj | 1 + OpenDiablo2.Core/SessionServer.cs | 18 ++++ OpenDiablo2.SDL2/SDL2MouseCursor.cs | 7 +- OpenDiablo2.SDL2/SDL2RenderWindow.cs | 92 +++++++++++++++---- OpenDiablo2.Scenes/SelectHeroClass.cs | 3 +- OpenDiablo2.ServiceBus/AutofacModule.cs | 24 ++++- OpenDiablo2.ServiceBus/LocalSessionManager.cs | 47 ++++++++++ .../OpenDiablo2.ServiceBus.csproj | 1 + 17 files changed, 243 insertions(+), 78 deletions(-) create mode 100644 OpenDiablo2.Common/Enums/eSessionType.cs create mode 100644 OpenDiablo2.Common/Interfaces/ISessionManager.cs create mode 100644 OpenDiablo2.Common/Interfaces/ISessionServer.cs create mode 100644 OpenDiablo2.Core/SessionServer.cs create mode 100644 OpenDiablo2.ServiceBus/LocalSessionManager.cs diff --git a/OpenDiablo2.Common/Enums/eSessionType.cs b/OpenDiablo2.Common/Enums/eSessionType.cs new file mode 100644 index 00000000..164bb0ed --- /dev/null +++ b/OpenDiablo2.Common/Enums/eSessionType.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Enums +{ + /// Defines the type of gameplay session we are running. + public enum eSessionType + { + /// This session is an offline single player game. + Local, + + /// This session is a multiplayer game, and this instance is the server. + Server, + + /// This session is a multiplayer game, and this instance is a client connected to an external server. + Remote + } +} diff --git a/OpenDiablo2.Common/Interfaces/Drawing/IRenderWindow.cs b/OpenDiablo2.Common/Interfaces/Drawing/IRenderWindow.cs index e7849595..e87979a9 100644 --- a/OpenDiablo2.Common/Interfaces/Drawing/IRenderWindow.cs +++ b/OpenDiablo2.Common/Interfaces/Drawing/IRenderWindow.cs @@ -6,6 +6,8 @@ namespace OpenDiablo2.Common.Interfaces { public interface IRenderWindow : IDisposable { + IMouseCursor MouseCursor { get; set; } + bool IsRunning { get; } void Update(); void Clear(); @@ -24,7 +26,6 @@ namespace OpenDiablo2.Common.Interfaces void Draw(ISprite sprite, int frame); void Draw(ISprite sprite, int xSegments, int ySegments, int offset); IMouseCursor LoadCursor(ISprite sprite, int frame, Point hotspot); - void SetCursor(IMouseCursor mouseCursor); void Draw(ILabel label); MapCellInfo CacheMapCell(MPQDT1Tile mapCell); void DrawMapCell(MapCellInfo mapCellInfo, int xPixel, int yPixel); diff --git a/OpenDiablo2.Common/Interfaces/IGameState.cs b/OpenDiablo2.Common/Interfaces/IGameState.cs index 9520f766..042d4e70 100644 --- a/OpenDiablo2.Common/Interfaces/IGameState.cs +++ b/OpenDiablo2.Common/Interfaces/IGameState.cs @@ -18,7 +18,7 @@ namespace OpenDiablo2.Common.Interfaces bool ToggleShowCharacterPanel(); bool ShowCharacterPanel { get; set; } - void Initialize(string text, eHero value); + void Initialize(string text, eHero value, eSessionType sessionType); void Update(long ms); IEnumerable GetMapCellInfo(int cellX, int cellY, eRenderCellType renderCellType); void UpdateMapCellInfo(int cellX, int cellY, eRenderCellType renderCellType, IEnumerable mapCellInfo); diff --git a/OpenDiablo2.Common/Interfaces/ISessionManager.cs b/OpenDiablo2.Common/Interfaces/ISessionManager.cs new file mode 100644 index 00000000..e4dffb3f --- /dev/null +++ b/OpenDiablo2.Common/Interfaces/ISessionManager.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Interfaces +{ + public interface ISessionManager : IDisposable + { + void Initialize(); + void Stop(); + } +} diff --git a/OpenDiablo2.Common/Interfaces/ISessionServer.cs b/OpenDiablo2.Common/Interfaces/ISessionServer.cs new file mode 100644 index 00000000..cba01fa6 --- /dev/null +++ b/OpenDiablo2.Common/Interfaces/ISessionServer.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Interfaces +{ + public interface ISessionServer : IDisposable + { + } +} diff --git a/OpenDiablo2.Common/OpenDiablo2.Common.csproj b/OpenDiablo2.Common/OpenDiablo2.Common.csproj index 58a815c1..d8f8826e 100644 --- a/OpenDiablo2.Common/OpenDiablo2.Common.csproj +++ b/OpenDiablo2.Common/OpenDiablo2.Common.csproj @@ -61,11 +61,14 @@ + + + @@ -134,6 +137,9 @@ - + + + + \ No newline at end of file diff --git a/OpenDiablo2.Core/AutofacModule.cs b/OpenDiablo2.Core/AutofacModule.cs index 52793968..7bb1497c 100644 --- a/OpenDiablo2.Core/AutofacModule.cs +++ b/OpenDiablo2.Core/AutofacModule.cs @@ -27,6 +27,7 @@ namespace OpenDiablo2.Core builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().InstancePerDependency(); + builder.RegisterType().As().InstancePerLifetimeScope(); } } } diff --git a/OpenDiablo2.Core/GameEngine.cs b/OpenDiablo2.Core/GameEngine.cs index 8e79d8d1..47dc301f 100644 --- a/OpenDiablo2.Core/GameEngine.cs +++ b/OpenDiablo2.Core/GameEngine.cs @@ -17,7 +17,6 @@ namespace OpenDiablo2.Core private readonly GlobalConfiguration globalConfig; private readonly IMPQProvider mpqProvider; private readonly Func getRenderWindow; - private readonly Func getMouseInfoProvider; private readonly Func getScene; private readonly Func getResourceManager; private readonly Func getGameState; @@ -36,7 +35,6 @@ namespace OpenDiablo2.Core GlobalConfiguration globalConfig, IMPQProvider mpqProvider, Func getRenderWindow, - Func getMouseInfoProvider, Func getScene, Func getResourceManager, Func getGameState @@ -45,7 +43,6 @@ namespace OpenDiablo2.Core this.globalConfig = globalConfig; this.mpqProvider = mpqProvider; this.getRenderWindow = getRenderWindow; - this.getMouseInfoProvider = getMouseInfoProvider; this.getScene = getScene; this.getResourceManager = getResourceManager; this.getGameState = getGameState; @@ -81,19 +78,14 @@ namespace OpenDiablo2.Core public void Run() { var renderWindow = getRenderWindow(); - var mouseInfoProvider = getMouseInfoProvider(); LoadPalettes(); LoadSoundData(); mouseSprite = renderWindow.LoadSprite(ResourcePaths.CursorDefault, Palettes.Units); - IMouseCursor cursor; - if (globalConfig.MouseMode == eMouseMode.Hardware) - { - cursor = renderWindow.LoadCursor(mouseSprite, 0, new Point(0, 0)); - renderWindow.SetCursor(cursor); - } - + var cursor = renderWindow.LoadCursor(mouseSprite, 0, new Point(0, 3)); + renderWindow.MouseCursor = cursor; + currentScene = getScene("Main Menu"); var lastTicks = renderWindow.GetTicks(); while (getRenderWindow().IsRunning) @@ -132,9 +124,6 @@ namespace OpenDiablo2.Core renderWindow.Clear(); currentScene.Render(); - if (globalConfig.MouseMode == eMouseMode.Software) - renderWindow.Draw(mouseSprite, new Point(mouseInfoProvider.MouseX, mouseInfoProvider.MouseY + 3)); - renderWindow.Sync(); } } diff --git a/OpenDiablo2.Core/GameState/GameState.cs b/OpenDiablo2.Core/GameState/GameState.cs index 2f765c2b..cc7cdc64 100644 --- a/OpenDiablo2.Core/GameState/GameState.cs +++ b/OpenDiablo2.Core/GameState/GameState.cs @@ -19,16 +19,12 @@ namespace OpenDiablo2.Core.GameState_ private readonly IEngineDataManager engineDataManager; private readonly IRenderWindow renderWindow; private readonly Func getMapEngine; + private readonly Func getSessionManager; private float animationTime = 0f; private List mapInfo; private List mapDataLookup = new List(); - - // TODO: Break this out further so we can support multiple maps--------------------------------------------- - //private MPQDS1 _mapDataTemp, _mapDataTemp2; - //private Dictionary> mapDataLookup = new Dictionary>(); - //private Dictionary> mapDataLookup2 = new Dictionary>(); - // --------------------------------------------------------------------------------------------------------- + private ISessionManager sessionManager; public int Act { get; private set; } public string MapName { get; private set; } @@ -45,57 +41,31 @@ namespace OpenDiablo2.Core.GameState_ IPaletteProvider paletteProvider, IEngineDataManager engineDataManager, IRenderWindow renderWindow, - Func getMapEngine + Func getMapEngine, + Func getSessionManager ) { this.sceneManager = sceneManager; this.resourceManager = resourceManager; this.paletteProvider = paletteProvider; this.getMapEngine = getMapEngine; + this.getSessionManager = getSessionManager; this.engineDataManager = engineDataManager; this.renderWindow = renderWindow; } - public void Initialize(string characterName, eHero hero) + public void Initialize(string characterName, eHero hero, eSessionType sessionType) { + sessionManager = getSessionManager(sessionType); + sessionManager.Initialize(); + var random = new Random(); - Seed = random.Next(); + Seed = random.Next(); // TODO: Seed does not go here ;-( sceneManager.ChangeScene("Game"); - - // Initialize our first village - // TODO: Loading may make this 'fun'.. mapInfo = new List(); - (new MapGenerator(this)).Generate(); - - // TODO: We need a map generator here... - /* - { - // TODO: TEMP CODE AHEAD! - var transId = nw ? 3 : 2; - var level = engineDataManager.LevelPresets.First(x => x.Def == (int)transId); - var levelDetails = engineDataManager.LevelDetails.First(x => x.Id == level.LevelId); - var levelType = engineDataManager.LevelTypes.First(x => x.Id == levelDetails.LevelType); - - // Some maps have variations, so lets pick a random one - var mapNames = new List(); - if (level.File1 != "0") mapNames.Add(level.File1); - if (level.File2 != "0") mapNames.Add(level.File2); - if (level.File3 != "0") mapNames.Add(level.File3); - if (level.File4 != "0") mapNames.Add(level.File4); - if (level.File5 != "0") mapNames.Add(level.File5); - if (level.File6 != "0") mapNames.Add(level.File6); - - - var random = new Random(Seed); - var mapName = "data\\global\\tiles\\" + mapNames[random.Next(mapNames.Count())].Replace("/", "\\"); - _mapDataTemp2 = resourceManager.GetMPQDS1(mapName, level, levelDetails, levelType); - - } - */ - } diff --git a/OpenDiablo2.Core/OpenDiablo2.Core.csproj b/OpenDiablo2.Core/OpenDiablo2.Core.csproj index f8b2fa72..3bf583db 100644 --- a/OpenDiablo2.Core/OpenDiablo2.Core.csproj +++ b/OpenDiablo2.Core/OpenDiablo2.Core.csproj @@ -65,6 +65,7 @@ + diff --git a/OpenDiablo2.Core/SessionServer.cs b/OpenDiablo2.Core/SessionServer.cs new file mode 100644 index 00000000..932d8755 --- /dev/null +++ b/OpenDiablo2.Core/SessionServer.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenDiablo2.Common.Interfaces; + +namespace OpenDiablo2.Core +{ + public sealed class SessionServer : ISessionServer + { + + public void Dispose() + { + + } + } +} diff --git a/OpenDiablo2.SDL2/SDL2MouseCursor.cs b/OpenDiablo2.SDL2/SDL2MouseCursor.cs index 6e1b7415..386ff57e 100644 --- a/OpenDiablo2.SDL2/SDL2MouseCursor.cs +++ b/OpenDiablo2.SDL2/SDL2MouseCursor.cs @@ -1,10 +1,15 @@ using System; +using System.Drawing; using OpenDiablo2.Common.Interfaces; +using SDL2; namespace OpenDiablo2.SDL2_ { public sealed class SDL2MouseCursor : IMouseCursor { - public IntPtr Surface { get; set; } + public IntPtr HWSurface { get; set; } + public IntPtr SWTexture { get; set; } + public Size ImageSize { get; set; } + public Point Hotspot { get; set; } } } diff --git a/OpenDiablo2.SDL2/SDL2RenderWindow.cs b/OpenDiablo2.SDL2/SDL2RenderWindow.cs index 6d69fd00..ce0ad941 100644 --- a/OpenDiablo2.SDL2/SDL2RenderWindow.cs +++ b/OpenDiablo2.SDL2/SDL2RenderWindow.cs @@ -25,6 +25,19 @@ namespace OpenDiablo2.SDL2_ public OnKeyPressed KeyPressCallback { get; set; } + private IMouseCursor mouseCursor = null; + public IMouseCursor MouseCursor + { + get => mouseCursor; + set + { + if (mouseCursor == value) + return; + + SetCursor(value); + } + } + private readonly IMPQProvider mpqProvider; private readonly IPaletteProvider paletteProvider; private readonly IResourceManager resourceManager; @@ -96,8 +109,33 @@ namespace OpenDiablo2.SDL2_ } - public void Sync() + public unsafe void Sync() { + if (globalConfig.MouseMode == eMouseMode.Software) + { + var cursor = mouseCursor as SDL2MouseCursor; + var texture = cursor.SWTexture; + + var srcRect = new SDL.SDL_Rect + { + x = 0, + y = 0, + w = cursor.ImageSize.Width, + h = cursor.ImageSize.Height + }; + + var destRect = new SDL.SDL_Rect + { + x = MouseX - cursor.Hotspot.X, + y = MouseY - cursor.Hotspot.Y, + w = cursor.ImageSize.Width, + h = cursor.ImageSize.Height + }; + + SDL.SDL_RenderCopy(renderer, texture, ref srcRect, ref destRect); + } + + SDL.SDL_RenderPresent(renderer); } @@ -145,7 +183,8 @@ namespace OpenDiablo2.SDL2_ fullscreen = !fullscreen; SDL.SDL_SetWindowFullscreen(window, (uint)(fullscreen ? SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN : 0)); } - else*/ if (evt.key.keysym.sym == SDL.SDL_Keycode.SDLK_BACKSPACE && KeyPressCallback != null) + else*/ + if (evt.key.keysym.sym == SDL.SDL_Keycode.SDLK_BACKSPACE && KeyPressCallback != null) KeyPressCallback('\b'); } else if (evt.type == SDL.SDL_EventType.SDL_TEXTINPUT) @@ -287,8 +326,6 @@ namespace OpenDiablo2.SDL2_ public unsafe MapCellInfo CacheMapCell(MPQDT1Tile mapCell) { - log.Debug($"Caching map cell {mapCell.Id}"); - var minX = mapCell.Blocks.Min(x => x.PositionX); var minY = mapCell.Blocks.Min(x => x.PositionY); var maxX = mapCell.Blocks.Max(x => x.PositionX + 32); @@ -379,34 +416,55 @@ namespace OpenDiablo2.SDL2_ public unsafe IMouseCursor LoadCursor(ISprite sprite, int frame, Point hotspot) { - if (globalConfig.MouseMode != eMouseMode.Hardware) - throw new ApplicationException("Tried to set a hardware cursor, but we are using software cursors!"); + if (globalConfig.MouseMode == eMouseMode.Software) + { + sprite.Frame = frame; + + var texId = SDL.SDL_CreateTexture(renderer, SDL.SDL_PIXELFORMAT_ARGB8888, + (int)SDL.SDL_TextureAccess.SDL_TEXTUREACCESS_TARGET, sprite.LocalFrameSize.Width, sprite.LocalFrameSize.Height); + SDL.SDL_SetTextureBlendMode(texId, SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND); + + SDL.SDL_SetRenderTarget(renderer, texId); + SDL.SDL_RenderCopy(renderer, (sprite as SDL2Sprite).texture, IntPtr.Zero, IntPtr.Zero); + SDL.SDL_SetRenderTarget(renderer, IntPtr.Zero); + + return new SDL2MouseCursor + { + Hotspot = hotspot, + ImageSize = sprite.LocalFrameSize, + SWTexture = texId + }; + } var multiple = globalConfig.HardwareMouseScale; var spr = sprite as SDL2Sprite; - var surface = SDL.SDL_CreateRGBSurface(0, spr.FrameSize.Width * multiple, spr.FrameSize.Height * multiple, 32, 0xFF0000, 0xFF00, 0xFF, 0xFF000000); - + var surface = SDL.SDL_CreateRGBSurface(0, spr.LocalFrameSize.Width * multiple, spr.LocalFrameSize.Height * multiple, 32, 0xFF0000, 0xFF00, 0xFF, 0xFF000000); + var yOffset = (spr.FrameSize.Height - spr.LocalFrameSize.Height); + var XOffset = (spr.FrameSize.Width - spr.LocalFrameSize.Width); var pixels = (UInt32*)((SDL.SDL_Surface*)surface)->pixels; - for (var y = 0; y < (spr.FrameSize.Height * multiple) - 1; y++) - for (var x = 0; x < (spr.FrameSize.Width * multiple) - 1; x++) + for (var y = 0; y < (spr.LocalFrameSize.Height * multiple) - 1; y++) + for (var x = 0; x < (spr.LocalFrameSize.Width * multiple) - 1; x++) { - pixels[x + (y * spr.FrameSize.Width * multiple)] = spr.source.Frames[frame].GetColor(x / multiple, y / multiple, sprite.CurrentPalette); + pixels[x + XOffset + ((y + yOffset) * spr.LocalFrameSize.Width * multiple)] = spr.source.Frames[frame].GetColor(x / multiple, y / multiple, sprite.CurrentPalette); } - var cursor = SDL.SDL_CreateColorCursor(surface, hotspot.X, hotspot.Y); + var cursor = SDL.SDL_CreateColorCursor(surface, hotspot.X * multiple, hotspot.Y * multiple); if (cursor == IntPtr.Zero) throw new ApplicationException($"Unable to set the cursor cursor: {SDL.SDL_GetError()}"); // TODO: Is this supported everywhere? May need to still support software cursors. - return new SDL2MouseCursor { Surface = cursor }; + return new SDL2MouseCursor { HWSurface = cursor }; } - public void SetCursor(IMouseCursor mouseCursor) + private void SetCursor(IMouseCursor mouseCursor) { - if (globalConfig.MouseMode != eMouseMode.Hardware) - throw new ApplicationException("Tried to set a hardware cursor, but we are using software cursors!"); + this.mouseCursor = mouseCursor; - SDL.SDL_SetCursor((mouseCursor as SDL2MouseCursor).Surface); + if (globalConfig.MouseMode != eMouseMode.Hardware) + return; + + SDL.SDL_SetCursor((mouseCursor as SDL2MouseCursor).HWSurface); } public uint GetTicks() => SDL.SDL_GetTicks(); + } } diff --git a/OpenDiablo2.Scenes/SelectHeroClass.cs b/OpenDiablo2.Scenes/SelectHeroClass.cs index 42d715db..8c75eb37 100644 --- a/OpenDiablo2.Scenes/SelectHeroClass.cs +++ b/OpenDiablo2.Scenes/SelectHeroClass.cs @@ -228,7 +228,8 @@ namespace OpenDiablo2.Scenes private void OnOkclicked() { - gameState.Initialize(characterNameTextBox.Text, selectedHero.Value); + // TODO: Support other session types + gameState.Initialize(characterNameTextBox.Text, selectedHero.Value, eSessionType.Local); } private void OnExitClicked() diff --git a/OpenDiablo2.ServiceBus/AutofacModule.cs b/OpenDiablo2.ServiceBus/AutofacModule.cs index 5cc073f6..4123564c 100644 --- a/OpenDiablo2.ServiceBus/AutofacModule.cs +++ b/OpenDiablo2.ServiceBus/AutofacModule.cs @@ -1,4 +1,7 @@ -using Autofac; +using System; +using Autofac; +using OpenDiablo2.Common.Enums; +using OpenDiablo2.Common.Interfaces; namespace OpenDiablo2.ServiceBus { @@ -6,7 +9,24 @@ namespace OpenDiablo2.ServiceBus { protected override void Load(ContainerBuilder builder) { - + builder.RegisterType().AsSelf().InstancePerLifetimeScope(); + + builder.Register>(c => + { + var componentContext = c.Resolve(); + return (sessionType) => + { + switch (sessionType) + { + case eSessionType.Local: + return componentContext.Resolve(); + case eSessionType.Server: + case eSessionType.Remote: + default: + throw new ApplicationException("Unsupported session type."); + } + }; + }); } } } diff --git a/OpenDiablo2.ServiceBus/LocalSessionManager.cs b/OpenDiablo2.ServiceBus/LocalSessionManager.cs new file mode 100644 index 00000000..f0720116 --- /dev/null +++ b/OpenDiablo2.ServiceBus/LocalSessionManager.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenDiablo2.Common.Interfaces; + +namespace OpenDiablo2.ServiceBus +{ + public sealed class LocalSessionManager : ISessionManager + { + private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ISessionServer sessionServer; + volatile bool running = false; + + public LocalSessionManager(ISessionServer sessionServer) + { + this.sessionServer = sessionServer; + } + + public void Initialize() + { + log.Info("Initializing a local multiplayer session."); + running = true; + Task.Run(() => Listen()); + } + + private void Listen() + { + log.Info("Local session manager is starting."); + while (running) + { + + } + + log.Info("Local session manager has stopped."); + } + + public void Dispose() + { + + } + + public void Stop() => running = false; + } +} diff --git a/OpenDiablo2.ServiceBus/OpenDiablo2.ServiceBus.csproj b/OpenDiablo2.ServiceBus/OpenDiablo2.ServiceBus.csproj index d0689a45..b22a6bbc 100644 --- a/OpenDiablo2.ServiceBus/OpenDiablo2.ServiceBus.csproj +++ b/OpenDiablo2.ServiceBus/OpenDiablo2.ServiceBus.csproj @@ -56,6 +56,7 @@ +