Система Игры Пакет


Обычно я около 200 пакетов, но для целей настоящего вопрос, который я включать только 1 пакет в коде, остальное-тот же принцип, если есть еще 200.

Вот это пакет системы, когда новые сообщения приходят в ExecutePacket метод будет вызываться из игрока класса, где они expecute входящего пакета.

Вот контроллер пакетов, Этот класс обрабатывает и хранит все входящие пакеты.

internal class PacketController
{
    private readonly Dictionary<int, IIncomingPacketEvent> _packets;

    public void ExecutePacket(Player player, IncomingPacket packet)
    {
        if (!_packets.TryGetValue(packet.Header, out var packetEvent))
        {
            Logger.Log("Unhandled packet: " + packet.Id);
            return;
        }

        Logger.Log("Handled packet: " + packet.Id);

        packetEvent.Execute();
    }

    public void LoadPackets()
    {
        LoadHandshake();
    }

    private void LoadHandshake()
    {
        _packets.Add(IncomingPacketHeaders.GetClientKeyEvent, new GetClientKeyEvent());
    }
}

Входящие и исходящие пакеты имеют заголовок, который в простых терминах-это идентификаторы (идентификаторы).

class IncomingPacketHeaders
{
    public const int GetClientKeyEvent = 2042;
}

class OutgoingPacketHeaders
{
    public const int SendSpecialKey = 9254;
}

Остальной код

interface IIncomingPacketEvent
{
    void Execute();
}

class GetClientKeyEvent : IIncomingPacketEvent
{
    public void Execute()
    {
        // Execute the packet...
    }
}

Чуть не забыл, OutgoingPackets такие "композиторы" (как мы их называем).

internal class SendSpecialKey : OutgoingPacket
{
    public SendSpecialKey() : base(OutgoingPacketHeaders.SendSpecialKey)
    {
        WriteString("some special key");
    }
}

Здесь представлены IncomingPacket и OutgoingPacket классов.

public class IncomingPacket
{
    private byte[] _body;
    private int _pointer;
    public int Header;

    public IncomingPacket(int messageId, byte[] body)
    {
        Load(messageId, body);
    }

    public int RemainingLength => _body.Length - _pointer;

    public void Load(int messageId, byte[] body)
    {
        if (body == null)
        {
            body = new byte[0];
        }

        Header = messageId;
        _body = body;

        _pointer = 0;
    }

    public void AdvancePointer(int i)
    {
        _pointer += i * 4;
    }

    public byte[] ReadBytes(int bytes)
    {
        if (bytes > RemainingLength)
        {
            bytes = RemainingLength;
        }

        var data = new byte[bytes];

        for (var i = 0; i < bytes; i++)
        {
            data[i] = _body[_pointer++];
        }

        return data;
    }

    public byte[] PlainReadBytes(int bytes)
    {
        if (bytes > RemainingLength)
        {
            bytes = RemainingLength;
        }

        var data = new byte[bytes];

        for (int x = 0, y = _pointer; x < bytes; x++, y++)
        {
            data[x] = _body[y];
        }

        return data;
    }

    public byte[] ReadFixedValue()
    {
        return ReadBytes(EncodingUtilities.DecodeInt16(ReadBytes(2)));
    }

    public string ReadString()
    {
        return PlusEnvironment.GetDefaultEncoding().GetString(ReadFixedValue());
    }

    public bool ReadBoolean()
    {
        return RemainingLength > 0 && _body[_pointer++] == Convert.ToChar(1);
    }

    public int ReadInt()
    {
        if (RemainingLength < 1)
        {
            return 0;
        }

        var data = PlainReadBytes(4);
        var i = EncodingUtilities.DecodeInt32(data);

        _pointer += 4;

        return i;
    }
}

И исходящих

public class OutgoingPacket
{
    private readonly List<byte> _body;

    private readonly Encoding _encoding;
    protected readonly int Id;

