Started on rooms, also created the private room categories map and made Repository log errors on exception instead of breaking

main
Tiger 2023-10-21 08:34:12 +02:00
parent df75c4d4e6
commit b5fbeb7852
17 changed files with 419 additions and 32 deletions

View File

@ -1,4 +1,5 @@
using Tiger.Communication.Messages.Interfaces;
using Tiger.Communication.Messages.Outgoing;
using Tiger.Communication.Messages.Outgoing.Navigator;
using Tiger.Communication.Messages.Types;
using Tiger.Game.Navigator;
@ -27,7 +28,14 @@ public class NavigatorSearchEvent : IMessageEvent
}
if (!_navigatorManager.NavigatorViews.TryGetValue(request.ReadString(), out var navigatorView)) return;
var qry = request.ReadString();
await gameSession.SendComposerAsync(new NavigatorSearchComposer(navigatorView, request.ReadString(), gameSession.Habbo));
var message = new ServerMessage((short)OutgoingHeaders.NavigatorSearch);
message.AppendString(navigatorView.Code);
message.AppendString(qry);
await navigatorView.Compose(message, gameSession.Habbo, qry);
await gameSession.SendMessageAsync(message);
// await gameSession.SendComposerAsync(new NavigatorSearchComposer(navigatorView, request.ReadString(), gameSession.Habbo));
}
}

View File

@ -23,6 +23,6 @@ public class NavigatorSearchComposer : IMessageComposer
{
message.AppendString(_navigatorView.Code);
message.AppendString(_query);
_navigatorView.Compose(message, _habbo, _query);
_navigatorView.Compose(message, _habbo, _query).Wait();
}
}

View File

@ -6,5 +6,5 @@ namespace Tiger.Game.Navigator.Views;
public interface INavigatorView
{
string Code { get; }
void Compose(ServerMessage message, Habbo habbo, string query);
Task Compose(ServerMessage message, Habbo habbo, string query);
}

View File

@ -1,11 +1,16 @@
using System.Collections;
using System.Collections.ObjectModel;
using Tiger.Communication.Messages.Types;
using Tiger.Game.Habbos;
using Tiger.Game.Rooms;
using Tiger.Storage;
namespace Tiger.Game.Navigator.Views;
public class MyWorldView : INavigatorView
{
private readonly IRepository<Room> _roomRepository;
private const int OwnRoomsType = 0;
private const int FavoriteRoomsType = 1;
private const int GroupRoomsType = 2;
@ -25,11 +30,12 @@ public class MyWorldView : INavigatorView
{"Rooms Where I Have Rights", RightsRoomType}
};
public MyWorldView()
public MyWorldView(IRepository<Room> roomRepository)
{
_roomRepository = roomRepository;
}
public void Compose(ServerMessage message, Habbo habbo, string query)
public async Task Compose(ServerMessage message, Habbo habbo, string query)
{
message.AppendInt32(_categories.Count);
@ -41,14 +47,23 @@ public class MyWorldView : INavigatorView
message.AppendBoolean(false); // closed?
message.AppendInt32(0); // mode?
var rooms = GetRoomsByType(habbo, type);
var rooms = await GetRoomsByType(habbo, type);
message.AppendInt32(rooms.Count); // TODO: Rooms, this will ALWAYS return 0.
message.AppendInt32(rooms.Count);
foreach (var room in rooms)
{
room.Serialize(message);
}
}
}
private ICollection GetRoomsByType(Habbo habbo, int type)
private async Task<ICollection<Room>> GetRoomsByType(Habbo habbo, int type)
{
return new List<object>();
return type switch
{
OwnRoomsType => (await _roomRepository.FindByAsync(r => r.Owner == habbo)).ToList(),
_ => new Collection<Room>()
};
}
}

View File

@ -6,8 +6,9 @@ namespace Tiger.Game.Navigator.Views;
public class OfficialView : INavigatorView
{
public string Code => "official_view";
public void Compose(ServerMessage message, Habbo habbo, string query)
public async Task Compose(ServerMessage message, Habbo habbo, string query)
{
message.AppendInt32(0);
await Task.CompletedTask;
}
}

