@@ -45,10 +45,21 @@ def get_ssl_context():
45
45
46
46
47
47
class ClientCommandProcessor (CommandProcessor ):
48
+ """
49
+ The Command Processor will parse every method of the class that starts with "_cmd_" as a command to be called
50
+ when parsing user input, i.e. _cmd_exit will be called when the user sends the command "/exit".
51
+
52
+ The decorator @mark_raw can be imported from MultiServer and tells the parser to only split on the first
53
+ space after the command i.e. "/exit one two three" will be passed in as method("one two three") with mark_raw
54
+ and method("one", "two", "three") without.
55
+
56
+ In addition all docstrings for command methods will be displayed to the user on launch and when using "/help"
57
+ """
48
58
def __init__ (self , ctx : CommonContext ):
49
59
self .ctx = ctx
50
60
51
61
def output (self , text : str ):
62
+ """Helper function to abstract logging to the CommonClient UI"""
52
63
logger .info (text )
53
64
54
65
def _cmd_exit (self ) -> bool :
@@ -164,13 +175,14 @@ def _cmd_ready(self):
164
175
async_start (self .ctx .send_msgs ([{"cmd" : "StatusUpdate" , "status" : state }]), name = "send StatusUpdate" )
165
176
166
177
def default (self , raw : str ):
178
+ """The default message parser to be used when parsing any messages that do not match a command"""
167
179
raw = self .ctx .on_user_say (raw )
168
180
if raw :
169
181
async_start (self .ctx .send_msgs ([{"cmd" : "Say" , "text" : raw }]), name = "send Say" )
170
182
171
183
172
184
class CommonContext :
173
- # Should be adjusted as needed in subclasses
185
+ # The following attributes are used to Connect and should be adjusted as needed in subclasses
174
186
tags : typing .Set [str ] = {"AP" }
175
187
game : typing .Optional [str ] = None
176
188
items_handling : typing .Optional [int ] = None
@@ -429,7 +441,10 @@ async def get_username(self):
429
441
self .auth = await self .console_input ()
430
442
431
443
async def send_connect (self , ** kwargs : typing .Any ) -> None :
432
- """ send `Connect` packet to log in to server """
444
+ """
445
+ Send a `Connect` packet to log in to the server,
446
+ additional keyword args can override any value in the connection packet
447
+ """
433
448
payload = {
434
449
'cmd' : 'Connect' ,
435
450
'password' : self .password , 'name' : self .auth , 'version' : Utils .version_tuple ,
@@ -459,13 +474,15 @@ def cancel_autoreconnect(self) -> bool:
459
474
return False
460
475
461
476
def slot_concerns_self (self , slot ) -> bool :
477
+ """Helper function to abstract player groups, should be used instead of checking slot == self.slot directly."""
462
478
if slot == self .slot :
463
479
return True
464
480
if slot in self .slot_info :
465
481
return self .slot in self .slot_info [slot ].group_members
466
482
return False
467
483
468
484
def is_echoed_chat (self , print_json_packet : dict ) -> bool :
485
+ """Helper function for filtering out messages sent by self."""
469
486
return print_json_packet .get ("type" , "" ) == "Chat" \
470
487
and print_json_packet .get ("team" , None ) == self .team \
471
488
and print_json_packet .get ("slot" , None ) == self .slot
@@ -497,13 +514,14 @@ def on_user_say(self, text: str) -> typing.Optional[str]:
497
514
"""Gets called before sending a Say to the server from the user.
498
515
Returned text is sent, or sending is aborted if None is returned."""
499
516
return text
500
-
517
+
501
518
def on_ui_command (self , text : str ) -> None :
502
519
"""Gets called by kivy when the user executes a command starting with `/` or `!`.
503
520
The command processor is still called; this is just intended for command echoing."""
504
521
self .ui .print_json ([{"text" : text , "type" : "color" , "color" : "orange" }])
505
522
506
523
def update_permissions (self , permissions : typing .Dict [str , int ]):
524
+ """Internal method to parse and save server permissions from RoomInfo"""
507
525
for permission_name , permission_flag in permissions .items ():
508
526
try :
509
527
flag = Permission (permission_flag )
@@ -613,6 +631,7 @@ def on_deathlink(self, data: typing.Dict[str, typing.Any]) -> None:
613
631
logger .info (f"DeathLink: Received from { data ['source' ]} " )
614
632
615
633
async def send_death (self , death_text : str = "" ):
634
+ """Helper function to send a deathlink using death_text as the unique death cause string."""
616
635
if self .server and self .server .socket :
617
636
logger .info ("DeathLink: Sending death to your friends..." )
618
637
self .last_death_link = time .time ()
@@ -626,6 +645,7 @@ async def send_death(self, death_text: str = ""):
626
645
}])
627
646
628
647
async def update_death_link (self , death_link : bool ):
648
+ """Helper function to set Death Link connection tag on/off and update the connection if already connected."""
629
649
old_tags = self .tags .copy ()
630
650
if death_link :
631
651
self .tags .add ("DeathLink" )
@@ -635,7 +655,7 @@ async def update_death_link(self, death_link: bool):
635
655
await self .send_msgs ([{"cmd" : "ConnectUpdate" , "tags" : self .tags }])
636
656
637
657
def gui_error (self , title : str , text : typing .Union [Exception , str ]) -> typing .Optional ["kvui.MessageBox" ]:
638
- """Displays an error messagebox"""
658
+ """Displays an error messagebox in the loaded Kivy UI. Override if using a different UI framework """
639
659
if not self .ui :
640
660
return None
641
661
title = title or "Error"
@@ -987,6 +1007,7 @@ async def console_loop(ctx: CommonContext):
987
1007
988
1008
989
1009
def get_base_parser (description : typing .Optional [str ] = None ):
1010
+ """Base argument parser to be reused for components subclassing off of CommonClient"""
990
1011
import argparse
991
1012
parser = argparse .ArgumentParser (description = description )
992
1013
parser .add_argument ('--connect' , default = None , help = 'Address of the multiworld host.' )
@@ -1037,6 +1058,7 @@ async def main(args):
1037
1058
parser .add_argument ("url" , nargs = "?" , help = "Archipelago connection url" )
1038
1059
args = parser .parse_args (args )
1039
1060
1061
+ # handle if text client is launched using the "archipelago://name:pass@host:port" url from webhost
1040
1062
if args .url :
1041
1063
url = urllib .parse .urlparse (args .url )
1042
1064
if url .scheme == "archipelago" :
@@ -1048,6 +1070,7 @@ async def main(args):
1048
1070
else :
1049
1071
parser .error (f"bad url, found { args .url } , expected url in form of archipelago://archipelago.gg:38281" )
1050
1072
1073
+ # use colorama to display colored text highlighting on windows
1051
1074
colorama .init ()
1052
1075
1053
1076
asyncio .run (main (args ))
0 commit comments