    public OutgoingPacket(int id)
    {
        _body = new List<byte>();
        _encoding = Encoding.Default;

        Id = id;
        WriteShort(id);
    }

    public void WriteByte(int b)
    {
        _body.Add((byte) b);
    }

    private void WriteBytes(IReadOnlyList<byte> b, bool isInt) // d
    {
        if (isInt)
        {
            for (var i = b.Count - 1; i > -1; i--)
            {
                _body.Add(b[i]);
            }
        }
        else
        {
            _body.AddRange(b);
        }
    }

    public void WriteDouble(double d)
    {
        var raw = Math.Round(d, 1).ToString(CultureInfo.InvariantCulture);

        if (raw.Length == 1)
        {
            raw += ".0";
        }

        WriteString(raw.Replace(',', '.'));
    }

    public void WriteString(string s)
    {
        WriteShort(s.Length);
        WriteBytes(_encoding.GetBytes(s), false);
    }

    public void WriteShort(int s)
    {
        WriteBytes(BitConverter.GetBytes((short) s), true);
    }

    public void WriteInteger(int i)
    {
        WriteBytes(BitConverter.GetBytes(i), true);
    }

    public void WriteBoolean(bool b)
    {
        WriteBytes(new[] {(byte) (b ? 1 : 0)}, false);
    }

    public byte[] GetBytes()
    {
        var final = new List<byte>();

        final.AddRange(BitConverter.GetBytes(_body.Count));
        final.Reverse();
        final.AddRange(_body);

        return final.ToArray();
    }
}

Я не разделяю клиентский код, как это, совершенно не относящиеся к вопросу.



182
3
задан 10 марта 2018 в 01:03 Источник Поделиться
Комментарии
1 ответ

Есть много маленьких вещей, чтобы сказать, Я конечно что-то пропустил, но здесь идет!

Порядок байтов

Очевидно, что вы думали о порядок байтов от isInt параметр, который является хорошим, и хотя это будет, конечно, работать нормально на большинстве машин, вы не должны быть предполагая, что порядок байтов системы: проверить IsLittleEndian чтобы определить это, и только обратный байты, поставляемые BitConverter.GetBytes(*) если это не правильный порядок байтов (я предполагаю, что вы используете большой ("сеть") с прямым порядком байтов).

Как Readonly Поля

У вас есть соответствующим образом помечены поля OutgoingPacket как readonly: то же лечение должно проводиться в IncomingPacket, особенно Header которые в настоящее время публично устанавливаемых.

Входящие/Исходящие Несоответствие Кодирования

Рассмотрим IncomingPacket.ReadString(): мне не нравится, что это прыгает в какой-либо другой части кода, чтобы читать строки, которые были написаны, простым коротким префиксом строки в кодировку, которая используется другой механизм. Это похоже на ремонтопригодность кошмар, потому что изменения PlusEnvironment.GetDefaultEncoding() можно легко сделать без обновления OutgoingPacket. В сочетании эти методы должны быть определены вместе, так что очевидно, что они соединены.

Тот же вопрос применим практически везде. Мое предложение было бы продлить EncodingUtilities чтобы обеспечить GetBytes стиль, методы, так, что все проблемы с порядком байтов и кодировки обрабатываются в одном месте, и выдрать все "пользовательский" вещи в OutgoingPacket

Составление данных

В IncomingPacket.ReadInt() у вас есть

if (RemainingLength < 1)
{
return 0;
}

Что это делаешь?! Нет ничего в OutgoingPacket это позволяет вам обрезать int если оно произойдет, будет ноль и последнее значение: какая утилита может это проверить возможно?

Если вы пытаетесь прочитать intи нет int читать, вы, вероятно, следует бросать исключение. Отсутствующие данные не то, что система должна иметь дело с, сделав его: программист сделал ошибку и они хотят знать об этом, так сказать, их в наиболее полезным способом, выдавая исключение.

Те же замечания могут быть сделаны для IncomingPacket.(Plain)ReadBytes(int bytes).

