-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
341 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
from .data import * | ||
from .encrypt import * | ||
from .packet import * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from .sequence_start import * | ||
from .packet_sequencer import * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
from .sequence_start import SequenceStart | ||
|
||
|
||
class PacketSequencer: | ||
"""A class for generating packet sequences.""" | ||
|
||
_start: SequenceStart | ||
_counter: int | ||
|
||
def __init__(self, start: SequenceStart): | ||
""" | ||
Constructs a new PacketSequencer with the provided SequenceStart. | ||
Args: | ||
start (SequenceStart): The sequence start. | ||
""" | ||
self._start = start | ||
self._counter = 0 | ||
|
||
def next_sequence(self) -> int: | ||
""" | ||
Returns the next sequence value, updating the sequence counter in the process. | ||
Note: | ||
This is not a monotonic operation. The sequence counter increases from 0 to 9 before | ||
looping back around to 0. | ||
Returns: | ||
int: The next sequence value. | ||
""" | ||
result = self._start.value + self._counter | ||
self._counter = (self._counter + 1) % 10 | ||
return result | ||
|
||
def set_sequence_start(self, start: SequenceStart) -> None: | ||
""" | ||
Sets the sequence start, also known as the "starting counter ID". | ||
Note: | ||
This does not reset the sequence counter. | ||
Args: | ||
start (SequenceStart): The new sequence start. | ||
""" | ||
self._start = start |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
import random | ||
from abc import ABC, abstractproperty | ||
from eolib.data.eo_numeric_limits import CHAR_MAX | ||
|
||
|
||
class SequenceStart(ABC): | ||
""" | ||
A value sent by the server to update the client's sequence start, also known as the 'starting | ||
counter ID'. | ||
""" | ||
|
||
@abstractproperty | ||
def value(self) -> int: | ||
""" | ||
int: Gets the sequence start value. | ||
""" | ||
raise NotImplementedError() | ||
|
||
@staticmethod | ||
def zero() -> 'SequenceStart': | ||
""" | ||
Returns an instance of SequenceStart with a value of 0. | ||
Returns: | ||
SequenceStart: An instance of SequenceStart. | ||
""" | ||
return SimpleSequenceStart(0) | ||
|
||
|
||
class SimpleSequenceStart(SequenceStart): | ||
_value: int | ||
|
||
def __init__(self, value: int): | ||
self._value = value | ||
|
||
@property | ||
def value(self): | ||
return self._value | ||
|
||
|
||
class AccountReplySequenceStart(SimpleSequenceStart): | ||
""" | ||
A class representing the sequence start value sent with the `ACCOUNT_REPLY` server packet. | ||
See Also: | ||
- [`AccountReplyServerPacket`][eolib.protocol._generated.net.server.AccountReplyServerPacket] | ||
""" | ||
|
||
def __init__(self, value: int): | ||
super().__init__(value) | ||
|
||
@staticmethod | ||
def from_value(value: int) -> 'AccountReplySequenceStart': | ||
""" | ||
Creates an instance of AccountReplySequenceStart from the value sent with the | ||
`ACCOUNT_REPLY` server packet. | ||
Args: | ||
value (int): The sequence start value sent with the `ACCOUNT_REPLY` server packet. | ||
Returns: | ||
AccountReplySequenceStart: An instance of AccountReplySequenceStart. | ||
""" | ||
return AccountReplySequenceStart(value) | ||
|
||
@staticmethod | ||
def generate() -> 'AccountReplySequenceStart': | ||
""" | ||
Generates an instance of AccountReplySequenceStart with a random value in the range 0-240. | ||
Returns: | ||
AccountReplySequenceStart: An instance of AccountReplySequenceStart. | ||
""" | ||
return AccountReplySequenceStart(random.randrange(0, 240)) | ||
|
||
|
||
class InitSequenceStart(SimpleSequenceStart): | ||
""" | ||
A class representing the sequence start value sent with the `INIT_INIT` server packet. | ||
See Also: | ||
- [`InitInitServerPacket`][eolib.protocol._generated.net.server.InitInitServerPacket] | ||
""" | ||
|
||
_seq1: int | ||
_seq2: int | ||
|
||
def __init__(self, value: int, seq1: int, seq2: int): | ||
super().__init__(value) | ||
self._seq1 = seq1 | ||
self._seq2 = seq2 | ||
|
||
@property | ||
def seq1(self) -> int: | ||
""" | ||
Returns the `seq1` byte value sent with the INIT_INIT server packet. | ||
Returns: | ||
int: The seq1 byte value. | ||
""" | ||
return self._seq1 | ||
|
||
@property | ||
def seq2(self) -> int: | ||
""" | ||
Returns the `seq2` byte value sent with the INIT_INIT server packet. | ||
Returns: | ||
int: The seq2 byte value. | ||
""" | ||
return self._seq2 | ||
|
||
@staticmethod | ||
def from_init_values(seq1: int, seq2: int) -> 'InitSequenceStart': | ||
""" | ||
Creates an instance of InitSequenceStart from the values sent with the `INIT_INIT` server | ||
packet. | ||
Args: | ||
seq1 (int): The `seq1` byte value sent with the `INIT_INIT` server packet. | ||
seq2 (int): The `seq2` byte value sent with the `INIT_INIT` server packet. | ||
Returns: | ||
InitSequenceStart: An instance of InitSequenceStart | ||
""" | ||
value = seq1 * 7 + seq2 - 13 | ||
return InitSequenceStart(value, seq1, seq2) | ||
|
||
@staticmethod | ||
def generate() -> 'InitSequenceStart': | ||
""" | ||
Generates an instance of InitSequenceStart with a random value in the range 0-1757. | ||
Returns: | ||
InitSequenceStart: An instance of InitSequenceStart. | ||
""" | ||
value = random.randrange(0, 1757) | ||
seq1_max = int((value + 13) / 7) | ||
seq1_min = max(0, int((value - (CHAR_MAX - 1) + 13 + 6) / 7)) | ||
|
||
seq1 = random.randrange(0, seq1_max - seq1_min) + seq1_min | ||
seq2 = value - seq1 * 7 + 13 | ||
|
||
return InitSequenceStart(value, seq1, seq2) | ||
|
||
|
||
class PingSequenceStart(SimpleSequenceStart): | ||
""" | ||
A class representing the sequence start value sent with the `CONNECTION_PLAYER` server packet. | ||
See Also: | ||
- [`ConnectionPlayerServerPacket`][eolib.protocol._generated.net.server.ConnectionPlayerServerPacket] | ||
""" | ||
|
||
def __init__(self, value: int, seq1: int, seq2: int): | ||
super().__init__(value) | ||
self._seq1 = seq1 | ||
self._seq2 = seq2 | ||
|
||
@property | ||
def seq1(self) -> int: | ||
""" | ||
Returns the seq1 short value sent with the `CONNECTION_PLAYER` server packet. | ||
Returns: | ||
int: The seq1 short value. | ||
""" | ||
return self._seq1 | ||
|
||
@property | ||
def seq2(self) -> int: | ||
""" | ||
Returns the seq2 char value sent with the CONNECTION_PLAYER server packet. | ||
Returns: | ||
int: The seq2 char value. | ||
""" | ||
return self._seq2 | ||
|
||
@staticmethod | ||
def from_ping_values(seq1: int, seq2: int) -> 'PingSequenceStart': | ||
""" | ||
Creates an instance of PingSequenceStart from the values sent with the `CONNECTION_PLAYER` | ||
server packet. | ||
Args: | ||
seq1 (int): The `seq1` short value sent with the `CONNECTION_PLAYER` server packet. | ||
seq2 (int): The `seq2` char value sent with the `CONNECTION_PLAYER` server packet. | ||
Returns: | ||
PingSequenceStart: An instance of PingSequenceStart. | ||
""" | ||
value = seq1 - seq2 | ||
return PingSequenceStart(value, seq1, seq2) | ||
|
||
@staticmethod | ||
def generate() -> 'PingSequenceStart': | ||
""" | ||
Generates an instance of PingSequenceStart with a random value in the range 0-1757. | ||
Returns: | ||
PingSequenceStart: An instance of PingSequenceStart. | ||
""" | ||
value = random.randrange(0, 1757) | ||
seq1 = value + random.randrange(0, CHAR_MAX - 1) | ||
seq2 = seq1 - value | ||
|
||
return PingSequenceStart(value, seq1, seq2) | ||
|
||
|
||
__all__ = ['SequenceStart', 'AccountReplySequenceStart', 'InitSequenceStart', 'PingSequenceStart'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
from eolib.packet.packet_sequencer import PacketSequencer | ||
from eolib.packet.sequence_start import AccountReplySequenceStart | ||
|
||
|
||
def test_next_sequence(): | ||
sequence_start = AccountReplySequenceStart.from_value(123) | ||
sequencer = PacketSequencer(sequence_start) | ||
|
||
for i in range(10): | ||
assert sequencer.next_sequence() == 123 + i | ||
|
||
assert sequencer.next_sequence() == 123 | ||
|
||
|
||
def test_sequence_start(): | ||
sequence_start = AccountReplySequenceStart.from_value(100) | ||
sequencer = PacketSequencer(sequence_start) | ||
|
||
assert sequencer.next_sequence() == 100 | ||
|
||
sequencer.set_sequence_start(AccountReplySequenceStart.from_value(200)) | ||
|
||
assert sequencer.next_sequence() == 201 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import math | ||
from eolib.packet.sequence_start import ( | ||
SequenceStart, | ||
AccountReplySequenceStart, | ||
InitSequenceStart, | ||
PingSequenceStart, | ||
) | ||
|
||
|
||
def randrange_midpoint(a, b): | ||
return math.ceil((a + b) / 2) | ||
|
||
|
||
def test_zero(): | ||
assert SequenceStart.zero().value == 0 | ||
|
||
|
||
def test_account_reply_from_value(): | ||
sequence_start = AccountReplySequenceStart.from_value(22) | ||
assert sequence_start.value == 22 | ||
|
||
|
||
def test_account_reply_generate(monkeypatch): | ||
monkeypatch.setattr('random.randrange', randrange_midpoint) | ||
|
||
sequence_start = AccountReplySequenceStart.generate() | ||
assert sequence_start.value == 120 | ||
|
||
|
||
def test_init__from_init_values(): | ||
sequence_start = InitSequenceStart.from_init_values(110, 122) | ||
assert sequence_start.value == 879 | ||
assert sequence_start.seq1 == 110 | ||
assert sequence_start.seq2 == 122 | ||
|
||
|
||
def test_init_generate(monkeypatch): | ||
monkeypatch.setattr('random.randrange', randrange_midpoint) | ||
|
||
sequence_start = InitSequenceStart.generate() | ||
assert sequence_start.value == 879 | ||
assert sequence_start.seq1 == 110 | ||
assert sequence_start.seq2 == 122 | ||
|
||
|
||
def test_ping_from_ping_values(): | ||
sequence_start = PingSequenceStart.from_ping_values(1005, 126) | ||
assert sequence_start.value == 879 | ||
assert sequence_start.seq1 == 1005 | ||
assert sequence_start.seq2 == 126 | ||
|
||
|
||
def test_ping_generate(monkeypatch): | ||
monkeypatch.setattr('random.randrange', randrange_midpoint) | ||
|
||
sequence_start = PingSequenceStart.generate() | ||
assert sequence_start.value == 879 | ||
assert sequence_start.seq1 == 1005 | ||
assert sequence_start.seq2 == 126 |