From fbafd7210d058a182de8ebbbc857d6bb6a9c14d9 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sat, 16 Jan 2021 21:01:54 -0800 Subject: [PATCH] fix(core): Converts some packets & misc fixes/cleanup (#414) - [X] Adds a stop music packet - [X] Converts chat packets - [X] Breaks out chat code a little bit more - [X] Converts character statue animation packet - [X] Renames some folders to have spaces - [X] Organizes and consolidates incoming/outgoing packets for other content - [X] Fixes virtual checks --- Projects/Server/Items/SecureTradeContainer.cs | 10 +- .../Network/Packets/OutgoingPlayerPackets.cs | 2 + Projects/Server/Regions/Region.cs | 3 +- .../Tests/Engines/Chat/ChatPacketTests.cs | 25 ++ .../Tests}/Engines/Chat/Packets.cs | 6 +- .../CharacterStatuePacketTests.cs | 27 ++ .../Character Statue Maker/Packets.cs | 4 +- Projects/UOContent/Engines/Chat/Chat.cs | 85 +----- .../UOContent/Engines/Chat/ChatPackets.cs | 127 +++++++++ Projects/UOContent/Engines/Chat/ChatUser.cs | 18 +- .../Character Statue Maker/CharacterStatue.cs | 153 +++++----- .../CharacterStatueMaker.cs | 0 .../CharacterStatuePackets.cs | 60 ++++ .../CharacterStatuePlinth.cs | 0 .../Gumps/CharacterStatueGump.cs | 0 .../RewardCategory.cs | 0 .../RewardChoiceGump.cs | 0 .../RewardConfirmGump.cs | 0 .../RewardDemolitionGump.cs | 0 .../RewardEntry.cs | 0 .../RewardList.cs | 0 .../RewardNoticeGump.cs | 0 .../RewardOptionGump.cs | 0 .../RewardSystem.cs | 0 .../Items/Bulletin Boards/BulletinBoard.cs | 184 ++---------- .../Bulletin Boards/BulletinBoardPackets.cs | 144 ++++++++++ .../Games/Mahjong/MahjongPacketHandlers.cs | 268 ------------------ .../Items/Games/Mahjong/MahjongPackets.cs | 260 +++++++++++++++++ Projects/UOContent/Items/Maps/MapItem.cs | 43 --- .../UOContent/Items/Maps/MapItemPackets.cs | 43 +++ .../Dawn's Music Box/DawnsMusicBox.cs | 12 +- Projects/UOContent/Misc/HardwareInfo.cs | 2 +- .../Engines/GiftGiving.cs | 0 .../Engines/ItemFixes.cs | 0 .../Engines/PreventInaccess.cs | 0 .../Engines/TestCenter.cs | 0 .../Items/Resurrection/ResGate.cs | 0 .../Items/Stones/AlchemyStone.cs | 0 .../Items/Stones/GamblingStone.cs | 0 .../Items/Stones/IngotStone.cs | 0 .../Items/Stones/RegStone.cs | 0 .../Items/Stones/ScribeStone.cs | 0 .../Items/Stones/SmithStone.cs | 0 .../Items/Stones/TailorStone.cs | 0 .../Items/Supply Bags}/AlchemyBag.cs | 0 .../Items/Supply Bags}/BagOfIngots.cs | 0 .../Items/Supply Bags}/ScribeBag.cs | 0 .../Items/Supply Bags}/SmithBag.cs | 0 .../Items/Supply Bags}/TailorBag.cs | 0 49 files changed, 814 insertions(+), 662 deletions(-) create mode 100644 Projects/UOContent.Tests/Tests/Engines/Chat/ChatPacketTests.cs rename Projects/{UOContent => UOContent.Tests/Tests}/Engines/Chat/Packets.cs (74%) create mode 100644 Projects/UOContent.Tests/Tests/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePacketTests.cs rename Projects/{UOContent/Engines/VeteranRewards => UOContent.Tests/Tests/Engines/Veteran Rewards}/Character Statue Maker/Packets.cs (77%) create mode 100644 Projects/UOContent/Engines/Chat/ChatPackets.cs rename Projects/UOContent/Engines/{VeteranRewards => Veteran Rewards}/Character Statue Maker/CharacterStatue.cs (85%) rename Projects/UOContent/Engines/{VeteranRewards => Veteran Rewards}/Character Statue Maker/CharacterStatueMaker.cs (100%) create mode 100644 Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePackets.cs rename Projects/UOContent/Engines/{VeteranRewards => Veteran Rewards}/Character Statue Maker/CharacterStatuePlinth.cs (100%) rename Projects/UOContent/Engines/{VeteranRewards => Veteran Rewards}/Character Statue Maker/Gumps/CharacterStatueGump.cs (100%) rename Projects/UOContent/Engines/{VeteranRewards => Veteran Rewards}/RewardCategory.cs (100%) rename Projects/UOContent/Engines/{VeteranRewards => Veteran Rewards}/RewardChoiceGump.cs (100%) rename Projects/UOContent/Engines/{VeteranRewards => Veteran Rewards}/RewardConfirmGump.cs (100%) rename Projects/UOContent/Engines/{VeteranRewards => Veteran Rewards}/RewardDemolitionGump.cs (100%) rename Projects/UOContent/Engines/{VeteranRewards => Veteran Rewards}/RewardEntry.cs (100%) rename Projects/UOContent/Engines/{VeteranRewards => Veteran Rewards}/RewardList.cs (100%) rename Projects/UOContent/Engines/{VeteranRewards => Veteran Rewards}/RewardNoticeGump.cs (100%) rename Projects/UOContent/Engines/{VeteranRewards => Veteran Rewards}/RewardOptionGump.cs (100%) rename Projects/UOContent/Engines/{VeteranRewards => Veteran Rewards}/RewardSystem.cs (100%) delete mode 100644 Projects/UOContent/Items/Games/Mahjong/MahjongPacketHandlers.cs rename Projects/UOContent/{SpecialSystems => Special Systems}/Engines/GiftGiving.cs (100%) rename Projects/UOContent/{SpecialSystems => Special Systems}/Engines/ItemFixes.cs (100%) rename Projects/UOContent/{SpecialSystems => Special Systems}/Engines/PreventInaccess.cs (100%) rename Projects/UOContent/{SpecialSystems => Special Systems}/Engines/TestCenter.cs (100%) rename Projects/UOContent/{SpecialSystems => Special Systems}/Items/Resurrection/ResGate.cs (100%) rename Projects/UOContent/{SpecialSystems => Special Systems}/Items/Stones/AlchemyStone.cs (100%) rename Projects/UOContent/{SpecialSystems => Special Systems}/Items/Stones/GamblingStone.cs (100%) rename Projects/UOContent/{SpecialSystems => Special Systems}/Items/Stones/IngotStone.cs (100%) rename Projects/UOContent/{SpecialSystems => Special Systems}/Items/Stones/RegStone.cs (100%) rename Projects/UOContent/{SpecialSystems => Special Systems}/Items/Stones/ScribeStone.cs (100%) rename Projects/UOContent/{SpecialSystems => Special Systems}/Items/Stones/SmithStone.cs (100%) rename Projects/UOContent/{SpecialSystems => Special Systems}/Items/Stones/TailorStone.cs (100%) rename Projects/UOContent/{SpecialSystems/Items/SupplyBags => Special Systems/Items/Supply Bags}/AlchemyBag.cs (100%) rename Projects/UOContent/{SpecialSystems/Items/SupplyBags => Special Systems/Items/Supply Bags}/BagOfIngots.cs (100%) rename Projects/UOContent/{SpecialSystems/Items/SupplyBags => Special Systems/Items/Supply Bags}/ScribeBag.cs (100%) rename Projects/UOContent/{SpecialSystems/Items/SupplyBags => Special Systems/Items/Supply Bags}/SmithBag.cs (100%) rename Projects/UOContent/{SpecialSystems/Items/SupplyBags => Special Systems/Items/Supply Bags}/TailorBag.cs (100%) diff --git a/Projects/Server/Items/SecureTradeContainer.cs b/Projects/Server/Items/SecureTradeContainer.cs index d4d5e64ba..2cc4f39c9 100644 --- a/Projects/Server/Items/SecureTradeContainer.cs +++ b/Projects/Server/Items/SecureTradeContainer.cs @@ -41,7 +41,7 @@ namespace Server.Items public override void OnItemAdded(Item item) { - if (!(item is VirtualCheck)) + if (item is not VirtualCheck) { ClearChecks(); } @@ -49,7 +49,7 @@ namespace Server.Items public override void OnItemRemoved(Item item) { - if (!(item is VirtualCheck)) + if (item is not VirtualCheck) { ClearChecks(); } @@ -57,7 +57,7 @@ namespace Server.Items public override void OnSubItemAdded(Item item) { - if (!(item is VirtualCheck)) + if (item is not VirtualCheck) { ClearChecks(); } @@ -65,7 +65,7 @@ namespace Server.Items public override void OnSubItemRemoved(Item item) { - if (!(item is VirtualCheck)) + if (item is not VirtualCheck) { ClearChecks(); } @@ -93,7 +93,7 @@ namespace Server.Items public override bool IsChildVisibleTo(Mobile m, Item child) => child is VirtualCheck - ? AccountGold.Enabled && m.NetState?.NewSecureTrading != true + ? !AccountGold.Enabled || m.NetState?.NewSecureTrading != true : base.IsChildVisibleTo(m, child); public override void Serialize(IGenericWriter writer) diff --git a/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs b/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs index 71511b296..97a8e81a0 100644 --- a/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs @@ -303,6 +303,8 @@ namespace Server.Network ns.Send(writer.Span); } + public static void SendStopMusic(this NetState ns) => ns?.Send(stackalloc byte[] { 0x6D, 0x1F, 0xFF }); + public static void SendScrollMessage(this NetState ns, int type, int tip, string text) { if (ns == null) diff --git a/Projects/Server/Regions/Region.cs b/Projects/Server/Regions/Region.cs index 9d0604b39..47716f75d 100644 --- a/Projects/Server/Regions/Region.cs +++ b/Projects/Server/Regions/Region.cs @@ -76,7 +76,8 @@ namespace Server Paws, SelimsBar, SerpentIsleCombat_U7, - ValoriaShips + ValoriaShips, + NoMusic = 0x1FFF } public class Region : IComparable diff --git a/Projects/UOContent.Tests/Tests/Engines/Chat/ChatPacketTests.cs b/Projects/UOContent.Tests/Tests/Engines/Chat/ChatPacketTests.cs new file mode 100644 index 000000000..4a8561d1b --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Engines/Chat/ChatPacketTests.cs @@ -0,0 +1,25 @@ +using System; +using Server.Engines.Chat; +using Server.Tests; +using Server.Tests.Network; +using Xunit; + +namespace UOContent.Tests +{ + public class ChatPacketTests + { + [Theory] + [InlineData(null, 100, "some param", "some other param")] + [InlineData("ENU", 200, "a third param", "another param")] + public void TestSendChatMessage(string lang, int number, string param1, string param2) + { + var expected = new ChatMessagePacket(lang, number, param1, param2).Compile(); + + using var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendChatMessage(lang, number, param1, param2); + + var result = ns.SendPipe.Reader.TryRead(); + AssertThat.Equal(result.Buffer[0].AsSpan(0), expected); + } + } +} diff --git a/Projects/UOContent/Engines/Chat/Packets.cs b/Projects/UOContent.Tests/Tests/Engines/Chat/Packets.cs similarity index 74% rename from Projects/UOContent/Engines/Chat/Packets.cs rename to Projects/UOContent.Tests/Tests/Engines/Chat/Packets.cs index 175a4f94b..e3ed863d8 100644 --- a/Projects/UOContent/Engines/Chat/Packets.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Chat/Packets.cs @@ -4,7 +4,7 @@ namespace Server.Engines.Chat { public sealed class ChatMessagePacket : Packet { - public ChatMessagePacket(Mobile who, int number, string param1, string param2) : base(0xB2) + public ChatMessagePacket(string lang, int number, string param1, string param2) : base(0xB2) { param1 ??= string.Empty; param2 ??= string.Empty; @@ -13,9 +13,9 @@ namespace Server.Engines.Chat Stream.Write((ushort)(number - 20)); - if (who != null) + if (lang != null) { - Stream.WriteAsciiFixed(who.Language, 4); + Stream.WriteAsciiFixed(lang, 4); } else { diff --git a/Projects/UOContent.Tests/Tests/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePacketTests.cs b/Projects/UOContent.Tests/Tests/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePacketTests.cs new file mode 100644 index 000000000..851ce32a4 --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePacketTests.cs @@ -0,0 +1,27 @@ +using System; +using Server; +using Server.Engines.VeteranRewards; +using Server.Network; +using Server.Tests; +using Server.Tests.Network; +using Xunit; + +namespace UOContent.Tests +{ + public class CharacterStatuePacketTests + { + [Theory] + [InlineData(0x1024u, 1, 100, 200)] + public void TestSendStatueAnimation(uint s, int status, int anim, int frame) + { + Serial serial = s; + var expected = new UpdateStatueAnimation(serial, status, anim, frame).Compile(); + + using var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendStatueAnimation(serial, status, anim, frame); + + var result = ns.SendPipe.Reader.TryRead(); + AssertThat.Equal(result.Buffer[0].AsSpan(0), expected); + } + } +} diff --git a/Projects/UOContent/Engines/VeteranRewards/Character Statue Maker/Packets.cs b/Projects/UOContent.Tests/Tests/Engines/Veteran Rewards/Character Statue Maker/Packets.cs similarity index 77% rename from Projects/UOContent/Engines/VeteranRewards/Character Statue Maker/Packets.cs rename to Projects/UOContent.Tests/Tests/Engines/Veteran Rewards/Character Statue Maker/Packets.cs index f31702064..8cc0b4b50 100644 --- a/Projects/UOContent/Engines/VeteranRewards/Character Statue Maker/Packets.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Veteran Rewards/Character Statue Maker/Packets.cs @@ -2,12 +2,12 @@ namespace Server.Network { public class UpdateStatueAnimation : Packet { - public UpdateStatueAnimation(Mobile m, int status, int animation, int frame) : base(0xBF, 17) + public UpdateStatueAnimation(Serial serial, int status, int animation, int frame) : base(0xBF, 17) { Stream.Write((short)0x11); Stream.Write((short)0x19); Stream.Write((byte)0x5); - Stream.Write(m.Serial); + Stream.Write(serial); Stream.Write((byte)0); Stream.Write((byte)0xFF); Stream.Write((byte)status); diff --git a/Projects/UOContent/Engines/Chat/Chat.cs b/Projects/UOContent/Engines/Chat/Chat.cs index fdef7716b..22452231b 100644 --- a/Projects/UOContent/Engines/Chat/Chat.cs +++ b/Projects/UOContent/Engines/Chat/Chat.cs @@ -1,6 +1,3 @@ -using System; -using Server.Network; - namespace Server.Engines.Chat { public static class ChatSystem @@ -12,36 +9,9 @@ namespace Server.Engines.Chat Enabled = ServerConfiguration.GetOrUpdateSetting("chat.enabled", false); } - public static void Initialize() - { - IncomingPackets.Register(0xB5, 0x40, true, OpenChatWindowRequest); - IncomingPackets.Register(0xB3, 0, true, ChatAction); - } - public static void SendCommandTo(Mobile to, ChatCommand type, string param1 = null, string param2 = null) { - to?.Send(new ChatMessagePacket(null, (int)type + 20, param1, param2)); - } - - public static void OpenChatWindowRequest(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (!Enabled) - { - from.SendMessage("The chat system has been disabled."); - return; - } - - // Newer clients don't send chat username anymore so we are ignoring the rest of this packet. - - // TODO: How does OSI handle incognito/disguise kits? - // TODO: Does OSI still allow duplicate names? - // For now we assume they should use their raw name. - var chatName = from.RawName ?? $"Unknown User {Utility.RandomMinMax(1000000, 9999999)}"; - - SendCommandTo(from, ChatCommand.OpenChatWindow, chatName); - ChatUser.AddChatUser(from, chatName); + to?.NetState.SendChatMessage(null, (int)type + 20, param1, param2); } public static ChatUser SearchForUser(ChatUser from, string name) @@ -55,58 +25,5 @@ namespace Server.Engines.Chat return user; } - - public static void ChatAction(NetState state, CircularBufferReader reader, ref int packetLength) - { - if (!Enabled) - { - return; - } - - try - { - var from = state.Mobile; - var user = ChatUser.GetChatUser(from); - - if (user == null) - { - return; - } - - var lang = reader.ReadAsciiSafe(4); - int actionID = reader.ReadInt16(); - var param = reader.ReadBigUniSafe(); - - var handler = ChatActionHandlers.GetHandler(actionID); - - if (handler == null) - { - state.WriteConsole("Unknown chat action 0x{0:X}: {1}", actionID, param); - return; - } - - var channel = user.CurrentChannel; - - if (handler.RequireConference && channel == null) - { - // You must be in a conference to do this. - // To join a conference, select one from the Conference menu. - user.SendMessage(31); - return; - } - - if (handler.RequireModerator && !user.IsModerator) - { - user.SendMessage(29); // You must have operator status to do this. - return; - } - - handler.Callback(user, channel, param); - } - catch (Exception e) - { - Console.WriteLine(e); - } - } } } diff --git a/Projects/UOContent/Engines/Chat/ChatPackets.cs b/Projects/UOContent/Engines/Chat/ChatPackets.cs new file mode 100644 index 000000000..9f19817fe --- /dev/null +++ b/Projects/UOContent/Engines/Chat/ChatPackets.cs @@ -0,0 +1,127 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ChatPackets.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Buffers; +using Server.Network; + +namespace Server.Engines.Chat +{ + public static class ChatPackets + { + public static void Configure() + { + IncomingPackets.Register(0xB5, 0x40, true, OpenChatWindowRequest); + IncomingPackets.Register(0xB3, 0, true, ChatAction); + } + + public static void OpenChatWindowRequest(NetState state, CircularBufferReader reader, ref int packetLength) + { + var from = state.Mobile; + + if (!ChatSystem.Enabled) + { + from.SendMessage("The chat system has been disabled."); + return; + } + + // Newer clients don't send chat username anymore so we are ignoring the rest of this packet. + + // TODO: How does OSI handle incognito/disguise kits? + // TODO: Does OSI still allow duplicate names? + // For now we assume they should use their raw name. + var chatName = from.RawName ?? $"Unknown User {Utility.RandomMinMax(1000000, 9999999)}"; + + ChatSystem.SendCommandTo(from, ChatCommand.OpenChatWindow, chatName); + ChatUser.AddChatUser(from, chatName); + } + + public static void ChatAction(NetState state, CircularBufferReader reader, ref int packetLength) + { + if (!ChatSystem.Enabled) + { + return; + } + + try + { + var from = state.Mobile; + var user = ChatUser.GetChatUser(from); + + if (user == null) + { + return; + } + + var lang = reader.ReadAsciiSafe(4); + int actionID = reader.ReadInt16(); + var param = reader.ReadBigUniSafe(); + + var handler = ChatActionHandlers.GetHandler(actionID); + + if (handler == null) + { + state.WriteConsole("Unknown chat action 0x{0:X}: {1}", actionID, param); + return; + } + + var channel = user.CurrentChannel; + + if (handler.RequireConference && channel == null) + { + // You must be in a conference to do this. + // To join a conference, select one from the Conference menu. + user.SendMessage(31); + return; + } + + if (handler.RequireModerator && !user.IsModerator) + { + user.SendMessage(29); // You must have operator status to do this. + return; + } + + handler.Callback(user, channel, param); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + public static void SendChatMessage(this NetState ns, string lang, int number, string param1, string param2) + { + if (ns == null) + { + return; + } + + param1 ??= ""; + param2 ??= ""; + + var length = 13 + (param1.Length + param2.Length) * 2; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0xB2); // Packet ID + writer.Write((ushort)length); + writer.Write((ushort)(number - 20)); // Command + + writer.WriteAscii(lang ?? "", 4); + writer.WriteBigUniNull(param1); + writer.WriteBigUniNull(param2); + + ns.Send(writer.Span); + } + } +} diff --git a/Projects/UOContent/Engines/Chat/ChatUser.cs b/Projects/UOContent/Engines/Chat/ChatUser.cs index 28742aeb1..79ae3d91f 100644 --- a/Projects/UOContent/Engines/Chat/ChatUser.cs +++ b/Projects/UOContent/Engines/Chat/ChatUser.cs @@ -52,21 +52,11 @@ namespace Server.Engines.Chat return false; } - public void SendMessage(int number, string param1 = null, string param2 = null) - { - if (Mobile.NetState != null) - { - Mobile.Send(new ChatMessagePacket(Mobile, number, param1, param2)); - } - } + public void SendMessage(int number, string param1 = null, string param2 = null) => + SendMessage(number, Mobile, param1, param2); - public void SendMessage(int number, Mobile from, string param1, string param2) - { - if (Mobile.NetState != null) - { - Mobile.Send(new ChatMessagePacket(from, number, param1, param2)); - } - } + public void SendMessage(int number, Mobile from, string param1 = null, string param2 = null) => + Mobile.NetState.SendChatMessage(from.Language, number, param1, param2); public bool IsIgnored(ChatUser check) => Ignored.Contains(check); diff --git a/Projects/UOContent/Engines/VeteranRewards/Character Statue Maker/CharacterStatue.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs similarity index 85% rename from Projects/UOContent/Engines/VeteranRewards/Character Statue Maker/CharacterStatue.cs rename to Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs index f5f816c68..056cfd7cb 100644 --- a/Projects/UOContent/Engines/VeteranRewards/Character Statue Maker/CharacterStatue.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Server.Accounting; using Server.ContextMenus; using Server.Engines.VeteranRewards; @@ -197,9 +198,10 @@ namespace Server.Mobiles public override bool CanBeDamaged() => false; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void OnRequestedAnimation(Mobile from) { - from.Send(new UpdateStatueAnimation(this, 1, m_Animation, m_Frames)); + from.NetState.SendStatueAnimation(Serial, 1, m_Animation, m_Frames); } public override void OnAosSingleClick(Mobile from) @@ -384,27 +386,24 @@ namespace Server.Mobiles break; } - if (Map != null) + if (Map == null) { - ProcessDelta(); - - Packet p = null; - - var eable = Map.GetClientsInRange(Location); - - foreach (var state in eable) - { - state.Mobile.ProcessDelta(); - - p ??= Packet.Acquire(new UpdateStatueAnimation(this, 1, m_Animation, m_Frames)); - - state.Send(p); - } - - Packet.Release(p); - - eable.Free(); + return; } + + ProcessDelta(); + + var eable = Map.GetClientsInRange(Location); + Span animPacket = stackalloc byte[CharacterStatuePackets.StatueAnimationPacketLength].InitializePacket(); + + foreach (var state in eable) + { + state.Mobile.ProcessDelta(); + CharacterStatuePackets.CreateStatueAnimation(animPacket, Serial, 1, m_Animation, m_Frames); + state.Send(animPacket); + } + + eable.Free(); } private class DemolishEntry : ContextMenuEntry @@ -415,12 +414,10 @@ namespace Server.Mobiles public override void OnClick() { - if (m_Statue.Deleted) + if (!m_Statue.Deleted) { - return; + m_Statue.Demolish(Owner.From); } - - m_Statue.Demolish(Owner.From); } } } @@ -607,71 +604,75 @@ namespace Server.Mobiles return; } - if (m_Maker.IsChildOf(from.Backpack)) + if (!m_Maker.IsChildOf(from.Backpack)) { - SpellHelper.GetSurfaceTop(ref p); - BaseHouse house = null; - var loc = new Point3D(p); + from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. + return; + } - if (targeted is Item item && !item.IsLockedDown && !item.IsSecure && !(item is AddonComponent)) + SpellHelper.GetSurfaceTop(ref p); + BaseHouse house = null; + var loc = new Point3D(p); + + if (targeted is Item item && !item.IsLockedDown && !item.IsSecure && !(item is AddonComponent)) + { + from.SendLocalizedMessage(1076191); // Statues can only be placed in houses. + return; + } + + if (from.IsBodyMod) + { + from.SendLocalizedMessage(1073648); // You may only proceed while in your original state... + return; + } + + var result = CouldFit(loc, map, from, ref house); + + if (result == AddonFitResult.Valid) + { + var statue = new CharacterStatue(from, m_Type); + var plinth = new CharacterStatuePlinth(statue); + + house.Addons.Add(plinth); + + if (m_Maker is IRewardItem rewardItem) { - from.SendLocalizedMessage(1076191); // Statues can only be placed in houses. - return; + statue.IsRewardItem = rewardItem.IsRewardItem; } - if (from.IsBodyMod) - { - from.SendLocalizedMessage(1073648); // You may only proceed while in your original state... - return; - } + statue.Plinth = plinth; + plinth.MoveToWorld(loc, map); + statue.InvalidatePose(); - var result = CouldFit(loc, map, from, ref house); - - if (result == AddonFitResult.Valid) - { - var statue = new CharacterStatue(from, m_Type); - var plinth = new CharacterStatuePlinth(statue); - - house.Addons.Add(plinth); - - if (m_Maker is IRewardItem rewardItem) - { - statue.IsRewardItem = rewardItem.IsRewardItem; - } - - statue.Plinth = plinth; - plinth.MoveToWorld(loc, map); - statue.InvalidatePose(); - - /* + /* * TODO: Previously the maker wasn't deleted until after statue * customization, leading to redeeding issues. Exact OSI behavior * needs looking into. */ - m_Maker.Delete(); - statue.Sculpt(from); + m_Maker.Delete(); + statue.Sculpt(from); - from.CloseGump(); - from.SendGump(new CharacterStatueGump(m_Maker, statue, from)); - } - else if (result == AddonFitResult.Blocked) - { - from.SendLocalizedMessage(500269); // You cannot build that there. - } - else if (result == AddonFitResult.NotInHouse) - { - from.SendLocalizedMessage( - 1076192 - ); // Statues can only be placed in houses where you are the owner or co-owner. - } - else if (result == AddonFitResult.DoorTooClose) - { - from.SendLocalizedMessage(500271); // You cannot build near the door. - } + from.CloseGump(); + from.SendGump(new CharacterStatueGump(m_Maker, statue, from)); + return; } - else + + if (result == AddonFitResult.Blocked) { - from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. + from.SendLocalizedMessage(500269); // You cannot build that there. + return; + } + + if (result == AddonFitResult.NotInHouse) + { + // Statues can only be placed in houses where you are the owner or co-owner. + from.SendLocalizedMessage(1076192); + return; + } + + if (result == AddonFitResult.DoorTooClose) + { + from.SendLocalizedMessage(500271); // You cannot build near the door. } } diff --git a/Projects/UOContent/Engines/VeteranRewards/Character Statue Maker/CharacterStatueMaker.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs similarity index 100% rename from Projects/UOContent/Engines/VeteranRewards/Character Statue Maker/CharacterStatueMaker.cs rename to Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs diff --git a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePackets.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePackets.cs new file mode 100644 index 000000000..b7a6a59e3 --- /dev/null +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePackets.cs @@ -0,0 +1,60 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: CharacterStatuePackets.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Buffers; +using Server.Network; + +namespace Server.Engines.VeteranRewards +{ + public static class CharacterStatuePackets + { + public const int StatueAnimationPacketLength = 17; + + public static void CreateStatueAnimation(Span buffer, Serial serial, int status, int anim, int frame) + { + if (buffer[0] != 0) + { + return; + } + + var writer = new SpanWriter(buffer); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)17); + writer.Write((short)0x19); + writer.Write((byte)0x5); + writer.Write(serial); + writer.Write((byte)0); + writer.Write((byte)0xFF); + writer.Write((byte)status); + writer.Write((byte)0); + writer.Write((byte)anim); + writer.Write((byte)0); + writer.Write((byte)frame); + } + + public static void SendStatueAnimation(this NetState ns, Serial serial, int status, int anim, int frame) + { + if (ns == null) + { + return; + } + + Span buffer = stackalloc byte[StatueAnimationPacketLength].InitializePacket(); + CreateStatueAnimation(buffer, serial, status, anim, frame); + ns.Send(buffer); + } + } +} diff --git a/Projects/UOContent/Engines/VeteranRewards/Character Statue Maker/CharacterStatuePlinth.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePlinth.cs similarity index 100% rename from Projects/UOContent/Engines/VeteranRewards/Character Statue Maker/CharacterStatuePlinth.cs rename to Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePlinth.cs diff --git a/Projects/UOContent/Engines/VeteranRewards/Character Statue Maker/Gumps/CharacterStatueGump.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/Gumps/CharacterStatueGump.cs similarity index 100% rename from Projects/UOContent/Engines/VeteranRewards/Character Statue Maker/Gumps/CharacterStatueGump.cs rename to Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/Gumps/CharacterStatueGump.cs diff --git a/Projects/UOContent/Engines/VeteranRewards/RewardCategory.cs b/Projects/UOContent/Engines/Veteran Rewards/RewardCategory.cs similarity index 100% rename from Projects/UOContent/Engines/VeteranRewards/RewardCategory.cs rename to Projects/UOContent/Engines/Veteran Rewards/RewardCategory.cs diff --git a/Projects/UOContent/Engines/VeteranRewards/RewardChoiceGump.cs b/Projects/UOContent/Engines/Veteran Rewards/RewardChoiceGump.cs similarity index 100% rename from Projects/UOContent/Engines/VeteranRewards/RewardChoiceGump.cs rename to Projects/UOContent/Engines/Veteran Rewards/RewardChoiceGump.cs diff --git a/Projects/UOContent/Engines/VeteranRewards/RewardConfirmGump.cs b/Projects/UOContent/Engines/Veteran Rewards/RewardConfirmGump.cs similarity index 100% rename from Projects/UOContent/Engines/VeteranRewards/RewardConfirmGump.cs rename to Projects/UOContent/Engines/Veteran Rewards/RewardConfirmGump.cs diff --git a/Projects/UOContent/Engines/VeteranRewards/RewardDemolitionGump.cs b/Projects/UOContent/Engines/Veteran Rewards/RewardDemolitionGump.cs similarity index 100% rename from Projects/UOContent/Engines/VeteranRewards/RewardDemolitionGump.cs rename to Projects/UOContent/Engines/Veteran Rewards/RewardDemolitionGump.cs diff --git a/Projects/UOContent/Engines/VeteranRewards/RewardEntry.cs b/Projects/UOContent/Engines/Veteran Rewards/RewardEntry.cs similarity index 100% rename from Projects/UOContent/Engines/VeteranRewards/RewardEntry.cs rename to Projects/UOContent/Engines/Veteran Rewards/RewardEntry.cs diff --git a/Projects/UOContent/Engines/VeteranRewards/RewardList.cs b/Projects/UOContent/Engines/Veteran Rewards/RewardList.cs similarity index 100% rename from Projects/UOContent/Engines/VeteranRewards/RewardList.cs rename to Projects/UOContent/Engines/Veteran Rewards/RewardList.cs diff --git a/Projects/UOContent/Engines/VeteranRewards/RewardNoticeGump.cs b/Projects/UOContent/Engines/Veteran Rewards/RewardNoticeGump.cs similarity index 100% rename from Projects/UOContent/Engines/VeteranRewards/RewardNoticeGump.cs rename to Projects/UOContent/Engines/Veteran Rewards/RewardNoticeGump.cs diff --git a/Projects/UOContent/Engines/VeteranRewards/RewardOptionGump.cs b/Projects/UOContent/Engines/Veteran Rewards/RewardOptionGump.cs similarity index 100% rename from Projects/UOContent/Engines/VeteranRewards/RewardOptionGump.cs rename to Projects/UOContent/Engines/Veteran Rewards/RewardOptionGump.cs diff --git a/Projects/UOContent/Engines/VeteranRewards/RewardSystem.cs b/Projects/UOContent/Engines/Veteran Rewards/RewardSystem.cs similarity index 100% rename from Projects/UOContent/Engines/VeteranRewards/RewardSystem.cs rename to Projects/UOContent/Engines/Veteran Rewards/RewardSystem.cs diff --git a/Projects/UOContent/Items/Bulletin Boards/BulletinBoard.cs b/Projects/UOContent/Items/Bulletin Boards/BulletinBoard.cs index 07a85955e..cfc0aec69 100644 --- a/Projects/UOContent/Items/Bulletin Boards/BulletinBoard.cs +++ b/Projects/UOContent/Items/Bulletin Boards/BulletinBoard.cs @@ -1,9 +1,38 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Server.Network; namespace Server.Items { + public static class BulletinBoardSystem + { + // Threads will be removed six hours after the last post was made + public static TimeSpan ThreadDeletionTime { get; private set; } + + // A player may only create a thread once every two minutes + public static TimeSpan ThreadCreateTime { get; private set; } + + // A player may only reply once every thirty seconds + public static TimeSpan ThreadReplyTime { get; private set; } + + public static void Configure() + { + ThreadCreateTime = ServerConfiguration.GetOrUpdateSetting("bulletinboards.creationTimeDelay", TimeSpan.FromMinutes(2.0)); + ThreadDeletionTime = ServerConfiguration.GetOrUpdateSetting("bulletinboards.expireDuration", TimeSpan.FromHours(6.0)); + ThreadReplyTime = ServerConfiguration.GetOrUpdateSetting("bulletinboards.replyDelay", TimeSpan.FromSeconds(30.0)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool CheckCreateTime(DateTime time) => time + ThreadCreateTime < DateTime.UtcNow; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + + public static bool CheckDeletionTime(DateTime time) => time + ThreadDeletionTime < DateTime.UtcNow; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool CheckReplyTime(DateTime time) => time + ThreadReplyTime < DateTime.UtcNow; + } + [Flippable(0x1E5E, 0x1E5F)] public class BulletinBoard : BaseBulletinBoard { @@ -33,15 +62,6 @@ namespace Server.Items public abstract class BaseBulletinBoard : Item { - // Threads will be removed six hours after the last post was made - private static readonly TimeSpan ThreadDeletionTime = TimeSpan.FromHours(6.0); - - // A player may only create a thread once every two minutes - private static readonly TimeSpan ThreadCreateTime = TimeSpan.FromMinutes(2.0); - - // A player may only reply once every thirty seconds - private static readonly TimeSpan ThreadReplyTime = TimeSpan.FromSeconds(30.0); - public BaseBulletinBoard(int itemID) : base(itemID) { BoardName = "bulletin board"; @@ -55,27 +75,6 @@ namespace Server.Items [CommandProperty(AccessLevel.GameMaster)] public string BoardName { get; set; } - public static bool CheckTime(DateTime time, TimeSpan range) => time + range < DateTime.UtcNow; - - public static string FormatTS(TimeSpan ts) - { - var totalSeconds = (int)ts.TotalSeconds; - var seconds = totalSeconds % 60; - var minutes = totalSeconds / 60; - - if (minutes != 0 && seconds != 0) - { - return $"{minutes} minute{(minutes == 1 ? "" : "s")} and {seconds} second{(seconds == 1 ? "" : "s")}"; - } - - if (minutes != 0) - { - return $"{minutes} minute{(minutes == 1 ? "" : "s")}"; - } - - return $"{seconds} second{(seconds == 1 ? "" : "s")}"; - } - public virtual void Cleanup() { var items = Items; @@ -92,7 +91,7 @@ namespace Server.Items continue; } - if (msg.Thread == null && CheckTime(msg.LastPostTime, ThreadDeletionTime)) + if (msg.Thread == null && BulletinBoardSystem.CheckDeletionTime(msg.LastPostTime)) { msg.Delete(); RecurseDelete(msg); // A root-level thread has expired @@ -211,128 +210,5 @@ namespace Server.Items } } } - - public static void Initialize() - { - IncomingPackets.Register(0x71, 0, true, BBClientRequest); - } - - public static void BBClientRequest(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - int packetID = reader.ReadByte(); - - if (World.FindItem(reader.ReadUInt32()) is not BaseBulletinBoard board || !board.CheckRange(from)) - { - return; - } - - switch (packetID) - { - case 3: - BBRequestContent(from, board, reader); - break; - case 4: - BBRequestHeader(from, board, reader); - break; - case 5: - BBPostMessage(from, board, reader); - break; - case 6: - BBRemoveMessage(from, board, reader); - break; - } - } - - public static void BBRequestContent(Mobile from, BaseBulletinBoard board, CircularBufferReader reader) - { - if (World.FindItem(reader.ReadUInt32()) is not BulletinMessage msg || msg.Parent != board) - { - return; - } - - from.NetState.SendBBMessage(board, msg, true); - } - - public static void BBRequestHeader(Mobile from, BaseBulletinBoard board, CircularBufferReader reader) - { - if (World.FindItem(reader.ReadUInt32()) is not BulletinMessage msg || msg.Parent != board) - { - return; - } - - from.NetState.SendBBMessage(board, msg); - } - - public static void BBPostMessage(Mobile from, BaseBulletinBoard board, CircularBufferReader reader) - { - var thread = World.FindItem(reader.ReadUInt32()) as BulletinMessage; - - if (thread != null && thread.Parent != board) - { - thread = null; - } - - var breakout = 0; - - while (thread?.Thread != null && breakout++ < 10) - { - thread = thread.Thread; - } - - var lastPostTime = DateTime.MinValue; - - if (board.GetLastPostTime(from, thread == null, ref lastPostTime) && - !CheckTime(lastPostTime, thread == null ? ThreadCreateTime : ThreadReplyTime)) - { - if (thread == null) - { - from.SendMessage("You must wait {0} before creating a new thread.", FormatTS(ThreadCreateTime)); - } - else - { - from.SendMessage("You must wait {0} before replying to another thread.", FormatTS(ThreadReplyTime)); - } - - return; - } - - var subject = reader.ReadUTF8Safe(reader.ReadByte()); - - if (subject.Length == 0) - { - return; - } - - var lines = new string[reader.ReadByte()]; - - if (lines.Length == 0) - { - return; - } - - for (var i = 0; i < lines.Length; ++i) - { - lines[i] = reader.ReadUTF8Safe(reader.ReadByte()); - } - - board.PostMessage(from, thread, subject, lines); - } - - public static void BBRemoveMessage(Mobile from, BaseBulletinBoard board, CircularBufferReader reader) - { - if (World.FindItem(reader.ReadUInt32()) is not BulletinMessage msg || msg.Parent != board) - { - return; - } - - if (from.AccessLevel < AccessLevel.GameMaster && msg.Poster != from) - { - return; - } - - msg.Delete(); - } } } diff --git a/Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs b/Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs index 58f2f8ca5..206e70336 100644 --- a/Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs +++ b/Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs @@ -24,6 +24,150 @@ namespace Server.Network { public static class BulletinBoardPackets { + public static void Configure() + { + IncomingPackets.Register(0x71, 0, true, BBClientRequest); + } + + public static string FormatTS(TimeSpan ts) + { + var totalSeconds = (int)ts.TotalSeconds; + var seconds = totalSeconds % 60; + var minutes = totalSeconds / 60; + + if (minutes != 0 && seconds != 0) + { + return $"{minutes} minute{(minutes == 1 ? "" : "s")} and {seconds} second{(seconds == 1 ? "" : "s")}"; + } + + if (minutes != 0) + { + return $"{minutes} minute{(minutes == 1 ? "" : "s")}"; + } + + return $"{seconds} second{(seconds == 1 ? "" : "s")}"; + } + + public static void BBClientRequest(NetState state, CircularBufferReader reader, ref int packetLength) + { + var from = state.Mobile; + + int packetID = reader.ReadByte(); + + if (World.FindItem(reader.ReadUInt32()) is not BaseBulletinBoard board || !board.CheckRange(from)) + { + return; + } + + switch (packetID) + { + case 3: + BBRequestContent(from, board, reader); + break; + case 4: + BBRequestHeader(from, board, reader); + break; + case 5: + BBPostMessage(from, board, reader); + break; + case 6: + BBRemoveMessage(from, board, reader); + break; + } + } + + public static void BBRequestContent(Mobile from, BaseBulletinBoard board, CircularBufferReader reader) + { + if (World.FindItem(reader.ReadUInt32()) is not BulletinMessage msg || msg.Parent != board) + { + return; + } + + from.NetState.SendBBMessage(board, msg, true); + } + + public static void BBRequestHeader(Mobile from, BaseBulletinBoard board, CircularBufferReader reader) + { + if (World.FindItem(reader.ReadUInt32()) is not BulletinMessage msg || msg.Parent != board) + { + return; + } + + from.NetState.SendBBMessage(board, msg); + } + + public static void BBPostMessage(Mobile from, BaseBulletinBoard board, CircularBufferReader reader) + { + var thread = World.FindItem(reader.ReadUInt32()) as BulletinMessage; + + if (thread != null && thread.Parent != board) + { + thread = null; + } + + var breakout = 0; + + while (thread?.Thread != null && breakout++ < 10) + { + thread = thread.Thread; + } + + var lastPostTime = DateTime.MinValue; + + if (board.GetLastPostTime(from, thread == null, ref lastPostTime)) + { + if (thread == null) + { + if (!BulletinBoardSystem.CheckCreateTime(lastPostTime)) + { + from.SendMessage($"You must wait {FormatTS(BulletinBoardSystem.ThreadCreateTime)} before creating a new thread."); + return; + } + } + else if (!BulletinBoardSystem.CheckReplyTime(lastPostTime)) + { + from.SendMessage($"You must wait {FormatTS(BulletinBoardSystem.ThreadReplyTime)} before replying to another thread."); + return; + } + } + + var subject = reader.ReadUTF8Safe(reader.ReadByte()); + + if (subject.Length == 0) + { + return; + } + + var lines = new string[reader.ReadByte()]; + + if (lines.Length == 0) + { + return; + } + + for (var i = 0; i < lines.Length; ++i) + { + lines[i] = reader.ReadUTF8Safe(reader.ReadByte()); + } + + board.PostMessage(from, thread, subject, lines); + } + + public static void BBRemoveMessage(Mobile from, BaseBulletinBoard board, CircularBufferReader reader) + { + if (World.FindItem(reader.ReadUInt32()) is not BulletinMessage msg || msg.Parent != board) + { + return; + } + + if (from.AccessLevel < AccessLevel.GameMaster && msg.Poster != from) + { + return; + } + + msg.Delete(); + } + public static void SendBBDisplayBoard(this NetState ns, BaseBulletinBoard board) { if (ns == null) diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongPacketHandlers.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongPacketHandlers.cs deleted file mode 100644 index 23c735aa5..000000000 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongPacketHandlers.cs +++ /dev/null @@ -1,268 +0,0 @@ -using Server.Network; - -namespace Server.Engines.Mahjong -{ - public delegate void OnMahjongPacketReceive(MahjongGame game, NetState state, CircularBufferReader reader); - - public sealed class MahjongPacketHandlers - { - private static readonly OnMahjongPacketReceive[] m_SubCommandDelegates = new OnMahjongPacketReceive[0x100]; - - public static void RegisterSubCommand(int subCmd, OnMahjongPacketReceive onReceive) - { - m_SubCommandDelegates[subCmd] = onReceive; - } - - public static OnMahjongPacketReceive GetSubCommandDelegate(int cmd) - { - if (cmd >= 0 && cmd < 0x100) - { - return m_SubCommandDelegates[cmd]; - } - - return null; - } - - public static void Initialize() - { - IncomingPackets.Register(0xDA, 0, true, OnPacket); - - RegisterSubCommand(0x6, ExitGame); - RegisterSubCommand(0xA, GivePoints); - RegisterSubCommand(0xB, RollDice); - RegisterSubCommand(0xC, BuildWalls); - RegisterSubCommand(0xD, ResetScores); - RegisterSubCommand(0xF, AssignDealer); - RegisterSubCommand(0x10, OpenSeat); - RegisterSubCommand(0x11, ChangeOption); - RegisterSubCommand(0x15, MoveWallBreakIndicator); - RegisterSubCommand(0x16, TogglePublicHand); - RegisterSubCommand(0x17, MoveTile); - RegisterSubCommand(0x18, MoveDealerIndicator); - } - - public static void OnPacket(NetState state, CircularBufferReader reader, ref int packetLength) - { - var game = World.FindItem(reader.ReadUInt32()) as MahjongGame; - - game?.Players.CheckPlayers(); - - reader.ReadByte(); - - int cmd = reader.ReadByte(); - - var onReceive = GetSubCommandDelegate(cmd); - - if (onReceive != null) - { - onReceive(game, state, reader); - } - else - { - reader.Trace(state); - } - } - - private static MahjongPieceDirection GetDirection(int value) - { - return value switch - { - 0 => MahjongPieceDirection.Up, - 1 => MahjongPieceDirection.Left, - 2 => MahjongPieceDirection.Down, - _ => MahjongPieceDirection.Right - }; - } - - private static MahjongWind GetWind(int value) - { - return value switch - { - 0 => MahjongWind.North, - 1 => MahjongWind.East, - 2 => MahjongWind.South, - _ => MahjongWind.West - }; - } - - public static void ExitGame(MahjongGame game, NetState state, CircularBufferReader reader) - { - if (game == null) - { - return; - } - - var from = state.Mobile; - - game.Players.LeaveGame(from); - } - - public static void GivePoints(MahjongGame game, NetState state, CircularBufferReader reader) - { - if (game?.Players.IsInGamePlayer(state.Mobile) != true) - { - return; - } - - int to = reader.ReadByte(); - var amount = reader.ReadInt32(); - - game.Players.TransferScore(state.Mobile, to, amount); - } - - public static void RollDice(MahjongGame game, NetState state, CircularBufferReader reader) - { - if (game?.Players.IsInGamePlayer(state.Mobile) != true) - { - return; - } - - game.Dices.RollDices(state.Mobile); - } - - public static void BuildWalls(MahjongGame game, NetState state, CircularBufferReader reader) - { - if (game?.Players.IsInGameDealer(state.Mobile) != true) - { - return; - } - - game.ResetWalls(state.Mobile); - } - - public static void ResetScores(MahjongGame game, NetState state, CircularBufferReader reader) - { - if (game?.Players.IsInGameDealer(state.Mobile) != true) - { - return; - } - - game.Players.ResetScores(MahjongGame.BaseScore); - } - - public static void AssignDealer(MahjongGame game, NetState state, CircularBufferReader reader) - { - if (game?.Players.IsInGameDealer(state.Mobile) != true) - { - return; - } - - int position = reader.ReadByte(); - - game.Players.AssignDealer(position); - } - - public static void OpenSeat(MahjongGame game, NetState state, CircularBufferReader reader) - { - if (game?.Players.IsInGameDealer(state.Mobile) != true) - { - return; - } - - int position = reader.ReadByte(); - - if (game.Players.GetPlayer(position) == state.Mobile) - { - return; - } - - game.Players.OpenSeat(position); - } - - public static void ChangeOption(MahjongGame game, NetState state, CircularBufferReader reader) - { - if (game?.Players.IsInGameDealer(state.Mobile) != true) - { - return; - } - - reader.ReadInt16(); - reader.ReadByte(); - - int options = reader.ReadByte(); - - game.ShowScores = (options & 0x1) != 0; - game.SpectatorVision = (options & 0x2) != 0; - } - - public static void MoveWallBreakIndicator(MahjongGame game, NetState state, CircularBufferReader reader) - { - if (game?.Players.IsInGameDealer(state.Mobile) != true) - { - return; - } - - int y = reader.ReadInt16(); - int x = reader.ReadInt16(); - - game.WallBreakIndicator.Move(new Point2D(x, y)); - } - - public static void TogglePublicHand(MahjongGame game, NetState state, CircularBufferReader reader) - { - if (game?.Players.IsInGamePlayer(state.Mobile) != true) - { - return; - } - - reader.ReadInt16(); - reader.ReadByte(); - - var publicHand = reader.ReadBoolean(); - - game.Players.SetPublic(game.Players.GetPlayerIndex(state.Mobile), publicHand); - } - - public static void MoveTile(MahjongGame game, NetState state, CircularBufferReader reader) - { - if (game?.Players.IsInGamePlayer(state.Mobile) != true) - { - return; - } - - int number = reader.ReadByte(); - - if (number < 0 || number >= game.Tiles.Length) - { - return; - } - - reader.ReadByte(); // Current direction - - var direction = GetDirection(reader.ReadByte()); - - reader.ReadByte(); - - var flip = reader.ReadBoolean(); - - reader.ReadInt16(); // Current Y - reader.ReadInt16(); // Current X - - reader.ReadByte(); - - int y = reader.ReadInt16(); - int x = reader.ReadInt16(); - - reader.ReadByte(); - - game.Tiles[number].Move(new Point2D(x, y), direction, flip, game.Players.GetPlayerIndex(state.Mobile)); - } - - public static void MoveDealerIndicator(MahjongGame game, NetState state, CircularBufferReader reader) - { - if (game?.Players.IsInGameDealer(state.Mobile) != true) - { - return; - } - - var direction = GetDirection(reader.ReadByte()); - - var wind = GetWind(reader.ReadByte()); - - int y = reader.ReadInt16(); - int x = reader.ReadInt16(); - - game.DealerIndicator.Move(new Point2D(x, y), direction, wind); - } - } -} diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs index 98b18923f..770706c53 100644 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs +++ b/Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs @@ -20,10 +20,270 @@ using Server.Network; namespace Server.Engines.Mahjong { + public delegate void OnMahjongPacketReceive(MahjongGame game, NetState state, CircularBufferReader reader); + public static class MahjongPackets { public const int MahjongGeneralInfoPacketLength = 25; public const int MahjongRelievePacketLength = 9; + private static readonly OnMahjongPacketReceive[] m_SubCommandDelegates = new OnMahjongPacketReceive[0x100]; + + public static void RegisterSubCommand(int subCmd, OnMahjongPacketReceive onReceive) + { + m_SubCommandDelegates[subCmd] = onReceive; + } + + public static OnMahjongPacketReceive GetSubCommandDelegate(int cmd) + { + if (cmd >= 0 && cmd < 0x100) + { + return m_SubCommandDelegates[cmd]; + } + + return null; + } + + public static void Configure() + { + IncomingPackets.Register(0xDA, 0, true, OnPacket); + + RegisterSubCommand(0x6, ExitGame); + RegisterSubCommand(0xA, GivePoints); + RegisterSubCommand(0xB, RollDice); + RegisterSubCommand(0xC, BuildWalls); + RegisterSubCommand(0xD, ResetScores); + RegisterSubCommand(0xF, AssignDealer); + RegisterSubCommand(0x10, OpenSeat); + RegisterSubCommand(0x11, ChangeOption); + RegisterSubCommand(0x15, MoveWallBreakIndicator); + RegisterSubCommand(0x16, TogglePublicHand); + RegisterSubCommand(0x17, MoveTile); + RegisterSubCommand(0x18, MoveDealerIndicator); + } + + public static void OnPacket(NetState state, CircularBufferReader reader, ref int packetLength) + { + var game = World.FindItem(reader.ReadUInt32()) as MahjongGame; + + game?.Players.CheckPlayers(); + + reader.ReadByte(); + + int cmd = reader.ReadByte(); + + var onReceive = GetSubCommandDelegate(cmd); + + if (onReceive != null) + { + onReceive(game, state, reader); + } + else + { + reader.Trace(state); + } + } + + private static MahjongPieceDirection GetDirection(int value) + { + return value switch + { + 0 => MahjongPieceDirection.Up, + 1 => MahjongPieceDirection.Left, + 2 => MahjongPieceDirection.Down, + _ => MahjongPieceDirection.Right + }; + } + + private static MahjongWind GetWind(int value) + { + return value switch + { + 0 => MahjongWind.North, + 1 => MahjongWind.East, + 2 => MahjongWind.South, + _ => MahjongWind.West + }; + } + + public static void ExitGame(MahjongGame game, NetState state, CircularBufferReader reader) + { + if (game == null) + { + return; + } + + var from = state.Mobile; + + game.Players.LeaveGame(from); + } + + public static void GivePoints(MahjongGame game, NetState state, CircularBufferReader reader) + { + if (game?.Players.IsInGamePlayer(state.Mobile) != true) + { + return; + } + + int to = reader.ReadByte(); + var amount = reader.ReadInt32(); + + game.Players.TransferScore(state.Mobile, to, amount); + } + + public static void RollDice(MahjongGame game, NetState state, CircularBufferReader reader) + { + if (game?.Players.IsInGamePlayer(state.Mobile) != true) + { + return; + } + + game.Dices.RollDices(state.Mobile); + } + + public static void BuildWalls(MahjongGame game, NetState state, CircularBufferReader reader) + { + if (game?.Players.IsInGameDealer(state.Mobile) != true) + { + return; + } + + game.ResetWalls(state.Mobile); + } + + public static void ResetScores(MahjongGame game, NetState state, CircularBufferReader reader) + { + if (game?.Players.IsInGameDealer(state.Mobile) != true) + { + return; + } + + game.Players.ResetScores(MahjongGame.BaseScore); + } + + public static void AssignDealer(MahjongGame game, NetState state, CircularBufferReader reader) + { + if (game?.Players.IsInGameDealer(state.Mobile) != true) + { + return; + } + + int position = reader.ReadByte(); + + game.Players.AssignDealer(position); + } + + public static void OpenSeat(MahjongGame game, NetState state, CircularBufferReader reader) + { + if (game?.Players.IsInGameDealer(state.Mobile) != true) + { + return; + } + + int position = reader.ReadByte(); + + if (game.Players.GetPlayer(position) == state.Mobile) + { + return; + } + + game.Players.OpenSeat(position); + } + + public static void ChangeOption(MahjongGame game, NetState state, CircularBufferReader reader) + { + if (game?.Players.IsInGameDealer(state.Mobile) != true) + { + return; + } + + reader.ReadInt16(); + reader.ReadByte(); + + int options = reader.ReadByte(); + + game.ShowScores = (options & 0x1) != 0; + game.SpectatorVision = (options & 0x2) != 0; + } + + public static void MoveWallBreakIndicator(MahjongGame game, NetState state, CircularBufferReader reader) + { + if (game?.Players.IsInGameDealer(state.Mobile) != true) + { + return; + } + + int y = reader.ReadInt16(); + int x = reader.ReadInt16(); + + game.WallBreakIndicator.Move(new Point2D(x, y)); + } + + public static void TogglePublicHand(MahjongGame game, NetState state, CircularBufferReader reader) + { + if (game?.Players.IsInGamePlayer(state.Mobile) != true) + { + return; + } + + reader.ReadInt16(); + reader.ReadByte(); + + var publicHand = reader.ReadBoolean(); + + game.Players.SetPublic(game.Players.GetPlayerIndex(state.Mobile), publicHand); + } + + public static void MoveTile(MahjongGame game, NetState state, CircularBufferReader reader) + { + if (game?.Players.IsInGamePlayer(state.Mobile) != true) + { + return; + } + + int number = reader.ReadByte(); + + if (number < 0 || number >= game.Tiles.Length) + { + return; + } + + reader.ReadByte(); // Current direction + + var direction = GetDirection(reader.ReadByte()); + + reader.ReadByte(); + + var flip = reader.ReadBoolean(); + + reader.ReadInt16(); // Current Y + reader.ReadInt16(); // Current X + + reader.ReadByte(); + + int y = reader.ReadInt16(); + int x = reader.ReadInt16(); + + reader.ReadByte(); + + game.Tiles[number].Move(new Point2D(x, y), direction, flip, game.Players.GetPlayerIndex(state.Mobile)); + } + + public static void MoveDealerIndicator(MahjongGame game, NetState state, CircularBufferReader reader) + { + if (game?.Players.IsInGameDealer(state.Mobile) != true) + { + return; + } + + var direction = GetDirection(reader.ReadByte()); + + var wind = GetWind(reader.ReadByte()); + + int y = reader.ReadInt16(); + int x = reader.ReadInt16(); + + game.DealerIndicator.Move(new Point2D(x, y), direction, wind); + } public static void SendMahjongJoinGame(this NetState ns, Serial game) { diff --git a/Projects/UOContent/Items/Maps/MapItem.cs b/Projects/UOContent/Items/Maps/MapItem.cs index c17cdb714..1037cb3a4 100644 --- a/Projects/UOContent/Items/Maps/MapItem.cs +++ b/Projects/UOContent/Items/Maps/MapItem.cs @@ -320,48 +320,5 @@ namespace Server.Items } } } - - public static void Initialize() - { - IncomingPackets.Register(0x56, 11, true, OnMapCommand); - } - - private static void OnMapCommand(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (!(World.FindItem(reader.ReadUInt32()) is MapItem map)) - { - return; - } - - int command = reader.ReadByte(); - int number = reader.ReadByte(); - - int x = reader.ReadInt16(); - int y = reader.ReadInt16(); - - switch (command) - { - case 1: - map.OnAddPin(from, x, y); - break; - case 2: - map.OnInsertPin(from, number, x, y); - break; - case 3: - map.OnChangePin(from, number, x, y); - break; - case 4: - map.OnRemovePin(from, number); - break; - case 5: - map.OnClearPins(from); - break; - case 6: - map.OnToggleEditable(from); - break; - } - } } } diff --git a/Projects/UOContent/Items/Maps/MapItemPackets.cs b/Projects/UOContent/Items/Maps/MapItemPackets.cs index 305f6ccbe..cce464580 100644 --- a/Projects/UOContent/Items/Maps/MapItemPackets.cs +++ b/Projects/UOContent/Items/Maps/MapItemPackets.cs @@ -20,6 +20,49 @@ namespace Server.Network { public static class MapItemPackets { + public static void Configure() + { + IncomingPackets.Register(0x56, 11, true, OnMapCommand); + } + + private static void OnMapCommand(NetState state, CircularBufferReader reader, ref int packetLength) + { + var from = state.Mobile; + + if (!(World.FindItem(reader.ReadUInt32()) is MapItem map)) + { + return; + } + + int command = reader.ReadByte(); + int number = reader.ReadByte(); + + int x = reader.ReadInt16(); + int y = reader.ReadInt16(); + + switch (command) + { + case 1: + map.OnAddPin(from, x, y); + break; + case 2: + map.OnInsertPin(from, number, x, y); + break; + case 3: + map.OnChangePin(from, number, x, y); + break; + case 4: + map.OnRemovePin(from, number); + break; + case 5: + map.OnClearPins(from); + break; + case 6: + map.OnToggleEditable(from); + break; + } + } + public static void SendMapDetails(this NetState ns, MapItem map) { if (ns == null) diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs index b14929273..2155fe21c 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs @@ -7,16 +7,6 @@ using Server.Network; namespace Server.Items { - public sealed class StopMusic : Packet - { - public static readonly Packet Instance = SetStatic(new StopMusic()); - - public StopMusic() : base(0x6D, 3) - { - Stream.Write((short)0x1FFF); - } - } - [Flippable(0x2AF9, 0x2AFD)] public class DawnsMusicBox : Item, ISecurable { @@ -244,7 +234,7 @@ namespace Server.Items m_Timer.Stop(); } - m.Send(StopMusic.Instance); + m.NetState.SendStopMusic(); if (m_Count > 0) { diff --git a/Projects/UOContent/Misc/HardwareInfo.cs b/Projects/UOContent/Misc/HardwareInfo.cs index 62ae94148..191cd3321 100644 --- a/Projects/UOContent/Misc/HardwareInfo.cs +++ b/Projects/UOContent/Misc/HardwareInfo.cs @@ -87,7 +87,7 @@ namespace Server [CommandProperty(AccessLevel.GameMaster)] public DateTime TimeReceived { get; private set; } - public static void Initialize() + public static void Configure() { IncomingPackets.Register(0xD9, 0x10C, false, OnReceive); diff --git a/Projects/UOContent/SpecialSystems/Engines/GiftGiving.cs b/Projects/UOContent/Special Systems/Engines/GiftGiving.cs similarity index 100% rename from Projects/UOContent/SpecialSystems/Engines/GiftGiving.cs rename to Projects/UOContent/Special Systems/Engines/GiftGiving.cs diff --git a/Projects/UOContent/SpecialSystems/Engines/ItemFixes.cs b/Projects/UOContent/Special Systems/Engines/ItemFixes.cs similarity index 100% rename from Projects/UOContent/SpecialSystems/Engines/ItemFixes.cs rename to Projects/UOContent/Special Systems/Engines/ItemFixes.cs diff --git a/Projects/UOContent/SpecialSystems/Engines/PreventInaccess.cs b/Projects/UOContent/Special Systems/Engines/PreventInaccess.cs similarity index 100% rename from Projects/UOContent/SpecialSystems/Engines/PreventInaccess.cs rename to Projects/UOContent/Special Systems/Engines/PreventInaccess.cs diff --git a/Projects/UOContent/SpecialSystems/Engines/TestCenter.cs b/Projects/UOContent/Special Systems/Engines/TestCenter.cs similarity index 100% rename from Projects/UOContent/SpecialSystems/Engines/TestCenter.cs rename to Projects/UOContent/Special Systems/Engines/TestCenter.cs diff --git a/Projects/UOContent/SpecialSystems/Items/Resurrection/ResGate.cs b/Projects/UOContent/Special Systems/Items/Resurrection/ResGate.cs similarity index 100% rename from Projects/UOContent/SpecialSystems/Items/Resurrection/ResGate.cs rename to Projects/UOContent/Special Systems/Items/Resurrection/ResGate.cs diff --git a/Projects/UOContent/SpecialSystems/Items/Stones/AlchemyStone.cs b/Projects/UOContent/Special Systems/Items/Stones/AlchemyStone.cs similarity index 100% rename from Projects/UOContent/SpecialSystems/Items/Stones/AlchemyStone.cs rename to Projects/UOContent/Special Systems/Items/Stones/AlchemyStone.cs diff --git a/Projects/UOContent/SpecialSystems/Items/Stones/GamblingStone.cs b/Projects/UOContent/Special Systems/Items/Stones/GamblingStone.cs similarity index 100% rename from Projects/UOContent/SpecialSystems/Items/Stones/GamblingStone.cs rename to Projects/UOContent/Special Systems/Items/Stones/GamblingStone.cs diff --git a/Projects/UOContent/SpecialSystems/Items/Stones/IngotStone.cs b/Projects/UOContent/Special Systems/Items/Stones/IngotStone.cs similarity index 100% rename from Projects/UOContent/SpecialSystems/Items/Stones/IngotStone.cs rename to Projects/UOContent/Special Systems/Items/Stones/IngotStone.cs diff --git a/Projects/UOContent/SpecialSystems/Items/Stones/RegStone.cs b/Projects/UOContent/Special Systems/Items/Stones/RegStone.cs similarity index 100% rename from Projects/UOContent/SpecialSystems/Items/Stones/RegStone.cs rename to Projects/UOContent/Special Systems/Items/Stones/RegStone.cs diff --git a/Projects/UOContent/SpecialSystems/Items/Stones/ScribeStone.cs b/Projects/UOContent/Special Systems/Items/Stones/ScribeStone.cs similarity index 100% rename from Projects/UOContent/SpecialSystems/Items/Stones/ScribeStone.cs rename to Projects/UOContent/Special Systems/Items/Stones/ScribeStone.cs diff --git a/Projects/UOContent/SpecialSystems/Items/Stones/SmithStone.cs b/Projects/UOContent/Special Systems/Items/Stones/SmithStone.cs similarity index 100% rename from Projects/UOContent/SpecialSystems/Items/Stones/SmithStone.cs rename to Projects/UOContent/Special Systems/Items/Stones/SmithStone.cs diff --git a/Projects/UOContent/SpecialSystems/Items/Stones/TailorStone.cs b/Projects/UOContent/Special Systems/Items/Stones/TailorStone.cs similarity index 100% rename from Projects/UOContent/SpecialSystems/Items/Stones/TailorStone.cs rename to Projects/UOContent/Special Systems/Items/Stones/TailorStone.cs diff --git a/Projects/UOContent/SpecialSystems/Items/SupplyBags/AlchemyBag.cs b/Projects/UOContent/Special Systems/Items/Supply Bags/AlchemyBag.cs similarity index 100% rename from Projects/UOContent/SpecialSystems/Items/SupplyBags/AlchemyBag.cs rename to Projects/UOContent/Special Systems/Items/Supply Bags/AlchemyBag.cs diff --git a/Projects/UOContent/SpecialSystems/Items/SupplyBags/BagOfIngots.cs b/Projects/UOContent/Special Systems/Items/Supply Bags/BagOfIngots.cs similarity index 100% rename from Projects/UOContent/SpecialSystems/Items/SupplyBags/BagOfIngots.cs rename to Projects/UOContent/Special Systems/Items/Supply Bags/BagOfIngots.cs diff --git a/Projects/UOContent/SpecialSystems/Items/SupplyBags/ScribeBag.cs b/Projects/UOContent/Special Systems/Items/Supply Bags/ScribeBag.cs similarity index 100% rename from Projects/UOContent/SpecialSystems/Items/SupplyBags/ScribeBag.cs rename to Projects/UOContent/Special Systems/Items/Supply Bags/ScribeBag.cs diff --git a/Projects/UOContent/SpecialSystems/Items/SupplyBags/SmithBag.cs b/Projects/UOContent/Special Systems/Items/Supply Bags/SmithBag.cs similarity index 100% rename from Projects/UOContent/SpecialSystems/Items/SupplyBags/SmithBag.cs rename to Projects/UOContent/Special Systems/Items/Supply Bags/SmithBag.cs diff --git a/Projects/UOContent/SpecialSystems/Items/SupplyBags/TailorBag.cs b/Projects/UOContent/Special Systems/Items/Supply Bags/TailorBag.cs similarity index 100% rename from Projects/UOContent/SpecialSystems/Items/SupplyBags/TailorBag.cs rename to Projects/UOContent/Special Systems/Items/Supply Bags/TailorBag.cs