View File

@ -3,5 +3,7 @@ namespace Tiger.Game.Rooms;
public interface IRoomManager
{
IDictionary<int, RoomPrivateCategory> PrivateCategories { get; }
IDictionary<int, RoomModel> RoomModels { get; }
Task LoadPrivateCategoriesAsync();
Task LoadRoomModels();
}

View File

@ -0,0 +1,17 @@
namespace Tiger.Game.Rooms.Mapping.Tiles;
public class RoomTile
{
public int X { get; set; }
public int Y { get; set; }
public double Height { get; set; }
public RoomTileState TileState { get; set; }
public RoomTile(int x, int y, double height, RoomTileState tileState)
{
X = x;
Y = y;
Height = height;
TileState = tileState;
}
}

View File

@ -0,0 +1,17 @@
namespace Tiger.Game.Rooms.Mapping.Tiles;
/// <summary>
/// Empty and Unavailable can be set in both the RoomTile and RoomPoint.
/// RoomTile belongs to the Model (the tile is either unavailable or empty)
/// RoomPoint belongs to the Grid (the tile has multiple states based on once actors and items are added)
/// </summary>
public enum RoomTileState
{
Empty,
Unavailable,
HasWalkableStackableItem,
WalkBlockedByItem,
StackBlockedByItem,
TileLockedByItem,
BlockedByUser
}

131
Game/Rooms/Room.cs Normal file
View File

@ -0,0 +1,131 @@
using Tiger.Communication.Messages.Types;
using Tiger.Game.Habbos;
namespace Tiger.Game.Rooms;
public class Room
{
public virtual int Id { get; set; }
public virtual string Name { get; set; } = null!;
public virtual string Description { get; set; } = null!;
public virtual int ModelId { get; set; }
public virtual string Password { get; set; } = null!;
public virtual string State { get; set; } = null!;
public virtual int UsersIn { get; set; }
public virtual int UsersMax { get; set; }
public virtual int Score { get; set; }
public virtual string Floor { get; set; } = null!;
public virtual string Wallpaper { get; set; } = null!;
public virtual string Landscape { get; set; } = null!;
public virtual int WallThickness { get; set; }
public virtual int WallHeight { get; set; }
public virtual int FloorThickness { get; set; }
public virtual string MoodlightData { get; set; } = null!;
public virtual IList<string> Tags { get; set; } = new List<string>();
public virtual bool IsPublic { get; set; }
public virtual bool IsStaffPicked { get; set; }
public virtual bool AllowOtherPets { get; set; }
public virtual bool AllowOtherPetsEat { get; set; }
public virtual bool AllowWalkthrough { get; set; }
public virtual bool HideWalls { get; set; }
public virtual int ChatMode { get; set; }
public virtual int ChatWeight { get; set; }
public virtual int ChatSpeed { get; set; }
public virtual int ChatHearingDistance { get; set; }
public virtual int ChatProtection { get; set; }
public virtual bool OverrideModel { get; set; }
public virtual int WhoCanMute { get; set; }
public virtual int WhoCanKick { get; set; }
public virtual int WhoCanBan { get; set; }
public virtual int RollerSpeed { get; set; }
public virtual bool Promoted { get; set; }
public virtual int TradeMode { get; set; }
public virtual bool MoveDiagonally { get; set; }
public virtual bool JukeboxActive { get; set; }
public virtual bool HideWired { get; set; }
public virtual bool IsForSale { get; set; }
public virtual Habbo? Owner { get; set; }
public virtual RoomPrivateCategory Category { get; set; } = null!;
public virtual int StateNumber => State switch
{
"locked" => 1,
"password" => 2,
"invisible" => 3,
_ => 0
};
public virtual int Base
{
get
{
var @base = 0;
// if (Guild != null)
// {
// @base += 2;
// }
// if (Promoted)
// {
// @base += 4;
// }
if (!IsPublic)
{
@base += 8;
}
if (!IsPublic && AllowOtherPets)
{
@base += 16;
}
return @base;
}
}
public virtual void Serialize(ServerMessage response)
{
response.AppendInt32(Id);
response.AppendString(Name);
if (IsPublic)
{
response.AppendInt32(0);
response.AppendString(string.Empty);
}
else
{
response.AppendInt32(Owner?.Id ?? 0);
response.AppendString(Owner?.Username ?? string.Empty);
}
response.AppendInt32(StateNumber);
response.AppendInt32(UsersIn);
response.AppendInt32(UsersMax);
response.AppendString(Description);
response.AppendInt32(0);
response.AppendInt32(Score);
response.AppendInt32(0);
response.AppendInt32(Category.Id);
response.AppendInt32(Tags.Count);
foreach (var tag in Tags)
{
response.AppendString(tag);
}
response.AppendInt32(Base);
// if (Guild != null)
// {
// response.AppendInt32(Guild.Id);
// response.WriteString(Guild.Name);
// response.WriteString(Guild.Badge);
// }
// TODO: Promotion
}
}

View File

@ -6,16 +6,21 @@ namespace Tiger.Game.Rooms;
public class RoomManager : IRoomManager
{
private readonly IRepository<RoomPrivateCategory> _roomPrivateCategoryRepository;
private readonly IRepository<RoomModel> _roomModelRepository;
private readonly ILogger<IRoomManager> _logger;
public IDictionary<int, RoomPrivateCategory> PrivateCategories { get; private set; }
public IDictionary<int, RoomModel> RoomModels { get; private set; }
public RoomManager(IRepository<RoomPrivateCategory> roomPrivateCategoryRepository, ILogger<IRoomManager> logger)
public RoomManager(IRepository<RoomPrivateCategory> roomPrivateCategoryRepository,
IRepository<RoomModel> roomModelRepository, ILogger<IRoomManager> logger)
{
_roomPrivateCategoryRepository = roomPrivateCategoryRepository;
_roomModelRepository = roomModelRepository;
_logger = logger;
PrivateCategories = new Dictionary<int, RoomPrivateCategory>();
RoomModels = new Dictionary<int, RoomModel>();
}
public async Task LoadPrivateCategoriesAsync()
@ -25,4 +30,16 @@ public class RoomManager : IRoomManager
_logger.LogInformation("Loaded {Count} private room categories", PrivateCategories.Count);
}
public async Task LoadRoomModels()
{
RoomModels = (await _roomModelRepository.FindByAsync()).ToDictionary(rpc => rpc.Id);
foreach (var model in RoomModels.Values)
{
model.Parse();
}
_logger.LogInformation("Loaded and parsed {Count} room models", RoomModels.Count);
}
}

53
Game/Rooms/RoomMap.cs Normal file
View File

@ -0,0 +1,53 @@
using FluentNHibernate.Mapping;
using Tiger.Storage;
namespace Tiger.Game.Rooms;
public class RoomMap : ClassMap<Room>
{
public RoomMap()
{
Table("rooms");
Id(x => x.Id).Column("id").GeneratedBy.Identity();
Map(x => x.Name).Column("name").Not.Nullable();
Map(x => x.Description).Column("description").Not.Nullable();
Map(x => x.ModelId).Column("model_id").Not.Nullable();
Map(x => x.Password).Column("password").Nullable();
Map(x => x.State).Column("state").Not.Nullable();
Map(x => x.UsersIn).Column("users_in").Not.Nullable();
Map(x => x.UsersMax).Column("users_max").Not.Nullable();
Map(x => x.Score).Column("score").Not.Nullable();
Map(x => x.Floor).Column("floor").Not.Nullable();
Map(x => x.Wallpaper).Column("wallpaper").Not.Nullable();
Map(x => x.Landscape).Column("landscape").Not.Nullable();
Map(x => x.WallThickness).Column("wall_thickness").Not.Nullable();
Map(x => x.WallHeight).Column("wall_height").Not.Nullable();
Map(x => x.FloorThickness).Column("floor_thickness").Not.Nullable();
Map(x => x.MoodlightData).Column("moodlight_data").Nullable();
Map(x => x.Tags).Column("tags").CustomType<StringListTypeConverter>().Nullable();
Map(x => x.IsPublic).Column("is_public").Not.Nullable();
Map(x => x.IsStaffPicked).Column("is_staff_picked").Not.Nullable();
Map(x => x.AllowOtherPets).Column("allow_other_pets").Not.Nullable();
Map(x => x.AllowOtherPetsEat).Column("allow_other_pets_eat").Not.Nullable();
Map(x => x.AllowWalkthrough).Column("allow_walkthrough").Not.Nullable();
Map(x => x.HideWalls).Column("hide_walls").Not.Nullable();
Map(x => x.ChatMode).Column("chat_mode").Not.Nullable();
Map(x => x.ChatWeight).Column("chat_weight").Not.Nullable();
Map(x => x.ChatSpeed).Column("chat_speed").Not.Nullable();
Map(x => x.ChatHearingDistance).Column("chat_hearing_distance").Not.Nullable();
Map(x => x.ChatProtection).Column("chat_protection").Not.Nullable();
Map(x => x.OverrideModel).Column("override_model").Not.Nullable();
Map(x => x.WhoCanMute).Column("who_can_mute").Not.Nullable();
Map(x => x.WhoCanKick).Column("who_can_kick").Not.Nullable();
Map(x => x.WhoCanBan).Column("who_can_ban").Not.Nullable();
Map(x => x.RollerSpeed).Column("roller_speed").Not.Nullable();
Map(x => x.Promoted).Column("promoted").Not.Nullable();
Map(x => x.TradeMode).Column("trade_mode").Not.Nullable();
Map(x => x.MoveDiagonally).Column("move_diagonally").Not.Nullable();
Map(x => x.JukeboxActive).Column("jukebox_active").Not.Nullable();
Map(x => x.HideWired).Column("hide_wired").Not.Nullable();
Map(x => x.IsForSale).Column("is_for_sale").Not.Nullable();
References(x => x.Owner).Column("owner_id").Nullable();
References(x => x.Category).Column("category_id").Not.Nullable();
}
}

41
Game/Rooms/RoomModel.cs Normal file
View File

@ -0,0 +1,41 @@
using Tiger.Game.Rooms.Mapping.Tiles;
namespace Tiger.Game.Rooms;
public class RoomModel
{
public virtual int Id { get; set; }
public virtual string Name { get; set; } = null!;
public virtual int DoorX { get; set; }
public virtual int DoorY { get; set; }
public virtual int DoorDirection { get; set; }
public virtual string Heightmap { get; set; } = null!;
public virtual (int X, int Y) MapSize { get; set; }
public virtual RoomTile[,] RoomTiles { get; set; }
public virtual void Parse()
{
var raw = Heightmap.Replace("\r\n", "\r").Split('\r');
MapSize = (raw[0].Length, raw.Length);
RoomTiles = new RoomTile[MapSize.X, MapSize.Y];
for (var y = 0; y < MapSize.Y; y++)
{
for (var x = 0; x < MapSize.X; x++)
{
if (double.TryParse(raw[y][x].ToString(), out var height))
{
RoomTiles[x, y] = new RoomTile(x, y, height, RoomTileState.Empty);
}
else
{
RoomTiles[x, y] = new RoomTile(x, y, 0, RoomTileState.Unavailable);
}
}
}
}
}

View File

@ -0,0 +1,18 @@
using FluentNHibernate.Mapping;
namespace Tiger.Game.Rooms;
public class RoomModelMap : ClassMap<RoomModel>
{
public RoomModelMap()
{
Table("room_models");
LazyLoad();
Id(rm => rm.Id).Column("id").GeneratedBy.Identity();
Map(rm => rm.Name).Column("name").Not.Nullable();
Map(rm => rm.DoorX).Column("door_x").Not.Nullable();
Map(rm => rm.DoorY).Column("door_y").Not.Nullable();
Map(rm => rm.DoorDirection).Column("door_direction").Not.Nullable();
Map(rm => rm.Heightmap).Column("heightmap").Not.Nullable();
}
}

View File

