Skip to content

Commit

Permalink
Support Epic Online Services (EOS) Protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
BattlefieldDuck committed Nov 14, 2023
1 parent 90fceb9 commit f02b4c2
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ from applications written in the Python language.
from opengsq.protocols.ase import ASE
from opengsq.protocols.battlefield import Battlefield
from opengsq.protocols.doom3 import Doom3
from opengsq.protocols.eos import EOS
from opengsq.protocols.gamespy1 import GameSpy1
from opengsq.protocols.gamespy2 import GameSpy2
from opengsq.protocols.gamespy3 import GameSpy3
Expand Down
1 change: 1 addition & 0 deletions opengsq/protocols/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from opengsq.protocols.ase import ASE
from opengsq.protocols.battlefield import Battlefield
from opengsq.protocols.doom3 import Doom3
from opengsq.protocols.eos import EOS
from opengsq.protocols.gamespy1 import GameSpy1
from opengsq.protocols.gamespy2 import GameSpy2
from opengsq.protocols.gamespy3 import GameSpy3
Expand Down
96 changes: 96 additions & 0 deletions opengsq/protocols/eos.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import aiohttp
import base64
import json

from opengsq.exceptions import ServerNotFoundException
from opengsq.protocol_base import ProtocolBase


class EOS(ProtocolBase):
"""Epic Online Services (EOS) Protocol"""
full_name = 'Epic Online Services (EOS) Protocol'

_api_url = 'https://api.epicgames.dev'

def __init__(self, host: str, port: int, timeout: float = 5, client_id: str = None, client_secret: str = None, deployment_id: str = None):
super().__init__(host, port, timeout)

if client_id is None or client_secret is None or deployment_id is None:
raise ValueError(
"client_id, client_secret, and deployment_id must not be None")

self.client_id = client_id
self.client_secret = client_secret
self.deployment_id = deployment_id
self.access_token = None

async def _get_access_token(self) -> str:
url = f'{self._api_url}/auth/v1/oauth/token'
body = f"grant_type=client_credentials&deployment_id={self.deployment_id}"
headers = {
"Authorization": f"Basic {base64.b64encode(f'{self.client_id}:{self.client_secret}'.encode('utf-8')).decode('utf-8')}",
"Content-Type": "application/x-www-form-urlencoded"
}

async with aiohttp.ClientSession() as session:
async with session.post(url, data=body, headers=headers) as response:
response.raise_for_status()
data = await response.json()

return data["access_token"]

async def _get_matchmaking(self, data: dict):
if self.access_token is None:
self.access_token = await self._get_access_token()
assert self.access_token is not None, "Failed to get access token"

url = f"{self._api_url}/matchmaking/v1/{self.deployment_id}/filter"
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization": f"Bearer {self.access_token}"
}

async with aiohttp.ClientSession() as session:
async with session.post(url, data=json.dumps(data), headers=headers) as response:
response.raise_for_status()
data = await response.json()

return data

async def get_info(self) -> dict:
data = await self._get_matchmaking({
"criteria": [
{
"key": "attributes.ADDRESS_s",
"op": "EQUAL",
"value": self._host
},
{
"key": "attributes.ADDRESSBOUND_s",
"op": "CONTAINS",
"value": self._port
},
]
})

if data["count"] <= 0:
raise ServerNotFoundException()

return data['sessions'][0]


if __name__ == '__main__':
import asyncio

async def main_async():
client_id = 'xyza7891muomRmynIIHaJB9COBKkwj6n'
client_secret = 'PP5UGxysEieNfSrEicaD1N2Bb3TdXuD7xHYcsdUHZ7s'
deployment_id = 'ad9a8feffb3b4b2ca315546f038c3ae2'

eos = EOS(host='150.138.77.118', port=10019, timeout=5.0, client_id=client_id,
client_secret=client_secret, deployment_id=deployment_id)
data = await eos.get_info()
print(json.dumps(data, indent=None) + '\n')

asyncio.run(main_async())
2 changes: 1 addition & 1 deletion opengsq/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '2.1.2'
__version__ = '2.2.0'
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
aiohttp==3.8.6
4 changes: 4 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@
with open('README.md', 'r', encoding='utf-8') as f:
long_description = f.read()

