fix(content): Converts mahjong packets (#402)

- [X] Converts mahjong packets
This commit is contained in:
Kamron Batman 2021-01-12 00:59:42 -08:00 • committed by GitHub
parent f4dd7c8e7f
commit ca196e4729
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 436 additions and 51 deletions

View file

@ -304,7 +304,6 @@ namespace Server
return ex switch
{
Expansion.None => FeatureFlags.ExpansionNone,
Expansion.T2A => FeatureFlags.ExpansionT2A,
Expansion.UOR => FeatureFlags.ExpansionUOR,
Expansion.UOTD => FeatureFlags.ExpansionUOTD,

View file

@ -24,7 +24,7 @@ namespace Server.Network
public static class PacketUtilities
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WritePacketLength(this CircularBufferWriter writer)
public static void WritePacketLength(this ref CircularBufferWriter writer)
{
var length = writer.Position;
writer.Seek(1, SeekOrigin.Begin);
@ -33,7 +33,7 @@ namespace Server.Network
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WritePacketLength(this SpanWriter writer)
public static void WritePacketLength(this ref SpanWriter writer)
{
writer.Seek(1, SeekOrigin.Begin);
writer.Write((ushort)writer.BytesWritten);

View file

@ -0,0 +1,118 @@
using System;
using Server;
using Server.Engines.Mahjong;
using Server.Tests;
using Server.Tests.Network;
using Xunit;
namespace UOContent.Tests
{
public class MahjongPacketTests : IClassFixture<ServerFixture>
{
[Fact]
public void TestMahjongJoinGame()
{
Serial game = 0x1024u;
var expected = new MahjongJoinGame(game).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendMahjongJoinGame(game);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void TestMahjongPlayersInfo(bool showScores)
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var game = new MahjongGame { ShowScores = showScores };
game.Players.Join(m);
var expected = new MahjongPlayersInfo(game, m).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendMahjongPlayersInfo(game, m);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Theory]
[InlineData(true, true)]
[InlineData(true, false)]
[InlineData(false, true)]
[InlineData(false, false)]
public void TestMahjongGeneralInfo(bool showScores, bool spectatorVision)
{
var game = new MahjongGame { ShowScores = showScores, SpectatorVision = spectatorVision};
var expected = new MahjongGeneralInfo(game).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendMahjongGeneralInfo(game);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void TestMahjongTilesInfo(bool spectatorVision)
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var game = new MahjongGame { SpectatorVision = spectatorVision };
game.Players.Join(m);
var expected = new MahjongTilesInfo(game, m).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendMahjongTilesInfo(game, m);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void TestMahjongTileInfo(bool spectatorVision)
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var game = new MahjongGame { SpectatorVision = spectatorVision };
game.Players.Join(m);
var expected = new MahjongTileInfo(game.Tiles[0], m).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendMahjongTileInfo(game.Tiles[0], m);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Fact]
public void TestMahjongRelieve()
{
Serial game = 0x1024u;
var expected = new MahjongRelieve(game).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendMahjongRelieve(game);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
}
}

View file

@ -5,11 +5,11 @@ namespace Server.Engines.Mahjong
{
public sealed class MahjongJoinGame : Packet
{
public MahjongJoinGame(MahjongGame game) : base(0xDA)
public MahjongJoinGame(Serial game) : base(0xDA)
{
EnsureCapacity(9);
Stream.Write(game.Serial);
Stream.Write(game);
Stream.Write((byte)0);
Stream.Write((byte)0x19);
}
@ -209,11 +209,11 @@ namespace Server.Engines.Mahjong
public sealed class MahjongRelieve : Packet
{
public MahjongRelieve(MahjongGame game) : base(0xDA)
public MahjongRelieve(Serial game) : base(0xDA)
{
EnsureCapacity(9);
Stream.Write(game.Serial);
Stream.Write(game);
Stream.Write((byte)0);
Stream.Write((byte)0x1A);
}

View file

@ -83,7 +83,7 @@ namespace Server.Engines.Mahjong
if (Players.IsInGamePlayer(Players.DealerPosition))
{
Players.Dealer.Send(new MahjongGeneralInfo(this));
Players.Dealer.NetState.SendMahjongGeneralInfo(this);
}
Players.SendTilesPacket(false, true);

View file

@ -0,0 +1,271 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2021 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: MahjongPackets.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 <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Buffers;
using System.IO;
using Server.Network;
namespace Server.Engines.Mahjong
{
public static class MahjongPackets
{
public const int MahjongGeneralInfoPacketLength = 25;
public const int MahjongRelievePacketLength = 9;
public static void SendMahjongJoinGame(this NetState ns, Serial game)
{
if (ns == null)
{
return;
}
var writer = new SpanWriter(stackalloc byte[9]);
writer.Write((byte)0xDA); // Packet ID
writer.Write((ushort)9);
writer.Write(game);
writer.Write((ushort)0x19); // Command
ns.Send(writer.Span);
}
public static void SendMahjongPlayersInfo(this NetState ns, MahjongGame game, Mobile to)
{
if (ns == null)
{
return;
}
var maxLength = 11 + game.Players.Seats * 45;
var writer = new SpanWriter(stackalloc byte[maxLength]);
writer.Write((byte)0xDA); // Packet ID
writer.Seek(2, SeekOrigin.Current);
writer.Write(game.Serial);
writer.Write((ushort)0x02); // Command
writer.Seek(2, SeekOrigin.Current); // Seats
var players = game.Players;
var count = 0;
for (var i = 0; i < players.Seats; i++)
{
var m = players.GetPlayer(i);
if (m == null && !game.ShowScores)
{
continue;
}
writer.Write(m?.Serial ?? Serial.Zero);
writer.Write(m != null && players.DealerPosition == i ? (byte)0x1 : (byte)0x2);
writer.Write((byte)i);
if (game.ShowScores || m == to)
{
writer.Write(players.GetScore(i));
}
else
{
writer.Write(0);
}
writer.Write((short)0);
writer.Write((byte)0);
writer.Write(players.IsPublic(i));
writer.WriteAscii(m?.Name ?? "", 30);
writer.Write(m == null || !players.IsInGamePlayer(i));
count++;
}
writer.Seek(9, SeekOrigin.Begin);
writer.Write((ushort)count);
writer.WritePacketLength();
ns.Send(writer.Span);
}
public static void SendMahjongTileInfo(this NetState ns, MahjongTile tile, Mobile to)
{
if (ns == null)
{
return;
}
var game = tile.Game;
var players = game.Players;
var writer = new SpanWriter(stackalloc byte[18]);
writer.Write((byte)0xDA); // Packet ID
writer.Write((ushort)18);
writer.Write(game.Serial);
writer.Write((ushort)0x03); // Command
writer.Write((byte)tile.Number);
if (tile.Flipped)
{
var hand = tile.Dimensions.GetHandArea();
if (hand < 0 || players.IsPublic(hand) || players.GetPlayer(hand) == to ||
game.SpectatorVision && players.IsSpectator(to))
{
writer.Write((byte)tile.Value);
}
else
{
writer.Write((byte)0);
}
}
else
{
writer.Write((byte)0);
}
writer.Write((short)tile.Position.Y);
writer.Write((short)tile.Position.X);
writer.Write((byte)tile.StackLevel);
writer.Write((byte)tile.Direction);
writer.Write((byte)(tile.Flipped ? 0x10 : 0x0));
ns.Send(writer.Span);
}
public static void SendMahjongTilesInfo(this NetState ns, MahjongGame game, Mobile to)
{
if (ns == null)
{
return;
}
var tiles = game.Tiles;
var players = game.Players;
var length = 11 + tiles.Length * 9;
var writer = new SpanWriter(stackalloc byte[length]);
writer.Write((byte)0xDA); // Packet ID
writer.Write((ushort)length);
writer.Write(game.Serial);
writer.Write((ushort)0x04); // Command
writer.Write((short)tiles.Length);
foreach (var tile in tiles)
{
writer.Write((byte)tile.Number);
if (tile.Flipped)
{
var hand = tile.Dimensions.GetHandArea();
if (hand < 0 || players.IsPublic(hand) || players.GetPlayer(hand) == to ||
game.SpectatorVision && players.IsSpectator(to))
{
writer.Write((byte)tile.Value);
}
else
{
writer.Write((byte)0);
}
}
else
{
writer.Write((byte)0);
}
writer.Write((short)tile.Position.Y);
writer.Write((short)tile.Position.X);
writer.Write((byte)tile.StackLevel);
writer.Write((byte)tile.Direction);
writer.Write((byte)(tile.Flipped ? 0x10 : 0x0));
}
ns.Send(writer.Span);
}
public static void SendMahjongGeneralInfo(this NetState ns, MahjongGame game)
{
if (ns == null)
{
return;
}
Span<byte> buffer = stackalloc byte[MahjongGeneralInfoPacketLength].InitializePacket();
CreateMahjongGeneralInfo(buffer, game);
ns.Send(buffer);
}
public static void CreateMahjongGeneralInfo(Span<byte> buffer, MahjongGame game)
{
if (buffer[0] != 0)
{
return;
}
var writer = new SpanWriter(buffer);
writer.Write((byte)0xDA); // Packet ID
writer.Write((ushort)MahjongGeneralInfoPacketLength);
writer.Write(game.Serial);
writer.Write((ushort)0x05); // Command
writer.Write((short)0);
writer.Write((byte)0);
writer.Write((byte)((game.ShowScores ? 0x1 : 0x0) | (game.SpectatorVision ? 0x2 : 0x0)));
writer.Write((byte)game.Dices.First);
writer.Write((byte)game.Dices.Second);
writer.Write((byte)game.DealerIndicator.Wind);
writer.Write((short)game.DealerIndicator.Position.Y);
writer.Write((short)game.DealerIndicator.Position.X);
writer.Write((byte)game.DealerIndicator.Direction);
writer.Write((short)game.WallBreakIndicator.Position.Y);
writer.Write((short)game.WallBreakIndicator.Position.X);
}
public static void SendMahjongRelieve(this NetState ns, Serial game)
{
if (ns == null)
{
return;
}
Span<byte> buffer = stackalloc byte[MahjongRelievePacketLength].InitializePacket();
CreateMahjongRelieve(buffer, game);
ns.Send(buffer);
}
public static void CreateMahjongRelieve(Span<byte> buffer, Serial game)
{
if (buffer[0] != 0)
{
return;
}
var writer = new SpanWriter(buffer);
writer.Write((byte)0xDA); // Packet ID
writer.Write((ushort)MahjongRelievePacketLength);
writer.Write(game);
writer.Write((ushort)0x1A); // Command
}
}
}

View file

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using Server.Network;
namespace Server.Engines.Mahjong
{
@ -171,6 +173,8 @@ namespace Server.Engines.Mahjong
{
var removed = false;
Span<byte> relievePacket = stackalloc byte[MahjongPackets.MahjongRelievePacketLength].InitializePacket();
for (var i = 0; i < m_Players.Length; i++)
{
var player = m_Players[i];
@ -205,7 +209,8 @@ namespace Server.Engines.Mahjong
{
m_InGame[i] = false;
player.Send(new MahjongRelieve(Game));
MahjongPackets.CreateMahjongRelieve(relievePacket, Game.Serial);
player.NetState?.Send(relievePacket);
SendPlayerExitMessage(player);
UpdateDealer(true);
@ -228,7 +233,8 @@ namespace Server.Engines.Mahjong
{
m_Spectators.RemoveAt(i);
mobile.Send(new MahjongRelieve(Game));
MahjongPackets.CreateMahjongRelieve(relievePacket, Game.Serial);
mobile.NetState?.Send(relievePacket);
}
else
{
@ -335,13 +341,13 @@ namespace Server.Engines.Mahjong
if (sendJoinGame)
{
player.Send(new MahjongJoinGame(Game));
player.NetState.SendMahjongJoinGame(Game.Serial);
}
SendPlayersPacket(true, true);
player.Send(new MahjongGeneralInfo(Game));
player.Send(new MahjongTilesInfo(Game, player));
player.NetState.SendMahjongGeneralInfo(Game);
player.NetState.SendMahjongTilesInfo(Game, player);
if (DealerPosition == index)
{
@ -360,10 +366,10 @@ namespace Server.Engines.Mahjong
m_Spectators.Add(mobile);
}
mobile.Send(new MahjongJoinGame(Game));
mobile.Send(new MahjongPlayersInfo(Game, mobile));
mobile.Send(new MahjongGeneralInfo(Game));
mobile.Send(new MahjongTilesInfo(Game, mobile));
mobile.NetState.SendMahjongJoinGame(Game.Serial);
mobile.NetState.SendMahjongPlayersInfo(Game, mobile);
mobile.NetState.SendMahjongGeneralInfo(Game);
mobile.NetState.SendMahjongTilesInfo(Game, mobile);
}
public void Join(Mobile mobile)
@ -373,19 +379,18 @@ namespace Server.Engines.Mahjong
if (index >= 0)
{
AddPlayer(mobile, index, true);
return;
}
var nextSeat = GetNextSeat();
if (nextSeat >= 0)
{
AddPlayer(mobile, nextSeat, true);
}
else
{
var nextSeat = GetNextSeat();
if (nextSeat >= 0)
{
AddPlayer(mobile, nextSeat, true);
}
else
{
AddSpectator(mobile);
}
AddSpectator(mobile);
}
}
@ -438,14 +443,12 @@ namespace Server.Engines.Mahjong
}
else
{
from.Send(new MahjongPlayersInfo(Game, from));
to.Send(new MahjongPlayersInfo(Game, to));
from.NetState.SendMahjongPlayersInfo(Game, from);
to.NetState.SendMahjongPlayersInfo(Game, to);
}
SendLocalizedMessage(
1062774,
$"{from.Name}\t{to.Name}\t{amount}"
); // ~1_giver~ gives ~2_receiver~ ~3_number~ points.
// ~1_giver~ gives ~2_receiver~ ~3_number~ points.
SendLocalizedMessage(1062774, $"{from.Name}\t{to.Name}\t{amount}");
}
public void OpenSeat(int index)
@ -458,7 +461,7 @@ namespace Server.Engines.Mahjong
if (m_InGame[index])
{
player.Send(new MahjongRelieve(Game));
player.NetState.SendMahjongRelieve(Game.Serial);
}
m_Players[index] = null;
@ -488,10 +491,10 @@ namespace Server.Engines.Mahjong
if (IsInGamePlayer(oldDealer))
{
m_Players[oldDealer].Send(new MahjongPlayersInfo(Game, m_Players[oldDealer]));
m_Players[oldDealer].NetState.SendMahjongPlayersInfo(Game, m_Players[oldDealer]);
}
to.Send(new MahjongPlayersInfo(Game, to));
to.NetState.SendMahjongPlayersInfo(Game, to);
SendDealerChangedMessage();
}
@ -513,7 +516,7 @@ namespace Server.Engines.Mahjong
{
foreach (var mobile in GetInGameMobiles(players, spectators))
{
mobile.Send(new MahjongPlayersInfo(Game, mobile));
mobile.NetState.SendMahjongPlayersInfo(Game, mobile);
}
}
@ -526,23 +529,20 @@ namespace Server.Engines.Mahjong
return;
}
var generalInfo = new MahjongGeneralInfo(Game);
generalInfo.Acquire();
Span<byte> generalInfo = stackalloc byte[MahjongPackets.MahjongGeneralInfoPacketLength].InitializePacket();
foreach (var mobile in mobiles)
{
mobile.Send(generalInfo);
MahjongPackets.CreateMahjongGeneralInfo(generalInfo, Game);
mobile.NetState?.Send(generalInfo);
}
generalInfo.Release();
}
public void SendTilesPacket(bool players, bool spectators)
{
foreach (var mobile in GetInGameMobiles(players, spectators))
{
mobile.Send(new MahjongTilesInfo(Game, mobile));
mobile.NetState.SendMahjongTilesInfo(Game, mobile);
}
}
@ -550,7 +550,7 @@ namespace Server.Engines.Mahjong
{
foreach (var mobile in GetInGameMobiles(players, spectators))
{
mobile.Send(new MahjongTileInfo(tile, mobile));
mobile.NetState.SendMahjongTileInfo(tile, mobile);
}
}
@ -563,16 +563,13 @@ namespace Server.Engines.Mahjong
return;
}
var relieve = new MahjongRelieve(Game);
relieve.Acquire();
Span<byte> relievePacket = stackalloc byte[MahjongPackets.MahjongRelievePacketLength].InitializePacket();
foreach (var mobile in mobiles)
{
mobile.Send(relieve);
MahjongPackets.CreateMahjongRelieve(relievePacket, Game.Serial);
mobile.NetState?.Send(relievePacket);
}
relieve.Release();
}
public void SendLocalizedMessage(int number)