if (bytes > RemainingLength)
{
bytes = RemainingLength;
}

Я просил bytes байт, не дать мне меньше bytes байт! (Кстати, мне не нравится имя переменной bytesэто звучит как буфер: count или byteCount казалось бы, яснее). Повторюсь: исключение: скажи мне не хватало байта, когда вы впервые поняли.

PacketController.ExecutePacket(Player, IncomingPacket)

Этот код не проходит пакет в случае пакетной передачи: вы все это код для чтения/записи полезных данных для пакета, но вы, кажется, не делать ничего с ним.

// Execute the packet...

Какой пакет?!

IncomingPacket.ReadBoolean()

... Я чувствую, что я должен быть отсутствует что-то: зачем вы сравниваете byte к char?

return RemainingLength > 0 && _body[_pointer++] == Convert.ToChar(1);

Почему бы просто не сравнить байт byte значение 1?

Опять же, RemainingLength > 0 помогает никто.

Я был бы склонен вытаскивать метод, который считывает один байт (например, ReadByte()), поскольку в настоящее время это одна строка кода (я бы расширять if а) выполняет ряд заданий.

/// <Summary> Reads a single byte from the stream </Summary>
public byte ReadByte()
{
if (RemainingLength < 1)
throw new IncomingPacketException("Attempted to read a Byte from a consumed packet");

return _body[_pointer++];
}

/// <Summary> Reads a boolean value from the stream </Summary>
public bool ReadBoolean()
{
return ReadByte() == (byte)1;
}

У вас публичная OutgoingPacket.WriteByte(byte) метод, таким образом, глупо не иметь публичных IncomingPacket.ReadByte() способ также.

IncomingPacket.ReadInt()

Это единственный метод, который использует PlainReadBytes(int) метод. Почему просто не использовать ReadBytes(int)? Почему PlainReadBytes(int) даже при условии?

Вы загружаете задачи на ReadInt() (_pointer += 4) что должно быть сделано ReadBytes(int), что не согласуется с большинством других методов OutgoingPacket.

OutgoingPacket.WriteByte(int b)

Почему это int?! WriteByte(??) следует принять _byte_! Если, как потребитель этот метод, я хочу написать int и делать вид, что это byteтогда я должен был выполнить, что бросил себя. Все, что вы сделали здесь написано в неясной API, который поможет ввести скопировать и вставить ошибки. По крайней мере, это поведение отливки должны быть задокументированы.

OutgoingPacket.WriteBytes(IReadOnlyList<byte>, bool)

Мне бы хотелось разбить это на два метода, один для вещей, где порядок байтов вопросы, и один для вещей, где порядок байтов не имеет значения. Это делает его намного понятнее по призыву-сайте, что правильный метод используется, скорее, чем кто-либо, глядя на код, что бы понять второй параметр и помните, в какую сторону вокруг его.

OutgoingPacket.WriteDouble(double)

Вы уже используете CultureInvarientнет необходимости, чтобы заменить запятые с точками.

Есть ли хорошая причина, вы не вручную добавляя ".0" в дубль, который кодирует к одной цифре?

Все это двойные вещи немного пугает, потому что это выглядит как общего назначения писателя, но в действительности это кадрировании doubleи это совершенно без документов. Если вся система построена вокруг концепции не более одной десятичной точности, то это нормально (это наверное все-таки требует комментария) а если нет, то это выглядит как отличный способ, чтобы запутать кого-то незнакомого с кодом.

Все, что сказал, Нет IncomingPacket.ReadDouble() метод, таким образом, этот метод является своего рода бесполезно.

Outgoing.WriteBool(bool b)

Я бы в этом положиться WriteByte(byte)это просто чище.

WriteByte((byte)(b ? 1 : 0));

OutgoingBytes.GetBytes()

Мне не нравится, что при этом используется другой набор кода для написания обратный-перевернутый целое... но ты нужен рефакторинг, чтобы изменить это.

2
ответ дан 10 марта 2018 в 09:03 Источник Поделиться