From 0a23e387dcd2cec3f4bdc886101eac72bb9bd00b Mon Sep 17 00:00:00 2001 From: Marcin Badurowicz Date: Wed, 29 Apr 2026 15:59:17 +0200 Subject: [PATCH 01/13] refactor: single seed for item generation --- Server/Modules/Items/Services/ItemGenerator.cs | 3 ++- Server/Modules/Shared/RandomProvider.cs | 12 ++++++++++++ Server/Program.cs | 3 +++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 Server/Modules/Shared/RandomProvider.cs diff --git a/Server/Modules/Items/Services/ItemGenerator.cs b/Server/Modules/Items/Services/ItemGenerator.cs index 6c1491f..8cd6a0a 100644 --- a/Server/Modules/Items/Services/ItemGenerator.cs +++ b/Server/Modules/Items/Services/ItemGenerator.cs @@ -17,6 +17,7 @@ public class ItemGenerator : IItemGenerator public ItemGenerator( INameGenerator nameGenerator, PrefixesGenerator prefixes, + RandomProvider rndProvider, SingleResponseProvider? ai = null ) { @@ -24,7 +25,7 @@ public ItemGenerator( _prefixes = prefixes; _ai = ai; - _rnd = new Random(); + _rnd = rndProvider.Random; var configData = File.ReadAllText("Config/ItemParameters/itemgeneratorconfig.json"); _modifiers = JsonSerializer.Deserialize>(configData)!; diff --git a/Server/Modules/Shared/RandomProvider.cs b/Server/Modules/Shared/RandomProvider.cs new file mode 100644 index 0000000..5921085 --- /dev/null +++ b/Server/Modules/Shared/RandomProvider.cs @@ -0,0 +1,12 @@ +namespace Fracture.Server.Modules.Shared +{ + public class RandomProvider + { + public Random Random { get; private set; } + + public RandomProvider(int seed) + { + Random = new Random(seed); + } + } +} diff --git a/Server/Program.cs b/Server/Program.cs index 11b874d..774b6ff 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -33,6 +33,9 @@ reloadOnChange: true ); // Przeniesienie NameGenerator a appsettings do folderu Config +var r = new RandomProvider(42); +builder.Services.AddSingleton(r); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); From 44ae9b0435784b0706688e0b9123e5613d19497e Mon Sep 17 00:00:00 2001 From: Marcin Badurowicz Date: Wed, 29 Apr 2026 16:09:37 +0200 Subject: [PATCH 02/13] refactor: rnd in map generator --- .../Modules/MapGenerator/InMemoryMapRepository.cs | 10 ++++++++-- Server/Modules/MapGenerator/Models/Map/Map.cs | 15 +++++++++++---- .../MapGenerator/Services/MapGeneratorService.cs | 10 +++++++--- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/Server/Modules/MapGenerator/InMemoryMapRepository.cs b/Server/Modules/MapGenerator/InMemoryMapRepository.cs index 36702fb..b265737 100644 --- a/Server/Modules/MapGenerator/InMemoryMapRepository.cs +++ b/Server/Modules/MapGenerator/InMemoryMapRepository.cs @@ -1,10 +1,17 @@ using Fracture.Server.Modules.MapGenerator.Models.Map; +using Fracture.Server.Modules.Shared; namespace Fracture.Server.Modules.MapGenerator; public class InMemoryMapRepository : IMapRepository { private readonly Dictionary _maps = new(); + private readonly Random _rnd; + + public InMemoryMapRepository(RandomProvider rndProvider) + { + _rnd = rndProvider.Random; + } public void SaveMap(Map map) { @@ -27,8 +34,7 @@ public Map GetRandomMapByLocation(LocationType locationType) if (!maps.Any()) throw new InvalidOperationException($"No maps found for location type {locationType}!"); - var random = new Random(); - return maps[random.Next(maps.Count)]; + return maps[_rnd.Next(maps.Count)]; } public bool RemoveMap(string mapName) diff --git a/Server/Modules/MapGenerator/Models/Map/Map.cs b/Server/Modules/MapGenerator/Models/Map/Map.cs index e2bf9c7..0512ebb 100644 --- a/Server/Modules/MapGenerator/Models/Map/Map.cs +++ b/Server/Modules/MapGenerator/Models/Map/Map.cs @@ -1,7 +1,11 @@ -namespace Fracture.Server.Modules.MapGenerator.Models.Map; +using Fracture.Server.Modules.Shared; + +namespace Fracture.Server.Modules.MapGenerator.Models.Map; public class Map { + private readonly Random _rnd; + public string? Name { get; set; } public LocationType LocationType { get; init; } @@ -12,14 +16,17 @@ public class Map public int Height { get; init; } public required Node[,] Grid { get; init; } - public Position GetRandomWalkableNode() + public Map(RandomProvider rndProvider) { - var rnd = new Random(); + _rnd = rndProvider.Random; + } + public Position GetRandomWalkableNode() + { Position node; do { - node = new Position() { X = rnd.Next(0, Width), Y = rnd.Next(0, Height) }; + node = new Position() { X = _rnd.Next(0, Width), Y = _rnd.Next(0, Height) }; } while (!Grid[node.X, node.Y].Walkable); return node; diff --git a/Server/Modules/MapGenerator/Services/MapGeneratorService.cs b/Server/Modules/MapGenerator/Services/MapGeneratorService.cs index bb3fcde..287be9e 100644 --- a/Server/Modules/MapGenerator/Services/MapGeneratorService.cs +++ b/Server/Modules/MapGenerator/Services/MapGeneratorService.cs @@ -1,17 +1,21 @@ using Fracture.Server.Modules.MapGenerator.Models.Map; using Fracture.Server.Modules.MapGenerator.Models.Map.Biome; using Fracture.Server.Modules.NoiseGenerator.Services; +using Fracture.Server.Modules.Shared; namespace Fracture.Server.Modules.MapGenerator.Services; public class MapGeneratorService : IMapGeneratorService { private readonly ILogger _logger; - private readonly Random _rnd = new(); + private readonly Random _rnd; + private readonly RandomProvider _randomProvider; - public MapGeneratorService(ILogger logger) + public MapGeneratorService(ILogger logger, RandomProvider rndProvider) { _logger = logger; + _rnd = rndProvider.Random; + _randomProvider = rndProvider; } public async Task GetMap(MapParameters? mapParameters) @@ -164,7 +168,7 @@ private Map GenerateMap(MapParameters? mapParameters) throw new ArgumentNullException(nameof(mapParameters)); } var grid = GenerateGrid(mapParameters); - return new Map() + return new Map(_randomProvider) { Grid = grid, LocationType = mapParameters.LocationType, From bf9d7c2480e3cc7827bd7cee9a53ae7e3441d4cf Mon Sep 17 00:00:00 2001 From: Marcin Badurowicz Date: Wed, 29 Apr 2026 16:14:30 +0200 Subject: [PATCH 03/13] refactor: single seed in map generation --- .../Modules/MapGenerator/Services/MapGeneratorService.cs | 2 -- Server/Modules/NoiseGenerator/Services/CustomPerlin.cs | 9 +++++---- .../NoiseGenerator/Services/PerlinNoiseGenerator.cs | 4 ++-- Server/Modules/Shared/RandomProvider.cs | 3 +++ 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Server/Modules/MapGenerator/Services/MapGeneratorService.cs b/Server/Modules/MapGenerator/Services/MapGeneratorService.cs index 287be9e..6ccc3bd 100644 --- a/Server/Modules/MapGenerator/Services/MapGeneratorService.cs +++ b/Server/Modules/MapGenerator/Services/MapGeneratorService.cs @@ -39,7 +39,6 @@ public async Task GetMap(MapParameters? mapParameters) var heightMap = CustomPerlin.GenerateNoiseMap( width, - noiseParameters.Seed, noiseParameters.Octaves, noiseParameters.Persistence, noiseParameters.Lacunarity, @@ -48,7 +47,6 @@ public async Task GetMap(MapParameters? mapParameters) var temperatureMap = CustomPerlin.GenerateNoiseMap( width, - noiseParameters.Seed + 1, noiseParameters.Octaves, noiseParameters.Persistence, noiseParameters.Lacunarity, diff --git a/Server/Modules/NoiseGenerator/Services/CustomPerlin.cs b/Server/Modules/NoiseGenerator/Services/CustomPerlin.cs index a1b37a4..211cf51 100644 --- a/Server/Modules/NoiseGenerator/Services/CustomPerlin.cs +++ b/Server/Modules/NoiseGenerator/Services/CustomPerlin.cs @@ -1,4 +1,6 @@ -namespace Fracture.Server.Modules.NoiseGenerator.Services; +using Fracture.Server.Modules.Shared; + +namespace Fracture.Server.Modules.NoiseGenerator.Services; public static class CustomPerlin { @@ -41,7 +43,7 @@ private static int[] GeneratePermutation(int seed) // Generates a permutation of // Main functions public static float Perlin(float x, float y, int seed) // Seed must be provided { - var permutation = GeneratePermutation(seed); + var permutation = GeneratePermutation(RandomProvider.Seed); var p = new int[permutation.Length * 2]; permutation.CopyTo(p, 0); permutation.CopyTo(p, permutation.Length); @@ -94,7 +96,6 @@ public static float PerlinOctaves( public static float[,] GenerateNoiseMap( int size, - int? seed = null, int octaves = 1, float persistence = 0.5f, float lacunarity = 2.0f, @@ -103,7 +104,7 @@ public static float PerlinOctaves( { // Noise map generation // can be called with a seed or without it - var actualSeed = seed ?? new Random().Next(); + var actualSeed = RandomProvider.Seed; var map = new float[size, size]; diff --git a/Server/Modules/NoiseGenerator/Services/PerlinNoiseGenerator.cs b/Server/Modules/NoiseGenerator/Services/PerlinNoiseGenerator.cs index b8316f9..347b7fb 100644 --- a/Server/Modules/NoiseGenerator/Services/PerlinNoiseGenerator.cs +++ b/Server/Modules/NoiseGenerator/Services/PerlinNoiseGenerator.cs @@ -1,5 +1,6 @@ using System.Numerics; using Fracture.Server.Modules.MapGenerator; +using Fracture.Server.Modules.Shared; namespace Fracture.Server.Modules.NoiseGenerator.Services; @@ -8,7 +9,6 @@ public static class PerlinNoiseGenerator public static float[,] Generate( int mapWidth, int mapHeight, - int seed, float scale, int octaves, float persistence, @@ -18,7 +18,7 @@ Vector2 offset { var noiseMap = new float[mapWidth, mapHeight]; - var prng = new Random(seed); + var prng = new Random(RandomProvider.Seed); var octaveOffsets = new Vector2[octaves]; for (var i = 0; i < octaves; i++) { diff --git a/Server/Modules/Shared/RandomProvider.cs b/Server/Modules/Shared/RandomProvider.cs index 5921085..06f7db7 100644 --- a/Server/Modules/Shared/RandomProvider.cs +++ b/Server/Modules/Shared/RandomProvider.cs @@ -4,8 +4,11 @@ public class RandomProvider { public Random Random { get; private set; } + public static int Seed { get; private set; } + public RandomProvider(int seed) { + Seed = seed; Random = new Random(seed); } } From 66e8fde25256b5ff6ca5c42642116bf47fc0edc5 Mon Sep 17 00:00:00 2001 From: Marcin Badurowicz Date: Wed, 29 Apr 2026 16:21:21 +0200 Subject: [PATCH 04/13] refactor: single seed in map generation 2 --- .../MapGenerator/Services/LocationGroupGeneratorService.cs | 5 +++-- .../Modules/MapGenerator/Services/MapGeneratorService.cs | 3 --- .../MapGenerator/Services/MapParameterSelectorService.cs | 7 +++++-- .../MapGenerator/Services/SubMapAssignmentService.cs | 7 +++++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Server/Modules/MapGenerator/Services/LocationGroupGeneratorService.cs b/Server/Modules/MapGenerator/Services/LocationGroupGeneratorService.cs index 907999c..5df0b38 100644 --- a/Server/Modules/MapGenerator/Services/LocationGroupGeneratorService.cs +++ b/Server/Modules/MapGenerator/Services/LocationGroupGeneratorService.cs @@ -8,10 +8,11 @@ namespace Fracture.Server.Modules.MapGenerator.Services; public class LocationGroupGeneratorService( ILocationWeightGeneratorService weightGen, ILocationGeneratorService locationGen, - IFloodFillService floodFill + IFloodFillService floodFill, + RandomProvider rndProvider ) : ILocationGroupGeneratorService { - private readonly Random _rnd = new(); + private readonly Random _rnd = rndProvider.Random; public List GenerateGroups( Node[,] grid, diff --git a/Server/Modules/MapGenerator/Services/MapGeneratorService.cs b/Server/Modules/MapGenerator/Services/MapGeneratorService.cs index 6ccc3bd..182c3da 100644 --- a/Server/Modules/MapGenerator/Services/MapGeneratorService.cs +++ b/Server/Modules/MapGenerator/Services/MapGeneratorService.cs @@ -26,9 +26,6 @@ public async Task GetMap(MapParameters? mapParameters) private Node[,] GenerateGrid(MapParameters mapParameters) { var noiseParameters = mapParameters.NoiseParameters; - noiseParameters.Seed = noiseParameters.UseRandomSeed - ? _rnd.Next(-100000, 100000) - : noiseParameters.Seed; var width = mapParameters.Width; var height = mapParameters.Height; var useFalloff = true; diff --git a/Server/Modules/MapGenerator/Services/MapParameterSelectorService.cs b/Server/Modules/MapGenerator/Services/MapParameterSelectorService.cs index 99ede48..2fb1070 100644 --- a/Server/Modules/MapGenerator/Services/MapParameterSelectorService.cs +++ b/Server/Modules/MapGenerator/Services/MapParameterSelectorService.cs @@ -1,4 +1,5 @@ using Fracture.Server.Modules.MapGenerator.Models.Map; +using Fracture.Server.Modules.Shared; namespace Fracture.Server.Modules.MapGenerator.Services; @@ -6,15 +7,17 @@ public class MapParameterSelectorService : IMapParameterSelectorService { private readonly MapDataImportService _dataImportService; private readonly ILogger _logger; - private readonly Random _rnd = new(); + private readonly Random _rnd; public MapParameterSelectorService( MapDataImportService dataImportService, - ILogger logger + ILogger logger, + RandomProvider rndProvider ) { _dataImportService = dataImportService; _logger = logger; + _rnd = rndProvider.Random; } public async Task> GetParametersAsync() => diff --git a/Server/Modules/MapGenerator/Services/SubMapAssignmentService.cs b/Server/Modules/MapGenerator/Services/SubMapAssignmentService.cs index 2a32e67..482af9c 100644 --- a/Server/Modules/MapGenerator/Services/SubMapAssignmentService.cs +++ b/Server/Modules/MapGenerator/Services/SubMapAssignmentService.cs @@ -2,6 +2,7 @@ using Fracture.Server.Modules.MapGenerator.Models.Map; using Fracture.Server.Modules.MapGenerator.Models.Map.MapObjects; using Fracture.Server.Modules.MapGenerator.Services.TownGen; +using Fracture.Server.Modules.Shared; namespace Fracture.Server.Modules.MapGenerator.Services; @@ -13,7 +14,7 @@ public class SubMapAssignmentService : ISubMapAssignmentService private readonly IMapGeneratorService _mapGenerator; private readonly ILocationGroupGeneratorService _groupGenerator; private readonly ILogger _logger; - private readonly Random _rnd = new(); + private readonly Random _rnd; public SubMapAssignmentService( ILocationWeightGeneratorService weightGenerator, @@ -21,7 +22,8 @@ public SubMapAssignmentService( IFloodFillService floodFillService, IMapGeneratorService mapGenerator, ILocationGroupGeneratorService groupGenerator, - ILogger logger + ILogger logger, + RandomProvider rndProvider ) { _weightGenerator = weightGenerator; @@ -30,6 +32,7 @@ ILogger logger _mapGenerator = mapGenerator; _groupGenerator = groupGenerator; _logger = logger; + _rnd = rndProvider.Random; } public async Task AssignSubMapsAsync(Map map, Dictionary parameters) From 56909dc4ac29e912cc67bfdaf2a11cdc2919ebee Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 5 May 2026 18:46:14 +0200 Subject: [PATCH 05/13] feat: ItemSpawningOnMap --- .../MapGenerator/Models/Map/ItemDrop.cs | 6 +++ .../Modules/MapGenerator/Models/Map/Node.cs | 2 + .../Services/IItemPlacementService.cs | 8 ++++ .../Services/ItemPlacementService.cs | 36 ++++++++++++++++++ .../Services/MapManagerService.cs | 5 ++- Server/Modules/MapGenerator/UI/MapView.razor | 5 +++ .../Modules/Users/Services/MovementService.cs | 37 ++++++++++++++++++- Server/Program.cs | 1 + 8 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 Server/Modules/MapGenerator/Models/Map/ItemDrop.cs create mode 100644 Server/Modules/MapGenerator/Services/IItemPlacementService.cs create mode 100644 Server/Modules/MapGenerator/Services/ItemPlacementService.cs diff --git a/Server/Modules/MapGenerator/Models/Map/ItemDrop.cs b/Server/Modules/MapGenerator/Models/Map/ItemDrop.cs new file mode 100644 index 0000000..6eaf0a3 --- /dev/null +++ b/Server/Modules/MapGenerator/Models/Map/ItemDrop.cs @@ -0,0 +1,6 @@ +namespace Fracture.Server.Modules.MapGenerator.Models.Map; + +public class ItemDrop +{ + public string Id { get; init; } = Guid.NewGuid().ToString("N"); +} diff --git a/Server/Modules/MapGenerator/Models/Map/Node.cs b/Server/Modules/MapGenerator/Models/Map/Node.cs index 45638b1..0fbc9b7 100644 --- a/Server/Modules/MapGenerator/Models/Map/Node.cs +++ b/Server/Modules/MapGenerator/Models/Map/Node.cs @@ -16,6 +16,8 @@ public class Node : IPathfindingNode public LocationType LocationType { get; set; } = LocationType.None; public IMapObject? MapObject { get; set; } + public ItemDrop? ItemDrop { get; set; } + public required int XId { get; set; } public required int YId { get; set; } diff --git a/Server/Modules/MapGenerator/Services/IItemPlacementService.cs b/Server/Modules/MapGenerator/Services/IItemPlacementService.cs new file mode 100644 index 0000000..5497e1e --- /dev/null +++ b/Server/Modules/MapGenerator/Services/IItemPlacementService.cs @@ -0,0 +1,8 @@ +using Fracture.Server.Modules.MapGenerator.Models.Map; + +namespace Fracture.Server.Modules.MapGenerator.Services; + +public interface IItemPlacementService +{ + void PlaceItems(Map map); +} diff --git a/Server/Modules/MapGenerator/Services/ItemPlacementService.cs b/Server/Modules/MapGenerator/Services/ItemPlacementService.cs new file mode 100644 index 0000000..a46dd7f --- /dev/null +++ b/Server/Modules/MapGenerator/Services/ItemPlacementService.cs @@ -0,0 +1,36 @@ +using Fracture.Server.Modules.MapGenerator.Models.Map; +using Fracture.Server.Modules.Shared; + +namespace Fracture.Server.Modules.MapGenerator.Services; + +public class ItemPlacementService(RandomProvider rndProvider) : IItemPlacementService +{ + private readonly Random _rnd = rndProvider.Random; + private const double ItemDensity = 0.01; + + public void PlaceItems(Map map) + { + var targetCount = Math.Max(1, (int)Math.Round(map.Width * map.Height * ItemDensity)); + var placed = 0; + var attempts = 0; + var maxAttempts = targetCount * 10; + + while (placed < targetCount && attempts < maxAttempts) + { + attempts++; + + var x = _rnd.Next(0, map.Width); + var y = _rnd.Next(0, map.Height); + var node = map.Grid[x, y]; + + if (!node.Walkable || node.ItemDrop != null || node.LocationType != LocationType.None) + continue; + + if (node.MapObject != null) + continue; + + node.ItemDrop = new ItemDrop(); + placed++; + } + } +} diff --git a/Server/Modules/MapGenerator/Services/MapManagerService.cs b/Server/Modules/MapGenerator/Services/MapManagerService.cs index 3282291..41076ad 100644 --- a/Server/Modules/MapGenerator/Services/MapManagerService.cs +++ b/Server/Modules/MapGenerator/Services/MapManagerService.cs @@ -6,7 +6,8 @@ namespace Fracture.Server.Modules.MapGenerator.Services; public class MapManagerService( IMapRepository mapRepository, ILogger logger, - IWorldGenerationService worldGenerationService + IWorldGenerationService worldGenerationService, + IItemPlacementService itemPlacementService ) { private readonly Lock _lock = new(); @@ -33,6 +34,7 @@ public async Task GenerateWorldAsync() { mapRepository.ClearMaps(); var map = await worldGenerationService.GenerateWorldMapAsync(); + itemPlacementService.PlaceItems(map); mapRepository.SaveMap(map); SetWorldMap(map); @@ -44,6 +46,7 @@ public async Task RefreshMapsAndSetNewAsync() logger.LogInformation("Generating new world..."); var newMap = await worldGenerationService.GenerateWorldMapAsync(); + itemPlacementService.PlaceItems(newMap); mapRepository.SaveMap(newMap); SetWorldMap(newMap); diff --git a/Server/Modules/MapGenerator/UI/MapView.razor b/Server/Modules/MapGenerator/UI/MapView.razor index a12a975..c3bea16 100644 --- a/Server/Modules/MapGenerator/UI/MapView.razor +++ b/Server/Modules/MapGenerator/UI/MapView.razor @@ -141,6 +141,11 @@ private string RenderLocation(Node node) { + if (node.ItemDrop != null) + { + return "x"; + } + return node.LocationType switch { LocationType.None => string.Empty, diff --git a/Server/Modules/Users/Services/MovementService.cs b/Server/Modules/Users/Services/MovementService.cs index ccbecaf..3136b03 100644 --- a/Server/Modules/Users/Services/MovementService.cs +++ b/Server/Modules/Users/Services/MovementService.cs @@ -1,10 +1,30 @@ -using Fracture.Server.Modules.MapGenerator.Models.Map; +using Fracture.Server.Modules.Database; +using Fracture.Server.Modules.Items.Services; +using Fracture.Server.Modules.MapGenerator.Models.Map; using Fracture.Server.Modules.MapGenerator.Services; namespace Fracture.Server.Modules.Users.Services; -public class MovementService(MapManagerService _mapManagerService) +public class MovementService { + private readonly MapManagerService _mapManagerService; + private readonly IItemGenerator _itemGenerator; + private readonly IItemsRepository _itemsRepository; + private readonly UserService _userService; + + public MovementService( + MapManagerService mapManagerService, + IItemGenerator itemGenerator, + IItemsRepository itemsRepository, + UserService userService + ) + { + _mapManagerService = mapManagerService; + _itemGenerator = itemGenerator; + _itemsRepository = itemsRepository; + _userService = userService; + } + public Map? CurrentMap { get; private set; } public int CurrentX { get; private set; } @@ -41,6 +61,19 @@ public async Task MoveAsync(int x, int y) CurrentX = x; CurrentY = y; + var node = CurrentMap?.Grid[x, y]; + if (node?.ItemDrop != null && _userService.User != null) + { + var item = await _itemGenerator.Generate(); + item.CreatedById = _userService.User.Id; + item.CreatedBy = _userService.User; + + await _itemsRepository.AddItemAsync(item); + _userService.Inventory.Add(item); + + node.ItemDrop = null; + } + OnMoved?.Invoke(this, new Position(CurrentX, CurrentY)); } } diff --git a/Server/Program.cs b/Server/Program.cs index 774b6ff..38efea8 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -52,6 +52,7 @@ builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); +builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); From c20e086362f07ea75b7e8b3ee37dfd2e57d16ca8 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 5 May 2026 19:11:24 +0200 Subject: [PATCH 06/13] fix: MarkerSizeAdjustment --- Server/Modules/MapGenerator/UI/MapView.razor | 18 ++++++++++++-- Server/wwwroot/css/game.css | 25 ++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/Server/Modules/MapGenerator/UI/MapView.razor b/Server/Modules/MapGenerator/UI/MapView.razor index c3bea16..eebf77f 100644 --- a/Server/Modules/MapGenerator/UI/MapView.razor +++ b/Server/Modules/MapGenerator/UI/MapView.razor @@ -40,7 +40,14 @@ else if (MapDisplayOptions.TileInformationDisplay == TileInformationDisplay.None) { - @RenderLocation(node) + @if (node.ItemDrop != null) + { + @RenderLocation(node) + } + else + { + @RenderLocation(node) + } } else if (MapDisplayOptions.TileInformationDisplay == TileInformationDisplay.Path) @@ -52,7 +59,14 @@ else { - @RenderLocation(node) + @if (node.ItemDrop != null) + { + @RenderLocation(node) + } + else + { + @RenderLocation(node) + } } } diff --git a/Server/wwwroot/css/game.css b/Server/wwwroot/css/game.css index 21eee7f..ccab000 100644 --- a/Server/wwwroot/css/game.css +++ b/Server/wwwroot/css/game.css @@ -281,6 +281,9 @@ body { .mapTable tr td { /*border: #000 1px solid;*/ background-color: blue; + font-size: 4px; + line-height: 1; + text-align: center; } .miniMapTable tr td { @@ -570,3 +573,25 @@ body { h3 { width: 70%; } +.node-info { + font-size: 12px; + line-height: 1.2; + padding: 4px 6px; + background: rgba(0, 0, 0, 0.7); + border-radius: 4px; + color: #fff; +} + +.map-marker { + line-height: 1; + display: inline-block; + text-align: center; +} + +.map-marker--location { + font-size: 12px; +} + +.map-marker--item { + font-size: 4px; +} From c9a2c60cfeeb7f881f65558bb59a0ba1428be1d6 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 5 May 2026 21:58:16 +0200 Subject: [PATCH 07/13] fix:UsersHaveOwnItems --- .../Services/IItemPlacementService.cs | 2 +- .../Services/ItemPlacementService.cs | 19 ++--- .../Services/MapManagerService.cs | 5 +- Server/Modules/MapGenerator/UI/MapView.razor | 10 +-- .../Users/Services/ItemDropStateService.cs | 72 +++++++++++++++++++ .../Modules/Users/Services/MovementService.cs | 31 +++++--- Server/Program.cs | 1 + 7 files changed, 111 insertions(+), 29 deletions(-) create mode 100644 Server/Modules/Users/Services/ItemDropStateService.cs diff --git a/Server/Modules/MapGenerator/Services/IItemPlacementService.cs b/Server/Modules/MapGenerator/Services/IItemPlacementService.cs index 5497e1e..ba86827 100644 --- a/Server/Modules/MapGenerator/Services/IItemPlacementService.cs +++ b/Server/Modules/MapGenerator/Services/IItemPlacementService.cs @@ -4,5 +4,5 @@ namespace Fracture.Server.Modules.MapGenerator.Services; public interface IItemPlacementService { - void PlaceItems(Map map); + IReadOnlySet GenerateDropPositions(Map map); } diff --git a/Server/Modules/MapGenerator/Services/ItemPlacementService.cs b/Server/Modules/MapGenerator/Services/ItemPlacementService.cs index a46dd7f..2e03f45 100644 --- a/Server/Modules/MapGenerator/Services/ItemPlacementService.cs +++ b/Server/Modules/MapGenerator/Services/ItemPlacementService.cs @@ -5,32 +5,33 @@ namespace Fracture.Server.Modules.MapGenerator.Services; public class ItemPlacementService(RandomProvider rndProvider) : IItemPlacementService { - private readonly Random _rnd = rndProvider.Random; private const double ItemDensity = 0.01; - public void PlaceItems(Map map) + public IReadOnlySet GenerateDropPositions(Map map) { + var rnd = new Random(RandomProvider.Seed); var targetCount = Math.Max(1, (int)Math.Round(map.Width * map.Height * ItemDensity)); - var placed = 0; var attempts = 0; var maxAttempts = targetCount * 10; + var drops = new HashSet(); - while (placed < targetCount && attempts < maxAttempts) + while (drops.Count < targetCount && attempts < maxAttempts) { attempts++; - var x = _rnd.Next(0, map.Width); - var y = _rnd.Next(0, map.Height); + var x = rnd.Next(0, map.Width); + var y = rnd.Next(0, map.Height); var node = map.Grid[x, y]; - if (!node.Walkable || node.ItemDrop != null || node.LocationType != LocationType.None) + if (!node.Walkable || node.LocationType != LocationType.None) continue; if (node.MapObject != null) continue; - node.ItemDrop = new ItemDrop(); - placed++; + drops.Add(new Position(x, y)); } + + return drops; } } diff --git a/Server/Modules/MapGenerator/Services/MapManagerService.cs b/Server/Modules/MapGenerator/Services/MapManagerService.cs index 41076ad..3282291 100644 --- a/Server/Modules/MapGenerator/Services/MapManagerService.cs +++ b/Server/Modules/MapGenerator/Services/MapManagerService.cs @@ -6,8 +6,7 @@ namespace Fracture.Server.Modules.MapGenerator.Services; public class MapManagerService( IMapRepository mapRepository, ILogger logger, - IWorldGenerationService worldGenerationService, - IItemPlacementService itemPlacementService + IWorldGenerationService worldGenerationService ) { private readonly Lock _lock = new(); @@ -34,7 +33,6 @@ public async Task GenerateWorldAsync() { mapRepository.ClearMaps(); var map = await worldGenerationService.GenerateWorldMapAsync(); - itemPlacementService.PlaceItems(map); mapRepository.SaveMap(map); SetWorldMap(map); @@ -46,7 +44,6 @@ public async Task RefreshMapsAndSetNewAsync() logger.LogInformation("Generating new world..."); var newMap = await worldGenerationService.GenerateWorldMapAsync(); - itemPlacementService.PlaceItems(newMap); mapRepository.SaveMap(newMap); SetWorldMap(newMap); diff --git a/Server/Modules/MapGenerator/UI/MapView.razor b/Server/Modules/MapGenerator/UI/MapView.razor index eebf77f..fa81638 100644 --- a/Server/Modules/MapGenerator/UI/MapView.razor +++ b/Server/Modules/MapGenerator/UI/MapView.razor @@ -40,9 +40,9 @@ else if (MapDisplayOptions.TileInformationDisplay == TileInformationDisplay.None) { - @if (node.ItemDrop != null) + @if (Map != null && MovementService.HasItemDrop(Map, node.XId, node.YId)) { - @RenderLocation(node) + @RenderLocation(node) } else { @@ -59,9 +59,9 @@ else { - @if (node.ItemDrop != null) + @if (Map != null && MovementService.HasItemDrop(Map, node.XId, node.YId)) { - @RenderLocation(node) + @RenderLocation(node) } else { @@ -155,7 +155,7 @@ private string RenderLocation(Node node) { - if (node.ItemDrop != null) + if (Map != null && MovementService.HasItemDrop(Map, node.XId, node.YId)) { return "x"; } diff --git a/Server/Modules/Users/Services/ItemDropStateService.cs b/Server/Modules/Users/Services/ItemDropStateService.cs new file mode 100644 index 0000000..2088896 --- /dev/null +++ b/Server/Modules/Users/Services/ItemDropStateService.cs @@ -0,0 +1,72 @@ +using System.Collections.Concurrent; +using Fracture.Server.Modules.MapGenerator.Models.Map; +using Fracture.Server.Modules.MapGenerator.Services; + +namespace Fracture.Server.Modules.Users.Services; + +public class ItemDropStateService +{ + private readonly IItemPlacementService _itemPlacementService; + private readonly ConcurrentDictionary> _baseDropsByMap = new(); + private readonly ConcurrentDictionary> _collectedDropsByUserMap = + new(); + private readonly Lock _lock = new(); + + public ItemDropStateService(IItemPlacementService itemPlacementService) + { + _itemPlacementService = itemPlacementService; + } + + public bool HasItemDrop(int userId, Map map, int x, int y) + { + var position = new Position(x, y); + var baseDrops = GetBaseDrops(map); + var collected = GetCollectedDrops(userId, map); + + lock (_lock) + { + return baseDrops.Contains(position) && !collected.Contains(position); + } + } + + public bool TryCollect(int userId, Map map, int x, int y) + { + var position = new Position(x, y); + var baseDrops = GetBaseDrops(map); + var collected = GetCollectedDrops(userId, map); + + lock (_lock) + { + if (!baseDrops.Contains(position) || collected.Contains(position)) + return false; + + collected.Add(position); + return true; + } + } + + private HashSet GetBaseDrops(Map map) + { + var mapKey = GetMapKey(map); + return _baseDropsByMap.GetOrAdd( + mapKey, + _ => new HashSet(_itemPlacementService.GenerateDropPositions(map)) + ); + } + + private HashSet GetCollectedDrops(int userId, Map map) + { + var userMapKey = GetUserMapKey(userId, map); + return _collectedDropsByUserMap.GetOrAdd(userMapKey, _ => new HashSet()); + } + + private static string GetMapKey(Map map) + { + return map.Name ?? $"{map.LocationType}:{map.Width}x{map.Height}"; + } + + private static string GetUserMapKey(int userId, Map map) + { + return $"{userId}:{GetMapKey(map)}"; + } +} diff --git a/Server/Modules/Users/Services/MovementService.cs b/Server/Modules/Users/Services/MovementService.cs index 3136b03..3355829 100644 --- a/Server/Modules/Users/Services/MovementService.cs +++ b/Server/Modules/Users/Services/MovementService.cs @@ -11,18 +11,21 @@ public class MovementService private readonly IItemGenerator _itemGenerator; private readonly IItemsRepository _itemsRepository; private readonly UserService _userService; + private readonly ItemDropStateService _itemDropState; public MovementService( MapManagerService mapManagerService, IItemGenerator itemGenerator, IItemsRepository itemsRepository, - UserService userService + UserService userService, + ItemDropStateService itemDropState ) { _mapManagerService = mapManagerService; _itemGenerator = itemGenerator; _itemsRepository = itemsRepository; _userService = userService; + _itemDropState = itemDropState; } public Map? CurrentMap { get; private set; } @@ -56,22 +59,30 @@ public bool CanMove(int x, int y) && CurrentMap.Grid[x, y].Walkable; } + public bool HasItemDrop(Map map, int x, int y) + { + if (_userService.User == null) + return false; + + return _itemDropState.HasItemDrop(_userService.User.Id, map, x, y); + } + public async Task MoveAsync(int x, int y) { CurrentX = x; CurrentY = y; - var node = CurrentMap?.Grid[x, y]; - if (node?.ItemDrop != null && _userService.User != null) + if (CurrentMap != null && _userService.User != null) { - var item = await _itemGenerator.Generate(); - item.CreatedById = _userService.User.Id; - item.CreatedBy = _userService.User; - - await _itemsRepository.AddItemAsync(item); - _userService.Inventory.Add(item); + if (_itemDropState.TryCollect(_userService.User.Id, CurrentMap, x, y)) + { + var item = await _itemGenerator.Generate(); + item.CreatedById = _userService.User.Id; + item.CreatedBy = _userService.User; - node.ItemDrop = null; + await _itemsRepository.AddItemAsync(item); + _userService.Inventory.Add(item); + } } OnMoved?.Invoke(this, new Position(CurrentX, CurrentY)); diff --git a/Server/Program.cs b/Server/Program.cs index 38efea8..554a633 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -97,6 +97,7 @@ }); builder.Services.AddLogging(); +builder.Services.AddSingleton(); var app = builder.Build(); From 3dcf69765991eeb6644105706847c3403b450a72 Mon Sep 17 00:00:00 2001 From: Marcin Badurowicz Date: Wed, 6 May 2026 16:47:39 +0200 Subject: [PATCH 08/13] feat: popup when item is encountered --- Fracture.sln | 4 ++-- Server/Components/Pages/GamePage.razor | 2 +- Server/Components/Pages/GamePage.razor.cs | 7 +++++++ Server/Components/Popups/TestPopup.razor | 5 +++++ Server/Modules/Users/Services/MovementService.cs | 6 +++++- 5 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 Server/Components/Popups/TestPopup.razor diff --git a/Fracture.sln b/Fracture.sln index 5435ea2..5292064 100644 --- a/Fracture.sln +++ b/Fracture.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.6.33829.357 +# Visual Studio Version 18 +VisualStudioVersion = 18.5.11716.220 stable MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fracture.Server", "Server\Fracture.Server.csproj", "{D06D3FCB-F773-4E5E-A6D3-6C6D052F7996}" EndProject diff --git a/Server/Components/Pages/GamePage.razor b/Server/Components/Pages/GamePage.razor index 3a861e2..fae5361 100644 --- a/Server/Components/Pages/GamePage.razor +++ b/Server/Components/Pages/GamePage.razor @@ -141,7 +141,7 @@
- +
diff --git a/Server/Components/Pages/GamePage.razor.cs b/Server/Components/Pages/GamePage.razor.cs index cc7ca73..9676eab 100644 --- a/Server/Components/Pages/GamePage.razor.cs +++ b/Server/Components/Pages/GamePage.razor.cs @@ -1,3 +1,4 @@ +using Fracture.Server.Components.Popups; using Fracture.Server.Components.UI; using Fracture.Server.Modules.MapGenerator.Models.Map; using Fracture.Server.Modules.MapGenerator.UI.Models; @@ -45,6 +46,12 @@ protected override async Task OnInitializedAsync() StateHasChanged(); }; + MovementService.OnItemEncountered += async (sender, args) => + { + _popup.ShowComponent(); + StateHasChanged(); + }; + _mapDisplayOptions.ShowColorMap = true; _mapPopupParameters = new Dictionary { diff --git a/Server/Components/Popups/TestPopup.razor b/Server/Components/Popups/TestPopup.razor new file mode 100644 index 0000000..e742152 --- /dev/null +++ b/Server/Components/Popups/TestPopup.razor @@ -0,0 +1,5 @@ +

You got a new item, check it out!

+ +@code { + +} diff --git a/Server/Modules/Users/Services/MovementService.cs b/Server/Modules/Users/Services/MovementService.cs index 3355829..0c74beb 100644 --- a/Server/Modules/Users/Services/MovementService.cs +++ b/Server/Modules/Users/Services/MovementService.cs @@ -1,4 +1,5 @@ -using Fracture.Server.Modules.Database; +using Fracture.Server.Components.Popups; +using Fracture.Server.Modules.Database; using Fracture.Server.Modules.Items.Services; using Fracture.Server.Modules.MapGenerator.Models.Map; using Fracture.Server.Modules.MapGenerator.Services; @@ -35,6 +36,7 @@ ItemDropStateService itemDropState public event EventHandler? OnMoved; public event EventHandler<(Map, Position)>? OnMapEntered; + public event EventHandler? OnItemEncountered; public void Initialize() { @@ -82,6 +84,8 @@ public async Task MoveAsync(int x, int y) await _itemsRepository.AddItemAsync(item); _userService.Inventory.Add(item); + + OnItemEncountered?.Invoke(this, new Position(x, y)); } } From f090c8f86e7e8c0bf154d158f958e9de90dc2e26 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 8 May 2026 17:16:39 +0200 Subject: [PATCH 09/13] fix:MigrationWithNullableCreatedById --- Server/Modules/Database/FractureDbContext.cs | 7 ++++++- Server/Modules/Items/Models/Item.cs | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Server/Modules/Database/FractureDbContext.cs b/Server/Modules/Database/FractureDbContext.cs index dd67721..defc862 100644 --- a/Server/Modules/Database/FractureDbContext.cs +++ b/Server/Modules/Database/FractureDbContext.cs @@ -17,7 +17,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity().HasOne(i => i.Statistics).WithOne(s => s.Item); - modelBuilder.Entity().HasOne(i => i.CreatedBy).WithMany(u => u.Items); + modelBuilder + .Entity() + .HasOne(i => i.CreatedBy) + .WithMany(u => u.Items) + .HasForeignKey(i => i.CreatedById) + .IsRequired(false); modelBuilder.Entity().HasMany(u => u.Items).WithOne(i => i.CreatedBy); diff --git a/Server/Modules/Items/Models/Item.cs b/Server/Modules/Items/Models/Item.cs index 44dd993..75166e0 100644 --- a/Server/Modules/Items/Models/Item.cs +++ b/Server/Modules/Items/Models/Item.cs @@ -24,12 +24,12 @@ public class Item public bool IsEquipped { get; set; } [ForeignKey(nameof(User.Id))] - public int CreatedById { get; set; } + public int? CreatedById { get; set; } [InverseProperty(nameof(ItemStatistics.Item))] public virtual ItemStatistics Statistics { get; set; } = null!; [InverseProperty(nameof(User.Items))] [JsonIgnore] - public virtual User CreatedBy { get; set; } = null!; + public virtual User? CreatedBy { get; set; } } From bc356dbb5e443ce364421eabe50980191adcca92 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 12 May 2026 22:58:59 +0200 Subject: [PATCH 10/13] feat: Migration - AddedItemsDropped --- Server/Components/Pages/GamePage.razor.cs | 2 +- Server/Modules/Database/FractureDbContext.cs | 20 ++++++ Server/Modules/Items/Models/ItemDropped.cs | 18 +++++ .../MapGenerator/Models/Map/ItemDrop.cs | 2 +- .../Services/ItemPlacementService.cs | 6 +- .../Users/Services/ItemDropStateService.cs | 69 ++++++++++++++++--- .../Modules/Users/Services/MovementService.cs | 13 ++-- Server/Program.cs | 2 +- 8 files changed, 111 insertions(+), 21 deletions(-) create mode 100644 Server/Modules/Items/Models/ItemDropped.cs diff --git a/Server/Components/Pages/GamePage.razor.cs b/Server/Components/Pages/GamePage.razor.cs index 9676eab..b78161f 100644 --- a/Server/Components/Pages/GamePage.razor.cs +++ b/Server/Components/Pages/GamePage.razor.cs @@ -30,7 +30,7 @@ protected override async Task OnInitializedAsync() if (MovementService.CurrentMap is null) { - MovementService.Initialize(); + await MovementService.InitializeAsync(); BackgroundImage = GetBackgroundImagePath(); } diff --git a/Server/Modules/Database/FractureDbContext.cs b/Server/Modules/Database/FractureDbContext.cs index defc862..f95f555 100644 --- a/Server/Modules/Database/FractureDbContext.cs +++ b/Server/Modules/Database/FractureDbContext.cs @@ -11,6 +11,7 @@ public FractureDbContext(DbContextOptions options) public virtual DbSet Items { get; set; } public virtual DbSet ItemStatistics { get; set; } + public virtual DbSet ItemsDropped { get; set; } public virtual DbSet Users { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) @@ -28,6 +29,25 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity().HasOne(s => s.Item).WithOne(i => i.Statistics); + modelBuilder.Entity().ToTable("ItemsDropped"); + modelBuilder + .Entity() + .HasIndex(d => new + { + d.UserId, + d.MapSeed, + d.X, + d.Y, + }) + .IsUnique(); + + modelBuilder + .Entity() + .HasOne() + .WithMany() + .HasForeignKey(d => d.UserId) + .IsRequired(); + base.OnModelCreating(modelBuilder); } } diff --git a/Server/Modules/Items/Models/ItemDropped.cs b/Server/Modules/Items/Models/ItemDropped.cs new file mode 100644 index 0000000..e7e72a2 --- /dev/null +++ b/Server/Modules/Items/Models/ItemDropped.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Fracture.Server.Modules.Items.Models; + +public class ItemDropped +{ + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } + + public int UserId { get; set; } + + public int MapSeed { get; set; } + + public int X { get; set; } + public int Y { get; set; } +} diff --git a/Server/Modules/MapGenerator/Models/Map/ItemDrop.cs b/Server/Modules/MapGenerator/Models/Map/ItemDrop.cs index 6eaf0a3..57cc446 100644 --- a/Server/Modules/MapGenerator/Models/Map/ItemDrop.cs +++ b/Server/Modules/MapGenerator/Models/Map/ItemDrop.cs @@ -2,5 +2,5 @@ namespace Fracture.Server.Modules.MapGenerator.Models.Map; public class ItemDrop { - public string Id { get; init; } = Guid.NewGuid().ToString("N"); + public string Id { get; init; } } diff --git a/Server/Modules/MapGenerator/Services/ItemPlacementService.cs b/Server/Modules/MapGenerator/Services/ItemPlacementService.cs index 2e03f45..3f39b31 100644 --- a/Server/Modules/MapGenerator/Services/ItemPlacementService.cs +++ b/Server/Modules/MapGenerator/Services/ItemPlacementService.cs @@ -9,7 +9,7 @@ public class ItemPlacementService(RandomProvider rndProvider) : IItemPlacementSe public IReadOnlySet GenerateDropPositions(Map map) { - var rnd = new Random(RandomProvider.Seed); + var _rnd = rndProvider.Random; var targetCount = Math.Max(1, (int)Math.Round(map.Width * map.Height * ItemDensity)); var attempts = 0; var maxAttempts = targetCount * 10; @@ -19,8 +19,8 @@ public IReadOnlySet GenerateDropPositions(Map map) { attempts++; - var x = rnd.Next(0, map.Width); - var y = rnd.Next(0, map.Height); + var x = _rnd.Next(0, map.Width); + var y = _rnd.Next(0, map.Height); var node = map.Grid[x, y]; if (!node.Walkable || node.LocationType != LocationType.None) diff --git a/Server/Modules/Users/Services/ItemDropStateService.cs b/Server/Modules/Users/Services/ItemDropStateService.cs index 2088896..d4fa6b8 100644 --- a/Server/Modules/Users/Services/ItemDropStateService.cs +++ b/Server/Modules/Users/Services/ItemDropStateService.cs @@ -1,27 +1,38 @@ using System.Collections.Concurrent; +using Fracture.Server.Modules.Database; +using Fracture.Server.Modules.Items.Models; using Fracture.Server.Modules.MapGenerator.Models.Map; using Fracture.Server.Modules.MapGenerator.Services; +using Fracture.Server.Modules.Shared; +using Microsoft.EntityFrameworkCore; namespace Fracture.Server.Modules.Users.Services; public class ItemDropStateService { + private static readonly HashSet EmptyPositions = new(); + private readonly IItemPlacementService _itemPlacementService; + private readonly FractureDbContext _dbContext; private readonly ConcurrentDictionary> _baseDropsByMap = new(); - private readonly ConcurrentDictionary> _collectedDropsByUserMap = + private readonly ConcurrentDictionary> _collectedDropsByUserSeed = new(); private readonly Lock _lock = new(); - public ItemDropStateService(IItemPlacementService itemPlacementService) + public ItemDropStateService( + IItemPlacementService itemPlacementService, + FractureDbContext dbContext + ) { _itemPlacementService = itemPlacementService; + _dbContext = dbContext; } public bool HasItemDrop(int userId, Map map, int x, int y) { var position = new Position(x, y); var baseDrops = GetBaseDrops(map); - var collected = GetCollectedDrops(userId, map); + var collected = GetCollectedDropsIfLoaded(userId); lock (_lock) { @@ -29,11 +40,16 @@ public bool HasItemDrop(int userId, Map map, int x, int y) } } - public bool TryCollect(int userId, Map map, int x, int y) + public async Task EnsureLoadedAsync(int userId) + { + await GetCollectedDropsAsync(userId); + } + + public async Task TryCollectAsync(int userId, Map map, int x, int y) { var position = new Position(x, y); var baseDrops = GetBaseDrops(map); - var collected = GetCollectedDrops(userId, map); + var collected = await GetCollectedDropsAsync(userId); lock (_lock) { @@ -41,8 +57,20 @@ public bool TryCollect(int userId, Map map, int x, int y) return false; collected.Add(position); - return true; } + + _dbContext.ItemsDropped.Add( + new ItemDropped + { + UserId = userId, + MapSeed = RandomProvider.Seed, + X = x, + Y = y, + } + ); + + await _dbContext.SaveChangesAsync(); + return true; } private HashSet GetBaseDrops(Map map) @@ -54,10 +82,29 @@ private HashSet GetBaseDrops(Map map) ); } - private HashSet GetCollectedDrops(int userId, Map map) + private HashSet GetCollectedDropsIfLoaded(int userId) + { + var userSeedKey = GetUserSeedKey(userId); + return _collectedDropsByUserSeed.TryGetValue(userSeedKey, out var collected) + ? collected + : EmptyPositions; + } + + private async Task> GetCollectedDropsAsync(int userId) { - var userMapKey = GetUserMapKey(userId, map); - return _collectedDropsByUserMap.GetOrAdd(userMapKey, _ => new HashSet()); + var userSeedKey = GetUserSeedKey(userId); + if (_collectedDropsByUserSeed.TryGetValue(userSeedKey, out var cached)) + return cached; + + var positions = await _dbContext + .ItemsDropped.AsNoTracking() + .Where(d => d.UserId == userId && d.MapSeed == RandomProvider.Seed) + .Select(d => new Position(d.X, d.Y)) + .ToListAsync(); + + var collected = new HashSet(positions); + _collectedDropsByUserSeed[userSeedKey] = collected; + return collected; } private static string GetMapKey(Map map) @@ -65,8 +112,8 @@ private static string GetMapKey(Map map) return map.Name ?? $"{map.LocationType}:{map.Width}x{map.Height}"; } - private static string GetUserMapKey(int userId, Map map) + private static string GetUserSeedKey(int userId) { - return $"{userId}:{GetMapKey(map)}"; + return $"{userId}:{RandomProvider.Seed}"; } } diff --git a/Server/Modules/Users/Services/MovementService.cs b/Server/Modules/Users/Services/MovementService.cs index 0c74beb..b00eef8 100644 --- a/Server/Modules/Users/Services/MovementService.cs +++ b/Server/Modules/Users/Services/MovementService.cs @@ -38,7 +38,7 @@ ItemDropStateService itemDropState public event EventHandler<(Map, Position)>? OnMapEntered; public event EventHandler? OnItemEncountered; - public void Initialize() + public async Task InitializeAsync() { CurrentMap = _mapManagerService.GetWorldMap() @@ -48,6 +48,11 @@ public void Initialize() CurrentX = start.X; CurrentY = start.Y; + if (_userService.User != null) + { + await _itemDropState.EnsureLoadedAsync(_userService.User.Id); + } + OnMapEntered?.Invoke(this, new(CurrentMap, new Position(CurrentX, CurrentY))); } @@ -76,11 +81,11 @@ public async Task MoveAsync(int x, int y) if (CurrentMap != null && _userService.User != null) { - if (_itemDropState.TryCollect(_userService.User.Id, CurrentMap, x, y)) + if (await _itemDropState.TryCollectAsync(_userService.User.Id, CurrentMap, x, y)) { var item = await _itemGenerator.Generate(); - item.CreatedById = _userService.User.Id; - item.CreatedBy = _userService.User; + item.CreatedById = null; + item.CreatedBy = null; await _itemsRepository.AddItemAsync(item); _userService.Inventory.Add(item); diff --git a/Server/Program.cs b/Server/Program.cs index 554a633..8333409 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -97,7 +97,7 @@ }); builder.Services.AddLogging(); -builder.Services.AddSingleton(); +builder.Services.AddScoped(); var app = builder.Build(); From 90215e8e167e3002f3f8424e5a0e47071acde43d Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 13 May 2026 23:09:44 +0200 Subject: [PATCH 11/13] feat: AddedPopUpforItems --- Fracture.sln | 2 +- Server/Components/Pages/GamePage.razor.cs | 31 +++++++- .../Components/Popups/ItemPickupRequest.razor | 36 ++++++++++ Server/Components/UI/PopupContainer.razor | 11 ++- Server/Modules/Database/FractureDbContext.cs | 11 ++- Server/Modules/Items/Models/Item.cs | 4 ++ Server/Modules/Items/Models/ItemDropped.cs | 8 +++ Server/Modules/Users/Models/User.cs | 4 ++ .../Users/Services/ItemDropStateService.cs | 14 ++++ .../Modules/Users/Services/MovementService.cs | 71 ++++++++++++++++--- 10 files changed, 174 insertions(+), 18 deletions(-) create mode 100644 Server/Components/Popups/ItemPickupRequest.razor diff --git a/Fracture.sln b/Fracture.sln index 5292064..93e0a9c 100644 --- a/Fracture.sln +++ b/Fracture.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 18 -VisualStudioVersion = 18.5.11716.220 stable +VisualStudioVersion = 18.5.11716.220 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fracture.Server", "Server\Fracture.Server.csproj", "{D06D3FCB-F773-4E5E-A6D3-6C6D052F7996}" EndProject diff --git a/Server/Components/Pages/GamePage.razor.cs b/Server/Components/Pages/GamePage.razor.cs index b78161f..0b550e6 100644 --- a/Server/Components/Pages/GamePage.razor.cs +++ b/Server/Components/Pages/GamePage.razor.cs @@ -1,5 +1,6 @@ using Fracture.Server.Components.Popups; using Fracture.Server.Components.UI; +using Fracture.Server.Modules.Items.Models; using Fracture.Server.Modules.MapGenerator.Models.Map; using Fracture.Server.Modules.MapGenerator.UI.Models; using Fracture.Server.Modules.Pathfinding.Models; @@ -46,10 +47,34 @@ protected override async Task OnInitializedAsync() StateHasChanged(); }; - MovementService.OnItemEncountered += async (sender, args) => + MovementService.OnItemPickupRequested += async (sender, item) => { - _popup.ShowComponent(); - StateHasChanged(); + var parameters = new Dictionary + { + { "Item", item }, + { + "OnConfirm", + (Func)( + async () => + { + await MovementService.ConfirmPickupAsync(); + _popup.Hide(); + } + ) + }, + { + "OnCancel", + (Func)( + async () => + { + await MovementService.CancelPickupAsync(); + _popup.Hide(); + } + ) + }, + }; + + _popup.ShowComponent(parameters); }; _mapDisplayOptions.ShowColorMap = true; diff --git a/Server/Components/Popups/ItemPickupRequest.razor b/Server/Components/Popups/ItemPickupRequest.razor new file mode 100644 index 0000000..e455ad1 --- /dev/null +++ b/Server/Components/Popups/ItemPickupRequest.razor @@ -0,0 +1,36 @@ +@using Fracture.Server.Modules.Items.Models +@using Fracture.Server.Components.UI + +
+

