-
-
Notifications
You must be signed in to change notification settings - Fork 47
/
ui_properties.py
222 lines (193 loc) · 7.53 KB
/
ui_properties.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
"""
The UI Properties will be set and hold in the class definition bellow.
"""
import json
import logging
import xmltodict as xd
from pyplanet.core.signals import pyplanet_start_after
from pyplanet.core.ui.exceptions import UIPropertyDoesNotExist
from pyplanet.utils.functional import empty
logger = logging.getLogger(__name__)
class UIProperties: # pragma: no cover
"""
Set the custom Script UI Properties.
.. tip::
Look at the possible UI Properties right here:
- Trackmania: https://github.com/maniaplanet/script-xmlrpc/blob/master/XmlRpcListing.md#trackmaniauisetproperties
- Shootmania: https://github.com/maniaplanet/script-xmlrpc/blob/master/XmlRpcListing.md#shootmaniauisetproperties
- TM2020: Undocumented by Nadeo, check in-game tool OpenPlanet and discover what properties exist and what it means.
Access this class with:
.. code-block:: python
self.instance.ui_manager.properties
"""
specials = ('_properties', '_instance')
def __init__(self, instance):
"""
:param instance: Instance
:type instance: pyplanet.core.instance.Instance
"""
self._instance = instance
self._raw = None
self._properties = dict()
self._update_properties = dict()
@property
def properties(self):
if self._instance.game.game in ['tm', 'sm']:
if 'ui_properties' in self._properties:
return self._properties['ui_properties']
else:
if self._properties:
return self._properties
return False
async def on_start(self):
await self.reset()
await self.refresh_properties()
self._instance.signals.listen(pyplanet_start_after, self.send_properties)
async def reset(self):
"""
Reset the UI Properties to the default ManiaPlanet ones.
:return:
"""
if self._instance.game.game == 'tm':
method = 'Trackmania.UI.ResetProperties'
elif self._instance.game.game == 'sm':
method = 'Shootmania.UI.ResetProperties'
else:
method = 'Common.UIModules.ResetProperties'
try:
logger.debug('Resetting UIProperties...')
await self._instance.gbx.script(method, response_id=False)
except Exception as e:
logger.warning('Unable to reset UIProperties: {}'.format(str(e)))
async def refresh_properties(self):
if self._instance.game.game == 'tm':
method = 'Trackmania.UI.GetProperties'
elif self._instance.game.game == 'sm':
method = 'Shootmania.UI.GetProperties'
else:
method = 'Common.UIModules.GetProperties'
try:
if self._instance.game.game == 'tm' or self._instance.game.game == 'sm':
self._raw = await self._instance.gbx(method, timeout=2)
self._properties = xd.parse(self._raw['raw_1'])
else:
self._raw = await self._instance.gbx(method, timeout=2)
self._properties = dict()
for entry in self._raw['uimodules']:
self._properties[entry['id']] = entry
except Exception as e:
self._properties = dict()
self._raw = None
def set_visibility(self, element: str, visible: bool):
"""
Set the visibility of the UI Property and don't complain about failing to set. Must be set at the start of the
app(s).
:param element: Element name, such as notices, map_info and chat.
Full list: https://github.com/maniaplanet/script-xmlrpc/blob/master/XmlRpcListing.md#shootmaniauisetproperties
:param visible: Boolean if the element should be visible.
:return: Boolean, true if is set, false if failed to set.
"""
return self.set_attribute(element, 'visible', 'true' if visible else 'false')
def get_visibility(self, element: str, default=empty):
"""
Set the visibility of the UI Property and don't complain about failing to set. Must be set at the start of the
app(s).
:param element: Element name, such as notices, map_info and chat.
Full list: https://github.com/maniaplanet/script-xmlrpc/blob/master/XmlRpcListing.md#shootmaniauisetproperties
:param default: The default value, or an exception if not given.
:return: The boolean if it's visible or raise exception if not exists (or the default if default is given).
"""
value = self.get_attribute(element, 'visible', default)
if isinstance(value, str):
if value.lower() == 'true':
return True
elif value.lower() == 'false':
return False
return value
def set_attribute(self, element: str, attribute: str, value):
"""
Set an attribute of an element and silent if it's not found. Useful to change positions but unsure if it will
and still exists. Returns boolean if it's set successfully.
:param element: Element name
:param attribute: Attribute name
:param value: New value of the attribute.
:return: Boolean if it's set correctly.
"""
if not self._properties:
return False
if self._instance.game.game in ['tm', 'sm']:
if element not in self.properties:
return False
if '@{}'.format(attribute) not in self.properties[element]:
return False
self.properties[element]['@{}'.format(attribute)] = value
else:
if element not in self.properties:
return False
self.properties[element][attribute] = value
if element not in self._update_properties:
self._update_properties[element] = dict(id=element)
self._update_properties[element][attribute] = value
self._update_properties[element]['{}_update'.format(attribute)] = True
return True
def get_attribute(self, element: str, attribute: str, default=empty):
"""
Get an attribute value of an element.
:param element: Element name
:param attribute: Attribute name
:param default: Default if not found.
:return: Boolean if it's set correctly.
"""
if not self._properties:
if default is not empty:
return default
raise UIPropertyDoesNotExist('UI Properties are not present')
if element not in self.properties:
if default is not empty:
return default
raise UIPropertyDoesNotExist('UI Properties has no element with name \'{}\''.format(element))
if self._instance.game.game in ['tm', 'sm'] and '@{}'.format(attribute) not in self.properties[element]:
if default is not empty:
return default
raise UIPropertyDoesNotExist('UI Properties has no attribute with name \'{}\''.format(attribute))
elif attribute not in self.properties[element]:
if default is not empty:
return default
raise UIPropertyDoesNotExist('UI Properties has no attribute with name \'{}\''.format(attribute))
if self._instance.game.game in ['tm', 'sm']:
return self.properties[element]['@{}'.format(attribute)]
return self.properties[element][attribute]
async def send_properties(self, **kwargs):
if not self._properties or (self._instance.game.game in ['tm', 'sm'] and 'ui_properties' not in self._properties):
return
# Decide the method to use.
if self._instance.game.game == 'tm':
method = 'Trackmania.UI.SetProperties'
elif self._instance.game.game == 'sm':
method = 'Shootmania.UI.SetProperties'
else:
method = 'Common.UIModules.SetProperties'
if self._instance.game.game in ['tm', 'sm']:
# Create XML document
try:
xml = xd.unparse(self._properties, full_document=False, short_empty_elements=True)
except Exception as e:
logger.warning('Can\'t convert UI Properties to XML document! Error: {}'.format(str(e)))
return
try:
await self._instance.gbx(method, xml, encode_json=False, response_id=False)
except Exception as e:
logger.warning('Can\'t send UI Properties! Error: {}'.format(str(e)))
return
else:
# Update TM2020 properties.
try:
await self._instance.gbx(
method,
json.dumps(dict(uimodules=list(self._update_properties.values()))),
encode_json=False, response_id=False
)
logger.debug('UI Properties send and reloaded')
except Exception as e:
logger.warning('Can\'t send UI Properties! Error: {}'.format(str(e)))
return