with open('requirements.txt', 'r', encoding='utf-8') as f:
install_requires = f.read().splitlines()

setup(
name='opengsq',
version=version_contents['__version__'],
description='🐍 OpenGSQ - Python library for querying game servers',
long_description=long_description,
long_description_content_type='text/markdown',
install_requires=install_requires,
entry_points={'console_scripts': ['opengsq=opengsq.cli:main']},
packages=find_packages(exclude=['tests', 'tests.*']),
python_requires='>=3.6',
Expand Down
23 changes: 23 additions & 0 deletions tests/protocols/test_eos.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import os

import pytest
from opengsq.protocols.eos import EOS

from .result_handler import ResultHandler

handler = ResultHandler(os.path.basename(__file__)[:-3])
# handler.enable_save = True

# ARK: Survival Ascended
client_id = 'xyza7891muomRmynIIHaJB9COBKkwj6n'
client_secret = 'PP5UGxysEieNfSrEicaD1N2Bb3TdXuD7xHYcsdUHZ7s'
deployment_id = 'ad9a8feffb3b4b2ca315546f038c3ae2'

eos = EOS(host='150.138.77.118', port=10019, client_id=client_id,
client_secret=client_secret, deployment_id=deployment_id)


@pytest.mark.asyncio
async def test_get_info():
result = await eos.get_info()
await handler.save_result('test_get_info', result)
57 changes: 57 additions & 0 deletions tests/results/test_eos/test_get_info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"deployment": "ad9a8feffb3b4b2ca315546f038c3ae2",
"id": "d0ce97bf965043b4a39d72c41d6aa10a",
"bucket": "TestGameMode_C:<None>:TheIsland_WP",
"settings": {
"maxPublicPlayers": 127,
"allowInvites": true,
"shouldAdvertise": true,
"allowReadById": true,
"allowJoinViaPresence": true,
"allowJoinInProgress": true,
"allowConferenceRoom": false,
"checkSanctions": false,
"allowMigration": false,
"rejoinAfterKick": "",
"platforms": null
},
"totalPlayers": 113,
"openPublicPlayers": 14,
"publicPlayers": [],
"started": false,
"lastUpdated": null,
"attributes": {
"MINORBUILDID_s": "11",
"MODID_l": 0,
"CUSTOMSERVERNAME_s": "服主云服务器出租QQ1165782150 低延迟-带假人++",
"ADDRESSDEV_s": "150.138.77.118,127.0.0.1",
"ISPRIVATE_l": 0,
"SERVERPASSWORD_b": false,
"MATCHTIMEOUT_d": 120.0,
"ENABLEDMODSFILEIDS_s": "4850852,4854705,4846256",
"DAYTIME_s": "126",
"SOTFMATCHSTARTED_b": false,
"STEELSHIELDENABLED_l": 0,
"SERVERUSESBATTLEYE_b": false,
"EOSSERVERPING_l": 252,
"ALLOWDOWNLOADCHARS_l": 1,
"OFFICIALSERVER_s": "0",
"GAMEMODE_s": "TestGameMode_C",
"ADDRESS_s": "150.138.77.118",
"SEARCHKEYWORDS_s": "Custom",
"__EOS_BLISTENING_b": true,
"ALLOWDOWNLOADITEMS_l": 1,
"LEGACY_l": 0,
"ADDRESSBOUND_s": "0.0.0.0:10019",
"SESSIONISPVE_l": 0,
"__EOS_BUSESPRESENCE_b": true,
"ENABLEDMODS_s": "931443,928793,900062",
"SESSIONNAMEUPPER_s": "服主云服务器出租QQ1165782150 低延迟-带假人++ - (V26.11)",
"SERVERPLATFORMTYPE_s": "All",
"MAPNAME_s": "TheIsland_WP",
"BUILDID_s": "26",
"SESSIONNAME_s": "服主云服务器出租QQ1165782150 低延迟-带假人++ - (v26.11)"
},
"owner": "Client_xyza7891muomRmynIIHaJB9COBKkwj6n",
"ownerPlatformId": null
}

0 comments on commit f02b4c2

Please sign in to comment.