leonhard-s / auraxium Goto Github PK
View Code? Open in Web Editor NEWA high-level Python wrapper for the PlanetSide 2 API.
Home Page: https://auraxium.readthedocs.io/
License: MIT License
A high-level Python wrapper for the PlanetSide 2 API.
Home Page: https://auraxium.readthedocs.io/
License: MIT License
Since one of the recent API restarts/changes, these redirect errors are now significantly more common than before (possibly one of the endpoints the load balancer forwards requests to went bad?)
Either way, these errors now are also intermittent, so the code checking for and handling them must also be included in the auto-back off system.
This also warrants checking to see if these redirects work now, which would mean that #14 must be reopened. The new solution might be to just back off up to 5 seconds before giving up and reporting the API as unreachable.
Auraxium does currently not provide useful errors if the API is undergoing maintenance.
This is due to the Census API not responding normally but instead redirecting the request to other sites, including:
As neither of these sites exists since the game left SOE, this causes a lower-level error like aiohttp.InvalidURL:
or aiohttp.ClientConnectorError:
, neither of which is unique to this error state.
aiohttp.ClientSession.get()
call here and provoke another error that way that we can catch.aiohttp.ClientResponse
instance for the failed request, we could use its history to detect the redirect to a Sony domain.station.sony.com
, which generally points towards maintenance taking place (still a tiny risk of masking real errors).In recent versions of Python, the SSL certificate bypass hack from #55 no longer works, and the default context used can even prevent successful connections entirely.
As a hotfix, the bypass will be disabled in the next version. This should not cause any interruptions to clients as the certs are still good for a few more months (and maybe will be updated this time, rather than expiring).
A more comprehensive fix will follow in conjunction with a migration to the new websockets
API. Auraxium is currently using the legacy client-based interface for connections, and any SSL context customization required is tied to it, so might as well do both rather than having to re-do it in a few months anyway.
Exception raised when trying to retrieve a character's items
Exception when awaiting a character's items
The function should return the requested character's items
Steps to reproduce the behavior:
await char.items()
import auraxium
from auraxium import ps2
import asyncio
async def main():
async with auraxium.Client() as client:
char = await client.get_by_name(ps2.Character, 'ElReyZero')
items = await char.items()
print(items)
asyncio.run(main())
Traceback (most recent call last):
File "d:\Users\User\Documents\testing.py", line 29, in
asyncio.run(main())
File "D:\Users\User\AppData\Roaming\Python\Python310\lib\asyncio\runners.py", line 44, in run
return loop.run_until_complete(main)
File "D:\Users\User\AppData\Roaming\Python\Python310\lib\asyncio\base_events.py", line 641, in run_until_complete
return future.result()
File "d:\Users\User\Documents\testing.py", line 11, in main
print(await char.items())
File "D:\Users\User\AppData\Roaming\Python\Python310\lib\site-packages\auraxium_proxy.py", line 168, in flatten
return [e async for e in self]
File "D:\Users\User\AppData\Roaming\Python\Python310\lib\site-packages\auraxium_proxy.py", line 168, in
return [e async for e in self]
File "D:\Users\User\AppData\Roaming\Python\Python310\lib\site-packages\auraxium_proxy.py", line 153, in anext
await self._poll()
File "D:\Users\User\AppData\Roaming\Python\Python310\lib\site-packages\auraxium_proxy.py", line 71, in poll
list = self._resolve_nested_payload(payload)
File "D:\Users\User\AppData\Roaming\Python\Python310\lib\site-packages\auraxium_proxy.py", line 129, in resolve_nested_payload
data.extend(resolve_join(join, parent))
File "D:\Users\User\AppData\Roaming\Python\Python310\lib\site-packages\auraxium_proxy.py", line 106, in resolve_join
value = element[f'{on}join{join.data.collection}']
KeyError: 'item_id_join_item'
3.11 Traceback: https://pastebin.com/kpz1SpWL
There is no way to add terms to a join.
In the constructor, it says:
# Additional kwargs are passed on to the `add_term` method
self._terms: List[Term] = []
_ = [Term(k.replace('__', '.'), kwargs[k]) for k in kwargs]
But the terms are not added to any list, and there is no add_term
method in Join.py.
The only available method is terms()
, which appears to expect a list of terms, but offers no way to convert the strings to terms.
def terms(self, *args: Term) -> 'Join':
"""Apply the given list of terms to the join."""
self._terms = list(args)
return self
One option is to mimic Query.py and implement the same add_terms()
and tweak the Join constructor so it accepts terms as kwargs.
On a side note, why is it self._terms
for Join and self.terms
for Query?
I just wanted to get some hear if there were some other thoughts before making a PR.
A lot of common operations, like calculating a basic statistic overview for a player, is fairly inefficient under the current object model.
It therefore makes sense to create helper methods that use a single, highly optimised query to retrieve all this data at once, then format it nicely before returning it to the user.
These helpers shall be called "reports" internally for the time being.
It was planned to provide an intermediate endpoint between the low-level auraxium.census
module and the high-level object model. Maybe reports could be this endpoint in the form of a Report
base class?
Currently, all API interactions are handled by the same client instance. In the interest of simplicity, it is worth exploring whether their functionality could be split or at least moved to different definitions (this would also allow making the websocket endpoint an optional dependency).
In the latter case, the EventClient
would subclass the regular Client
class to extend it with websocket-specific functionality.
I cannot think of a plausible use-case in which the event stream would be required on its own, without any need for REST API access.
When using the @client.trigger(EventType.DEATH)
(or any other trigger), you cannot pass a list of conditions to filter the websocket events. Here's your event streaming example, with the simplest condition I could find:
loop = asyncio.get_event_loop()
def example_condition(payload):
if payload['world_id'] == "1": # I know you can use the world filter via subscription, but this is a simple filter that allows many results
return True
return False
async def main():
@client.trigger(auraxium.EventType.BATTLE_RANK_UP, conditions=[example_condition])
async def print_levelup(event):
char_id = int(event.payload['character_id'])
char = await client.get_by_id(ps2.Character, char_id)
# NOTE: This value is likely different from char.data.battle_rank as
# the REST API tends to lag by a few minutes.
new_battle_rank = int(event.payload['battle_rank'])
print(f'{await char.name_long()} has reached BR {new_battle_rank}!')
loop.create_task(main())
loop.run_forever()
This raises the error
File "virtualenvs/ps2elo-VDVCoL-1/lib/python3.9/site-packages/auraxium/event.py", line 680, in trigger
trigger = Trigger(event, *args, name=name, **kwargs)
TypeError: __init__() got an unexpected keyword argument 'conditions'
In the init for Trigger, there's no way to provide conditions, except after initializing the object. As far as I know, it's not possible to modify the trigger object while also using the decorator.
Lines 267 to 288 in 5a80345
Unless I'm missing something obvious that hasn't been documented, it seems like the easiest thing to do would be something like:
def __init__(self, ..., conditions: List[Union[bool, Callable[[CensusData], bool]]] = None, ...):
...
self.conditions: List[Union[bool, Callable[[CensusData], bool]]] = conditions if conditions is not None else []
...
I'm happy to make a PR to fix this if you want.
(note: this issue is referring to the documentation at https://auraxium.readthedocs.io/en/latest/api/payloads.html#events, not the github or code documentation)
Some GainExperience events do not look quite how you might expect.
For example, grenade assist experience events (IDs 550-555) unintuitively have character_id = killing player & other_id = player killed. There can be multiple events sent for a single kill if the player killed was afflicted by multiple kinds of grenades. I don't know if there are other kinds of experience events that behave like this.
It'd be useful to have a doc page listing what character_id and other_id pertain to for each experience ID, or at least the weird or non-obvious cases. Perhaps group them by character_id-other_id combinations: killer-killed, player-assisted teammate, no other_id etc.
Could also note somewhere experience events that do not seem to actually get sent.
When pydantic encounters an invalid payload, the following exception is raised (example taken from #50):
auraxium\event\_client.py:167> exception=ValidationError(model='MetagameEvent', errors=[{'loc': ('experience_bonus',), 'msg': 'value is not a valid integer', 'type': 'type_error.integer'}])>
This being an error is of no benefit to the user as this is due to a model mismatch in the library and cannot be fixed by the user.
Instead, we should ignore these payloads with a warning and ask the user to submit an issue so the model can be fixed.
Additional stats and metrics to provide to the user to allow them to judge the health of their application. This has no set date or priority yet, just collecting ideas for now.
Forked repositories do not have access to the unencrypted GitHub secrets, which causes the unit tests to fail.
Organisations and private repositories can pass these secrets along, but public repositories currently cannot, so we will have to skip the tests that require secrets on forked repositories.
Hello,
For the character collection, is there a reason Client.get_by_name
is using a query with case(False)
on name.first
instead of a simple query on name.first_lower
:
Line 197 in 1454707
While the docstring for case
mentions that case-insensitive look-ups are significantly slower and should be avoided when possible, citing the character collection as an example?
auraxium/auraxium/census/_query.py
Line 299 in 1454707
Hi again 👋🏻
I'm using the outfit.leader() classmethod expecting to get the outfit leader's character data. The documentation says the object returned is Character
:
But looking at the response it looks more like OutfitMember
.
EDIT: Removed second issue, user error.
The current placeholder proxy objects work, but do not supported nested operations. It'd be very neat if they did.
Example for such a currently unsupported interface:
outfit = await ps2.Outfit.get_by_tag('<your_tag_here>', client=auraxium.Client())
# This is now a proxy object, awaiting it would return a list of OutfitMember instances
members = outfit.members()
# The character attribute failed over to the OutfitMember class and this is now a
# SequenceProxy of Character, with the URL dynamically updated in the background.
characters = outfit.members().character()
Supporting this in parallel to the existing syntax (which I would like to keep) either requires a revamp of how proxy objects work, or some decorator-infused Descriptor object that allows accessing the URL used to link these related object types.
Currently, the "active" classes (e.g. Character
, Outfit
) are tightly associated with their respective data class (CharacterData
and OutfitData
respectively).
This seemed like a promising idea initially, but it is already starting to break down with relational data types, which will only worsen as generic sub-query interfaces (#15) or reports (#18) are implemented. There simply are many types of server responses that do not match a collection.
It seems like moving all of these data classes into a separate models
module would be a good solution to underline this difference, and it would greatly declutter some of the wordier modules like ps2.fire
as well.
When working with websocket data, it is common to have a barrage of similar responses sent to the REST API all the time, like when resolving experience IDs or character names.
For these time-insensitive use-cases, it would be better to bundle these requests depending on their URL and query as part of a single, larger query.
A few notes on implementation:
auraxium.census.Query
level (as opposed to the object model or HTTP level)There were plans for the proxy system to take on similar capabilities at one point. We should make sure this does not overlap with the new proxy system (#22) too much.
With the internal restructuring going on in the models
branch right now (065a0e9 and following), unit tests for the object model should be added before merging to ensure compatibility.
Housekeeping:
cache_test.py
census_test.py
query_test.py
Object model tests:
models
submodule. These will be run regularly to ensure the object model matches the API, without having to wait for users to find errors.Bonus points:
The connection certs for the API server expired three times in the last few years, resulting in Auraxium not connecting at all.
Since the PS2 API event stream and any fetched data are both publicly available, insecure connections are fine for most apps.
A flag should be added that falls back to insecure connections if the original certification check fails. The default behaviour will still be use use secure connections.
Hello,
I am quite confused on how I would go about obtaining a player's overall kills, deaths and KDR.
I am unsure what method I need to call on Character in order to obtain these stats.
Would I use stat()
or stat_by_faction()
or stat_history()
?
Appreciate any help or guidance you can give.
The object model docstrings are not using any cross-referencing yet, and they're also lacklustre regarding helpfulness to the user.
Conforming this documentation to the standard set for the other modules is a prerequisite to new users being able to use the object model effectively.
Turns out type-enforcing data classes are a thing that already exists.
The move to pydantic should clean the models up a fair bit, too. I'll take a closer look at it ASAP, this should be dealt with before any other shenanigans get finalised.
Hi,
I just ran into an issue using the library.
The following code produces the error:
payload = await run_query(query, session=self._client.session)
line 106, in run_query
data: CensusData = await response.json()
aiohttp.client_exceptions.ContentTypeError: 0, message='Attempt to decode JSON with unexpected mimetype: ', url=URL('https://census.daybreakgames.com/s:example/get/ps2:v2/characters_online_status?character_id=id
My code:
import auraxium
import asyncio
async def main():
async with auraxium.Client() as client:
player = await client.get_by_name(auraxium.ps2.Character, "character")
print(await player.events_grouped())
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Hi there👋🏻
In the tests API response for faction.json
I can see that image_path
should be present however, I'm not receiving a value for it from the API.
Can anyone replicate this?
My code:
async with auraxium.Client() as client:
# Get Character object
player = await client.get_by_name(auraxium.ps2.Character, "Player")
faction = await player.faction()
print(faction.data)
# image_path is not present in the data.
The response data missing image_path
:
faction_id=1 name=LocaleData(de='Vanu-Souveränität', en='Vanu Sovereignty', es='Soberanía Vanu', fr='Souveraineté Vanu', it='Sovranità Vanu') code_tag='VS' user_selectable=True
Relational tables like characters_item
are currently exposed via a provisional interface that effectively wraps a census.Query
object with an anonymous **kwargs
annotation. This makes them difficult to use, while still not featuring the full join capabilities of the underlying query.
Since this system was introduced, the URL generator has been promoted to be part of the main API. It would therefore be possible to just turn these into query factories, then have the user perform the request.
Alternatively, these methods need to be extended with useful argument types to make them easier to use. Users who want to use the lower-level Query
interface can always generate a query to a given instance via the .query()
factory and then join away as per the census
module API.
Since the Census API might change at any moment (and break at any moment...), it could be very useful to have tests to ensure everything is working. Either to fix things on our side, or to warn the devs that they broke something.
Hi, I'm using a slightly modified version of a snippet of code from the README.
I have altered: @client.trigger(event.BattleRankUp)
to @client.trigger(event.MetagameEvent)
.
Here is my code:
import asyncio
from auraxium import event, ps2
async def main():
# Initialise event streaming client to the Planetside 2 Census API
client = event.EventClient(service_id="s:example")
# Register client trigger
@client.trigger(event.MetagameEvent)
async def show_event(evt):
print(repr(evt))
print(evt)
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.create_task(main())
loop.run_forever()
However, the following error occurs on when an event is received:
auraxium\event\_client.py:167> exception=ValidationError(model='MetagameEvent', errors=[{'loc': ('experience_bonus',), 'msg': 'value is not a valid integer', 'type': 'type_error.integer'}])>
Is my usage incorrect?
The original object model was very similar to the native API tables/collections.
This makes using the wrapper easier for people used to the Census API's structure, but the entire point of the wrapper was to abstract its clumsy tables into a simple system any player can make sense of without digging through piles of documentation.
The differences will be slight for most common objects (characters, weapons, worlds, etc.), but some tables, more abstraction might be better. Some examples:
Is this a step we want to make? The existing Query interface would always remain available for users that seek lower-level access or need features that cannot be easily implemented via objects.
When an error is encountered when parsing a payload, the root converter function' (e.g. int(<something>
's) ValueError
propagates out to the user.
Instead, this should be wrapped in a neat PayloadError
informing the user that the payload encountered could not be parsed.
Currently, the only way to use the images is to concatenate the URL yourself using the Ps2Object.data
keys. The image-related keys image_id
, image_set_id
, and image_path
are duplicated for every instance.
It would make sense to move these image-specific keys to a separate mix-in class. This would also reduce the attribute count for the affected data classes.
Additionally, this class could hold information on how to retrieve the image/URL and inject its attribute information into the other classes via Sphinx (as it will follow inheritance trees).
Example mix-in class:
@dataclasses.dataclass(frozen=True)
class HasImage:
"""Mix-in class for data types with linked image assets.
Lots of detailed docs about using images can go here, and
the attribute documentation below will be included for
inheriting classes thanks to Sphinx/autodoc.
Attributes:
image_id: The unique ID of the image asset.
...
"""
image_id: int
image_set_id: int
image_path: str
Regular dataclass when using the mix-in:
@dataclasses.dataclass(frozen=True)
class AchievementData(Ps2Data, HasImage):
achievement_id: int
item_id: int
...
# image_set_id: int
# image_id: int
# image_path: str
@classmethod
def from_census(cls, data: CensusData) -> 'AchievementData':
return cls(
# NOTE: Image assets provided first as the parent class is
# populated first
int(data['image_set_id']),
int(data['image_id']),
str(data['image_path']),
# Regular attributes provided below
int(data['achievement_id']),
int(data['item_id']),
int(data['objective_group_id']),
int(data['reward_id']),
bool(int(data['repeatable'])),
LocaleData.from_census(data['name']),
LocaleData.from_census(data['description']))
Describe the bug
Inner joins are currently all separated with parenthesis: c:join=join1(join1a)(join1b)
Expected behavior
Inner joins should be separated with a comma : c:join=join1(join1a, join1b)
Several collections are incomplete, such as ps2/experience
or ps2/loadout
. It would be beneficial to have an endpoint as part of the object model that allows hard-coding certain dummy payloads to specific IDs.
For example, if a user attempts to retrieve a Loadout
instance and a NotFoundError
is raised, the API wrapper could consult its fallback table and return a dummy payload corresponding to the entries below:
loadout_id |
profile_id |
faction_id |
code_name |
---|---|---|---|
28 | 190 | 4 | NSO Infiltrator |
29 | 191 | 4 | NSO Light Assault |
30 | 192 | 4 | NSO Medic |
31 | 193 | 4 | NSO Engineer |
32 | 194 | 4 | NSO Heavy Assault |
45 | 252 | 4 | NSO MAX |
This would fix the error for the user despite the bad API data, but still allows falling back to the API data if it becomes available in the future.
Apologies, I'm too rusty to fix this myself, though I can find the right values. Take this as a suggestion/warning to others trying to use this for damage calculation tools as much as a bug.
Lets say we're looking at the NS-11A. The weapon datasheet from auraxium
yields this snippet:
damage=143 damage_min=75 damage_max=225
That can't be right.
Lets look at the ps2 wiki query for the NS11-A's page
The damage ranges have the same quirk. still 143, 75, 225. This is the same across all the infantry weapons I've looked at. Reddit knows the API way better than I do, most of my knowledge of it is from stumbling through them.
But when we look under firemodes, each firemode does have the correct values
"max_damage": "143",
"max_damage_range": "10",
"min_damage": "125",
"min_damage_range": "65",
The weapons with different damage per firemode (underbarrel grenade launcher, godsaw, etc) will be edge cases, this can likely be preëmpted by using the description
field, and only grabbing semi-auto
Ideally, fixing these as the default, or at least making a flag for it.
In my experience, the non-recoil stats most important for comparing weapons on paper are:
max_damage
max_damage_range
min_damage
min_damage_range
fire_rate_ms
(time per bullet, not bullet per time as the wiki goes)
Having a method that just outputs these, would help a lot for people new to programming to be able to make tools to compare infantry weapons on paper, instead of the spreadsheets they currently use.
Thanks again for making this! The API is labrynthine and I know everyone is busy, this serves as much as a heads up as an issue.
The latest version on PyPi was uploaded on October 16, 2019. Do you have the ability to upload the latest version, with the bugfixes we've made?
Members of the PS2 API developer community have created fallback endpoints to provide more accurate/reliable data in case the API itself falls behind game updates or has other issues.
With some minor tweaks to the RestClient
and EventClient
classes, it should be straightforward to support any combination of third-party endpoints as long as they are fully compatible with the Census API formats.
Should be reasonably simple to set up once #56 is done.
On readthedocs "Getting started" page there is a Service ID application form (https://auraxium.readthedocs.io/en/latest/usage/basic/serviceIDsignup) - the link is broken/doesn't exist.
Some objects (like ps2.Character
or ps2.Oufit
) require an endpoint that lets the user query related information in a fairly dynamic manner. Good examples are ps2.Character.directives()
, which cannot easily be abstracted through objects.
We therefore need a standard way of dealing with these relational tables in an intuitive fashion - the current implementations are placeholders that only redirect kwargs to a census.Query
.
I am mostly unsure about the syntax to use - it should be easy to use and read, work for any such relational table, and ideally it would follow the same patterns and kwargs as client.get()
or client.find()
.
Syntax suggestions or other feedback welcome!
Currently most data received through the object model is only available through Ps2Object.data
, which is a named tuple containing the original data received.
However, all commonly used data should also be available through class @property
-s. The named tuple is only for reference when making requests.
Some thoughts/tentative guidelines:
datetime.datetime
for timestamps, seconds as float
for any durations, etc.)Example:
# auraxium.ps2.Character class
@property
def playtime(self) -> float:
"""Return the character's playtime in hours.
This uses :attr:`Character.data.times.minutes_played`, divided
by 60.
"""
return self.data.times.minutes_played / 60.0
I feel like picking up the threads where I left off some months ago: a full rewrite.
There are currently two parallel interfaces, one provides the low-level URL generation stuff (which should support any game with a DBG API), the other is the object-oriented, Pythonic model for PlanetSide 2.
I would like to start over with focussing on the Python end, making sure that is a nice, clean API without weird "query commands go into methods" decisions - the end user within Python shouldn't even have to think about query commands existing.
My instinct would be "add unit tests, fix the bugs, start rewriting and keep testing using the shiny new unit tests", but only if there is shared interest in making the API nicer to use.
Is your feature request related to a problem? Please describe.
I would like to pass a list of fields to show into my query without having to call the show()
function.
Describe the solution you'd like
Adding show to the initializer of Query and Join of the type List[str]
Describe alternatives you've considered
Passing in show as a Term
does not add the command.
Additionally, hide_fields()
(or set_hide_fields()
depending on if the PR goes through) takes fields after the first as *args
instead of a list, so it is more challenging to set the fields if you already have a list.
Additional context
Add any other context or screenshots about the feature request here.
I'll add to the PR soon.
Currently, there is a single Event
class representing any payloads returned by the websocket API.
It would be beneficial to return a custom data class for each of the ~20 event types. I do not want to replace the EventType
enum values as listing the subclasses defined for a given base is more hassle than just looking at the enum values.
import asyncio
import auraxium
from auraxium import ps2
async def main():
async with auraxium.Client() as client:
char = await client.get_by_name(ps2.Character, 'SYSTEMICSHOCK')
print(char.name())
print(char.data.prestige_level)
# NOTE: Any methods that might incur network traffic are asynchronous.
# If the data type has been cached locally, no network communication
# is required.
# This will only generate a request once per faction, as the faction
# data type is cached forever by default.
print(await char.faction())
# The outfit data type is only cached for a few seconds before being
# required as it might change.
outfit = await char.outfit()
print(outfit.name())
asyncio.run(main())
There are some issues with the example code
Typos that i fixed in my example above
1.1 NameError: name 'character' is not defined
1.1 'CharacterData' object has no attribute 'asp_rank'
2 AttributeError: module 'asyncio' has no attribute 'run_until_complete'
The example character you provided as an example is not part of an outfit and thus outfit is None, thus outfit.name() raises an Exception. I added a character that is in an outfit in my example.
AttributeError: 'NoneType' object has no attribute 'name'
While the first warning regarding the default service ID makes sense, I also get the following warning that you might want to investigate further.
src/auraxium/auraxium/census/urlgen.py:43: UserWarning: The default service ID is heavily rate-limited. Consider applying for your own service ID at https://census.daybreakgames.com/#devSignup
warnings.warn('The default service ID is heavily rate-limited. '
SYSTEMICSHOCK
1
Vanu Sovereignty
src/auraxium/auraxium/base.py:145: UserWarning: Unexpected keykeys in payload: []
Please report this error as it hints at a mismatch between the auraxium object model and the API.
warnings.warn(
The Wiki is still refering to the original, pre-rewrite version of Auraxium. This should be addressed.
Additional things to add to the Wiki:
auraxium.census
TutorialNot really relevant for real use, but might be impactful for new users as it's a basic example from the README
Config: Python 3.10.4, auraxium 0.2.2, windows 10.
Running the following script as main.py from the github README:
import asyncio
import auraxium
from auraxium import ps2
async def main():
async with auraxium.Client(service_id="REDACTED") as client:
char = await client.get_by_name(ps2.Character, 'auroram')
print(char.name)
print(char.data.prestige_level)
# NOTE: Any methods that might incur network traffic are asynchronous.
# If the data type has been cached locally, no network communication
# is required.
# This will only generate a request once per faction, as the faction
# data type is cached forever by default.
print(await char.faction())
# The online status is never cached as it is bound to change at any
# moment.
print(await char.is_online())
asyncio.run(main())
The script is working, data from the api is properly displayed => OK
RuntimeError raised during the cleanup phase:
Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x0000016243D50280>
Traceback (most recent call last):
File "[REDACTED]\Python310\lib\asyncio\proactor_events.py", line 116, in __del__
self.close()
File "[REDACTED]\Python310\lib\asyncio\proactor_events.py", line 108, in close
self._loop.call_soon(self._call_connection_lost, None)
File "[REDACTED]\Python310\lib\asyncio\base_events.py", line 750, in call_soon
self._check_closed()
File "[REDACTED]\Python310\lib\asyncio\base_events.py", line 515, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
Adding asyncio.sleep(2)
at the end of the main
function fixes the problem, I assume the connection is not closed properly before the loop is closed.
The trigger system gives us some redundancy to define custom events aside from the ones defined on Daybreak's side. One useful custom event for outfit bots would be to detect members joining or leaving the outfit.
This could easily be done by polling the outfit_member
collection and emitting the event if a change is detected.
Alternatively, players might show up as being part of an outfit via the capture/defence experience ticks - this would have to be tested.
As mentioned in #23, there currently exist a large number of tiny modules; only defining 1 or 2 classes each.
These should be grouped together thematically, as was already done for directive-related classes (ps2.directive.py
) or the weapon firing mechanics (ps2.fire.py
). Similar grouping must be performed for other modules to get them to a sensible length, both for the classes in auraxium.ps2
, and auraxium.models
(since the two are tightly linked).
The object model currently raises error when expected keys are not present in a given payload dictionary received. This is fine since the current object representation is closely tied to the payload itself.
However, if a new field is added (as was done for ASP rank with ps2/characters
and the prestige_level
field), the object model currently quietly ignores the extraneous key.
We could change the auraxium.base.Ps2Data.from_census()
method(s) in the object model to use dict.pop()
rather than direct member access or dict.get()
. That way, any expected keys are consumed, and any leftovers are new, fresh, and might provoke some form of UnexpectedPayloadWarning
.
Things to look out for:
.pop()
-ing out keys, since that payload might be reused elsewhere_join_
in them, since the object model never creates any custom-named joinsThe exponential backoff system is currently only supported for object-model queries handled by the main client.
This logic should be moved outside the request
module and extended to include other use-cases such as the event stream's websocket reconnect attempts.
In auraxium/query.py
, __init__()
method create two arrays hide_fields
and show_fields
to store the fields given to the methods hide_fields()
and show_fields()
.
Since the arrays and the methods have the same name, the arrays overwrite the methods, causing a TypeError: 'list' object is not callable
when trying to call the methods.
The same happens in auraxium/join.py
with both the arrays and the methods called hide
and show
.
Describe the bug
Fields are currently separated with a comma c:join=join1^hide:a,b^show:c,d
Expected behavior
Fields must be separated with single quote c:join=join1^hide:a'b^show:c'd
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.