Skip to content

Commit 22d1b59

Browse files
committed
Re-work async code + more
Lots of changes but I'll try to summarize: - Changed around lots of exports - Removed EventDispatcher (in favor of inheriting from client classes and overriding their handlers) - Add AsyncClientBase and update all clients to inherit from it (handles receiving from a socket through packet reader classes and manages receive + keep alive tasks. - Add HEADER_SIZE attribute to PacketReader. Implementations should define this as the size of the header bytes. AsyncClientBase will reset the reader to this position after checking for handlers to receive into. - Clean up CheckRevision code, add preload() and get_files() function - Significant rewrite of BnetClient. See the related script for new usage example. Only partially tested. - Probably other things
1 parent 6cf3434 commit 22d1b59

29 files changed

+2438
-1723
lines changed

bncs/__init__.py

+5-15
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,14 @@
11

2+
from . import chat
23
from . import crev
34
from . import hashing
5+
from . import packets
46
from . import utils
57

6-
from .packets import *
8+
from .chat import ChatUser, ChatEvent, ChatEventType, ChannelFlags, UserFlags
79

8-
from .client import BnetClient, ChatEvent
9-
10-
# Chat event IDs
11-
from .chat import EID_SHOWUSER, EID_JOIN, EID_LEAVE, EID_WHISPER, EID_TALK, EID_BROADCAST, EID_CHANNEL, EID_USERFLAGS, \
12-
EID_WHISPERSENT, EID_CHANNELFULL, EID_CHANNELDOESNOTEXIST, EID_CHANNELRESTRICTED, EID_INFO, EID_ERROR, \
13-
EID_IGNORE, EID_ACCEPT, EID_EMOTE, ChatEventType
14-
15-
# Channel flags
16-
from .chat import CHANNEL_PUBLIC, CHANNEL_MODERATED, CHANNEL_RESTRICTED, CHANNEL_SILENT, CHANNEL_SYSTEM, \
17-
CHANNEL_PRODUCT, CHANNEL_GLOBAL, CHANNEL_REDIRECT, CHANNEL_CHAT, CHANNEL_SUPPORT, ChannelFlags
18-
19-
# User flags
20-
from .chat import FLAG_BLIZZARD, FLAG_CHANOP, FLAG_CHANVOICE, FLAG_BNETADMIN, FLAG_NOUDP, FLAG_SQUELCH, FLAG_GUEST, \
21-
UserFlags
10+
from .client import BnetClient, InvalidOperationError, ClientStatus, ClientAuthResult, AccountLoginResult, \
11+
AccountCreateResult, LadderDataSorting, FriendStatus, FriendLocation
2212

2313
from .products import BncsProduct, PRODUCT_STAR, PRODUCT_SEXP, PRODUCT_W2BN, PRODUCT_D2DV, PRODUCT_D2XP, \
2414
PRODUCT_JSTR, PRODUCT_WAR3, PRODUCT_W3XP, PRODUCT_DRTL, PRODUCT_DSHR, PRODUCT_SSHR, PRODUCT_W3DM, \

bncs/chat.py

+101
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class ChatEventType(enum.IntEnum):
6363

6464

6565
class ChannelFlags(enum.IntFlag):
66+
Private = 0
6667
Public = CHANNEL_PUBLIC
6768
Moderated = CHANNEL_MODERATED
6869
Restricted = CHANNEL_RESTRICTED
@@ -76,10 +77,110 @@ class ChannelFlags(enum.IntFlag):
7677

7778