You found a new item, Do you want to pick it up?

+
+ @Item.Name (@Item.Type) - @Item.Rarity +
+ +
+ +
+ + +
+ +@code { + [Parameter] public Item Item { get; set; } = null!; + [Parameter] public Func? OnConfirm { get; set; } + [Parameter] public Func? OnCancel { get; set; } + + private async Task ConfirmAsync() + { + if (OnConfirm is not null) + await OnConfirm(); + } + + private async Task CancelAsync() + { + if (OnCancel is not null) + await OnCancel(); + } +} \ No newline at end of file diff --git a/Server/Components/UI/PopupContainer.razor b/Server/Components/UI/PopupContainer.razor index 3f59e5c..7e74f68 100644 --- a/Server/Components/UI/PopupContainer.razor +++ b/Server/Components/UI/PopupContainer.razor @@ -1,14 +1,13 @@ @using Fracture.Server.Components.Pages @rendermode InteractiveServer - @@ -25,9 +24,15 @@ StateHasChanged(); } + public void Hide() + { + IsVisible = false; + _componentType = null; + _parameters = null; + StateHasChanged(); + } public DynamicComponent? Component = null; private Type? _componentType = null; private IDictionary? _parameters; - } \ No newline at end of file diff --git a/Server/Modules/Database/FractureDbContext.cs b/Server/Modules/Database/FractureDbContext.cs index f95f555..75f5603 100644 --- a/Server/Modules/Database/FractureDbContext.cs +++ b/Server/Modules/Database/FractureDbContext.cs @@ -43,11 +43,18 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder .Entity() - .HasOne() - .WithMany() + .HasOne(d => d.User) + .WithMany(u => u.ItemDrops) .HasForeignKey(d => d.UserId) .IsRequired(); + modelBuilder + .Entity() + .HasOne(d => d.Item) + .WithMany(i => i.DroppedItems) + .HasForeignKey(d => d.ItemId) + .IsRequired(false); + base.OnModelCreating(modelBuilder); } } diff --git a/Server/Modules/Items/Models/Item.cs b/Server/Modules/Items/Models/Item.cs index 75166e0..792bc31 100644 --- a/Server/Modules/Items/Models/Item.cs +++ b/Server/Modules/Items/Models/Item.cs @@ -32,4 +32,8 @@ public class Item [InverseProperty(nameof(User.Items))] [JsonIgnore] public virtual User? CreatedBy { get; set; } + + [InverseProperty(nameof(ItemDropped.Item))] + [JsonIgnore] + public virtual ICollection DroppedItems { get; set; } = null!; } diff --git a/Server/Modules/Items/Models/ItemDropped.cs b/Server/Modules/Items/Models/ItemDropped.cs index e7e72a2..e4ed8d7 100644 --- a/Server/Modules/Items/Models/ItemDropped.cs +++ b/Server/Modules/Items/Models/ItemDropped.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using Fracture.Server.Modules.Users.Models; namespace Fracture.Server.Modules.Items.Models; @@ -9,10 +10,17 @@ public class ItemDropped [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } + [ForeignKey(nameof(User))] public int UserId { get; set; } + [ForeignKey(nameof(Item))] + public int? ItemId { get; set; } + public int MapSeed { get; set; } public int X { get; set; } public int Y { get; set; } + + public virtual User User { get; set; } = null!; + public virtual Item? Item { get; set; } } diff --git a/Server/Modules/Users/Models/User.cs b/Server/Modules/Users/Models/User.cs index dda1883..11df551 100644 --- a/Server/Modules/Users/Models/User.cs +++ b/Server/Modules/Users/Models/User.cs @@ -18,4 +18,8 @@ public class User [InverseProperty(nameof(Item.CreatedBy))] [JsonIgnore] public virtual ICollection Items { get; set; } = null!; + + [InverseProperty(nameof(ItemDropped.User))] + [JsonIgnore] + public virtual ICollection ItemDrops { get; set; } = null!; } diff --git a/Server/Modules/Users/Services/ItemDropStateService.cs b/Server/Modules/Users/Services/ItemDropStateService.cs index d4fa6b8..6e70fe6 100644 --- a/Server/Modules/Users/Services/ItemDropStateService.cs +++ b/Server/Modules/Users/Services/ItemDropStateService.cs @@ -73,6 +73,20 @@ public async Task TryCollectAsync(int userId, Map map, int x, int y) return true; } + public async Task AttachItemAsync(int userId, Map map, int x, int y, int itemId) + { + var mapSeed = RandomProvider.Seed; + var entry = await _dbContext.ItemsDropped.FirstOrDefaultAsync(d => + d.UserId == userId && d.MapSeed == mapSeed && d.X == x && d.Y == y + ); + + if (entry == null) + return; + + entry.ItemId = itemId; + await _dbContext.SaveChangesAsync(); + } + private HashSet GetBaseDrops(Map map) { var mapKey = GetMapKey(map); diff --git a/Server/Modules/Users/Services/MovementService.cs b/Server/Modules/Users/Services/MovementService.cs index b00eef8..971d9ce 100644 --- a/Server/Modules/Users/Services/MovementService.cs +++ b/Server/Modules/Users/Services/MovementService.cs @@ -1,5 +1,6 @@ using Fracture.Server.Components.Popups; using Fracture.Server.Modules.Database; +using Fracture.Server.Modules.Items.Models; using Fracture.Server.Modules.Items.Services; using Fracture.Server.Modules.MapGenerator.Models.Map; using Fracture.Server.Modules.MapGenerator.Services; @@ -14,6 +15,9 @@ public class MovementService private readonly UserService _userService; private readonly ItemDropStateService _itemDropState; + private Position? _pendingPickupPosition; + private Item? _pendingPickupItem; + public MovementService( MapManagerService mapManagerService, IItemGenerator itemGenerator, @@ -37,6 +41,7 @@ ItemDropStateService itemDropState public event EventHandler? OnMoved; public event EventHandler<(Map, Position)>? OnMapEntered; public event EventHandler? OnItemEncountered; + public event EventHandler? OnItemPickupRequested; public async Task InitializeAsync() { @@ -81,19 +86,67 @@ public async Task MoveAsync(int x, int y) if (CurrentMap != null && _userService.User != null) { - if (await _itemDropState.TryCollectAsync(_userService.User.Id, CurrentMap, x, y)) + var position = new Position(x, y); + if (HasItemDrop(CurrentMap, x, y) && _pendingPickupPosition != position) { - var item = await _itemGenerator.Generate(); - item.CreatedById = null; - item.CreatedBy = null; - - await _itemsRepository.AddItemAsync(item); - _userService.Inventory.Add(item); - - OnItemEncountered?.Invoke(this, new Position(x, y)); + _pendingPickupPosition = position; + _pendingPickupItem = await _itemGenerator.Generate(); + OnItemPickupRequested?.Invoke(this, _pendingPickupItem); } } OnMoved?.Invoke(this, new Position(CurrentX, CurrentY)); } + + public async Task ConfirmPickupAsync() + { + if (CurrentMap == null || _userService.User == null || _pendingPickupPosition is null) + return false; + + var position = _pendingPickupPosition.Value; + var item = _pendingPickupItem; + + _pendingPickupPosition = null; + _pendingPickupItem = null; + + if (item == null) + return false; + + if ( + !await _itemDropState.TryCollectAsync( + _userService.User.Id, + CurrentMap, + position.X, + position.Y + ) + ) + return false; + + item.CreatedById = _userService.User.Id; + item.CreatedBy = _userService.User; + + await _itemsRepository.AddItemAsync(item); + _userService.Inventory.Add(item); + + OnItemEncountered?.Invoke(this, position); + return true; + } + + public async Task CancelPickupAsync() + { + if (CurrentMap == null || _userService.User == null || _pendingPickupPosition is null) + return; + + var position = _pendingPickupPosition.Value; + + _pendingPickupPosition = null; + _pendingPickupItem = null; + + await _itemDropState.TryCollectAsync( + _userService.User.Id, + CurrentMap, + position.X, + position.Y + ); + } } From 1985adbe6fc19b733de034353247677bedfe0340 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 14 May 2026 00:21:36 +0200 Subject: [PATCH 12/13] reafactor: DbContextClean --- Server/Modules/Database/FractureDbContext.cs | 21 ------------------- .../MapGenerator/Models/Map/ItemDrop.cs | 2 +- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/Server/Modules/Database/FractureDbContext.cs b/Server/Modules/Database/FractureDbContext.cs index 75f5603..76c1e8f 100644 --- a/Server/Modules/Database/FractureDbContext.cs +++ b/Server/Modules/Database/FractureDbContext.cs @@ -18,13 +18,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity().HasOne(i => i.Statistics).WithOne(s => s.Item); - modelBuilder - .Entity() - .HasOne(i => i.CreatedBy) - .WithMany(u => u.Items) - .HasForeignKey(i => i.CreatedById) - .IsRequired(false); - modelBuilder.Entity().HasMany(u => u.Items).WithOne(i => i.CreatedBy); modelBuilder.Entity().HasOne(s => s.Item).WithOne(i => i.Statistics); @@ -41,20 +34,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) }) .IsUnique(); - modelBuilder - .Entity() - .HasOne(d => d.User) - .WithMany(u => u.ItemDrops) - .HasForeignKey(d => d.UserId) - .IsRequired(); - - modelBuilder - .Entity() - .HasOne(d => d.Item) - .WithMany(i => i.DroppedItems) - .HasForeignKey(d => d.ItemId) - .IsRequired(false); - base.OnModelCreating(modelBuilder); } } diff --git a/Server/Modules/MapGenerator/Models/Map/ItemDrop.cs b/Server/Modules/MapGenerator/Models/Map/ItemDrop.cs index 57cc446..62d08dc 100644 --- a/Server/Modules/MapGenerator/Models/Map/ItemDrop.cs +++ b/Server/Modules/MapGenerator/Models/Map/ItemDrop.cs @@ -2,5 +2,5 @@ namespace Fracture.Server.Modules.MapGenerator.Models.Map; public class ItemDrop { - public string Id { get; init; } + public required string Id { get; init; } } From f1faef42f2f0218358bccbbeb9ef24b8f9b2bdf1 Mon Sep 17 00:00:00 2001 From: Marcin Badurowicz Date: Wed, 20 May 2026 16:03:44 +0200 Subject: [PATCH 13/13] fix: apply migration --- .../FractureDbContextModelSnapshot.cs | 180 +++++++++++++++++ .../FractureDbContextModelSnapshot.txt | 130 +++++++++++++ Server/Migrations/ItemsDropped.Designer.cs | 183 ++++++++++++++++++ Server/Migrations/ItemsDropped.cs | 101 ++++++++++ 4 files changed, 594 insertions(+) create mode 100644 Server/Migrations/FractureDbContextModelSnapshot.cs create mode 100644 Server/Migrations/FractureDbContextModelSnapshot.txt create mode 100644 Server/Migrations/ItemsDropped.Designer.cs create mode 100644 Server/Migrations/ItemsDropped.cs diff --git a/Server/Migrations/FractureDbContextModelSnapshot.cs b/Server/Migrations/FractureDbContextModelSnapshot.cs new file mode 100644 index 0000000..560be54 --- /dev/null +++ b/Server/Migrations/FractureDbContextModelSnapshot.cs @@ -0,0 +1,180 @@ +// +using System; +using Fracture.Server.Modules.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Fracture.Server.Migrations +{ + [DbContext(typeof(FractureDbContext))] + partial class FractureDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.5"); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.Item", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("INTEGER"); + + b.Property("History") + .HasColumnType("TEXT"); + + b.Property("IsEquipped") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Rarity") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.ToTable("Items"); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.ItemDropped", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.Property("MapSeed") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.Property("X") + .HasColumnType("INTEGER"); + + b.Property("Y") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ItemId"); + + b.HasIndex("UserId", "MapSeed", "X", "Y") + .IsUnique(); + + b.ToTable("ItemsDropped", (string)null); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.ItemStatistics", b => + { + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.Property("Armor") + .HasColumnType("INTEGER"); + + b.Property("Health") + .HasColumnType("INTEGER"); + + b.Property("Luck") + .HasColumnType("INTEGER"); + + b.Property("Power") + .HasColumnType("INTEGER"); + + b.HasKey("ItemId"); + + b.ToTable("ItemStatistics"); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Users.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.Item", b => + { + b.HasOne("Fracture.Server.Modules.Users.Models.User", "CreatedBy") + .WithMany("Items") + .HasForeignKey("CreatedById"); + + b.Navigation("CreatedBy"); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.ItemDropped", b => + { + b.HasOne("Fracture.Server.Modules.Items.Models.Item", "Item") + .WithMany("DroppedItems") + .HasForeignKey("ItemId"); + + b.HasOne("Fracture.Server.Modules.Users.Models.User", "User") + .WithMany("ItemDrops") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.ItemStatistics", b => + { + b.HasOne("Fracture.Server.Modules.Items.Models.Item", "Item") + .WithOne("Statistics") + .HasForeignKey("Fracture.Server.Modules.Items.Models.ItemStatistics", "ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.Item", b => + { + b.Navigation("DroppedItems"); + + b.Navigation("Statistics") + .IsRequired(); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Users.Models.User", b => + { + b.Navigation("ItemDrops"); + + b.Navigation("Items"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Server/Migrations/FractureDbContextModelSnapshot.txt b/Server/Migrations/FractureDbContextModelSnapshot.txt new file mode 100644 index 0000000..0133843 --- /dev/null +++ b/Server/Migrations/FractureDbContextModelSnapshot.txt @@ -0,0 +1,130 @@ +// +using System; +using Fracture.Server.Modules.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Fracture.Server.Migrations +{ + [DbContext(typeof(FractureDbContext))] + partial class FractureDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.1"); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.Item", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("INTEGER"); + + b.Property("History") + .HasColumnType("TEXT"); + + b.Property("IsEquipped") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Rarity") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.ToTable("Items"); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.ItemStatistics", b => + { + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.Property("Armor") + .HasColumnType("INTEGER"); + + b.Property("Health") + .HasColumnType("INTEGER"); + + b.Property("Luck") + .HasColumnType("INTEGER"); + + b.Property("Power") + .HasColumnType("INTEGER"); + + b.HasKey("ItemId"); + + b.ToTable("ItemStatistics"); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.Item", b => + { + b.HasOne("Fracture.Server.Modules.Users.User", "CreatedBy") + .WithMany("Items") + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CreatedBy"); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.ItemStatistics", b => + { + b.HasOne("Fracture.Server.Modules.Items.Models.Item", "Item") + .WithOne("Statistics") + .HasForeignKey("Fracture.Server.Modules.Items.Models.ItemStatistics", "ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.Item", b => + { + b.Navigation("Statistics") + .IsRequired(); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Users.User", b => + { + b.Navigation("Items"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Server/Migrations/ItemsDropped.Designer.cs b/Server/Migrations/ItemsDropped.Designer.cs new file mode 100644 index 0000000..00525b4 --- /dev/null +++ b/Server/Migrations/ItemsDropped.Designer.cs @@ -0,0 +1,183 @@ +// +using System; +using Fracture.Server.Modules.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Fracture.Server.Migrations +{ + [DbContext(typeof(FractureDbContext))] + [Migration("20260520135532_ItemsDropped")] + partial class ItemsDropped + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.5"); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.Item", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("INTEGER"); + + b.Property("History") + .HasColumnType("TEXT"); + + b.Property("IsEquipped") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Rarity") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.ToTable("Items"); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.ItemDropped", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.Property("MapSeed") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.Property("X") + .HasColumnType("INTEGER"); + + b.Property("Y") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ItemId"); + + b.HasIndex("UserId", "MapSeed", "X", "Y") + .IsUnique(); + + b.ToTable("ItemsDropped", (string)null); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.ItemStatistics", b => + { + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.Property("Armor") + .HasColumnType("INTEGER"); + + b.Property("Health") + .HasColumnType("INTEGER"); + + b.Property("Luck") + .HasColumnType("INTEGER"); + + b.Property("Power") + .HasColumnType("INTEGER"); + + b.HasKey("ItemId"); + + b.ToTable("ItemStatistics"); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Users.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.Item", b => + { + b.HasOne("Fracture.Server.Modules.Users.Models.User", "CreatedBy") + .WithMany("Items") + .HasForeignKey("CreatedById"); + + b.Navigation("CreatedBy"); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.ItemDropped", b => + { + b.HasOne("Fracture.Server.Modules.Items.Models.Item", "Item") + .WithMany("DroppedItems") + .HasForeignKey("ItemId"); + + b.HasOne("Fracture.Server.Modules.Users.Models.User", "User") + .WithMany("ItemDrops") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.ItemStatistics", b => + { + b.HasOne("Fracture.Server.Modules.Items.Models.Item", "Item") + .WithOne("Statistics") + .HasForeignKey("Fracture.Server.Modules.Items.Models.ItemStatistics", "ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Items.Models.Item", b => + { + b.Navigation("DroppedItems"); + + b.Navigation("Statistics") + .IsRequired(); + }); + + modelBuilder.Entity("Fracture.Server.Modules.Users.Models.User", b => + { + b.Navigation("ItemDrops"); + + b.Navigation("Items"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Server/Migrations/ItemsDropped.cs b/Server/Migrations/ItemsDropped.cs new file mode 100644 index 0000000..7cbe8cf --- /dev/null +++ b/Server/Migrations/ItemsDropped.cs @@ -0,0 +1,101 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Fracture.Server.Migrations +{ + /// + public partial class ItemsDropped : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Items_Users_CreatedById", + table: "Items"); + + migrationBuilder.AlterColumn( + name: "CreatedById", + table: "Items", + type: "INTEGER", + nullable: true, + oldClrType: typeof(int), + oldType: "INTEGER"); + + migrationBuilder.CreateTable( + name: "ItemsDropped", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UserId = table.Column(type: "INTEGER", nullable: false), + ItemId = table.Column(type: "INTEGER", nullable: true), + MapSeed = table.Column(type: "INTEGER", nullable: false), + X = table.Column(type: "INTEGER", nullable: false), + Y = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ItemsDropped", x => x.Id); + table.ForeignKey( + name: "FK_ItemsDropped_Items_ItemId", + column: x => x.ItemId, + principalTable: "Items", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_ItemsDropped_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_ItemsDropped_ItemId", + table: "ItemsDropped", + column: "ItemId"); + + migrationBuilder.CreateIndex( + name: "IX_ItemsDropped_UserId_MapSeed_X_Y", + table: "ItemsDropped", + columns: new[] { "UserId", "MapSeed", "X", "Y" }, + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_Items_Users_CreatedById", + table: "Items", + column: "CreatedById", + principalTable: "Users", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Items_Users_CreatedById", + table: "Items"); + + migrationBuilder.DropTable( + name: "ItemsDropped"); + + migrationBuilder.AlterColumn( + name: "CreatedById", + table: "Items", + type: "INTEGER", + nullable: false, + defaultValue: 0, + oldClrType: typeof(int), + oldType: "INTEGER", + oldNullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_Items_Users_CreatedById", + table: "Items", + column: "CreatedById", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +}