Skip to content

Commit

Permalink
Add packet handling for Board packets
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanmoffat committed May 16, 2023
1 parent 2205483 commit e277af5
Show file tree
Hide file tree
Showing 10 changed files with 275 additions and 11 deletions.
10 changes: 4 additions & 6 deletions EOLib.Localization/DialogResourceID.cs
Expand Up @@ -58,14 +58,12 @@ public enum DialogResourceID
BANK_ACCOUNT_UNABLE_TO_DEPOSIT = 102,
SHOP_NOTHING_IS_FOR_SALE = 104,
//106: confirmed (AFAIK) that the client does not show this message separately from NOT_BUYING_YOUR_ITEMS message
// ReSharper disable once UnusedMember.Global
SHOP_NOT_BUYING_USED_ITEMS = 106,
SHOP_NOT_BUYING_YOUR_ITEMS = 108,
WARNING_YOU_HAVE_NOT_ENOUGH = 110,
SHOP_DOES_NOT_BUY = 112,
LOCKER_FULL_SINGLE_ITEM_MAX = 114,
//116: confirmed (AFAIK) that the client does not show a message when you hit the max (unless the server is missing a message for this)
// ReSharper disable once UnusedMember.Global
LOCKER_FULL_DIFF_ITEMS_MAX = 116,
LOCKER_DEPOSIT_GOLD_ERROR = 118,
DROP_MANY_GOLD_ON_GROUND = 120,
Expand Down Expand Up @@ -108,10 +106,10 @@ public enum DialogResourceID
GUILD_DEPOSIT_NEW_BALANCE = 194,
ITEM_IS_LORE_ITEM = 196,
ITEM_IS_CURSED_ITEM = 198,
BOARD_ERROR_NO_SUBJECT = 200,
BOARD_ERROR_NO_MESSAGE = 202,
BOARD_ERROR_MSG_NO_SUBJECT = 204,
BOARD_ERROR_SUBJECT_NO_MSG = 206,
DEPRECATED_BOARD_ERROR_NO_SUBJECT = 200,
DEPRECATED_BOARD_ERROR_NO_MESSAGE = 202,
BOARD_ERROR_NO_SUBJECT = 204,
BOARD_ERROR_NO_MESSAGE = 206,
BOARD_ERROR_TOO_MANY_MESSAGES = 208,
ITEM_CURSE_REMOVE_PROMPT = 210,
JUKEBOX_REQUESTED_RECENTLY = 212,
Expand Down
70 changes: 70 additions & 0 deletions EOLib/Domain/Interact/Board/BoardActions.cs
@@ -0,0 +1,70 @@
using AutomaticTypeMapper;
using EOLib.Net;
using EOLib.Net.Communication;

namespace EOLib.Domain.Interact.Board
{
[AutoMappedType]
public class BoardActions : IBoardActions
{
private readonly IPacketSendService _packetSendService;
private readonly IBoardProvider _boardProvider;

public BoardActions(IPacketSendService packetSendService,
IBoardProvider boardProvider)
{
_packetSendService = packetSendService;
_boardProvider = boardProvider;
}

public void AddPost(string subject, string body)
{
_boardProvider.BoardId.MatchSome(boardId =>
{
var packet = new PacketBuilder(PacketFamily.Board, PacketAction.Create)
.AddShort(boardId)
.AddByte(255)
.AddBreakString(subject.Replace('y', (char)255)) // this is in EOSERV for some reason. Probably due to chunking (see Sanitization here: https://github.com/Cirras/eo-protocol/blob/master/docs/chunks.md)
.AddBreakString(body)
.Build();
_packetSendService.SendPacket(packet);
});
}

public void DeletePost(int postId)
{
_boardProvider.BoardId.MatchSome(boardId =>
{
var packet = new PacketBuilder(PacketFamily.Board, PacketAction.Remove)
.AddShort(boardId)
.AddShort(postId)
.Build();
_packetSendService.SendPacket(packet);
});
}

public void ViewPost(int postId)
{
_boardProvider.BoardId.MatchSome(boardId =>
{
var packet = new PacketBuilder(PacketFamily.Board, PacketAction.Take)
.AddShort(boardId)
.AddShort(postId)
.Build();
_packetSendService.SendPacket(packet);
});
}
}

public interface IBoardActions
{
void AddPost(string subject, string body);

void ViewPost(int postId);

void DeletePost(int postId);
}
}
55 changes: 55 additions & 0 deletions EOLib/Domain/Interact/Board/BoardDataRepository.cs
@@ -0,0 +1,55 @@
using AutomaticTypeMapper;
using Optional;
using System.Collections.Generic;

namespace EOLib.Domain.Interact.Board
{
public interface IBoardRepository
{
Option<int> BoardId { get; set; }

HashSet<BoardPostInfo> Posts { get; set; }

Option<BoardPostInfo> ActivePost { get; set; }

Option<string> ActivePostMessage { get; set; }
}

public interface IBoardProvider
{
Option<int> BoardId { get; }

IReadOnlyCollection<BoardPostInfo> Posts { get; }

Option<BoardPostInfo> ActivePost { get; }

Option<string> ActivePostMessage { get; }
}

[AutoMappedType(IsSingleton = true)]
public class BoardDataRepository : IBoardRepository, IBoardProvider, IResettable
{
public Option<int> BoardId { get; set; }

public HashSet<BoardPostInfo> Posts { get; set; }

IReadOnlyCollection<BoardPostInfo> IBoardProvider.Posts => Posts;

public Option<BoardPostInfo> ActivePost { get; set; }

public Option<string> ActivePostMessage { get; set; }

public BoardDataRepository()
{
ResetState();
}

public void ResetState()
{
BoardId = Option.None<int>();
Posts = new HashSet<BoardPostInfo>();
ActivePost = Option.None<BoardPostInfo>();
ActivePostMessage = Option.None<string>();
}
}
}
@@ -1,7 +1,7 @@
using Amadevus.RecordGenerator;
using EOLib.IO.Map;

namespace EOLib.Domain.Board
namespace EOLib.Domain.Interact.Board
{
[Record]
public sealed partial class BoardMapEntity : IMapEntity
Expand Down
9 changes: 9 additions & 0 deletions EOLib/Domain/Interact/Board/BoardPostDetail.cs
@@ -0,0 +1,9 @@
using Amadevus.RecordGenerator;

namespace EOLib.Domain.Interact.Board
{
[Record(Features.Default | Features.ObjectEquals | Features.EquatableEquals)]
public sealed partial class BoardPostDetail
{
}
}
14 changes: 14 additions & 0 deletions EOLib/Domain/Interact/Board/BoardPostInfo.cs
@@ -0,0 +1,14 @@
using Amadevus.RecordGenerator;

namespace EOLib.Domain.Interact.Board
{
[Record(Features.Default | Features.ObjectEquals | Features.EquatableEquals)]
public sealed partial class BoardPostInfo
{
public int PostId { get; }

public string Author { get; }

public string Subject { get; }
}
}
74 changes: 74 additions & 0 deletions EOLib/PacketHandlers/Board/BoardOpenHandler.cs
@@ -0,0 +1,74 @@
using AutomaticTypeMapper;
using EOLib.Domain.Interact.Board;
using EOLib.Domain.Login;
using EOLib.Domain.Notifiers;
using EOLib.Net;
using EOLib.Net.Handlers;
using Optional;
using System.Collections.Generic;

namespace EOLib.PacketHandlers.Board
{
/// <summary>
/// Sent by the server when a board should be opened
/// </summary>
[AutoMappedType]
public class BoardOpenHandler : InGameOnlyPacketHandler
{
private readonly IBoardRepository _boardRepository;
private readonly IEnumerable<IUserInterfaceNotifier> _userInterfaceNotifiers;

public override PacketFamily Family => PacketFamily.Board;

public override PacketAction Action => PacketAction.Open;

public BoardOpenHandler(IPlayerInfoProvider playerInfoProvider,
IBoardRepository boardRepository,
IEnumerable<IUserInterfaceNotifier> userInterfaceNotifiers)
: base(playerInfoProvider)
{
_boardRepository = boardRepository;
_userInterfaceNotifiers = userInterfaceNotifiers;
}

public override bool HandlePacket(IPacket packet)
{
_boardRepository.BoardId = Option.Some(packet.ReadChar());

var numPosts = packet.ReadChar();
_boardRepository.Posts = new HashSet<BoardPostInfo>();

var chunks = new List<IPacket>();
while (packet.ReadPosition < packet.Length)
{
var chunkData = new List<byte> { (byte)PacketFamily.Board, (byte)PacketAction.Open };
while (packet.ReadPosition < packet.Length && packet.PeekByte() != 255)
chunkData.Add(packet.ReadByte());

if (packet.ReadPosition < packet.Length)
packet.ReadByte();

chunks.Add(new Packet(chunkData));
}

if (chunks.Count % 3 != 0 || chunks.Count / 3 != numPosts)
throw new MalformedPacketException("Unexpected number of elements in BOARD_OPEN packet", packet);

for (int i = 0; i < chunks.Count / 3; i += 3)
{
var postId = chunks[i].ReadShort();
var author = chunks[i + 1].ReadEndString();
var subject = chunks[i + 2].ReadEndString();
_boardRepository.Posts.Add(new BoardPostInfo(postId, author, subject));
}

_boardRepository.ActivePost = Option.None<BoardPostInfo>();
_boardRepository.ActivePostMessage = Option.None<string>();

foreach (var notifier in _userInterfaceNotifiers)
notifier.NotifyPacketDialog(PacketFamily.Board);

return true;
}
}
}
45 changes: 45 additions & 0 deletions EOLib/PacketHandlers/Board/BoardTakeHandler.cs
@@ -0,0 +1,45 @@
using AutomaticTypeMapper;
using EOLib.Domain.Interact.Board;
using EOLib.Domain.Login;
using EOLib.Net;
using EOLib.Net.Handlers;
using Optional;

namespace EOLib.PacketHandlers.Board
{
/// <summary>
/// Sent by the server to read a post on a board
/// </summary>
[AutoMappedType]
public class BoardTakeHandler : InGameOnlyPacketHandler
{
private readonly IBoardRepository _boardRepository;

public override PacketFamily Family => PacketFamily.Board;

public override PacketAction Action => PacketAction.Take;

public BoardTakeHandler(IPlayerInfoProvider playerInfoProvider,
IBoardRepository boardRepository)
: base(playerInfoProvider)
{
_boardRepository = boardRepository;
}

public override bool HandlePacket(IPacket packet)
{
var postId = packet.ReadShort();
var message = packet.ReadEndString();

_boardRepository.ActivePost.MatchSome(post =>
{
if (post.PostId == postId)
{
_boardRepository.ActivePostMessage = Option.Some(message);
}
});

return true;
}
}
}
1 change: 1 addition & 0 deletions EndlessClient/HUD/ServerMessageActions.cs
Expand Up @@ -22,6 +22,7 @@ public void NotifyPacketDialog(PacketFamily packetFamily)
{
case PacketFamily.Locker: _inGameDialogActions.ShowLockerDialog(); break;
case PacketFamily.Chest: _inGameDialogActions.ShowChestDialog(); break;
case PacketFamily.Board: _inGameDialogActions.ShowBoardDialog(); break;
}
}

Expand Down
6 changes: 2 additions & 4 deletions EndlessClient/Rendering/Map/ClickDispatcher.cs
@@ -1,13 +1,12 @@
using AutomaticTypeMapper;
using EndlessClient.Controllers;
using EndlessClient.Controllers;
using EndlessClient.ControlSets;
using EndlessClient.HUD.Controls;
using EndlessClient.HUD.Spells;
using EndlessClient.Rendering.Character;
using EndlessClient.Rendering.NPC;
using EOLib.Domain.Board;
using EOLib.Domain.Character;
using EOLib.Domain.Extensions;
using EOLib.Domain.Interact.Board;
using EOLib.Domain.Map;
using EOLib.IO.Map;
using Microsoft.Xna.Framework;
Expand All @@ -18,7 +17,6 @@
using System.Collections.Generic;
using System.Linq;
using XNAControls;

using DomainCharacter = EOLib.Domain.Character.Character;
using DomainNPC = EOLib.Domain.NPC.NPC;

Expand Down

0 comments on commit e277af5

Please sign in to comment.