7879
class UserFlags(enum.IntFlag):
80+
NoFlags = 0
7981
Blizzard = FLAG_BLIZZARD
8082
ChannelOperator = FLAG_CHANOP
8183
ChannelSpeaker = FLAG_CHANVOICE
8284
ServerAdmin = FLAG_BNETADMIN
8385
UdpPlug = FLAG_NOUDP
8486
Squelched = FLAG_SQUELCH
8587
Guest = FLAG_GUEST
88+
89+
90+
class ChatUser:
91+
def __init__(self, username, flags=0, ping=0, statstring=None):
92+
self.name = username
93+
self.flags = flags
94+
self.ping = ping
95+
self.statstring = statstring
96+
97+
self.ip_address = None
98+
self.account_id = None
99+
self.regauth = None
100+
101+
102+
class ChatEvent:
103+
def __init__(self, event_id, user=None, msg=None):
104+
self.eid = ChatEventType(event_id)
105+
self.text = msg
106+
self.text_encoding = None
107+
self.is_update = False
108+
109+
self.flags = user.flags if user else 0
110+
self.ping = user.ping if user else 0
111+
self.username = user.name if user else ""
112+
113+
# Deprecated fields, may be supported on some private servers
114+
self.ip_address = user.ip_address if user else None
115+
self.account_id = user.account_id if user else None
116+
self.regauth = user.regauth if user else None
117+
118+
def __repr__(self):
119+
s = f"<BncsChatEvent eid={self.eid.name}, " \
120+
f"flags={self.flags.name or hex(self.flags.value)}, ping={self.ping}ms"
121+
122+
# Defunct values, only show if they are different from expected.
123+
if self.ip_address != "0.0.0.0":
124+
s += f", ip={self.ip_address}"
125+
if self.account_id != 0xbaadf00d:
126+
s += f", account={self.account_id}"
127+
if self.regauth != 0xbaadf00d:
128+
s += f", authority={self.regauth}",
129+
130+
s += f", username='{self.username}', text='{self.text}'>"
131+
return s
132+
133+
def build_packet(self, hide_private_info=True):
134+
"""Builds a BncsPacket to represent this chat event.
135+
136+
If hide_private_info is TRUE then the IpAddress, AccountId, and RegAuth fields will be obscured.
137+
"""
138+
from .packets import BncsPacket, SID_CHATEVENT
139+
pak = BncsPacket(SID_CHATEVENT)
140+
pak.insert_dword(self.eid.value)
141+
pak.insert_dword(self.flags.value)
142+
pak.insert_dword(self.ping)
143+
144+
hidden_value = 0xbaadf00d
145+
if hide_private_info:
146+
pak.insert_dword(0)
147+
pak.insert_dword(hidden_value)
148+
pak.insert_dword(hidden_value)
149+
else:
150+
pak.insert_ipv4(self.ip_address or 0)
151+
pak.insert_dword(self.account_id or hidden_value)
152+
pak.insert_dword(self.regauth or hidden_value)
153+
154+
pak.insert_string(self.username)
155+
pak.insert_string(self.text, encoding=self.text_encoding)
156+
return pak
157+
158+
@classmethod
159+
def parse(cls, packet):
160+
from .packets import BncsReader
161+
162+
if not isinstance(packet, BncsReader):
163+
raise TypeError("ChatEvent can only be parsed from a BncsReader")
164+
165+
event = cls(packet.get_dword())
166+
flag_type = ChannelFlags if event.eid == ChatEventType.JoinChannel else UserFlags
167+
168+
event.flags = flag_type(packet.get_dword())
169+
event.ping = packet.get_dword()
170+
event.ip_address = packet.get_ipv4()
171+
event.account_id = packet.get_dword()
172+
event.regauth = packet.get_dword()
173+
event.username = packet.get_string()
174+
175+
# The text field can have mixed encodings depending on context
176+
event.text = packet.get_string(encoding=None)
177+
if event.eid not in [ChatEventType.ShowUser, ChatEventType.UserJoin, ChatEventType.UserLeave,
178+
ChatEventType.FlagUpdate]:
179+
try:
180+
event.text = event.text.decode("utf-8")
181+
event.text_encoding = "utf-8"
182+
except UnicodeDecodeError:
183+
event.text = event.text.decode("latin-1", errors='ignore')
184+
event.text_encoding = "latin-1"
185+
186+
return event

0 commit comments

Comments
 (0)