@ -1,6 +1,17 @@
using FluentNHibernate.Mapping;
namespace Tiger.Game.Rooms;
public class RoomPrivateCategoryMap
public class RoomPrivateCategoryMap : ClassMap<RoomPrivateCategory>
{
public RoomPrivateCategoryMap()
{
Table("room_private_categories");
LazyLoad();
Id(rpc => rpc.Id).Column("id").GeneratedBy.Identity();
Map(rpc => rpc.MinRank).Column("min_rank").Not.Nullable();
Map(rpc => rpc.Code).Column("code").Not.Nullable();
Map(rpc => rpc.Name).Column("name").Not.Nullable();
Map(rpc => rpc.CanTrade).Column("can_trade").Not.Nullable();
}
}

View File

@ -25,4 +25,11 @@ public class GameSession
await WebSocket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Binary, true,
CancellationToken.None);
}
public async Task SendMessageAsync(ServerMessage message)
{
var bytes = message.ToArray();
await WebSocket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Binary, true,
CancellationToken.None);
}
}

View File

@ -60,6 +60,7 @@ provider.GetRequiredService<IFigureDataManager>();
await provider.GetRequiredService<ILandingViewManager>().LoadPromoArticlesAsync();
await provider.GetRequiredService<IAchievementManager>().LoadAchievementsAsync();
await provider.GetRequiredService<IRoomManager>().LoadPrivateCategoriesAsync();
await provider.GetRequiredService<IRoomManager>().LoadRoomModels();
provider.GetRequiredService<IWebSocketServer>().Start($"http://{configuration["Network:Game:Ip"]}:{configuration["Network:Game:Port"]}/");

View File

@ -1,4 +1,5 @@
using System.Linq.Expressions;
using Microsoft.Extensions.Logging;
using NHibernate;
using NHibernate.Linq;
@ -7,57 +8,104 @@ namespace Tiger.Storage;
public class Repository<T> : IRepository<T> where T : class
{
private readonly ISession _session;
private readonly ILogger<IRepository<T>> _logger;
public Repository(ISession session)
public Repository(ISession session, ILogger<IRepository<T>> logger)
{
_session = session;
_logger = logger;
}
public async Task<T?> FindAsync(object id)
{
return await _session.GetAsync<T>(id);
try
{
return await _session.GetAsync<T>(id);
}
catch (Exception ex)
{
_logger.LogError(ex, "Tried to get {Type} with id {Id}", typeof(T).Name, id);
return null;
}
}
public async Task SaveAsync(T entity)
{
await _session.SaveOrUpdateAsync(entity);
await _session.FlushAsync();
try
{
await _session.SaveOrUpdateAsync(entity);
await _session.FlushAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "Tried to save {Type}", typeof(T).Name);
}
}
public async Task SaveManyAsync(params T[] entities)
{
foreach (var entity in entities)
try
{
await _session.SaveOrUpdateAsync(entity);
}
foreach (var entity in entities)
{
await _session.SaveOrUpdateAsync(entity);
}
await _session.FlushAsync();
await _session.FlushAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "Tried to save many {Type}", typeof(T).Name);
}
}
public async Task SaveManyAsync(IEnumerable<T> entities)
{
foreach (var entity in entities)
try
{
await _session.SaveOrUpdateAsync(entity);
}
foreach (var entity in entities)
{
await _session.SaveOrUpdateAsync(entity);
}
await _session.FlushAsync();
await _session.FlushAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "Tried to save many {Type}", typeof(T).Name);
}
}
public async Task<IEnumerable<T>> FindByAsync(Expression<Func<T, bool>>? expression)
{
var query = _session.Query<T>();
if (expression != null)
try
{
query = query.Where(expression);
}
var query = _session.Query<T>();
return await query.ToListAsync();
if (expression != null)
{
query = query.Where(expression);
}
return await query.ToListAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "Tried find many {Type} by query", typeof(T).Name);
return Array.Empty<T>();
}
}
public async Task<T?> FindOneByAsync(Expression<Func<T, bool>> expression)
{
return await _session.Query<T>().FirstOrDefaultAsync(expression);
try
{
return await _session.Query<T>().FirstOrDefaultAsync(expression);
}
catch (Exception ex)
{
_logger.LogError(ex, "Tried find single {Type} by query", typeof(T).Name);
return null;
}
}
}