| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  | import os | 
					
						
							|  |  |  | import logging | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  | import typing | 
					
						
							|  |  |  | import asyncio | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | os.environ["KIVY_NO_CONSOLELOG"] = "1" | 
					
						
							|  |  |  | os.environ["KIVY_NO_FILELOG"] = "1" | 
					
						
							|  |  |  | os.environ["KIVY_NO_ARGS"] = "1" | 
					
						
							| 
									
										
										
										
											2021-11-08 18:57:03 +01:00
										 |  |  | os.environ["KIVY_LOG_ENABLE"] = "0" | 
					
						
							| 
									
										
										
										
											2021-10-29 10:03:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  | from kivy.app import App | 
					
						
							| 
									
										
										
										
											2021-10-29 10:03:15 +02:00
										 |  |  | from kivy.core.window import Window | 
					
						
							| 
									
										
										
										
											2021-11-19 21:25:01 +01:00
										 |  |  | from kivy.core.clipboard import Clipboard | 
					
						
							| 
									
										
										
										
											2021-11-21 23:45:15 +01:00
										 |  |  | from kivy.core.text.markup import MarkupLabel | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  | from kivy.base import ExceptionHandler, ExceptionManager, Config, Clock | 
					
						
							| 
									
										
										
										
											2021-10-29 10:03:15 +02:00
										 |  |  | from kivy.factory import Factory | 
					
						
							|  |  |  | from kivy.properties import BooleanProperty, ObjectProperty | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  | from kivy.uix.button import Button | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  | from kivy.uix.gridlayout import GridLayout | 
					
						
							|  |  |  | from kivy.uix.textinput import TextInput | 
					
						
							|  |  |  | from kivy.uix.recycleview import RecycleView | 
					
						
							|  |  |  | from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelItem | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  | from kivy.uix.boxlayout import BoxLayout | 
					
						
							| 
									
										
										
										
											2021-10-29 10:03:15 +02:00
										 |  |  | from kivy.uix.floatlayout import FloatLayout | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  | from kivy.uix.label import Label | 
					
						
							| 
									
										
										
										
											2021-10-22 05:25:09 +02:00
										 |  |  | from kivy.uix.progressbar import ProgressBar | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  | from kivy.utils import escape_markup | 
					
						
							|  |  |  | from kivy.lang import Builder | 
					
						
							| 
									
										
										
										
											2021-11-19 21:25:01 +01:00
										 |  |  | from kivy.uix.recycleview.views import RecycleDataViewBehavior | 
					
						
							|  |  |  | from kivy.uix.behaviors import FocusBehavior | 
					
						
							|  |  |  | from kivy.uix.recycleboxlayout import RecycleBoxLayout | 
					
						
							|  |  |  | from kivy.uix.recycleview.layout import LayoutSelectionBehavior | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | import Utils | 
					
						
							|  |  |  | from NetUtils import JSONtoTextParser, JSONMessagePart | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  | if typing.TYPE_CHECKING: | 
					
						
							|  |  |  |     import CommonClient | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     context_type = CommonClient.CommonContext | 
					
						
							|  |  |  | else: | 
					
						
							|  |  |  |     context_type = object | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-30 09:09:21 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-29 10:03:15 +02:00
										 |  |  | # I was surprised to find this didn't already exist in kivy :( | 
					
						
							|  |  |  | class HoverBehavior(object): | 
					
						
							|  |  |  |     """from https://stackoverflow.com/a/605348110""" | 
					
						
							|  |  |  |     hovered = BooleanProperty(False) | 
					
						
							|  |  |  |     border_point = ObjectProperty(None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, **kwargs): | 
					
						
							|  |  |  |         self.register_event_type('on_enter') | 
					
						
							|  |  |  |         self.register_event_type('on_leave') | 
					
						
							|  |  |  |         Window.bind(mouse_pos=self.on_mouse_pos) | 
					
						
							| 
									
										
										
										
											2021-10-31 08:07:37 -07:00
										 |  |  |         Window.bind(on_cursor_leave=self.on_cursor_leave) | 
					
						
							| 
									
										
										
										
											2021-10-29 10:03:15 +02:00
										 |  |  |         super(HoverBehavior, self).__init__(**kwargs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def on_mouse_pos(self, *args): | 
					
						
							|  |  |  |         if not self.get_root_window(): | 
					
						
							|  |  |  |             return  # do proceed if I'm not displayed <=> If have no parent | 
					
						
							|  |  |  |         pos = args[1] | 
					
						
							|  |  |  |         # Next line to_widget allow to compensate for relative layout | 
					
						
							|  |  |  |         inside = self.collide_point(*self.to_widget(*pos)) | 
					
						
							|  |  |  |         if self.hovered == inside: | 
					
						
							|  |  |  |             return  # We have already done what was needed | 
					
						
							|  |  |  |         self.border_point = pos | 
					
						
							|  |  |  |         self.hovered = inside | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if inside: | 
					
						
							|  |  |  |             self.dispatch("on_enter") | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.dispatch("on_leave") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-31 08:07:37 -07:00
										 |  |  |     def on_cursor_leave(self, *args): | 
					
						
							|  |  |  |         # if the mouse left the window, it is obviously no longer inside the hover label. | 
					
						
							|  |  |  |         self.hovered = BooleanProperty(False) | 
					
						
							|  |  |  |         self.border_point = ObjectProperty(None) | 
					
						
							|  |  |  |         self.dispatch("on_leave") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-29 10:03:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | Factory.register('HoverBehavior', HoverBehavior) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ServerToolTip(Label): | 
					
						
							|  |  |  |     pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ServerLabel(HoverBehavior, Label): | 
					
						
							|  |  |  |     def __init__(self, *args, **kwargs): | 
					
						
							|  |  |  |         super(ServerLabel, self).__init__(*args, **kwargs) | 
					
						
							|  |  |  |         self.layout = FloatLayout() | 
					
						
							|  |  |  |         self.popuplabel = ServerToolTip(text="Test") | 
					
						
							|  |  |  |         self.layout.add_widget(self.popuplabel) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def on_enter(self): | 
					
						
							|  |  |  |         self.popuplabel.text = self.get_text() | 
					
						
							|  |  |  |         App.get_running_app().root.add_widget(self.layout) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def on_leave(self): | 
					
						
							|  |  |  |         App.get_running_app().root.remove_widget(self.layout) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_text(self): | 
					
						
							|  |  |  |         if self.ctx.server: | 
					
						
							|  |  |  |             ctx = self.ctx | 
					
						
							|  |  |  |             text = f"Connected to: {ctx.server_address}." | 
					
						
							|  |  |  |             if ctx.slot is not None: | 
					
						
							| 
									
										
										
										
											2021-10-30 07:52:03 +02:00
										 |  |  |                 text += f"\nYou are Slot Number {ctx.slot} in Team Number {ctx.team}, " \ | 
					
						
							|  |  |  |                         f"named {ctx.player_names[ctx.slot]}." | 
					
						
							| 
									
										
										
										
											2021-10-29 10:03:15 +02:00
										 |  |  |                 if ctx.items_received: | 
					
						
							|  |  |  |                     text += f"\nYou have received {len(ctx.items_received)} items. " \ | 
					
						
							|  |  |  |                             f"You can list them in order with /received." | 
					
						
							|  |  |  |                 if ctx.total_locations: | 
					
						
							|  |  |  |                     text += f"\nYou have checked {len(ctx.checked_locations)} " \ | 
					
						
							|  |  |  |                             f"out of {ctx.total_locations} locations. " \ | 
					
						
							|  |  |  |                             f"You can get more info on missing checks with /missing." | 
					
						
							|  |  |  |                 if ctx.permissions: | 
					
						
							|  |  |  |                     text += "\nPermissions:" | 
					
						
							|  |  |  |                     for permission_name, permission_data in ctx.permissions.items(): | 
					
						
							|  |  |  |                         text += f"\n    {permission_name}: {permission_data}" | 
					
						
							| 
									
										
										
										
											2021-11-07 14:42:05 +01:00
										 |  |  |                 if ctx.hint_cost is not None and ctx.total_locations: | 
					
						
							| 
									
										
										
										
											2021-10-29 10:03:15 +02:00
										 |  |  |                     text += f"\nA new !hint <itemname> costs {ctx.hint_cost}% of checks made. " \ | 
					
						
							| 
									
										
										
										
											2021-10-30 07:52:03 +02:00
										 |  |  |                             f"For you this means every {max(0, int(ctx.hint_cost * 0.01 * ctx.total_locations))} " \ | 
					
						
							|  |  |  |                             "location checks." | 
					
						
							| 
									
										
										
										
											2021-10-29 10:03:15 +02:00
										 |  |  |                 elif ctx.hint_cost == 0: | 
					
						
							|  |  |  |                     text += "\n!hint is free to use." | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 text += f"\nYou are not authenticated yet." | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return text | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return "No current server connection. \nPlease connect to an Archipelago server." | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def ctx(self) -> context_type: | 
					
						
							|  |  |  |         return App.get_running_app().ctx | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MainLayout(GridLayout): | 
					
						
							|  |  |  |     pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ContainerLayout(FloatLayout): | 
					
						
							|  |  |  |     pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-19 21:25:01 +01:00
										 |  |  | class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior, | 
					
						
							|  |  |  |                                  RecycleBoxLayout): | 
					
						
							|  |  |  |     """ Adds selection and focus behaviour to the view. """ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SelectableLabel(RecycleDataViewBehavior, Label): | 
					
						
							|  |  |  |     """ Add selection support to the Label """ | 
					
						
							|  |  |  |     index = None | 
					
						
							|  |  |  |     selected = BooleanProperty(False) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def refresh_view_attrs(self, rv, index, data): | 
					
						
							|  |  |  |         """ Catch and handle the view changes """ | 
					
						
							|  |  |  |         self.index = index | 
					
						
							|  |  |  |         return super(SelectableLabel, self).refresh_view_attrs( | 
					
						
							|  |  |  |             rv, index, data) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def on_touch_down(self, touch): | 
					
						
							|  |  |  |         """ Add selection on touch down """ | 
					
						
							|  |  |  |         if super(SelectableLabel, self).on_touch_down(touch): | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         if self.collide_point(*touch.pos): | 
					
						
							| 
									
										
										
										
											2021-11-22 17:44:14 +01:00
										 |  |  |             if self.selected: | 
					
						
							|  |  |  |                 self.parent.clear_selection() | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 # Not a fan of the following few lines, but they work. | 
					
						
							|  |  |  |                 temp = MarkupLabel(text=self.text).markup | 
					
						
							|  |  |  |                 text = "".join(part for part in temp if not part.startswith(("[color", "[/color]"))) | 
					
						
							|  |  |  |                 cmdinput = App.get_running_app().textinput | 
					
						
							| 
									
										
										
										
											2021-11-28 01:51:13 +01:00
										 |  |  |                 if not cmdinput.text and " did you mean " in text: | 
					
						
							|  |  |  |                     for question in ("Didn't find something that closely matches, did you mean ", | 
					
						
							|  |  |  |                                      "Too many close matches, did you mean "): | 
					
						
							|  |  |  |                         if text.startswith(question): | 
					
						
							|  |  |  |                             name = Utils.get_text_between(text, question, | 
					
						
							|  |  |  |                                                           "? (") | 
					
						
							| 
									
										
										
										
											2021-11-29 21:35:06 +01:00
										 |  |  |                             cmdinput.text = f"!{App.get_running_app().last_autofillable_command} {name}" | 
					
						
							| 
									
										
										
										
											2021-11-28 01:51:13 +01:00
										 |  |  |                             break | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-22 17:44:14 +01:00
										 |  |  |                 Clipboard.copy(text) | 
					
						
							|  |  |  |                 return self.parent.select_with_touch(self.index, touch) | 
					
						
							| 
									
										
										
										
											2021-11-19 21:25:01 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def apply_selection(self, rv, index, is_selected): | 
					
						
							|  |  |  |         """ Respond to the selection of items in the view. """ | 
					
						
							|  |  |  |         self.selected = is_selected | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  | class GameManager(App): | 
					
						
							|  |  |  |     logging_pairs = [ | 
					
						
							|  |  |  |         ("Client", "Archipelago"), | 
					
						
							|  |  |  |     ] | 
					
						
							| 
									
										
										
										
											2021-11-29 21:35:06 +01:00
										 |  |  |     base_title: str = "Archipelago Client" | 
					
						
							|  |  |  |     last_autofillable_command: str | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  |     def __init__(self, ctx: context_type): | 
					
						
							|  |  |  |         self.title = self.base_title | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  |         self.ctx = ctx | 
					
						
							|  |  |  |         self.commandprocessor = ctx.command_processor(ctx) | 
					
						
							|  |  |  |         self.icon = r"data/icon.png" | 
					
						
							|  |  |  |         self.json_to_kivy_parser = KivyJSONtoTextParser(ctx) | 
					
						
							|  |  |  |         self.log_panels = {} | 
					
						
							| 
									
										
										
										
											2021-11-29 21:35:06 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # keep track of last used command to autofill on click | 
					
						
							|  |  |  |         self.last_autofillable_command = "hint" | 
					
						
							| 
									
										
										
										
											2021-12-02 03:14:26 +01:00
										 |  |  |         autofillable_commands = ("hint_location", "hint", "getitem") | 
					
						
							| 
									
										
										
										
											2021-11-29 21:35:06 +01:00
										 |  |  |         original_say = ctx.on_user_say | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def intercept_say(text): | 
					
						
							|  |  |  |             text = original_say(text) | 
					
						
							|  |  |  |             if text: | 
					
						
							|  |  |  |                 for command in autofillable_commands: | 
					
						
							|  |  |  |                     if text.startswith("!"+command): | 
					
						
							|  |  |  |                         self.last_autofillable_command = command | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |             return text | 
					
						
							|  |  |  |         ctx.on_user_say = intercept_say | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  |         super(GameManager, self).__init__() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def build(self): | 
					
						
							| 
									
										
										
										
											2021-10-29 10:03:15 +02:00
										 |  |  |         self.container = ContainerLayout() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.grid = MainLayout() | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  |         self.grid.cols = 1 | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  |         connect_layout = BoxLayout(orientation="horizontal", size_hint_y=None, height=30) | 
					
						
							| 
									
										
										
										
											2021-10-22 05:25:09 +02:00
										 |  |  |         # top part | 
					
						
							| 
									
										
										
										
											2021-10-29 10:03:15 +02:00
										 |  |  |         server_label = ServerLabel() | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  |         connect_layout.add_widget(server_label) | 
					
						
							| 
									
										
										
										
											2021-11-21 05:47:19 +01:00
										 |  |  |         self.server_connect_bar = TextInput(text="archipelago.gg", size_hint_y=None, height=30, multiline=False, | 
					
						
							|  |  |  |                                             write_tab=False) | 
					
						
							| 
									
										
										
										
											2021-11-01 21:43:17 +01:00
										 |  |  |         self.server_connect_bar.bind(on_text_validate=self.connect_button_action) | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  |         connect_layout.add_widget(self.server_connect_bar) | 
					
						
							|  |  |  |         self.server_connect_button = Button(text="Connect", size=(100, 30), size_hint_y=None, size_hint_x=None) | 
					
						
							|  |  |  |         self.server_connect_button.bind(on_press=self.connect_button_action) | 
					
						
							|  |  |  |         connect_layout.add_widget(self.server_connect_button) | 
					
						
							|  |  |  |         self.grid.add_widget(connect_layout) | 
					
						
							| 
									
										
										
										
											2021-10-22 05:25:09 +02:00
										 |  |  |         self.progressbar = ProgressBar(size_hint_y=None, height=3) | 
					
						
							|  |  |  |         self.grid.add_widget(self.progressbar) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # middle part | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  |         self.tabs = TabbedPanel(size_hint_y=1) | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  |         self.tabs.default_tab_text = "All" | 
					
						
							|  |  |  |         self.log_panels["All"] = self.tabs.default_tab_content = UILog(*(logging.getLogger(logger_name) | 
					
						
							|  |  |  |                                                                          for logger_name, name in | 
					
						
							|  |  |  |                                                                          self.logging_pairs)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for logger_name, display_name in self.logging_pairs: | 
					
						
							|  |  |  |             bridge_logger = logging.getLogger(logger_name) | 
					
						
							|  |  |  |             panel = TabbedPanelItem(text=display_name) | 
					
						
							|  |  |  |             self.log_panels[display_name] = panel.content = UILog(bridge_logger) | 
					
						
							|  |  |  |             self.tabs.add_widget(panel) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.grid.add_widget(self.tabs) | 
					
						
							| 
									
										
										
										
											2021-10-22 00:37:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if len(self.logging_pairs) == 1: | 
					
						
							|  |  |  |             # Hide Tab selection if only one tab | 
					
						
							|  |  |  |             self.tabs.clear_tabs() | 
					
						
							|  |  |  |             self.tabs.do_default_tab = False | 
					
						
							|  |  |  |             self.tabs.current_tab.height = 0 | 
					
						
							|  |  |  |             self.tabs.tab_height = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-22 05:25:09 +02:00
										 |  |  |         # bottom part | 
					
						
							|  |  |  |         bottom_layout = BoxLayout(orientation="horizontal", size_hint_y=None, height=30) | 
					
						
							|  |  |  |         info_button = Button(height=30, text="Command:", size_hint_x=None) | 
					
						
							| 
									
										
										
										
											2021-10-24 23:22:06 +02:00
										 |  |  |         info_button.bind(on_release=self.command_button_action) | 
					
						
							| 
									
										
										
										
											2021-10-22 05:25:09 +02:00
										 |  |  |         bottom_layout.add_widget(info_button) | 
					
						
							| 
									
										
										
										
											2021-11-22 17:44:14 +01:00
										 |  |  |         self.textinput = TextInput(size_hint_y=None, height=30, multiline=False, write_tab=False) | 
					
						
							|  |  |  |         self.textinput.bind(on_text_validate=self.on_message) | 
					
						
							| 
									
										
										
										
											2021-11-21 05:47:19 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         def text_focus(event): | 
					
						
							|  |  |  |             """Needs to be set via delay, as unfocusing happens after on_message""" | 
					
						
							| 
									
										
										
										
											2021-11-22 17:44:14 +01:00
										 |  |  |             self.textinput.focus = True | 
					
						
							| 
									
										
										
										
											2021-11-21 05:47:19 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-22 17:44:14 +01:00
										 |  |  |         self.textinput.text_focus = text_focus | 
					
						
							|  |  |  |         bottom_layout.add_widget(self.textinput) | 
					
						
							| 
									
										
										
										
											2021-10-22 05:25:09 +02:00
										 |  |  |         self.grid.add_widget(bottom_layout) | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  |         self.commandprocessor("/help") | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  |         Clock.schedule_interval(self.update_texts, 1 / 30) | 
					
						
							| 
									
										
										
										
											2021-10-29 10:03:15 +02:00
										 |  |  |         self.container.add_widget(self.grid) | 
					
						
							|  |  |  |         return self.container | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  |     def update_texts(self, dt): | 
					
						
							|  |  |  |         if self.ctx.server: | 
					
						
							| 
									
										
										
										
											2021-10-29 15:18:58 +02:00
										 |  |  |             self.title = self.base_title + " " + Utils.__version__ + \ | 
					
						
							|  |  |  |                          f" | Connected to: {self.ctx.server_address} " \ | 
					
						
							|  |  |  |                          f"{'.'.join(str(e) for e in self.ctx.server_version)}" | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  |             self.server_connect_button.text = "Disconnect" | 
					
						
							| 
									
										
										
										
											2021-10-22 05:25:09 +02:00
										 |  |  |             self.progressbar.max = len(self.ctx.checked_locations) + len(self.ctx.missing_locations) | 
					
						
							|  |  |  |             self.progressbar.value = len(self.ctx.checked_locations) | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  |         else: | 
					
						
							|  |  |  |             self.server_connect_button.text = "Connect" | 
					
						
							| 
									
										
										
										
											2021-10-29 15:18:58 +02:00
										 |  |  |             self.title = self.base_title + " " + Utils.__version__ | 
					
						
							| 
									
										
										
										
											2021-10-22 05:25:09 +02:00
										 |  |  |             self.progressbar.value = 0 | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-24 23:22:06 +02:00
										 |  |  |     def command_button_action(self, button): | 
					
						
							| 
									
										
										
										
											2021-11-19 21:25:01 +01:00
										 |  |  |         if self.ctx.server: | 
					
						
							|  |  |  |             logging.getLogger("Client").info("/help for client commands and !help for server commands.") | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             logging.getLogger("Client").info("/help for client commands and once you are connected, " | 
					
						
							|  |  |  |                                              "!help for server commands.") | 
					
						
							| 
									
										
										
										
											2021-10-24 23:22:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  |     def connect_button_action(self, button): | 
					
						
							|  |  |  |         if self.ctx.server: | 
					
						
							|  |  |  |             self.ctx.server_address = None | 
					
						
							|  |  |  |             asyncio.create_task(self.ctx.disconnect()) | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2021-10-22 00:37:20 +02:00
										 |  |  |             asyncio.create_task(self.ctx.connect(self.server_connect_bar.text.replace("/connect ", ""))) | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  |     def on_stop(self): | 
					
						
							| 
									
										
										
										
											2021-10-29 10:03:15 +02:00
										 |  |  |         # "kill" input tasks | 
					
						
							|  |  |  |         for x in range(self.ctx.input_requests): | 
					
						
							|  |  |  |             self.ctx.input_queue.put_nowait("") | 
					
						
							|  |  |  |         self.ctx.input_requests = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  |         self.ctx.exit_event.set() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def on_message(self, textinput: TextInput): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             input_text = textinput.text.strip() | 
					
						
							|  |  |  |             textinput.text = "" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if self.ctx.input_requests > 0: | 
					
						
							|  |  |  |                 self.ctx.input_requests -= 1 | 
					
						
							|  |  |  |                 self.ctx.input_queue.put_nowait(input_text) | 
					
						
							|  |  |  |             elif input_text: | 
					
						
							|  |  |  |                 self.commandprocessor(input_text) | 
					
						
							| 
									
										
										
										
											2021-11-21 05:47:19 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             Clock.schedule_once(textinput.text_focus) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  |         except Exception as e: | 
					
						
							|  |  |  |             logging.getLogger("Client").exception(e) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def print_json(self, data): | 
					
						
							|  |  |  |         text = self.json_to_kivy_parser(data) | 
					
						
							|  |  |  |         self.log_panels["Archipelago"].on_message_markup(text) | 
					
						
							|  |  |  |         self.log_panels["All"].on_message_markup(text) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FactorioManager(GameManager): | 
					
						
							|  |  |  |     logging_pairs = [ | 
					
						
							|  |  |  |         ("Client", "Archipelago"), | 
					
						
							|  |  |  |         ("FactorioServer", "Factorio Server Log"), | 
					
						
							|  |  |  |         ("FactorioWatcher", "Bridge Data Log"), | 
					
						
							|  |  |  |     ] | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  |     base_title = "Archipelago Factorio Client" | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-30 09:09:21 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | class SNIManager(GameManager): | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |     logging_pairs = [ | 
					
						
							|  |  |  |         ("Client", "Archipelago"), | 
					
						
							|  |  |  |         ("SNES", "SNES"), | 
					
						
							|  |  |  |     ] | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |     base_title = "Archipelago SNI Client" | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-30 09:09:21 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | class TextManager(GameManager): | 
					
						
							|  |  |  |     logging_pairs = [ | 
					
						
							|  |  |  |         ("Client", "Archipelago") | 
					
						
							|  |  |  |     ] | 
					
						
							| 
									
										
										
										
											2021-10-19 05:38:17 +02:00
										 |  |  |     base_title = "Archipelago Text Client" | 
					
						
							| 
									
										
										
										
											2021-09-30 09:09:21 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-02 07:14:55 +01:00
										 |  |  | class FF1Manager(GameManager): | 
					
						
							|  |  |  |     logging_pairs = [ | 
					
						
							|  |  |  |         ("Client", "Archipelago") | 
					
						
							|  |  |  |     ] | 
					
						
							|  |  |  |     base_title = "Archipelago Final Fantasy 1 Client" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  | class LogtoUI(logging.Handler): | 
					
						
							|  |  |  |     def __init__(self, on_log): | 
					
						
							| 
									
										
										
										
											2021-11-17 22:46:32 +01:00
										 |  |  |         super(LogtoUI, self).__init__(logging.INFO) | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  |         self.on_log = on_log | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def handle(self, record: logging.LogRecord) -> None: | 
					
						
							| 
									
										
										
										
											2021-11-01 06:40:29 +01:00
										 |  |  |         self.on_log(self.format(record)) | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class UILog(RecycleView): | 
					
						
							|  |  |  |     cols = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, *loggers_to_handle, **kwargs): | 
					
						
							|  |  |  |         super(UILog, self).__init__(**kwargs) | 
					
						
							|  |  |  |         self.data = [] | 
					
						
							|  |  |  |         for logger in loggers_to_handle: | 
					
						
							|  |  |  |             logger.addHandler(LogtoUI(self.on_log)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 06:40:29 +01:00
										 |  |  |     def on_log(self, record: str) -> None: | 
					
						
							|  |  |  |         self.data.append({"text": escape_markup(record)}) | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def on_message_markup(self, text): | 
					
						
							|  |  |  |         self.data.append({"text": text}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class E(ExceptionHandler): | 
					
						
							|  |  |  |     logger = logging.getLogger("Client") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def handle_exception(self, inst): | 
					
						
							| 
									
										
										
										
											2021-11-01 06:40:29 +01:00
										 |  |  |         self.logger.exception("Uncaught Exception:", exc_info=inst) | 
					
						
							|  |  |  |         return ExceptionManager.PASS | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class KivyJSONtoTextParser(JSONtoTextParser): | 
					
						
							|  |  |  |     color_codes = { | 
					
						
							|  |  |  |         # not exact color names, close enough but decent looking | 
					
						
							|  |  |  |         "black": "000000", | 
					
						
							|  |  |  |         "red": "EE0000", | 
					
						
							|  |  |  |         "green": "00FF7F", | 
					
						
							|  |  |  |         "yellow": "FAFAD2", | 
					
						
							|  |  |  |         "blue": "6495ED", | 
					
						
							|  |  |  |         "magenta": "EE00EE", | 
					
						
							|  |  |  |         "cyan": "00EEEE", | 
					
						
							|  |  |  |         "white": "FFFFFF" | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _handle_color(self, node: JSONMessagePart): | 
					
						
							|  |  |  |         colors = node["color"].split(";") | 
					
						
							|  |  |  |         node["text"] = escape_markup(node["text"]) | 
					
						
							|  |  |  |         for color in colors: | 
					
						
							|  |  |  |             color_code = self.color_codes.get(color, None) | 
					
						
							|  |  |  |             if color_code: | 
					
						
							|  |  |  |                 node["text"] = f"[color={color_code}]{node['text']}[/color]" | 
					
						
							|  |  |  |                 return self._handle_text(node) | 
					
						
							|  |  |  |         return self._handle_text(node) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ExceptionManager.add_handler(E()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Config.set("input", "mouse", "mouse,disable_multitouch") | 
					
						
							| 
									
										
										
										
											2021-09-22 08:02:15 +02:00
										 |  |  | Config.set('kivy', 'exit_on_escape', '0') | 
					
						
							| 
									
										
										
										
											2021-07-30 20:18:03 +02:00
										 |  |  | Builder.load_file(Utils.local_path("data", "client.kv")) |